frontend

package
v0.2.1-alpha Latest Latest
Warning

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

Go to latest
Published: Jun 18, 2020 License: Apache-2.0 Imports: 8 Imported by: 710

README

Under the hood of a circuit

Technically, inside gnark, a circuit is implemented in a ConstraintSystem.

A ConstraintSystem is (mostly) a collection of Constraint. In a circuit, those would be "gates": a box that takes "wires" in, and outputs a single "wire". As we will see, a Constraint is a more abstract and more general representation.

In gnark, when we design a circuit, we declare inputs (i.e x := circuit.SECRET_INPUT("x")) and use gnark API to describe the circuit (i.e a := circuit.ADD(x, 3)).

This phase fills the data structure of the ConstraintSystem.

Some proving systems, like Groth16, use a particular type of constraint system called "rank 1 constraint system" (R1CS), which are constraints in the form a * b == c.

ConstraintSystem to R1CS

The Constraint struct used in the ConstraintSystem is different than the r1c (rank 1 constraint) used in the R1CS.

One can see the R1CS as a "compiled" circuit --> the data structure is immutable and hence, simpler. It is also pointer-less, to avoid garbage collection overhead with large circuits.

What is a Constraint?

A Constraint is a list of expression that must be equal to an output wire (if they are not, we say the Constraint is not satisfied).

An expression is an arithmetic statement (at most, operations are quadratic) f(x0, x1, ..xn) = y.

For example,

x := circuit.ADD(y, z)

creates the expression y * 1 + z * 1:

expression := &linearExpression{
	term{Wire: y.outputWire, Coeff: curve.One()},
	term{Wire: z.outputWire, Coeff: curve.One()},
}

Example: c = a xor b has this form as a rank one constraint: (2a)*b = a+b-c, so c = a+b-2ab, the expression is then a+b-2ab.

An expression has to be converted into a rank one constraint. For the previours example, a+b-2ab along with c will be converted to 2ab = a+b-c.

The conversion

From the ConstraintSystem , we build a R1CS in 4 steps:

  1. Loop through all the Constraint and collect the variables
  2. Loop though all the Constraint, isolate the expressions involved in the computational graph, link the variable computed from the expression to the expression
  3. Number the variables
  4. Split the Constraint into r1c
Solving the R1CS

Each expression provides one or several outputs once its inputs are instantiated. We can see then see them as a computational graph, each node being a constraint.

If the user specifies all the inputs, all the variables can be instantiated. The only way to declare unconstrained variables is to declare an input. All other constraints are using these or resulting constraints for operations on this to be computed.

The constraints involved in the computational graph have been isolated in the previous step. We just have to post order them to know in what order we should loop through them.

Example:

    s := cs.New()

    x := s.SECRET_INPUT("x")

    v1 := s.MUL(x, x)
    v2 := s.MUL(v1, v1)
    s.ADD(v1, v2)

    r1cs := cs.NewR1CS(&s)

The state of the r1cs is as follows (the numbering of the wires does not correspond to the variables v1 and v2):

variables:
wire_0, wire_1, wire_2, x, ONE_WIRE (=wire_4)

computational gaph:
0. (1*wire_2 )*(1*wire_2 )=1*wire_0
1. (1wire_2 +1*wire_0)*(1*wire_4) )=1*wire_1
2. (1*x )*(1*x)=1*wire_2

Graph ordering:
2, 0, 1

See that the constraints in the computational graph are not ordered (the order in which they are stored doesn't matter). After the post ordering, we see that the solver will use constraint 2 to compute the wire2 (corresponding to v1) from x, the user input, then the constraint 0, to compute wire0 (v2) from wire2, then the constraint 1 to get wire_1. The ONE_WIRE wire (wire_4) is a constant variable equal to 1, given to each circuit.

Documentation

Overview

package frontend contains Constraint System representation and R1CS to be used with zero knowledge proof systems in gnark

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInconsistantConstraint = errors.New("inconsistant constraint")
)

Functions

This section is empty.

Types

type Assert

type Assert struct {
	*require.Assertions
	// contains filtered or unexported fields
}

Assert is a helper to test circuits

func NewAssert

func NewAssert(t *testing.T) *Assert

NewAssert returns an helper to test Constraint Systems this helper embeds a stretch/testify Assert object for convenience

type CS

type CS struct {

	// under the key i are all the expressions that must be equal to a single wire
	Constraints map[uint64]*Constraint

	// constraints yielding multiple outputs (eg unpacking)
	MOConstraints []moExpression

	// constraints yielding no outputs (eg boolean constraints)
	NOConstraints []expression
	// contains filtered or unexported fields
}

CS Constraint System

func New

func New() CS

New returns a new constraint system

func (*CS) ADD

func (cs *CS) ADD(i1, i2 interface{}, in ...interface{}) *Constraint

ADD Adds 2+ inputs and returns resulting Constraint

