# coding task __Implement a circuit for poseidon hash using a vanilla plonk gate.__ The plonk is configured as follows. It uses a custom gate $$q_a·a + q_b·b + q_c·c + q_{ab}·a·b + constant + instance = 0$$ and use the following struct to store the witnesses and selectors. ``` rust #[derive(Clone, Copy)] pub struct StandardPlonkConfig { a: Column<Advice>, b: Column<Advice>, c: Column<Advice>, q_a: Column<Fixed>, q_b: Column<Fixed>, q_c: Column<Fixed>, q_ab: Column<Fixed>, constant: Column<Fixed>, #[allow(dead_code)] instance: Column<Instance>, } ``` Then we enforce the custom gates over the witnesses and selectors via the following configuration. ``` rust impl<F:Prim> StandardPlonkConfig<F> { fn configure(meta: &mut ConstraintSystem<F>) -> Self { let [a, b, c] = [(); 3].map(|_| meta.advice_column()); let [q_a, q_b, q_c, q_ab, constant] = [(); 5].map(|_| meta.fixed_column()); let instance = meta.instance_column(); [a, b, c].map(|column| meta.enable_equality(column)); meta.create_gate( "q_a·a + q_b·b + q_c·c + q_ab·a·b + constant + instance = 0", |meta| { let [a, b, c] = [a, b, c].map(|column| meta.query_advice(column, Rotation::cur())); let [q_a, q_b, q_c, q_ab, constant] = [q_a, q_b, q_c, q_ab, constant] .map(|column| meta.query_fixed(column, Rotation::cur())); let instance = meta.query_instance(instance, Rotation::cur()); Some( q_a * a.clone() + q_b * b.clone() + q_c * c + q_ab * a * b + constant + instance, ) }, ); StandardPlonkConfig { a, b, c, q_a, q_b, q_c, q_ab, constant, instance } } } ``` The task is to design a circuit for the following subroutine of a 4-to-1 poseidon hash function. ``` rust struct PoseidonState<F: PrimeField> { state: [F; 4], index: usize } ``` You may assume that the state is already initialized. It will go through a `MAX_ITER` number of iterations. `index` is used to track which iteration it is current at. Each iterations is parameterized with an iteration key `k_i: [F; 4]`. The hash function also takes a global parameter M which is a 4-by-4 matrix, i.e. `M: [[F; 4];4]`. Suppose the current state is $a_{i,1}, a_{i,2}, a_{i,3}, a_{i,4}$, then the next state is defined as \begin{align*} \left<a_{i+1,1}, a_{i+1,2}, a_{i+1,3}, a_{i+1,4}\right>&:=\left<a_{i,1}^5, a_{i,2}, a_{i,3}, a_{i,4}\right>\times\begin{pmatrix} M_{1,1}, M_{1,2}, M_{1,3}, M_{1,4}\\M_{2,1}, M_{2,2}, M_{2,3}, M_{2,4}\\M_{3,1}, M_{3,2}, M_{3,3}, M_{3,4}\\M_{4,1}, M_{4,2}, M_{4,3}, M_{4,4} \end{pmatrix} \\ &+ \left<k_{i,1}, k_{i,2}, k_{i,3}, k_{i,4}\right> \end{align*} # Skelton of the code ``` rust /// Assume we do a maximum of 5 iterations const MAX_ITER: usize = 5; /// Assume the round key is given as an array of size MAX_ITER; /// each element of the array is the round key for the corresponding iteration /// You DO NOT need to complete this part. const ROUND_KEY: [[F;4];MAX_ITER] = ... /// Assume the matrix is given as a 4-by-4 2-dimentional array; /// You DO NOT need to complete this part. const MDS_MATRIX: [[F;4];4] = ... /// The state that holds the exsitng #[derive(Clone, Default)] struct PoseidonState([F; 4]); impl Circuit<Fr> for PoseidonState { type Config = StandardPlonkConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { Self::default() } fn configure(meta: &mut ConstraintSystem<Fr>) -> Self::Config { meta.set_minimum_degree(4); StandardPlonkConfig::configure(meta) } fn synthesize( &self, config: Self::Config, mut layouter: impl Layouter<Fr>, ) -> Result<(), Error> { layouter.assign_region( || "", |mut region| { // // BEGIN of SAMPLE code // // In this sample code we constraint that // // -1 * State[0] + 5 * State[1] + State[2] = 0 // region.assign_advice(|| "", config.a, 0, || Value::known(self.0[0]))?; region.assign_fixed(|| "", config.q_a, 0, || Value::known(-F::one()))?; region.assign_advice(|| "", config.b, 0, || Value::known(self.0[1]))?; region.assign_fixed(|| "", config.q_b, 0, || Value::known(F::from(5)))?; region.assign_advice(|| "", config.c, 0, || Value::known(self.0[2]))?; region.assign_fixed(|| "", config.q_c, 0, || Value::known(F::one()))?; // q_a·a + q_b·b + q_c·c + q_{ab}·a·b + constant + instance = 0 /* multiply x and y region.assign_advice(|| "", config.a, 0, || Value::known(x))?; region.assign_advice(|| "", config.b, 0, || Value::known(y))?; region.assign_fixed(|| "", config.q_ab, 0, || Value::known(F::one()))?; */ // start of code region.assign_advice(|| "", config.a, 1, || Value::known(ai,1))?; region.assign_advice(|| "", config.b, 1, || Value::known(ai,1))?; region.assign_fixed(|| "", config.q_ab, 1, || Value::known(F::one()))?; region.assign_advice(|| "", config.c, 1, || Value::known(ai2,1))?; region.assign_fixed(|| "", config.q_ab, 1, || Value::known(-F::one()))?; // ai4 region.assign_advice(|| "", config.a, 2, || Value::known(ai2,1))?; region.assign_advice(|| "", config.b, 2, || Value::known(ai2,1))?; region.assign_fixed(|| "", config.q_ab, 2, || Value::known(F::one()))?; region.assign_advice(|| "", config.c, 2, || Value::known(ai4,1))?; region.assign_fixed(|| "", config.q_ab, 2, || Value::known(-F::one()))?; // ai5 region.assign_advice(|| "", config.a, 3, || Value::known(ai4,1))?; region.assign_advice(|| "", config.b, 3, || Value::known(ai,1))?; region.assign_fixed(|| "", config.q_ab, 3, || Value::known(F::one()))?; region.assign_advice(|| "", config.c, 3, || Value::known(ai5,1))?; region.assign_fixed(|| "", config.q_ab, 3, || Value::known(-F::one()))?; // get a single multiplication with M coefficient region.assign_advice(|| "", config.a, 4, || Value::known(ai5,1))?; region.assign_fixed(|| "", config.q_a, 4, || Value::known(M1,1))?; region.assign_advice(|| "", config.b, 4, || Value::known(ai,2))?; region.assign_fixed(|| "", config.q_b, 4, || Value::known(M2,1))?; region.assign_advice(|| "", config.c, 4, || Value::known(a,1))?; region.assign_fixed(|| "", config.q_c, 4, || Value::known(-F::one()))?; // q_a·a + q_b·b + q_c·c + q_{ab}·a·b + constant + instance = 0 // q_dot * (q_m1 * a1 * a1 * a1 * a1 * a1 + q_m2 * a2 + q_m3 * a3 + q_m4 * a4 + q_k) // // END of SAMPLE code // // You may comment out the sample code and write your code here Ok(()) }, ) } } ```