openvm_poseidon2_air/
permute.rs

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
use std::{any::TypeId, marker::PhantomData};

use derivative::Derivative;
use openvm_stark_backend::p3_field::AbstractField;
use openvm_stark_sdk::p3_baby_bear::{BabyBear, BabyBearInternalLayerParameters};
use p3_monty_31::InternalLayerBaseParameters;
use p3_poseidon2::{
    add_rc_and_sbox_generic, mds_light_permutation, ExternalLayer, ExternalLayerConstants,
    ExternalLayerConstructor, GenericPoseidon2LinearLayers, InternalLayer,
    InternalLayerConstructor, MDSMat4,
};

use super::{babybear_internal_linear_layer, BABY_BEAR_POSEIDON2_SBOX_DEGREE};

const WIDTH: usize = crate::POSEIDON2_WIDTH;

pub trait Poseidon2MatrixConfig: Clone + Sync {
    fn int_diag_m1_matrix<F: AbstractField>() -> [F; WIDTH];
}

/// This type needs to implement GenericPoseidon2LinearLayers generic in F so that our Poseidon2SubAir can also
/// be generic in F, but in reality each implementation of this struct's functions should be field specific. To
/// circumvent this, Poseidon2LinearLayers is generic in F but **currently requires** that F is BabyBear.
#[derive(Debug, Clone)]
pub struct BabyBearPoseidon2LinearLayers;

// This is the same as the implementation for GenericPoseidon2LinearLayersMonty31<BabyBearParameters, BabyBearInternalLayerParameters> except that we drop the
// clause that FA needs be multipliable by BabyBear.
// TODO[jpw/stephen]: This is clearly not the best way to do this, but it would
// require some reworking in plonky3 to get around the generics.
impl<FA: AbstractField> GenericPoseidon2LinearLayers<FA, WIDTH> for BabyBearPoseidon2LinearLayers {
    fn internal_linear_layer(state: &mut [FA; WIDTH]) {
        let diag_m1_matrix = &<BabyBearInternalLayerParameters as InternalLayerBaseParameters<
            _,
            16,
        >>::INTERNAL_DIAG_MONTY;
        assert_eq!(
            TypeId::of::<FA::F>(),
            TypeId::of::<BabyBear>(),
            "BabyBear is the only supported field type"
        );
        let diag_m1_matrix =
            unsafe { std::mem::transmute::<&[BabyBear; WIDTH], &[FA::F; WIDTH]>(diag_m1_matrix) };
        babybear_internal_linear_layer(state, diag_m1_matrix);
    }

    fn external_linear_layer(state: &mut [FA; WIDTH]) {
        mds_light_permutation(state, &MDSMat4);
    }
}

// Below are generic implementations of the Poseidon2 Internal and External Layers
// generic in the field. These are currently used for the runtime poseidon2
// execution even though they are less optimized than the Monty31 specific
// implementations in Plonky3. We could use those more optimized implementations,
// but it would require many unsafe transmutes.

#[derive(Debug, Derivative)]
#[derivative(Clone)]
pub struct Poseidon2InternalLayer<F: AbstractField, LinearLayers> {
    pub internal_constants: Vec<F>,
    _marker: PhantomData<LinearLayers>,
}

impl<AF: AbstractField, LinearLayers> InternalLayerConstructor<AF>
    for Poseidon2InternalLayer<AF::F, LinearLayers>
{
    fn new_from_constants(internal_constants: Vec<AF::F>) -> Self {
        Self {
            internal_constants,
            _marker: PhantomData,
        }
    }
}

impl<FA: AbstractField, LinearLayers, const WIDTH: usize>
    InternalLayer<FA, WIDTH, BABY_BEAR_POSEIDON2_SBOX_DEGREE>
    for Poseidon2InternalLayer<FA::F, LinearLayers>
where
    LinearLayers: GenericPoseidon2LinearLayers<FA, WIDTH>,
{
    /// Perform the internal layers of the Poseidon2 permutation on the given state.
    fn permute_state(&self, state: &mut [FA; WIDTH]) {
        self.internal_constants.iter().for_each(|&rc| {
            add_rc_and_sbox_generic::<_, BABY_BEAR_POSEIDON2_SBOX_DEGREE>(&mut state[0], rc);
            LinearLayers::internal_linear_layer(state);
        })
    }
}

#[derive(Debug, Derivative)]
#[derivative(Clone)]
pub struct Poseidon2ExternalLayer<F: AbstractField, LinearLayers, const WIDTH: usize> {
    pub constants: ExternalLayerConstants<F, WIDTH>,
    _marker: PhantomData<LinearLayers>,
}

impl<FA: AbstractField, LinearLayers, const WIDTH: usize> ExternalLayerConstructor<FA, WIDTH>
    for Poseidon2ExternalLayer<FA::F, LinearLayers, WIDTH>
{
    fn new_from_constants(external_layer_constants: ExternalLayerConstants<FA::F, WIDTH>) -> Self {
        Self {
            constants: external_layer_constants,
            _marker: PhantomData,
        }
    }
}

impl<FA: AbstractField, LinearLayers, const WIDTH: usize>
    ExternalLayer<FA, WIDTH, BABY_BEAR_POSEIDON2_SBOX_DEGREE>
    for Poseidon2ExternalLayer<FA::F, LinearLayers, WIDTH>
where
    LinearLayers: GenericPoseidon2LinearLayers<FA, WIDTH>,
{
    fn permute_state_initial(&self, state: &mut [FA; WIDTH]) {
        LinearLayers::external_linear_layer(state);
        external_permute_state::<FA, LinearLayers, WIDTH>(
            state,
            self.constants.get_initial_constants(),
        );
    }

    fn permute_state_terminal(&self, state: &mut [FA; WIDTH]) {
        external_permute_state::<FA, LinearLayers, WIDTH>(
            state,
            self.constants.get_terminal_constants(),
        );
    }
}

fn external_permute_state<FA: AbstractField, LinearLayers, const WIDTH: usize>(
    state: &mut [FA; WIDTH],
    constants: &[[FA::F; WIDTH]],
) where
    LinearLayers: GenericPoseidon2LinearLayers<FA, WIDTH>,
{
    for elem in constants.iter() {
        state.iter_mut().zip(elem.iter()).for_each(|(s, &rc)| {
            add_rc_and_sbox_generic::<_, BABY_BEAR_POSEIDON2_SBOX_DEGREE>(s, rc)
        });
        LinearLayers::external_linear_layer(state);
    }
}