Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
poseidon2_quad_params.hpp
Go to the documentation of this file.
1// Derived parameters for the K=4 "quad" compressed Poseidon2 internal-round encoding on BN254.
2// Treated like the base Poseidon2 constants: fixed, derivable from the sponge spec, pre-computed.
3//
4// See `barretenberg/cpp/src/barretenberg/stdlib/hash/poseidon2/README.md` for the algebraic
5// derivation. The short version:
6//
7// The compressed K=4 row stores state[0] at 4 consecutive internal rounds. Solving for the
8// non-S-boxed elements (s_1, s_2, s_3) at row-start reduces (via row-reduction) to a 3x3
9// Vandermonde system with nodes (D_2, D_3, D_4). Its Lagrange-basis inverse has 9 fixed
10// coefficients α_j^(k) that let us write s_j = Σ_k α_j^(k) b_k where b_k are linear in wires.
11//
12// This file exposes those 9 coefficients, the derived diagonal constants used by the entry
13// relation, and the closed-form propagation tables consumed by the quad relations.
14//
15// Static assertions guard invertibility: the three Vandermonde differences (D_3 - D_2),
16// (D_4 - D_2), (D_4 - D_3) must all be nonzero.
17
18#pragma once
19
22
23#include <array>
24#include <cstddef>
25
26namespace bb::crypto {
27
31
32 // Internal matrix diagonal D_i (computed from the stored `D_i - 1` values).
37
38 static constexpr FF SIGMA = D2 + D3 + D4; // Σ = D_2 + D_3 + D_4, recurs in the relation algebra
39
40 private:
41 // Vandermonde differences (used below and also asserted non-zero).
42 static constexpr FF D2_minus_D3 = D2 - D3;
43 static constexpr FF D2_minus_D4 = D2 - D4;
44 static constexpr FF D3_minus_D4 = D3 - D4;
45
46 // 1 / ((D_2 - D_3)(D_2 - D_4)) — denominator for α_1^(·)
47 static constexpr FF inv_denom_1 = (D2_minus_D3 * D2_minus_D4).invert();
48 // 1 / ((D_3 - D_2)(D_3 - D_4)) — denominator for α_2^(·)
49 static constexpr FF inv_denom_2 = ((-D2_minus_D3) * D3_minus_D4).invert();
50 // 1 / ((D_4 - D_2)(D_4 - D_3)) — denominator for α_3^(·)
51 static constexpr FF inv_denom_3 = ((-D2_minus_D4) * (-D3_minus_D4)).invert();
52
53 // Invertibility guard. det(V) = (D_3 - D_2)(D_4 - D_2)(D_4 - D_3).
54 static_assert(!D2_minus_D3.is_zero(), "Poseidon2 quad: D_2 == D_3, Vandermonde singular");
55 static_assert(!D2_minus_D4.is_zero(), "Poseidon2 quad: D_2 == D_4, Vandermonde singular");
56 static_assert(!D3_minus_D4.is_zero(), "Poseidon2 quad: D_3 == D_4, Vandermonde singular");
57
58 public:
59 // Lagrange basis coefficients α_j^(k).
60 //
61 // s_j = α_j^(1) * b_1 + α_j^(2) * b_2 + α_j^(3) * b_3
62 //
63 // where b_k is the k-th right-hand side of the row-reduced Vandermonde system. These are
64 // the coefficients of the Lagrange polynomial at node D_{j+1} (taking nodes (D_2, D_3, D_4)):
65 //
66 // L_j(x) = α_j^(1) + α_j^(2) * x + α_j^(3) * x^2
67 // = Π_{k ≠ j} (x - D_{k+1}) / (D_{j+1} - D_{k+1})
68 //
69 // Concretely:
70 // α_1^(1) = D_3 * D_4 / ((D_2 - D_3)(D_2 - D_4))
71 // α_1^(2) = -(D_3 + D_4) / ((D_2 - D_3)(D_2 - D_4))
72 // α_1^(3) = 1 / ((D_2 - D_3)(D_2 - D_4))
73 // (and analogously for α_2^(k), α_3^(k))
74 // α_j^(1): constant term of L_j.
75 static constexpr FF alpha_1_1 = D3 * D4 * inv_denom_1;
76 static constexpr FF alpha_2_1 = D2 * D4 * inv_denom_2;
77 static constexpr FF alpha_3_1 = D2 * D3 * inv_denom_3;
78
79 // α_j^(2): linear term (negated sum of other nodes, divided by the denominator)
80 static constexpr FF alpha_1_2 = -(D3 + D4) * inv_denom_1;
81 static constexpr FF alpha_2_2 = -(D2 + D4) * inv_denom_2;
82 static constexpr FF alpha_3_2 = -(D2 + D3) * inv_denom_3;
83
84 // α_j^(3): quadratic term (pure reciprocal of the denominator)
85 static constexpr FF alpha_1_3 = inv_denom_1;
86 static constexpr FF alpha_2_3 = inv_denom_2;
87 static constexpr FF alpha_3_3 = inv_denom_3;
88
89 // Closed-form 4-round propagation coefficients.
90 //
91 // The four-round internal-block update on the non-S-boxed lanes (s_1, s_2, s_3) is linear
92 // once the four S-boxed scalars u_k = (w_k + c_k)^5 are taken as opaque inputs:
93 //
94 // step(v, u) = A v + u · 1, A = [[D_2,1,1],[1,D_3,1],[1,1,D_4]]
95 //
96 // After 4 rounds with inputs u_0..u_3, the state-at-round-4 components (out_1, out_2, out_3)
97 // and the state-at-round-3 row-sum T_3 (used by out_0 = D_1 u_3 + T_3) are all fixed linear
98 // combinations of (w_r, w_o, w_4, u_0, u_1, u_2, u_3), where the (w_r, w_o, w_4)-dependence
99 // enters through s^{(0)} = V^{-1} b and b_k = linear(w_*, u_0..u_2). Composing A^4 V^{-1}
100 // with the b_k formulas gives the 28 constants below, one per (output, input) cell.
101 //
102 // Equivalence to the step iteration is verified in a unit test (see `poseidon2_quad_closed_form.test.cpp`).
103 //
104 // Linear round-propagation vectors (A^k · 1)_j for k = 1, 2.
105 //
106 // Used by both the entry relation (which checks state[0] at rounds 1, 2 from a standard
107 // encoded predecessor) and the closed-form table builder below. These simple scalar formulas
108 // remain constexpr.
109 //
110 // A_one[j] = (A · 1)_j = D_{j+1} + 2
111 // A2_one[j] = (A^2 · 1)_j = D_{j+1}^2 + D_{j+1} + Σ + 4
112 // sum_A_one = 1^T A · 1 = Σ + 6 (also = (A · 1) summed over rows)
113 static constexpr std::array<FF, VANDERMONDE_SIZE> A_one = { D2 + FF(2), D3 + FF(2), D4 + FF(2) };
115 D2 * D2 + D2 + SIGMA + FF(4),
116 D3* D3 + D3 + SIGMA + FF(4),
117 D4* D4 + D4 + SIGMA + FF(4),
118 };
119 static constexpr FF sum_A_one = SIGMA + FF(6);
120
121 // Closed-form coefficient table layout. Each row gives coefficients for the inputs
122 // (w_r, w_o, w_4, u_0, u_1, u_2, u_3),
123 // where u_k = (s_0^{(k)} + c_k)^5.
124 //
125 // closed_form[j] for j in {0,1,2,3}: coefficients of out_j, i.e. state[j] after four
126 // internal rounds. The terminal relation consumes all
127 // four rows; the interior relation consumes row 0.
128 //
129 // forward_vandermonde_lhs[k] for k in {0,1,2}: coefficients of the forward-Vandermonde
130 // combinations used by the interior relation:
131 // row 0 = out_1 + out_2 + out_3
132 // row 1 = D_2 out_1 + D_3 out_2 + D_4 out_3
133 // row 2 = D_2^2 out_1 + D_3^2 out_2 + D_4^2 out_3
150 static_assert(CLOSED_FORM_INPUT_COUNT == U_3 + 1);
152 using ClosedFormTable = std::array<ClosedFormRow, 4>;
153 using ForwardVandermondeTable = std::array<ClosedFormRow, VANDERMONDE_SIZE>;
154
155 private:
156 // Derive the coefficient tables once from the fixed Poseidon2 parameters. The relation code
157 // reads only the resulting `closed_form` and `forward_vandermonde_lhs` tables.
162
165
166 static constexpr Mat matrix_multiply(const Mat& a, const Mat& b)
167 {
168 Mat r{};
169 for (size_t i = 0; i < VANDERMONDE_SIZE; ++i) {
170 for (size_t j = 0; j < VANDERMONDE_SIZE; ++j) {
171 FF s = FF(0);
172 for (size_t k = 0; k < VANDERMONDE_SIZE; ++k) {
173 s += a[i][k] * b[k][j];
174 }
175 r[i][j] = s;
176 }
177 }
178 return r;
179 }
180
181 static constexpr Vec matrix_vector_multiply(const Mat& a, const Vec& v)
182 {
183 Vec r{};
184 for (size_t i = 0; i < VANDERMONDE_SIZE; ++i) {
185 FF s = FF(0);
186 for (size_t k = 0; k < VANDERMONDE_SIZE; ++k) {
187 s += a[i][k] * v[k];
188 }
189 r[i] = s;
190 }
191 return r;
192 }
193
194 static constexpr Vec vector_matrix_multiply(const Vec& v, const Mat& a)
195 {
196 Vec r{};
197 for (size_t j = 0; j < VANDERMONDE_SIZE; ++j) {
198 FF s = FF(0);
199 for (size_t k = 0; k < VANDERMONDE_SIZE; ++k) {
200 s += v[k] * a[k][j];
201 }
202 r[j] = s;
203 }
204 return r;
205 }
206
207 static constexpr FF vector_sum(const Vec& v)
208 {
209 FF result = FF(0);
210 for (const auto& entry : v) {
211 result += entry;
212 }
213 return result;
214 }
215
216 static constexpr ClosedFormRow weighted_closed_form_sum(const Vec& weights, const ClosedFormTable& table)
217 {
218 ClosedFormRow r{};
219 for (size_t i = 0; i < CLOSED_FORM_INPUT_COUNT; ++i) {
220 r[i] = weights[0] * table[OUT_1][i] + weights[1] * table[OUT_2][i] + weights[2] * table[OUT_3][i];
221 }
222 return r;
223 }
224
226 {
227 const Vec ones = { FF(1), FF(1), FF(1) };
228 // A: internal-round update on (s_1, s_2, s_3). step(v, u) = A v + u·1.
229 const Mat A = { { { D2, FF(1), FF(1) }, { FF(1), D3, FF(1) }, { FF(1), FF(1), D4 } } };
230 const Mat A2 = matrix_multiply(A, A);
231 const Mat A3 = matrix_multiply(A2, A);
232 const Mat A4 = matrix_multiply(A3, A);
233 const Vec A_one = matrix_vector_multiply(A, ones);
234 const Vec A2_one = matrix_vector_multiply(A2, ones);
235 const Vec A3_one = matrix_vector_multiply(A3, ones);
236
237 // V_inv (rows are Lagrange coefs α_j^(*)).
238 const Mat Vinv = { { { alpha_1_1, alpha_1_2, alpha_1_3 },
240 { alpha_3_1, alpha_3_2, alpha_3_3 } } };
241 // M = A^4 · V_inv: maps b → b-derived part of out_{1,2,3} at round 4.
242 const Mat M = matrix_multiply(A4, Vinv);
243
244 // B_w: rows are w-coefs of b_1, b_2, b_3 on (w_r, w_o, w_4).
245 const Mat Bw = { { { FF(1), FF(0), FF(0) }, { -FF(2), FF(1), FF(0) }, { -(SIGMA + FF(2)), -FF(1), FF(1) } } };
246 // B_u: rows are (u_0, u_1, u_2)-coefs of b_1, b_2, b_3.
247 const Mat Bu = { { { -D1, FF(0), FF(0) },
248 { FF(2) * D1 - FF(3), -D1, FF(0) },
249 { (SIGMA + FF(2)) * D1 - SIGMA - FF(3), D1 - FF(3), -D1 } } };
250
251 const Mat Mw = matrix_multiply(M, Bw); // w-coefs of out_{1,2,3}
252 const Mat MBu = matrix_multiply(M, Bu); // b-derived part of u-coefs
253
254 // T_3 = sum of state[1..3] at round 3.
255 // q_T3 = (1^T A^3) · V_inv: projection coefficients for the b-derived part of T_3.
256 const Vec col_sum_A3 = vector_matrix_multiply(ones, A3);
257 const Vec q_T3 = vector_matrix_multiply(col_sum_A3, Vinv);
258 const FF sum_A_one = vector_sum(A_one);
259 const FF sum_A2_one = vector_sum(A2_one);
260
261 const Vec row0_wire_coefficients = vector_matrix_multiply(q_T3, Bw);
262 const Vec row0_u_coefficients = vector_matrix_multiply(q_T3, Bu);
263
264 // out_0 = D_1 u_3 + T_3.
265 // T_3's wire-coefs: q_T3 · B_w (1×3 · 3×3 → 1×3)
266 // T_3's u-coefs: q_T3 · B_u + (sum_A2_one, sum_A_one, 3) inhomogeneous additions
267 ClosedFormRow row0{};
268 for (size_t i = 0; i < VANDERMONDE_SIZE; ++i) {
269 row0[i] = row0_wire_coefficients[i];
270 }
271 row0[U_0] = row0_u_coefficients[0] + sum_A2_one;
272 row0[U_1] = row0_u_coefficients[1] + sum_A_one;
273 row0[U_2] = row0_u_coefficients[2] + FF(3);
274 row0[U_3] = D1;
275
276 // out_j (j=1,2,3): u_3 coefficient is identically 1 (free add at use site).
277 auto build_out_j = [&](size_t j) {
278 ClosedFormRow r{};
279 r[W_R] = Mw[j][0];
280 r[W_O] = Mw[j][1];
281 r[W_4] = Mw[j][2];
282 r[U_0] = MBu[j][0] + A3_one[j];
283 r[U_1] = MBu[j][1] + A2_one[j];
284 r[U_2] = MBu[j][2] + A_one[j];
285 r[U_3] = FF(1);
286 return r;
287 };
288
289 ClosedFormTable closed_form_table{ row0, build_out_j(0), build_out_j(1), build_out_j(2) };
290
291 // Forward-Vandermonde LHS rows: linear combinations across out_{1,2,3} weighted by
292 // row 0: (1, 1, 1) → out_1 + out_2 + out_3
293 // row 1: (D_2, D_3, D_4) → D_2 out_1 + D_3 out_2 + D_4 out_3
294 // row 2: (D_2², D_3², D_4²) → D_2² out_1 + D_3² out_2 + D_4² out_3
295 // Each row's coefficients on (w_*, u_*) are obtained by the same weighted sum applied
296 // to the corresponding (w_*, u_*) coefficients of out_1..out_3.
297 const std::array<Vec, VANDERMONDE_SIZE> lhs_weights = {
298 { { FF(1), FF(1), FF(1) }, { D2, D3, D4 }, { D2 * D2, D3 * D3, D4 * D4 } }
299 };
300 ForwardVandermondeTable lhs_table{};
301 for (size_t k = 0; k < VANDERMONDE_SIZE; ++k) {
302 lhs_table[k] = weighted_closed_form_sum(lhs_weights[k], closed_form_table);
303 }
304
305 return Tables{ closed_form_table, lhs_table };
306 }
307
308 public:
309 // Public coefficient tables consumed by the relations.
310 static inline const Tables tables = build_tables();
311};
312
313} // namespace bb::crypto
FF a
FF b
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr std::array< FF, t > internal_matrix_diagonal_minus_one
std::array< std::array< FF, VANDERMONDE_SIZE >, VANDERMONDE_SIZE > Mat
static constexpr Mat matrix_multiply(const Mat &a, const Mat &b)
std::array< FF, CLOSED_FORM_INPUT_COUNT > ClosedFormRow
static constexpr std::array< FF, VANDERMONDE_SIZE > A_one
static constexpr FF vector_sum(const Vec &v)
static constexpr ClosedFormRow weighted_closed_form_sum(const Vec &weights, const ClosedFormTable &table)
static constexpr Vec vector_matrix_multiply(const Vec &v, const Mat &a)
std::array< FF, VANDERMONDE_SIZE > Vec
static constexpr std::array< FF, VANDERMONDE_SIZE > A2_one
static constexpr Vec matrix_vector_multiply(const Mat &a, const Vec &v)
Poseidon2Bn254ScalarFieldParams::FF FF
std::array< ClosedFormRow, 4 > ClosedFormTable
std::array< ClosedFormRow, VANDERMONDE_SIZE > ForwardVandermondeTable
constexpr field invert() const noexcept
BB_INLINE constexpr bool is_zero() const noexcept