/ton-proof-verification-contest

Entry point for TON Proof Verification Contest.

Primary LanguageC++

TON Proof Verification Contest.

One of the exciting recent developments around zk-SNARKs is that it is now possible to verify a zk-SNARK proof in a lscs (a.k.a. smart contract) on FreeTON.

Let's see how we can create a Solidity smart contract to generate proofs for that circuit on FreeTON.

Building

Requirements: Boost >= 1.74.

git clone --recursive git@github.com:NilFoundation/ton-proof-verification-contest.git contest && cd contest
mkdir build && cd build
cmake ..
make cli

To update git submodule update --init --recursive

Verification instruction VERGRTH16 input creation

To create VERGRTH16 instruction input you need to represent the 'what you want to prove' in the form of a constraint system using =nil;Crypto3 Blueprint module and then prove it using =nil;Crypto3 ZK module. Then you can use byte-serialized output of the 'prove' function as input to the instruction in your lscs (a.k.a. smart contract).

The =nil;Crypto3 Blueprint zk-SNARK library is a powerful library for defining circuits, generating & verifying proofs. It can be hard to get a sense of how to use it in practice, so please follow the tutorial providing a sense of the high-level components of =nil;Crypto3 Blueprint and how to use it concretely, as well as how to connect the proofs to FreeTON lscs.

Serializing verification keys and proofs

If you have runned the generate and prove algorithms for Groth16, than you have all the data you need. There should be verification keys and proof in the appropriate format.

First we need to extract the verification keys and proofs from =nil;Crypto3 Blueprint in a way that can be consumed by Solidity smart contracts. In the file cli/src/main.cpp we demonstrate how to serialize the information from the objects r1cs_gg_ppzksnark<bls12<381>>::verification_key_type and r1cs_gg_ppzksnark<bls12<381>>::proof_type and write that information to a file in the form of field elements that can be interpreted as byteblobs in Solidity.

We won't go into detail here about the meaning of the values A, B, C etc in the proof data but check out Vitalik's blog post to learn more. The main thing to illustrate is that these values are elliptic curve points and hence will be represented by two elements of the underlying field.

When running the executable cli from within the build directory two files will be created: proof_data and vk_data containing the corresponding data in the form of byteblobs.

Building solidity contracts

You need to use a solc compiler and tvm linker with support for these instructions:

These forks need to be built using instructions from repo. You will need Boost with Boost.Filesystem module to build them.

After compilation you will have 2 files: solc (solidity compiler) and tvm_linker (linker).

To use these versions through tondev:

  • you need to put these files in the directory ~/.tondev/solidity/

  • give execution rights (chmod +x) to these files (otherwise tondev will crash)

Using verification keys and proofs in Solidity

We first take a look at the Solidity file examples/solidity/verifier.sol which contains the verification contract code. This file contains the function verify(), which stores incoming byteblob and gives it as input for the TVM instruction.

VERGRTH16 usage example

This example is a simple contract which allows to verify Groth16 zk-SNARK proof using TVM.

Methods

This contract has two methods.

  • verification::constructor() - method run on the contract's deploy.
  • bool verification::verify(slice proof) - proof packed into a slice with an inner format defined as follows.

Input format

zk-SNARK verifier bytes proof argument contains of 3 parts packed together:

  • verification_key_type vk
  • primary_input_type primary_input
  • proof_type proof

Type requirements for those are described in the Groth16 zk-SNARK policy

Byte vector assumes to be byte representation of all the underlying data types, recursively unwrapped to Fp field element and integral std::size_t values. All the values should be putted in the same order the recursion calculated.

Deploy instructions:

Creating a SetcodeMultisigWallet wallet:

Full instruction is here

  1. Add ZKP-ready nil's network to tondev: tondev network add nil net.freeton.nil.foundation
  2. Create / Add your wallet via tondev signer and save your <YOU_SIGNER_PUBLIC_ADDRESS>
  3. Download wallet files:
wget https://raw.githubusercontent.com/tonlabs/ton-labs-contracts/master/solidity/setcodemultisig/SetcodeMultisigWallet.abi.json

wget https://github.com/tonlabs/ton-labs-contracts/raw/master/solidity/setcodemultisig/SetcodeMultisigWallet.tvc
  1. Get wallet address: tondev contract info SetcodeMultisigWallet.abi.json -n nil

It should be printed as:

Address: 0:

(calculated from TVC and signer public)

  1. Request test token from Jury (Ask to fund this address someone in related telegram group) to <address>
  • ... Wait for it ...
  • now check your balance: tondev contract info -a 0:<address> -n nil | grep Balance
  1. Deploy wallet: tondev contract deploy SetcodeMultisigWallet.abi.json constructor -n nil -i owners:"[0x<YOU_SIGNER_PUBLIC_ADDRESS>]",reqConfirms:1

You will get something like this:

Deploying... Contract has deployed at address: 0:

  • Profit!

Now you have wallet and can deploy smart contracts.

Let's go to deployment step!