# Efficient Pedersen Hash Construction in PLONK Author: Shumo Chu and Tom Shen (Thanks comments from Luke Pearson and Zhenfei Zhang) Despite looks "simple", construct Pedersen Hash naively can result in a lot of constraint count. Now, we show how can we optimize the number of constraints of Pedersen Hash both in R1CS and using PLONK gates. ## Pedersen Hash Let $M$ be a bit sequence.The *Pedersen hash* function of $M$ on a group formed by a set ellitpic curve points $\mathbb{G}$ can be defined as follows: * Let $P_0, P_1, \ldots, P_l$ be $l+1$ **indedependently** and **randombly** sampled generators from $\mathbb{G}$ * Split $M$ into $200$ bits (assuming $|\mathbb{G}|\approx2^{256}$) blocks ($M_0, M_1, \ldots M_l$) and each block into chunks of $4$ bits ($m_0, m_1, \ldots m_k$): $$M = M_0 || M_1 || \ldots || M_l$$ where: $$M_i = m_0 || m_1 || \ldots || m_{49}$$ Each $m_j$ term is a chuck of $4$ bits $[b_0\;b_1\;b_2\;b_3]$. We define: $$enc(m_j) = (2 b_3 - 1 ) \cdot (1 + b_0 + 2b_1 + 4b_2)$$ and let: $$\langle M \rangle = \sum_{j=0}^{49}enc(m_j)\cdot 2^{5j} $$ Now we can define Pedersen hash of $M$ as: $$ H(M) = \sum_{i=0}^{l} \langle M_i \rangle \cdot P_i$$ Note that the expression above is a linear combination of elements of $\mathbb{G}$, thus itself is also an element of $\mathbb{G}$, i.e. a point in the elliptic curve. ## Efficient Construction of Pedersen Hash in Constraint Systems ### Baseline: Construct Pedersen Hash using Lookup Tables How can we construct Pedersen Hash using less number of constraints? A basic technique is for each 4 bit chunk, we can build a look up table for the first 3 bits ($[b_0\;b_1\;b_2]$) $f(b_0, b_1, b_2) = (1 + b_0 + 2b_1 + 4b_2) \cdot 2^{5j} \cdot P_i$ and view the last bit $b_3$ as the conditional select bit. Below is the look up table (let $c_i = (i+ 1) \cdot 2^{5j} \cdot P_i(x)$): | $b_2$ | $b_1$ | $b_0$ | $f(b_0, b_1, b_2)$ | |-------|-------|-------|---------------------| | 0 |0 | 0 | $c_0$ | | 0 |0 | 1 | $c_1$ | | 0 |1 | 0 | $c_2$ | | 0 |1 | 1 | $c_3$ | | 1 |0 | 0 | $c_4$ | | 1 |0 | 1 | $c_5$ | | 1 |1 | 0 | $c_6$ | | 1 |1 | 1 | $c_7$ | We can get a similar lookup table on $y$-axis, i.e. let $d_i = (i+ 1) \cdot 2^{5j} \cdot P_i(y)$ and replace $c_i$ with $d_i$. ### Arithmization in R1CS Now, we want to embed this lookup table into the constraint system. It would be actually expensive to actually build a function with a big "switch" statement in the constraint systems. Now, we use a "arithmization" technique to convert the the look up table into constraints system as follows (assuming we are using R1CS in zkSNARK schemes like Groth16): * $b_{10} = b_0 \cdot b_1$ * $$ \begin{align} out_x = & [(c_7 − c_6 − c_5 + c_4 − c_3 + c_2 + c_1 − c_0) \cdot b_{10} + (c_6 − c_4 − c_2 + c_0) \cdot b_1 \\ & + (c_5 − c_4 − c_1 + c_0) \cdot b_0 + (c_4 − c_0)] b_2 + (c_3 − c_2 − c_1 + c_0) \cdot b_{10} + (c_2 − c_0) \cdot b_1 \\ & + (c_1 − c_0) \cdot b_0 + c_0 \end{align} $$ * $$ \begin{align} out_y = & [(d_7 − d_6 − d_5 + d_4 − d_3 + d_2 + d_1 − d_0) \cdot b_{10} + (d_6 − d_4 − d_2 + d_0) \cdot b_1 \\ & + (d_5 − d_4 − d_1 + d_0) \cdot b_0 + (d_4 − d_0)] b_2 + (d_3 − d_2 − d_1 + d_0) \cdot b_{10} + (d_2 − d_0) \cdot b_1 \\ & + (d_1 − d_0) \cdot b_0 + d_0 \end{align} $$ TODO: show an example. ### Arithmization in PLONK Now, how can we do this efficiently on Plonk? On one hand, the second and third R1CS constraints can not be easily expressed as a single gate in PLONK. On the other hand, PLONK provides us the opportunities with more powerful customized gates. For example, we can construct a **big_poly** gate as the following in PLONK: $$ q_m \cdot (a \cdot b) + q_l \cdot a + q_r \cdot b + q_4 \cdot d + q_c + PI = c $$ where $q_i$ are constant, $a, b, c, d$ are variables, $PI$ is public input. By purely using **big_poly**, we can express the constraints on $x$ axis as following: * $$ \begin{align} out_1 = & (c_7 − c_6 − c_5 + c_4 − c_3 + c_2 + c_1 − c_0) \cdot (b_0 \cdot b_1) + (c_6 − c_4 − c_2 + c_0) \cdot b_1 \\ & + (c_5 − c_4 − c_1 + c_0) \cdot b_0 + (c_4 − c_0) \end{align} $$ * $$ out_2 = (c_3 − c_2 − c_1 + c_0) \cdot {b_1 \cdot b_0} + (c_2 − c_0) \cdot b_1 + (c_1 − c_0) \cdot b_0 + c_0 $$ * $$ out_x = out_1 \cdot b_2 + out_2 $$ Now we can derive $out_y$ similarly, and $b_3$ is used to conditionally negate the point. In details, $$ \begin{split} \text{neg_out}_x &= -1*out_x \\ \text{final_out}_x &= \text{cond_select}(b_3, neg\_out_x, out_x)\\ \text{final_out}_y &= out_y \end{split} $$ where generating $\text{neg_out}_x$ requires 1 constraint and $\text{cond_select}$ takes 1 constraint. And $out_y$ can be computed in a similar manner. For a $4$-bit chunk, the total constraint counts is: $8=3\cdot2+2(\text{used in conditional select})$ ## Furthur Optimization with Width 5 Poly_Gate If we have **big_poly** with width 5, i.e: $$ q_m \cdot (a \cdot b) + q_l \cdot c + q_r \cdot d + q_4 \cdot e + q_c + PI = c $$ we can further improve the constraint count as follows: * $b_{10} = b_0 \cdot b_1$ * $$ \begin{align} out_1 = & (c_7 − c_6 − c_5 + c_4 − c_3 + c_2 + c_1 − c_0) \cdot b_{10} + (c_6 − c_4 − c_2 + c_0) \cdot b_1 \\ & + (c_5 − c_4 − c_1 + c_0) \cdot b_0 + (c_4 − c_0) \end{align} $$ * $$ \begin{align} out_x = & out_1 \cdot b_2 + (c_3 − c_2 − c_1 + c_0) \cdot b_{10} + (c_2 − c_0) \cdot b_1 \\ & + (c_1 − c_0) \cdot b_0 + c_0 \end{align}$$ Since $b_{10}$ will be reused on both axis. We get $6=1 + 2\cdot2 + 1$ constraint per 4 bits. ## References 1. [Iden3 Pederden Hash Docuemnt](https://iden3-docs.readthedocs.io/en/latest/_downloads/4b929e0f96aef77b75bb5cfc0f832151/Pedersen-Hash.pdf)