Skip to main content

bench_goldilocks_poseidon2/
poseidon2.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2// Copyright (C) Rostro Foundation
3
4//! Poseidon2-Goldilocks WIDTH=8 permutation, bit-exact with Plonky3's
5//! `default_goldilocks_poseidon2_8`.
6//!
7//! Round structure:
8//!   - 1 initial MDS-light permutation
9//!   - 4 external-initial rounds: add RC, S-box (x^7), MDS-light
10//!   - 22 internal rounds: add RC[r] to state[0], S-box state[0], internal-MDS
11//!   - 4 external-final rounds: add RC, S-box (x^7), MDS-light
12//!
13//! Round constants (`GOLDILOCKS_POSEIDON2_RC_8_*`) and the internal
14//! diagonal matrix (`MATRIX_DIAG_8_GOLDILOCKS`) come verbatim from
15//! `p3_goldilocks::poseidon2`.
16
17use crate::field::{add, double, mul, square};
18
19pub const WIDTH: usize = 8;
20
21// Round constants — copied verbatim from
22// `p3_goldilocks::GOLDILOCKS_POSEIDON2_RC_8_EXTERNAL_INITIAL` etc.
23
24const RC_INITIAL: [[u64; WIDTH]; 4] = [
25    [
26        0xdd5743e7f2a5a5d9,
27        0xcb3a864e58ada44b,
28        0xffa2449ed32f8cdc,
29        0x42025f65d6bd13ee,
30        0x7889175e25506323,
31        0x34b98bb03d24b737,
32        0xbdcc535ecc4faa2a,
33        0x5b20ad869fc0d033,
34    ],
35    [
36        0xf1dda5b9259dfcb4,
37        0x27515210be112d59,
38        0x4227d1718c766c3f,
39        0x26d333161a5bd794,
40        0x49b938957bf4b026,
41        0x4a56b5938b213669,
42        0x1120426b48c8353d,
43        0x6b323c3f10a56cad,
44    ],
45    [
46        0xce57d6245ddca6b2,
47        0xb1fc8d402bba1eb1,
48        0xb5c5096ca959bd04,
49        0x6db55cd306d31f7f,
50        0xc49d293a81cb9641,
51        0x1ce55a4fe979719f,
52        0xa92e60a9d178a4d1,
53        0x002cc64973bcfd8c,
54    ],
55    [
56        0xcea721cce82fb11b,
57        0xe5b55eb8098ece81,
58        0x4e30525c6f1ddd66,
59        0x43c6702827070987,
60        0xaca68430a7b5762a,
61        0x3674238634df9c93,
62        0x88cee1c825e33433,
63        0xde99ae8d74b57176,
64    ],
65];
66
67const RC_FINAL: [[u64; WIDTH]; 4] = [
68    [
69        0x014ef1197d341346,
70        0x9725e20825d07394,
71        0xfdb25aef2c5bae3b,
72        0xbe5402dc598c971e,
73        0x93a5711f04cdca3d,
74        0xc45a9a5b2f8fb97b,
75        0xfe8946a924933545,
76        0x2af997a27369091c,
77    ],
78    [
79        0xaa62c88e0b294011,
80        0x058eb9d810ce9f74,
81        0xb3cb23eced349ae4,
82        0xa3648177a77b4a84,
83        0x43153d905992d95d,
84        0xf4e2a97cda44aa4b,
85        0x5baa2702b908682f,
86        0x082923bdf4f750d1,
87    ],
88    [
89        0x98ae09a325893803,
90        0xf8a6475077968838,
91        0xceb0735bf00b2c5f,
92        0x0a1a5d953888e072,
93        0x2fcb190489f94475,
94        0xb5be06270dec69fc,
95        0x739cb934b09acf8b,
96        0x537750b75ec7f25b,
97    ],
98    [
99        0xe9dd318bae1f3961,
100        0xf7462137299efe1a,
101        0xb1f6b8eee9adb940,
102        0xbdebcc8a809dfe6b,
103        0x40fc1f791b178113,
104        0x3ac1c3362d014864,
105        0x9a016184bdb8aeba,
106        0x95f2394459fbc25e,
107    ],
108];
109
110const RC_INTERNAL: [u64; 22] = [
111    0x488897d85ff51f56,
112    0x1140737ccb162218,
113    0xa7eeb9215866ed35,
114    0x9bd2976fee49fcc9,
115    0xc0c8f0de580a3fcc,
116    0x4fb2dae6ee8fc793,
117    0x343a89f35f37395b,
118    0x223b525a77ca72c8,
119    0x56ccb62574aaa918,
120    0xc4d507d8027af9ed,
121    0xa080673cf0b7e95c,
122    0xf0184884eb70dcf8,
123    0x044f10b0cb3d5c69,
124    0xe9e3f7993938f186,
125    0x1b761c80e772f459,
126    0x606cec607a1b5fac,
127    0x14a0c2e1d45f03cd,
128    0x4eace8855398574f,
129    0xf905ca7103eff3e6,
130    0xf8c8f8d20862c059,
131    0xb524fe8bdd678e5a,
132    0xfbb7865901a1ec41,
133];
134
135/// MATRIX_DIAG_8_GOLDILOCKS from p3-goldilocks. Diagonal of the internal
136/// linear-layer matrix (which is `1 + diag(MATRIX_DIAG_8_GOLDILOCKS)`).
137const MATRIX_DIAG: [u64; WIDTH] = [
138    0xfffffffeffffffff, // -2
139    0x0000000000000001, // 1
140    0x0000000000000002, // 2
141    0x7fffffff80000001, // 1/2
142    0x0000000000000003, // 3
143    0x7fffffff80000000, // -1/2
144    0xfffffffefffffffe, // -3
145    0xfffffffefffffffd, // -4
146];
147
148/// `x^7` S-box, decomposed into squares + multiplies for low constraint
149/// degree (matches the AIR layout in `rostro-poseidon-air`).
150#[inline(always)]
151fn sbox(x: u64) -> u64 {
152    let x2 = square(x);
153    let x4 = square(x2);
154    let x6 = mul(x4, x2);
155    mul(x6, x)
156}
157
158/// 4×4 MDS matrix multiply (Plonky3's `apply_mat4`):
159///   [ 2 3 1 1 ]
160///   [ 1 2 3 1 ]
161///   [ 1 1 2 3 ]
162///   [ 3 1 1 2 ]
163#[inline(always)]
164fn apply_mat4(x: &mut [u64; 4]) {
165    let t01 = add(x[0], x[1]);
166    let t23 = add(x[2], x[3]);
167    let t0123 = add(t01, t23);
168    let t01123 = add(t0123, x[1]);
169    let t01233 = add(t0123, x[3]);
170    // Order matters — write x[3] and x[1] before x[0] and x[2].
171    let new_x3 = add(t01233, double(x[0])); // 3*x[0] + x[1] + x[2] + 2*x[3]
172    let new_x1 = add(t01123, double(x[2])); // x[0] + 2*x[1] + 3*x[2] + x[3]
173    x[0] = add(t01123, t01); // 2*x[0] + 3*x[1] + x[2] + x[3]
174    x[2] = add(t01233, t23); // x[0] + x[1] + 2*x[2] + 3*x[3]
175    x[1] = new_x1;
176    x[3] = new_x3;
177}
178
179/// MDS-light permutation for WIDTH=8: apply mat4 to each 4-block, then
180/// for each i add the sum of state[i] and state[i+4].
181#[inline(always)]
182fn mds_light(state: &mut [u64; WIDTH]) {
183    let (head, tail) = state.split_at_mut(4);
184    let h: &mut [u64; 4] = head.try_into().unwrap();
185    let t: &mut [u64; 4] = tail.try_into().unwrap();
186    apply_mat4(h);
187    apply_mat4(t);
188    let sums: [u64; 4] = [
189        add(state[0], state[4]),
190        add(state[1], state[5]),
191        add(state[2], state[6]),
192        add(state[3], state[7]),
193    ];
194    let mut i = 0;
195    while i < WIDTH {
196        state[i] = add(state[i], sums[i % 4]);
197        i += 1;
198    }
199}
200
201/// One external round: add RC, S-box every cell, MDS-light.
202#[inline(always)]
203fn external_round(state: &mut [u64; WIDTH], rc: &[u64; WIDTH]) {
204    let mut i = 0;
205    while i < WIDTH {
206        state[i] = sbox(add(state[i], rc[i]));
207        i += 1;
208    }
209    mds_light(state);
210}
211
212/// One internal round: add RC[r] to state[0] only, S-box state[0] only,
213/// then internal MDS (`(1 + diag(MATRIX_DIAG)) * state`).
214#[inline(always)]
215fn internal_round(state: &mut [u64; WIDTH], rc: u64) {
216    state[0] = sbox(add(state[0], rc));
217    // matmul_internal: sum + state[i] * diag[i]
218    let mut sum = state[0];
219    let mut i = 1;
220    while i < WIDTH {
221        sum = add(sum, state[i]);
222        i += 1;
223    }
224    i = 0;
225    while i < WIDTH {
226        state[i] = add(mul(state[i], MATRIX_DIAG[i]), sum);
227        i += 1;
228    }
229}
230
231/// Full Poseidon2-Goldilocks-WIDTH8 permutation in place.
232pub fn permute(state: &mut [u64; WIDTH]) {
233    mds_light(state);
234    let mut r = 0;
235    while r < 4 {
236        external_round(state, &RC_INITIAL[r]);
237        r += 1;
238    }
239    let mut r = 0;
240    while r < 22 {
241        internal_round(state, RC_INTERNAL[r]);
242        r += 1;
243    }
244    let mut r = 0;
245    while r < 4 {
246        external_round(state, &RC_FINAL[r]);
247        r += 1;
248    }
249}