---
# System prepended metadata

title: coding task

---

# 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(())
            },
        )
    }
}

```