mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-29 23:33:37 +00:00
9e74fc5b47
The example shows that the proover knows the solution of the cubic equation: y = x^3 + x + 5. The example is constructed for BLS12-381 curve points using Groth-16 prooving algorithm. The example includes everything that developer needs to start using ZKP on the NEO platform with Go SDK: 1. The described cubic circuit implementation. 2. The off-chain proof generation with the help of gnark-crypto library. 3. Go verification contract generation and deployment with the help of NeoGo libraries. 4. The on-chain proof verification for various sets of input data. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
93 lines
3.8 KiB
Go
93 lines
3.8 KiB
Go
// Package cubic describes how to create and verify proofs on the Neo
|
|
// blockchain. The example shows how to check that the prover knows the solution
|
|
// of the cubic equation: y = x^3 + x + 5. The example is constructed for
|
|
// BLS12-381 curve points using Groth-16 proving system. The example includes
|
|
// everything that developer needs to start using ZKP on the Neo platform with
|
|
// Go SDK:
|
|
// 1. The described cubic circuit implementation.
|
|
// 2. The off-chain proof generation with the help of gnark-crypto library.
|
|
// 3. The Go verification contract generation and deployment with the help of
|
|
// NeoGo library.
|
|
// 4. The on-chain proof verification for various sets of input data (implemented
|
|
// as end-to-end test).
|
|
// 5. A set of unit-tests aimed to check the circuit validity.
|
|
package cubic
|
|
|
|
import (
|
|
"github.com/consensys/gnark-crypto/ecc"
|
|
"github.com/consensys/gnark/backend/groth16"
|
|
"github.com/consensys/gnark/frontend"
|
|
"github.com/consensys/gnark/frontend/cs/r1cs"
|
|
)
|
|
|
|
// CubicCircuit defines a simple circuit x**3 + x + 5 == y
|
|
// that checks that the prover knows the solution for the provided expression.
|
|
// The circuit must declare its public and secret inputs as frontend.Variable.
|
|
// At compile time, frontend.Compile(...) recursively parses the struct fields
|
|
// that contains frontend.Variable to build the frontend.constraintSystem.
|
|
// By default, a frontend.Variable has the gnark:",secret" visibility.
|
|
type CubicCircuit struct {
|
|
// Struct tags on a variable is optional.
|
|
// Default uses variable name and secret visibility.
|
|
X frontend.Variable `gnark:"x,secret"` // Secret input.
|
|
Y frontend.Variable `gnark:"y,public"` // Public input.
|
|
}
|
|
|
|
// A gnark circuit must implement the frontend.Circuit interface
|
|
// (https://docs.gnark.consensys.net/HowTo/write/circuit_structure).
|
|
var _ = frontend.Circuit(&CubicCircuit{})
|
|
|
|
// Define declares the circuit constraints
|
|
// x**3 + x + 5 == y.
|
|
func (circuit *CubicCircuit) Define(api frontend.API) error {
|
|
x3 := api.Mul(circuit.X, circuit.X, circuit.X)
|
|
|
|
// Can be used for the circuit debugging.
|
|
api.Println("X^3", x3)
|
|
|
|
api.AssertIsEqual(circuit.Y, api.Add(x3, circuit.X, 5))
|
|
return nil
|
|
}
|
|
|
|
// main demonstrates how to build the proof and verify it with the help of gnark
|
|
// library. Error handling omitted intentionally to simplify the example.
|
|
func main() {
|
|
var (
|
|
circuit CubicCircuit
|
|
assignment = CubicCircuit{X: 3, Y: 35}
|
|
)
|
|
|
|
// Compile our circuit into a R1CS (a constraint system).
|
|
ccs, _ := frontend.Compile(ecc.BLS12_381.ScalarField(), r1cs.NewBuilder, &circuit)
|
|
|
|
// Once the circuit is compiled, you can run the three algorithms of a zk-SNARK back end:
|
|
|
|
// 1. One time setup (groth16 zkSNARK).
|
|
pk, vk, _ := groth16.Setup(ccs)
|
|
|
|
// Intermediate step: witness definition.
|
|
witness, _ := frontend.NewWitness(&assignment, ecc.BLS12_381.ScalarField())
|
|
publicWitness, _ := witness.Public()
|
|
|
|
// 2. Proof creation (groth16).
|
|
proof, _ := groth16.Prove(ccs, pk, witness)
|
|
|
|
// 3. Proof verification (groth16) via gnark-crypto library.
|
|
_ = groth16.Verify(proof, vk, publicWitness)
|
|
|
|
// 4. If building ZKP systems for Neo, you'll need a verification contract
|
|
// deployed to the Neo chain to be able to verify generated proofs. This
|
|
// contract can be generated automatically using NeoGo zkpbinding package:
|
|
// err := zkpbinding.GenerateVerifier(zkpbinding.Config{
|
|
// VerifyingKey: vk,
|
|
// Output: f, // Verifier Go contract writer
|
|
// CfgOutput: fCfg, // Verifier Go contract configuration YAML file
|
|
// GomodOutput: fMod, // go.mod file for the Verifier contract
|
|
// GosumOutput: fSum, // go.sum file for the Verifier contract
|
|
// })
|
|
//
|
|
// Create arguments to invoke `verifyProof` mathod of Verifier contract:
|
|
// verifyProofArgs, err := zkpbinding.GetVerifyProofArgs(proof, publicWitness)
|
|
//
|
|
// For end-to-end usage example, please, see the TestCubicCircuit_EndToEnd test.
|
|
}
|