func (*CS) ALLOCATE

func (cs *CS) ALLOCATE(input interface{}) *Constraint

ALLOCATE will return an allocated cs.Constraint from input {Constraint, element, uint64, int, ...}

func (*CS) DIV

func (cs *CS) DIV(i1, i2 interface{}) *Constraint

DIV divides two constraints (i1/i2)

func (*CS) FROM_BINARY

func (cs *CS) FROM_BINARY(b ...*Constraint) *Constraint

FROM_BINARY packs b, seen as a fr.Element in little endian

func (*CS) INV

func (cs *CS) INV(c1 *Constraint) *Constraint

INV inverse a Constraint

func (*CS) MUL

func (cs *CS) MUL(i1, i2 interface{}, in ...interface{}) *Constraint

MUL Multiplies 2+ constraints together

func (*CS) MUSTBE_BOOLEAN

func (cs *CS) MUSTBE_BOOLEAN(c *Constraint)

MUSTBE_BOOLEAN boolean constrains a variable

func (*CS) MUSTBE_EQ

func (cs *CS) MUSTBE_EQ(i1, i2 interface{})

MUSTBE_EQ equalizes two constraints

func (*CS) MUSTBE_LESS_OR_EQ

func (cs *CS) MUSTBE_LESS_OR_EQ(c *Constraint, bound interface{}, nbBits int)

MUSTBE_LESS_OR_EQ constrains c to be less or equal than e (taken as lifted Integer values from Fr) from https://github.com/zcash/zips/blob/master/protocol/protocol.pdf

func (*CS) PUBLIC_INPUT

func (cs *CS) PUBLIC_INPUT(name string) *Constraint

PUBLIC_INPUT creates a Constraint containing an input

func (*CS) SECRET_INPUT

func (cs *CS) SECRET_INPUT(name string) *Constraint

SECRET_INPUT creates a Constraint containing an input

func (*CS) SELECT

func (cs *CS) SELECT(b *Constraint, i1, i2 interface{}) *Constraint

SELECT if b is true, yields c1 else yields c2

func (*CS) SELECT_LUT

func (cs *CS) SELECT_LUT(c1, c0 *Constraint, lookuptable [4]big.Int) *Constraint

SELECT_LUT select lookuptable[c1*2+c0] where c0 and c1 are boolean constrained cf https://z.cash/technology/jubjub/

func (*CS) SUB

func (cs *CS) SUB(i1, i2 interface{}) *Constraint

SUB Adds two constraints

func (*CS) String

func (cs *CS) String() string

func (*CS) TO_BINARY

func (cs *CS) TO_BINARY(c *Constraint, nbBits int) []*Constraint

TO_BINARY unpacks a variable in binary, n is the number of bits of the variable The result in in little endian (first bit= lsb)

func (*CS) ToR1CS

func (circuit *CS) ToR1CS() *R1CS

NewR1CS builds a R1CS from a system of Constraints

func (*CS) XOR

func (cs *CS) XOR(c1, c2 *Constraint) *Constraint

XOR compute the xor between two constraints

type Constraint

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

Constraint list of expressions that must be equal+an output wire, that can be computed out of the inputs wire. A Constraint is a list of expressions that are equal. Each expression yields the value of another wire. A Constraint contains only one wire expression, and at least one expression, unless the wire expression is an input. under the constraintID i are all the expressions that must be equal under the constraintID i, exactly one Constraint can be single wire and there is at least a linear Constraint or a single wire

func (*Constraint) Tag

func (c *Constraint) Tag(tag string)

Tag adds a tag to the constraint's singleWire once the R1CS system is solved r1cs.Inspect() may return a map[string]value of constraints with Tags

type LinearCombination

type LinearCombination []Term

LinearCombination linear combination of constraints

type LinearExpression

type LinearExpression []TermR1cs

LinearExpression

type R1C

R1C used to compute the wires

type R1CS

type R1CS struct {
	// Wires
	NbWires        int
	NbPublicWires  int // includes ONE wire
	NbPrivateWires int
	PrivateWires   []string         // private wire names
	PublicWires    []string         // public wire names
	WireTags       map[int][]string // optional tags -- debug info

	// Constraints
	NbConstraints   int // total number of constraints
	NbCOConstraints int // number of constraints that need to be solved, the first of the Constraints slice
	Constraints     []R1C
}

R1CS decsribes a set of R1CS constraint

type SolvingMethod

type SolvingMethod uint8

method to solve a r1cs

const (
	SingleOutput SolvingMethod = iota
	BinaryDec
)

type Term

type Term struct {
	Constraint *Constraint
	Coeff      big.Int
}

Term coeff*constraint

type TermR1cs

type TermR1cs struct {
	ID    int64   // index of the constraint used to compute this wire
	Coeff big.Int // coefficient by which the wire is multiplied
}

Term ...

Jump to

Keyboard shortcuts

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