Skip to content

giuliop/AlgoPlonk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AlgoPlonk

The power of zero knowledge proofs on the Algorand blockchain.

Disclaimer: AlgoPlonk is a new project and should be used with caution in production environments. Feedback and contributions are welcome as we work to advance the state of zero knowledge proofs on the Algorand blockchain.

AlgoPlonk automatically generates a smart contract verifier from a zk circuit definition. It integrates with the gnark toolchain, so you can use gnark to define a plonk based zk circuit and to generate proofs for it, and use AlgoPlonk to generate an Algorand smart contract verifier that can verify those proofs.

The typical workflow is the following:

  1. Define and compile a plonk based zk circuit with gnark using the trusted setup provided by AlgoPlonk
  2. Automatically generate a python Algorand Smart Contract with AlgoPlonk from your compiled circuit
  3. Compile the python code into the teal files to create the contract with algokit and the puyapy compiler
  4. Generate proofs and witnesses for your circuit with gnark
  5. Export proofs and witnesses with AlgoPlonk and generate the method calls to the smart contract verifier to verify them

To ensure compatibility with gnark (and gnark-crypto if you are using it as well), you can pin them to the versions shown in AlgoPlonk's go.mod file.

Supported curves

AlgoPlonk supports the curves for which the AVM offers elliptic curve operations: BN254 and BLS12-381. Note that at the moment AlgoPlonk does not support custom gates.

Practical considerations

A BN254 verifier consumes ~145,000 opcode budget, a BLS12-381 verifier ~185,000. Since these are smart contracts and currently there is a max pooled limit of ~180,000 opcode budget for smart contracts on the AVM, only the BN254 verifier can be used in practice at the moment.

Research is ongoing to see if it is possible to make the verifiers as smart signatures, since they enjoy a max pooled limit of 320,000. Also, the limits on the AVM might increase in the future, making a BLS12-381 smart contract verifier practical.

For now use BN254 verifiers.

Trusted Setup

AlgoPlonk provides an out of the box trusted setup for both BLS12-381 and bn256 verifiers using the Ethereum KZG Ceremony and the Perpetual Powers of Tau Ceremony, respectively.

The included trusted setup can support circuits with a number of constraints up to 2^14 (16K) for BLS12-381, and 2^17 (128k) for BN254, and the latter could be extended to circuits of up to 128M constraints in the future leveraging additional parameters from the Perpetual Powers of Tau Ceremony.

Check the doc.go file in the setup package for more details.

AlgoPlonk also provides test-only setups for circuits of any number of gates. These are NOT SUITABLE FOR PRODUCTION.

How to use AlgoPlonk

The examples folder contains some examples of how to use AlgoPlonk that you can run with go run main.go in each example subfolder.

Let's follow here the one in examples/basic, with some added commentary (but we'll be omitting error checking for brevity, check the full example for that).

After the mandatory imports...

package main

import (
	"fmt"
	"log"
	"path/filepath"

	"github.com/consensys/gnark-crypto/ecc"
	"github.com/consensys/gnark/frontend"

	ap "github.com/giuliop/algoplonk"
	"github.com/giuliop/algoplonk/setup"
	"github.com/giuliop/algoplonk/testutils"
	sdk "github.com/giuliop/algoplonk/testutils/algosdkwrapper"
	"github.com/giuliop/algoplonk/verifier"
)

...we define a simple zk circuit with gnark that given public variables a and b, verifies that the Prover knows a secret c that satisfies the Pythagorean equation: a^2 + b^2 == c^2

type BasicCircuit struct {
	A frontend.Variable `gnark:",public"`
	B frontend.Variable `gnark:",public"`
	C frontend.Variable
}

func (circuit *BasicCircuit) Define(api frontend.API) error {
	aa := api.Mul(circuit.A, circuit.A)
	bb := api.Mul(circuit.B, circuit.B)
	cc := api.Mul(circuit.C, circuit.C)
	api.AssertIsEqual(api.Add(aa, bb), cc)

	return nil
}

Let's also make an assignment that we'll use to generate a proof later

func main() {
	var circuit BasicCircuit

	// 3*3 + 4*4 == 5*5
	var assignment BasicCircuit
	assignment.A = 3
	assignment.B = 4
	assignment.C = 5

A bit of housekeeping now, we specify where to put the automatically generated files and how to call them.

AlgoPlonk will generate these files later on:

  • generated/BasicVerifier.py (the smart contract verifier)
  • generated/BasicVerifier.proof (input for the verifier)
  • generated/BasicVerifier.public_inputs (input for the verifier)

And puyapy will generate these files compiling BasicVerifier.py:

  • generated/BasicVerifier.approval.teal
  • generated/BasicVerifier.clear.teal
  • generated/BasicVerifier.arc32.json
	artefactsFolder := "generated"
	testutils.CreateDirectoryIfNeeded(artefactsFolder)

	verifierName := "BasicVerifier"

	puyaVerifierFilename := filepath.Join(artefactsFolder, verifierName+".py")
	proofFilename := filepath.Join(artefactsFolder, verifierName+".proof")
	publicInputsFilename := filepath.Join(artefactsFolder,
	    verifierName+".public_inputs")

Let's choose a curve, we use BLS12-381 here, and compile the circuit. Then we write to file the python code for the smart contract verifier with WritePuyaPyVerifier and finally we compile it to teal files

	curve := ecc.BLS12_381

	compiledCircuit, err := ap.Compile(&circuit, curve, setup.Trusted)
	err = compiledCircuit.WritePuyaPyVerifier(puyaVerifierFilename)
	err = testutils.CompileWithPuyaPy(puyaVerifierFilename, "")
	err = testutils.RenamePuyaPyOutput(verifier.VerifierContractName,
		verifierName, artefactsFolder)

Cool, let's now deploy the verifier contract on a local blockchain (use algokit localnet start to activate it).

	app_id, err := testutils.DeployArc4AppIfNeeded(verifierName, artefactsFolder)

Yes! We are ready to rock n' roll now, let's create a proof and export it to file together with its public inputs so we can verify it.

	verifiedProof, err := compiledCircuit.Verify(&assignment)
	err = verifiedProof.ExportProofAndPublicInputs(proofFilename,
		publicInputsFilename)

We simulate a call to the verify method of the verifier contract passing the generated proof and public inputs as parameters.

	simulate := true
	schema, err := testutils.ReadArc32Schema(filepath.Join(artefactsFolder,
	    verifierName+".arc32.json"))
	result, err := sdk.CallVerifyMethod(app_id, nil, proofFilename,
		publicInputsFilename, schema, simulate)

	fmt.Printf("Verifier app returned: %v\n", result.ReturnValue)
}

Verifier app returned: true

Life is sweet :)

The Verifier smart contract

The generated smart contract verifiers are ARC4 contracts with the following ABI methods:

  • create is used to create the application and will set two global properties:
    1. app_name with the provided name
    2. immutable with False
	@abimethod(create='require')
	def create(self, name: String) -> None:
  • update allows the creator to update / delete the application unless the immutable property has been set to True
	@abimethod(allow_actions=["UpdateApplication", "DeleteApplication"])
	def update(self) -> None:
  • make_immutable allows the creator to set the immutable property to True, making the contract fully decentralized with no one able to further modify or delete it.
	@abimethod
	def make_immutable(self) -> None:
  • verify takes as parameters a proof and public inputs as exported by AlgoPlonk and returns True if the proof is verifier, False otherwise
	@abimethod
	def verify(self, proof: ..., public_inputs: ...) -> arc4.Bool:

Next steps

Go unleash the power of zero knowledge proofs on Algorand!

Let us now what you create so that we can curate a list of zk applications.

GPG key

All release tags are signed by the GPG key 81E0FB63130466B782D4859D6C036245DBDB025D

About

The power of zero knowledge proofs on the Algorand blockchain

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages