I designed a new ZK-friendly hash function: Polocolo↗.
Conventional hash functions often struggle with inefficiency in zero-knowledge (ZK) proof settings, leading to the design of several ZK-friendly hash functions. On the other hand, lookup arguments have recently been incorporated into ZK protocols, allowing for more efficient handling of ZK-unfriendly operations. As a result, ZK-friendly hash functions based on lookup tables have been proposed.
Polocolo is targeted to a PlonKup↗, an extension of PLONK↗ with plookup↗. Polocolo requires fewer PLONK gates compared to the state-of-the-art ZK-friendly hash functions. For example, when , Polocolo requires 21% less PLONK gates compared to Anemoi↗, which is currently the most efficient ZK-friendly hash function, where denotes the size of the underlying permutation. For , Polocolo requires 24% less PLONK gates than Reinforced Concrete↗, which is one of the recent lookup-based ZK-friendly hash functions. Polocolo was presented at Eurocrypt 2025↗.
Part 1 of this post discusses the theoretical foundations of PLONK and PlonKup. After forming these foundations, we’ll build on top of it in part 2 to explore how exactly Polocolo works.
A Review of ZK-Friendly Hash Functions
Earlier, we discussed ZK-friendly hash functions and algebraic attacks against them in the posts “ZK-Friendly Hash Functions↗” and “Algebraic Attacks on ZK-Friendly Hash Functions↗”. For those who haven’t read them, here’s a brief summary.
Hash functions are used in ZK protocols for cryptographic purposes such as commitment schemes and pseudorandom number generation. While SHA-2 and SHA-3 are standardized and extensively studied, their efficiency in ZK protocols is highly inefficient because bitwise operations — though fast on traditional CPUs — are poorly suited to ZK proof systems. This inefficiency has motivated the development of ZK-friendly hash functions, which are optimized for performance in ZK settings.
Intuitively, ZK-friendly hash functions prioritize algebraic simplicity. We introduced MiMC↗, Poseidon↗, Vision↗, and Rescue↗ in a previous post↗. Additionally, newer proposals such as Anemoi↗, Griffin↗, and Arion↗ have emerged as advanced ZK-friendly hash functions.
Sponge Construction
The common approach for designing ZK-friendly hash functions is to first construct a permutation (a bijective function , where denotes the size of the permutation) and then convert it to a hash function using sponge construction↗.
The sponge construction — is input and is hashed output. The unused capacity should be twice the desired collision or preimage attacks. Image from Wikipedia↗. |
The sponge construction operates in two phases:
- Absorbing phase. Message blocks are processed and integrated into the internal state.
- Squeezing phase. Hash digests are extracted from the state.
And key advantages of this framework include
- Arbitrary input/output lengths — which supports variable-length inputs and outputs for the hash function.
- Provable security — the sponge construction’s security is formally reduced to the underlying permutation’s security.
From a designer’s perspective, the primary task simplifies to optimizing the permutation’s structure.
Algebraic Attacks
If permutation is an ideal permutation (where outputs of permutation are uniformly random), the hash function derived via the sponge construction is secure. However, real-world permutations use deterministic mathematical structures; the output of a permutation is not random. Any bias in the output can lead to an attack on the hash function.
An attack against a ZK-friendly hash function focuses on the constrained-input constrained-output (CICO) problem. For a permutation , the CICO problem involves finding such that Solving this proplem enables collision or preimage attacks against the hash function.
A simple brute-force attack without using characteristics of is to set arbitrarily and check whether the last element of is zero. This requires attempts. If the CICO probem can be solved faster than for certian security parameter , then the hash function derived from that permutation is considered insecure. In ZK-friendly hash functions, designers typically consider , meaning that expected attack complexity is at least .
In ZK-friendly hash functions, the function is algebraically simple. Therefore, the algebraic attack that involves representing the CICO problem as the system of the equations and solving it is usually the most effective attack against ZK-friendly hash functions. We introduced several attacks in the previous post↗.
The designers carefully choose their structures and the parameters after considering all of the proposed attacks, including even security margins. Unfortunately, as attack techniques continue to evolve, it is common for proposed ZK-friendly hash functions to be attacked, even if the hash function is peer-reviewed and introduced in a top conference.
![]() |
---|
Here is the timeline of the ZK-friendly hash function. Red flash signs denote the original specification is broken by algebraic attacks. |
How Does PLONK Work?
PLONK↗ is an universal and succinct ZK protocol. Among various ZK protocols, we’ll focus on PLONK, as Polocolo targets PLONK with lookup.
A zero-knowledge proof system is a two-party cryptographic protocol between a prover and a verifier that allows the prover to convince the verifier of their knowledge without revealing itself. For example, given a hash function and the hash value , the prover can convince the verifier their knowledge of preimage such that . Performance metrics for the protocol, including prover time, verifier time, and proof size, are influenced by the function . Generally, the simpler the can be expressed algebraically, the more efficient the protocol becomes.
Comparing PLONK to Groth16↗, proof size and verifier complexity still remains (though slightly less efficient), and the structured reference string (SRS) is universal and updatable. Therefore, the trusted setup can be performed in a universal manner. In Groth16, the trusted setup must be performed for each circuit. PLONK also has several variants, such as HyperPlonk↗ and PlonKup↗, which support custom gates and lookup gates, respectively. However, unlike Groth16 and zkSTARK, addition is not free. Moreover, PLONK lacks quantum resistance.
Let’s delve into how PLONK works by exploring its arithmetization technique. This will give us insight into how a prover’s complexity is determined.
Arithmetization
PLONK employs its own arithmetization technique. For a given arithmetic circuit, there are two types of constraints, the gate constraint and copy constraint.
Here is an example of proving the knowledge of such that . Note that satisfies the equation. The arithmetic circuit of the corresponding equation is shown below.
![]() |
---|
The arithmetic circuit of the equation . |
Respectively, represent the left and right inputs, while represents the output for each -th gate. The color of each variable indicates its assigned value, meaning variables sharing the same color have identical values.
Now the prover has to prove these two contraints:
- For the gate constraint, for each gate input and output , the gate is valid (i.e., for the addition gate and for the multiplication gate).
- For the copy constraint, if the color is the same, then the value is the same (i.e., ).
If both constraints are verified, the verifier convinces the prover’s knowledge of such that .
Note: Actual protocols use mathematical building blocks such as vanishing polynomials, FFTs, and polynomial commitments. To simplify understanding, this explanation focuses on PLONK’s core concepts rather than replicating the protocol exactly as described in the paper. For in-depth details, refer to the original PLONK paper↗.
Gate Constraint
For the gate constraint, each gate is represented by the following equation:
The selectors are determined by the gate type. In our example, the four gates are configured as follows:
Similar to Groth16↗, the prover constructs a polynomial such that for . Polynomials are also derived in the similar manner. The combined constraint is then for .
Copy Constraint
For the copy constraint, the prover must demonstrate the following equalities:
Equalities involving more than two variables can be decomposed into pairwise constraints:
To formalize this, define two vectors and representing the original and permuted variable assignments:
If , all required equalities hold. To verify the entire permutation at once, define a permutation function , where denotes the index of element in . For example, maps to . Therefore, .
The prover must show for each . Instead of direct equality checks, the prover demonstrates
for random . It can be easily seen that valid permutations ensure numerator and denominator terms cancel pairwise. For instance, the terms corresponding to (linked to ) yield:
If , the fraction simplifies to .
Let polynomials be defined as and , respectively.
Define a polynomial as and for .
Then
must hold.
By accumulating gate constraints and copy constraints, the prover need to show
The functions and are determined by the circuit and publicly calculated, while the functions and are derived from the prover’s private witness. These equations are efficiently verifiable using mathematical building blocks such as vanishing polynomials, FFTs, and polynomial commitments. Detailed explanations are omitted in this post.
An important point to note is that the degree of the polynomials and is determined by the number of gates. Thus, the prover’s complexity is proportional to the number of gates.
How Does PlonKup Work?
PLONK faced a common challenge: operations with high degrees in the native field require significant overhead. This led to the invention of an extension of PLONK, named PlonKup↗.
In a vanilla PLONK, the prover can use addition and multiplicative gates. PlonkUp supports one additional type of gate: lookup gates. Using lookup gates, a prover can prove a witness belongs to a public table .
In PlonKup, gate constraints are extended by introducing a new selector for lookup gates. For the -th element in the public table , the element is compressed as using a random value .
The gate constraints for input gates and output gate are extended as follows:
If the -th gate is an arithmetic gate, then . Conversely, if the -th gate is a lookup gate, then .
While the first two equations can be derived similarly through vanilla PLONK, the third equation requires the specialized strategy introduced in plookup↗. Due to the complexity of this component, it’s recommended to check plookup↗ and the PlonKup↗ papers for further details.
Lookup Arguments
To appreciate PlonKup’s utility, let’s examine why lookup arguments matter. If the ZK proof system only allows addition and multiplicative gates, operations that cannot be represented algebraically simply require a large number of constraints. For instance, XOR of two 16-bits elements is a lightweight operation in traditional CPUs. However, representing in requires complex computation as follows:
This is 48 multiplication gates and 77 addition gates.
If the ZK proof system supports lookup gates, the required number of gates is dramatically reduced. First, define the table as , which contains elements. Then the XOR operation can be computed as follows:
This requires only nine addition gates and four lookup gates.
![]() |
---|
Handling XOR using a lookup table. |
Since the prover’s complexity in PLONK is proportional to the total number of gates, XOR can be handled much more efficiently once the lookup table is enabled.
The First Lookup-Based ZK-Friendly Hash Function
It is natural to attempt to design ZK-friendly hash functions using lookup gates. The main advantage of using lookup gates in ZK-friendly hash functions is that they are not expressed in a simple algebraic way, which can provide resistance to algebraic attacks. For instance, representing the condition algebraically (e.g., ) requires a polynomial of degree .
Reinforced Concrete↗ is the first lookup-based ZK-friendly hash function, presented at CCS 2022. Reinforced Concrete consists of Bricks, Concrete, and Bars layers. Among them, lookup table is used in the Bars layer. In the Bars layer, elements are processed in parallel through the Bar function. In the Bar function, similar to computing XOR using lookup gates, an element is first decomposed into where
Then is fed into to a small-sized lookup table , yielding . The output is then determined by which reconstructs the composed output from .
![]() |
---|
Bar function in Reinforced Concrete. |
This structure uses a lookup table in a canonical way, maintaining reasonable efficiency with no known vulnerabilities reported. However, a key drawback is the high cost evaluating the Bars layer in a ZK setting. Out of the 15 layers in Reinforced Concrete, only one is the Bars layer — while it still accounts for 75% of the constraints in the PlonKup. In general, it is desirable and natural to iterate a simple layer over multiple rounds. Reducing the cost of the Bars layer and iterating it over multiple rounds may enhance security and allow a trade-off between efficiency and security. This motivates Polocolo’s design.
Introducing Polocolo
Polocolo is an another lookup-based ZK-friendly hash functions from the different design rationale. The name Polocolo derives from power residue for lower cost table lookup.
To map an input to a lookup table, the possible value of must be constrained via preprocessing. The Bar function in Reinforced Concrete uses a base expansion method to apply a lookup table to an element of : is decomposed into an -tuple then each is passing through small S-boxes, as explained in previous section. Each component is then fed into an S-box via table lookup. The outputs from the S-boxes are combined again to define the corresponding output of the Bar function. However, base expansion is significantly expensive in most ZK settings.
To address this, I propose an alternative approach, dubbed the power residue method, which efficiently applies lookup tables to elements for a large prime . A power residue↗ can be seen as a generalization of the Legendre symbol. When a positive integer divides , the -th power residue of is defined as The -th power residue takes distinct values, so each possibility can be an input to a lookup table of size .
Now our new S-box is defined as
By appropriately choosing , this S-box can be made bijective. Moreover, this S-box is of a high degree that requires only 14 PLONK gates — for example, when , which is significantly fewer than 94 gates required for the Bar function from Reinforced Concrete. Using this S-box, I propose Polocolo, a new lookup-based ZK-friendly hash function.
In part 2 of this post, I will introduce the detailed specification and cryptanalysis of Polocolo, along with a performance comparison with other ZK-friendly hash functions.
About Us
Zellic specializes in securing emerging technologies. Our security researchers have uncovered vulnerabilities in the most valuable targets, from Fortune 500s to DeFi giants.
Developers, founders, and investors trust our security assessments to ship quickly, confidently, and without critical vulnerabilities. With our background in real-world offensive security research, we find what others miss.
Contact us↗ for an audit that’s better than the rest. Real audits, not rubber stamps.