gkrapi

package
v0.15.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2026 License: Apache-2.0 Imports: 18 Imported by: 1

Documentation

Overview

Example
package main

import (
	"encoding/binary"
	"errors"

	"github.com/consensys/gnark-crypto/ecc"
	bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377"
	"github.com/consensys/gnark/frontend"
	"github.com/consensys/gnark/std/gkrapi"
	"github.com/consensys/gnark/std/gkrapi/gkr"
	_ "github.com/consensys/gnark/std/hash/all" // import all hash functions to register them
	"github.com/consensys/gnark/test"
)

func main() {
	// This example computes the double of multiple BLS12-377 G1 points, which can be computed natively over BW6-761.
	// The two curves form a "cycle", meaning the scalar field of one is the base field of the other.
	// The implementation is based on the function DoubleAssign() of type G1Jac in gnark-crypto v0.17.0.
	// github.com/consensys/gnark-crypto/ecc/bls12-377

	const nbInstances = 2
	// create instances
	assignment := exampleCircuit{
		X:    make([]frontend.Variable, nbInstances),
		Y:    make([]frontend.Variable, nbInstances),
		Z:    make([]frontend.Variable, nbInstances),
		XOut: make([]frontend.Variable, nbInstances),
		YOut: make([]frontend.Variable, nbInstances),
		ZOut: make([]frontend.Variable, nbInstances),
	}

	for i := range nbInstances {
		// create a "random" point
		var b [8]byte
		binary.BigEndian.PutUint64(b[:], uint64(i))
		a, err := bls12377.HashToG1(b[:], nil)
		assertNoError(err)
		var p bls12377.G1Jac
		p.FromAffine(&a)

		assignment.X[i] = p.X
		assignment.Y[i] = p.Y
		assignment.Z[i] = p.Z

		p.DoubleAssign()
		assignment.XOut[i] = p.X
		assignment.YOut[i] = p.Y
		assignment.ZOut[i] = p.Z
	}

	circuit := exampleCircuit{
		X:    make([]frontend.Variable, nbInstances),
		Y:    make([]frontend.Variable, nbInstances),
		Z:    make([]frontend.Variable, nbInstances),
		XOut: make([]frontend.Variable, nbInstances),
		YOut: make([]frontend.Variable, nbInstances),
		ZOut: make([]frontend.Variable, nbInstances),
	}

	assertNoError(test.IsSolved(&circuit, &assignment, ecc.BW6_761.ScalarField()))

}

type exampleCircuit struct {
	X, Y, Z          []frontend.Variable // Jacobian coordinates for each point (input)
	XOut, YOut, ZOut []frontend.Variable // Jacobian coordinates for the double of each point (expected output)
}

func (c *exampleCircuit) Define(api frontend.API) error {
	if len(c.X) != len(c.Y) || len(c.X) != len(c.Z) || len(c.X) != len(c.XOut) || len(c.X) != len(c.YOut) || len(c.X) != len(c.ZOut) {
		return errors.New("all inputs/outputs must have the same length (i.e. the number of instances)")
	}

	gkrApi, err := gkrapi.New(api)
	if err != nil {
		return err
	}

	// create the GKR circuit
	X := gkrApi.NewInput()
	Y := gkrApi.NewInput()
	Z := gkrApi.NewInput()

	XX := gkrApi.Gate(squareGate, X)    // 405: XX.Square(&p.X)
	YY := gkrApi.Gate(squareGate, Y)    // 406: YY.Square(&p.Y)
	YYYY := gkrApi.Gate(squareGate, YY) // 407: YYYY.Square(&YY)
	ZZ := gkrApi.Gate(squareGate, Z)    // 408: ZZ.Square(&p.Z)

	S := gkrApi.Gate(sGate, X, YY, XX, YYYY) // 409 - 413

	// 414: M.Double(&XX).Add(&M, &XX)
	// Note (but don't explicitly compute) that M = 3XX

	ZOut := gkrApi.Gate(zGate, Z, Y, YY, ZZ)      // 415 - 418
	XOut := gkrApi.Gate(xGate, XX, S)             // 419-422
	YOut := gkrApi.Gate(yGate, S, XOut, XX, YYYY) // 423 - 426

	// Mark XOut as an output, even though it is fed into another wire (YOut)
	gkrApi.Export(XOut)

	gkrCircuit, err := gkrApi.Compile("MIMC")
	if err != nil {
		return err
	}

	// add input and check output for correctness
	instanceIn := make(map[gkr.Variable]frontend.Variable)
	for i := range c.X {
		instanceIn[X] = c.X[i]
		instanceIn[Y] = c.Y[i]
		instanceIn[Z] = c.Z[i]

		instanceOut, err := gkrCircuit.AddInstance(instanceIn)
		if err != nil {
			return err
		}
		api.AssertIsEqual(instanceOut[XOut], c.XOut[i])
		api.AssertIsEqual(instanceOut[YOut], c.YOut[i])
		api.AssertIsEqual(instanceOut[ZOut], c.ZOut[i])
	}
	return nil
}

// custom gates

// squareGate x -> x²
func squareGate(api gkr.GateAPI, input ...frontend.Variable) frontend.Variable {
	return api.Mul(input[0], input[0])
}

