Skip to content

A e2e example of Circom + a SnarkJS fork that works for plutus

License

Notifications You must be signed in to change notification settings

perturbing/plutus-plonk-example

Repository files navigation

⚠️ Disclaimer

This repository is not audited and should be used only for educational purposes. Use at your own risk.

plutus-plonk-example

This is an e2e example of a plonk ZKP written in Circom, besides that, this repo uses a custom fork of SnarkJS to generate proofs. To run this example, enter a shell via nix develop, this will bring all the necessary tools in path. The goal of this example is to show that it is not difficult to develop and set up a script that uses zero knowledge on Cardano.

To verify the code / compile it yourself

Circom

In the circom/example.circom file you will find the logic of what we prove in zero knowledge. This circuit has two private inputs in[2] and one public output out, the idea is that this simple circuit proves that one knows a preimage of size two to the poseidon hash digest out. To compile this circuit over the BLS12-381 curve to a rank one constraint system (R1CS), you can use

circom circom/my-circuit/example.circom --r1cs --wasm --sym -p bls12381 -o circom/my-circuit

This will create the files and folder

├── example_js
│   ├── example.wasm
│   ├── generate_witness.js
│   └── witness_calculator.js
├── example.r1cs
└── example.sym

Here, the example.r1cs can also be converted to a readable form via

snarkjs r1cs export json circom/my-circuit/example.r1cs circom/my-circuit/example.r1cs.json

To view more information on this R1CS, you can use,

snarkjs r1cs info circom/my-circuit/example.r1cs

The trusted setup (power of tau)

In the test-vector directory, you will find the files that SnarkJS uses to convert this R1CS to something we can use to generate proofs. First note that in the test-vector/setup/ folder, we have the power of tau files (these are generated by me, and are not to be trusted, as this is an example, see the SnarkJS repo for more info). Besides that, the verification key (which is the mapping of the R1CS over the power of tau) is given, to calculate this yourself, you can use,

snarkjs plonk setup circom/my-circuit/example.r1cs test-vectors/setup/pot_final.ptau test-vectors/setup/example_final.zkey

To export this example_final.zkey to the more human-readable verification_key.json, you can use,

snarkjs zkey export verificationkey test-vectors/setup/example_final.zkey test-vectors/setup/verification_key.json

Please note that if you did these steps, no file changed (see git status), showing that the verification key that was in the repo, was the correct one.

Generate a proof from a witness

In the test-vector/example folder, I create a private-input.json. This holds the secret values a=3 and b=11 (you can pick different values) which, together with the wasm files generated in the Circom step above, can be used to create a witness via,

node circom/my-circuit/example_js/generate_witness.js circom/my-circuit/example_js/example.wasm test-vectors/example/private-input.json test-vectors/example/witness.wtns

this witness can be used to create a proof and deduce what the public output should be via,

snarkjs plonk prove test-vectors/setup/example_final.zkey test-vectors/example/witness.wtns test-vectors/example/proof.json test-vectors/example/public-input.json

You can verify this proof against the verification key via

snarkjs plonk verify test-vectors/setup/verification_key.json test-vectors/example/public-input.json test-vectors/example/proof.json

The Cardano side

Compile the script and create redeemer

In this repo, you will find a plutus library called plutus-plonk that implements the verifier logic needed to verify a proof and public input given a fixed verification key. Besides that, there is a basic minting script implemented in the plutus-scripts folder that uses this to verify the circuit we compiled and setup above. To compile this script (which hard-codes test-vectors/setup/verification_key.json in the minting script) and convert both the public-input.json and proof.json to plutus data, you can use

nix run .#write-scripts

This will write the script to assets/V3/zkMintingScript.plutus and the redeemer (which contains the combination of your public input and proof) to assets/redeemers/mintRedeemer.json. These two together should allow you to mint an asset with any name under the policy ID via

cardano-cli conway transaction build --testnet-magic 4 \
 --tx-in $(cardano-cli query utxo --address $(cat payment.addr) --output-json --testnet-magic 42 | jq -r 'keys[0]') \
 --tx-in-collateral $(cardano-cli query utxo --address $(cat payment.addr) --output-json --testnet-magic 4 | jq -r 'keys[0]') \
 --mint "1 $(cardano-cli transaction policyid --script-file assets/V3/zkMintingScript.plutus).eeeeee" \
 --tx-out $(cat payment.addr)+10000000+"1 $(cardano-cli transaction policyid --script-file assets/V3/zkMintingScript.plutus).eeeeee" \
 --change-address $(cat payment.addr) \
 --mint-script-file assets/V3/zkMintingScript.plutus --mint-redeemer-file assets/redeemers/mintRedeemer.json \
 --out-file tx

which should work on sanchonet, given that the payment.addr can cover for the tx.

Deploy a local testnet

If you would like to test this script on a local testnet, you can deploy one via

deploy-local-testnet

In a new shell (a new nix develop terminal), wait for it to make blocks via

cardano-cli query tip --testnet-magic 42

Then you can mint via

cp local-testnet/example/utxo-keys/utxo1.skey ./payment.skey
cp local-testnet/example/utxo-keys/utxo1.vkey ./payment.vkey
cardano-cli address build --testnet-magic 42 --payment-verification-key-file payment.vkey > payment.addr
cardano-cli conway transaction build --testnet-magic 42 \
 --tx-in $(cardano-cli query utxo --address $(cat payment.addr) --output-json --testnet-magic 42 | jq -r 'keys[0]') \
 --tx-in-collateral $(cardano-cli query utxo --address $(cat payment.addr) --output-json --testnet-magic 42 | jq -r 'keys[0]') \
 --mint "1 $(cardano-cli transaction policyid --script-file assets/V3/zkMintingScript.plutus).eeeeee" \
 --tx-out $(cat payment.addr)+10000000+"1 $(cardano-cli transaction policyid --script-file assets/V3/zkMintingScript.plutus).eeeeee" \
 --change-address $(cat payment.addr) \
 --mint-script-file assets/V3/zkMintingScript.plutus --mint-redeemer-file assets/redeemers/mintRedeemer.json \
 --out-file tx
cardano-cli transaction sign --testnet-magic 42 --signing-key-file payment.skey --tx-body-file tx --out-file tx.signed
cardano-cli transaction submit --testnet-magic 42 --tx-file tx.signed

You can respin the network by purging it first via

purge-local-testnet

followed by

deploy-local-testnet

again.

Benchmark

The isolated evaluation of the plonk verifier can be benchmarked agains the test vectors in the /test-vectors folder via

nix run .#bench-verifier

The current test circuit and public inputs run with

Run fast snarkJS plonk verifier with public inputs [.......]

   n     Script size             CPU usage               Memory usage
  ----------------------------------------------------------------------
    -    6274  (38.3%)      3585086348  (35.9%)          668618   (4.8%) 

Here the results are relative to the mainnet paramaters (see /plutus-benchmark/common/PlutusBenchmark/ProtocolParameters.hs)

About

A e2e example of Circom + a SnarkJS fork that works for plutus

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages