MACI stands for Minimal anti-collusion infrastructure which has widely used in various applications such as anonymous voting and quadratic funding. We plan to add Nova support for MACI so that we can aggregate multiple MACI circut instances (i.e. batches) together. This will save on-chain verification cost and reduce prover time.
The major tools are the Nova repo and Nova-Scotia repo. Nova repo implements recursive nova folding scheme describe by nova paper. Nova-Scotia is a middle layer that read circom generated R1CS and witness generation executable which allow us to convert MACI circuit into format required by Nova.
In this blog, we will describe the steps needed based on our dev work. Nova currently only supports pasta curves. However, MACI runs on BN256 curve and EVM only supports BN256 curve operation (e.g. pairing). In this case, we have to add BN256-Grumpkin curves support for Nova in order to use the Nova-Scotia. Besides MACI repo, we still need modify other repos: halo2curves
, nova
, nova-scotia
.
The first thing we need to do is to change the MACI circuit to StepCircuit interface defined in Nova. In MACI v1.0, the only public signal in MessageProcessor.circom
(i.e. StepCircuit in terms of Nova) is inputHash. inputHash=hash(inputState, outputState, auxSignals)
. Nova requires the input and output has the same format. We changed it as:
inputHash=hash(inputStateRoot, auxSignals)
outputHash=hash(outputStateRoot,auxSignals)
Here the auxSignals are hash of other private signals to ensure the soundness of the circuit.
After this modification, we need modify the affected parts in contracts, cli and core folders as well.
The second thing to modify is the on-chain verifier contract. In MACI v1.0, the verifier contract only allows for a single public signal. As our new change, we need it to accept array of public signals. Here is the new interface:
From groth16 paper, the verifier verifies:
where are public inputs with . We only needs to modify the second term on the right hand side.
In most recent halo2curves
repo, the BN256-Grumpkin curves supports are added. However, it still missed some functions to support the Nova Group
interface.
In order to setup pedersen commitment keys on BN256 curves, we need to implement hash_to_curve
method required by Nova repo. There are several algorithms to do this.
The pasta curves used this algorithm. For BN256 curve (which has ), which need map to "normal" case where both are non-zero. This can be done by an isogeny that mapping between .
The whole process is:
We need to implement above functions. The first thing we need to find is the isogeny map between and , which is curve specific. However, the degree of isogeny map is too high, this algorithm is not suitable.
This is the simpliest algorithm. In expectation, it only require two tries which is fast enough. We first hash a message to base element: . Then:
Depending on the field, we have different algorithms to calculate the square root in above algorithm. We omit the detail here.
As we mentioned before the simplified SWU algorithm requires , we have to find isogeny mapping such that coefficients are non-zero. It turned out for BN256 curve, the isogeny mapping has degree which is too high for practical use. It's better that we choose SWDW algorithm which works for any short weierstrass curves.
With the preparation of halo2curve
library, we are able to implement Group trait as well as other necessary traits required by Nova library for BN256-Grumpkin
curves. This step is a straightforward implementation comparing the pallas-vesta
curves.
This repo requires minimal modifications, we just need import from BN256-Grumpkin
curves in halo2curves
as we already added all necessary components in halo2curves
and Nova
.
The workflow of Nova-Maci
library is the following grpah:
MACI
library, maci-cli
will generate R1CS
file, circuitInputs
and witnessGenExe
artifacts.Nova-Scotia
will create PublicParams
based on the size of R1CS
file, generate witness
using witnessGenExe
and circuitInputs,
and finally IVC Circuit
from R1CS
fileNova-Scotia
will call Nova
library to generate either RecursiveSNARK
or CompressedRecursiveSNARK
for each stepRecursiveSNARK
or CompressedRecursiveSNARK
This is the last missing piece. We could use circom
or bellperson
library to write the verifier on Spartan
proof that created by Spartan
prover from Nova
library.
MACI
nova
nova-scotia
nova-maci
public
nova
nova-maci