// sGate combines the operations that define the first value assigned to variable S.
// input = [X, YY, XX, YYYY].
// S = 2 * [(X + YY)² - XX - YYYY].
func sGate(api gkr.GateAPI, input ...frontend.Variable) (S frontend.Variable) {
	S = api.Add(input[0], input[1])    // 409: S.Add(&p.X, &YY)
	S = api.Mul(S, S)                  // 410: S.Square(&S).
	S = api.Sub(S, input[2], input[3]) // 411: Sub(&S, &XX).
	//                                    412: Sub(&S, &YYYY).
	return api.Add(S, S) // 413: Double(&S)
}

// zGate combines the operations that define the assignment to p.Z.
// input = [p.Z, p.Y, YY, ZZ].
// p.Z = (p.Z + p.Y)² - YY - ZZ.
func zGate(api gkr.GateAPI, input ...frontend.Variable) (Z frontend.Variable) {
	Z = api.Add(input[0], input[1])    // 415: p.Z.Add(&p.Z, &p.Y).
	Z = api.Mul(Z, Z)                  // 416: p.Z.Square(&p.Z).
	Z = api.Sub(Z, input[2], input[3]) // 417: Sub(&p.Z, &YY).
	//                                    418: Sub(&p.Z, &ZZ)
	return
}

// xGate combines the operations that define the assignment to p.X.
// input = [XX, S].
// p.X = 9XX² - 2S.
func xGate(api gkr.GateAPI, input ...frontend.Variable) (X frontend.Variable) {
	M := api.Mul(input[0], 3)            // 414: M.Double(&XX).Add(&M, &XX)
	T := api.Mul(M, M)                   //     419: T.Square(&M)
	X = api.Sub(T, api.Mul(input[1], 2)) // 420: p.X = T
	//                                          421: T.Double(&S)
	//                                          422: p.X.Sub(&p.X, &T)
	return
}

// yGate combines the operations that define the assignment to p.Y.
// input = [S, p.X, XX, YYYY].
// p.Y = (S - p.X) * 3 * XX - 8 * YYYY.
func yGate(api gkr.GateAPI, input ...frontend.Variable) (Y frontend.Variable) {
	Y = api.Sub(input[0], input[1]) //         423: p.Y.Sub(&S, &p.X).
	Y = api.Mul(Y, input[2], 3)     //    414: M.Double(&XX).Add(&M, &XX)
	//                                         424:Mul(&p.Y, &M)
	Y = api.Sub(Y, api.Mul(input[3], 8)) // 425: YYYY.Double(&YYYY).Double(&YYYY).Double(&YYYY)
	//                                         426: p.Y.Sub(&p.Y, &YYYY)

	return
}

func assertNoError(err error) {
	if err != nil {
		panic(err)
	}
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type API

type API struct {
	// contains filtered or unexported fields
}

func New

func New(api frontend.API) (*API, error)

New creates a new GKR API

func (*API) Add

func (api *API) Add(i1, i2 gkr.Variable) gkr.Variable

func (*API) Compile added in v0.15.0

func (api *API) Compile(fiatshamirHashName string, options ...CompileOption) (*Circuit, error)

Compile finalizes the GKR circuit. From this point on, the circuit cannot be modified, but instances can be added to it.

func (*API) Export added in v0.15.0

func (api *API) Export(in ...gkr.Variable)

Export explicitly designates a wire as output. Wires that are not used as input to another are considered output by default.

func (*API) Gate

func (api *API) Gate(gate gkr.GateFunction, inputs ...gkr.Variable) gkr.Variable

Gate adds the given gate with the given inputs and returns its output wire.

func (*API) Mul

func (api *API) Mul(i1, i2 gkr.Variable) gkr.Variable

func (*API) NamedGate deprecated

func (api *API) NamedGate(gate gkr.GateName, inputs ...gkr.Variable) gkr.Variable

NamedGate adds a gate looked up by name from the registry.

Deprecated: Named gates are no longer needed. Pass GateFunction directly to API.Gate().

func (*API) Neg

func (api *API) Neg(i1 gkr.Variable) gkr.Variable

func (*API) NewInput added in v0.15.0

func (api *API) NewInput() gkr.Variable

NewInput creates a new input variable.

func (*API) Sub

func (api *API) Sub(i1, i2 gkr.Variable) gkr.Variable

type Circuit added in v0.15.0

type Circuit struct {
	// contains filtered or unexported fields
}

Circuit represents a GKR circuit.

func (*Circuit) AddInstance added in v0.15.0

func (c *Circuit) AddInstance(input map[gkr.Variable]frontend.Variable) (map[gkr.Variable]frontend.Variable, error)

AddInstance adds a new instance to the GKR circuit, returning the values of output variables for the instance.

func (*Circuit) GetValue added in v0.15.0

func (c *Circuit) GetValue(v gkr.Variable, i int) frontend.Variable

GetValue is a debugging utility returning the value of variable v at instance i. While v can be an input or output variable, GetValue is most useful for querying intermediate values in the circuit.

type CompileOption added in v0.15.0

type CompileOption func(*Circuit)

func WithInitialChallenge added in v0.15.0

func WithInitialChallenge(getInitialChallenge InitialChallengeGetter) CompileOption

WithInitialChallenge provides a getter for the I/O portion of the initial Fiat-Shamir challenge. If not provided, the I/O initial challenge will be a commitment to all the input and output values of the circuit.

type InitialChallengeGetter added in v0.15.0

type InitialChallengeGetter func() []frontend.Variable

The InitialChallengeGetter provides a one-time initial Fiat-Shamir challenge for the GKR prover. Normally, these should include a unique circuit identifier and all input-output pairs.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL