arnaucube
arnaucube/go-snarkPublic Self-hosted repository
go-snark/circuitcompiler/circuit.go
circuitcompiler/circuit.goRaw
  1package circuitcompiler
  2
  3import (
  4	"errors"
  5	"math/big"
  6	"strconv"
  7
  8	"github.com/arnaucube/go-snark/r1csqap"
  9)
 10
 11// Circuit is the data structure of the compiled circuit
 12type Circuit struct {
 13	NVars       int
 14	NPublic     int
 15	NSignals    int
 16	Inputs      []string
 17	Signals     []string
 18	Witness     []*big.Int
 19	Constraints []Constraint
 20	R1CS        struct {
 21		A [][]*big.Int
 22		B [][]*big.Int
 23		C [][]*big.Int
 24	}
 25}
 26
 27// Constraint is the data structure of a flat code operation
 28type Constraint struct {
 29	// v1 op v2 = out
 30	Op      string
 31	V1      string
 32	V2      string
 33	Out     string
 34	Literal string
 35
 36	Inputs []string // in func delcaration case
 37}
 38
 39func indexInArray(arr []string, e string) int {
 40	for i, a := range arr {
 41		if a == e {
 42			return i
 43		}
 44	}
 45	return -1
 46}
 47func isValue(a string) (bool, int) {
 48	v, err := strconv.Atoi(a)
 49	if err != nil {
 50		return false, 0
 51	}
 52	return true, v
 53}
 54func insertVar(arr []*big.Int, signals []string, v string, used map[string]bool) ([]*big.Int, map[string]bool) {
 55	isVal, value := isValue(v)
 56	valueBigInt := big.NewInt(int64(value))
 57	if isVal {
 58		arr[0] = new(big.Int).Add(arr[0], valueBigInt)
 59	} else {
 60		if !used[v] {
 61			panic(errors.New("using variable before it's set"))
 62		}
 63		arr[indexInArray(signals, v)] = new(big.Int).Add(arr[indexInArray(signals, v)], big.NewInt(int64(1)))
 64	}
 65	return arr, used
 66}
 67func insertVarNeg(arr []*big.Int, signals []string, v string, used map[string]bool) ([]*big.Int, map[string]bool) {
 68	isVal, value := isValue(v)
 69	valueBigInt := big.NewInt(int64(value))
 70	if isVal {
 71		arr[0] = new(big.Int).Add(arr[0], valueBigInt)
 72	} else {
 73		if !used[v] {
 74			panic(errors.New("using variable before it's set"))
 75		}
 76		arr[indexInArray(signals, v)] = new(big.Int).Add(arr[indexInArray(signals, v)], big.NewInt(int64(-1)))
 77	}
 78	return arr, used
 79}
 80
 81// GenerateR1CS generates the R1CS polynomials from the Circuit
 82func (circ *Circuit) GenerateR1CS() ([][]*big.Int, [][]*big.Int, [][]*big.Int) {
 83	// from flat code to R1CS
 84
 85	var a [][]*big.Int
 86	var b [][]*big.Int
 87	var c [][]*big.Int
 88
 89	used := make(map[string]bool)
 90	for _, constraint := range circ.Constraints {
 91		aConstraint := r1csqap.ArrayOfBigZeros(len(circ.Signals))
 92		bConstraint := r1csqap.ArrayOfBigZeros(len(circ.Signals))
 93		cConstraint := r1csqap.ArrayOfBigZeros(len(circ.Signals))
 94
 95		// if existInArray(constraint.Out) {
 96		if used[constraint.Out] {
 97			panic(errors.New("out variable already used: " + constraint.Out))
 98		}
 99		used[constraint.Out] = true
100		if constraint.Op == "in" {
101			for i := 0; i < len(constraint.Inputs); i++ {
102				aConstraint[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Add(aConstraint[indexInArray(circ.Signals, constraint.Out)], big.NewInt(int64(1)))
103				aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.Out, used)
104				bConstraint[0] = big.NewInt(int64(1))
105			}
106			continue
107
108		} else if constraint.Op == "+" {
109			cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
110			aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.V1, used)
111			aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.V2, used)
112			bConstraint[0] = big.NewInt(int64(1))
113		} else if constraint.Op == "-" {
114			cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
115			aConstraint, used = insertVarNeg(aConstraint, circ.Signals, constraint.V1, used)
116			aConstraint, used = insertVarNeg(aConstraint, circ.Signals, constraint.V2, used)
117			bConstraint[0] = big.NewInt(int64(1))
118		} else if constraint.Op == "*" {
119			cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
120			aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.V1, used)
121			bConstraint, used = insertVar(bConstraint, circ.Signals, constraint.V2, used)
122		} else if constraint.Op == "/" {
123			cConstraint, used = insertVar(cConstraint, circ.Signals, constraint.V1, used)
124			cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
125			bConstraint, used = insertVar(bConstraint, circ.Signals, constraint.V2, used)
126		}
127
128		a = append(a, aConstraint)
129		b = append(b, bConstraint)
130		c = append(c, cConstraint)
131
132	}
133	return a, b, c
134}
135
136func grabVar(signals []string, w []*big.Int, vStr string) *big.Int {
137	isVal, v := isValue(vStr)
138	vBig := big.NewInt(int64(v))
139	if isVal {
140		return vBig
141	} else {
142		return w[indexInArray(signals, vStr)]
143	}
144}
145
146// CalculateWitness calculates the Witness of a Circuit based on the given inputs
147func (circ *Circuit) CalculateWitness(inputs []*big.Int) []*big.Int {
148	w := r1csqap.ArrayOfBigZeros(len(circ.Signals))
149	w[0] = big.NewInt(int64(1))
150	for i, input := range inputs {
151		w[i+1] = input
152	}
153	for _, constraint := range circ.Constraints {
154		if constraint.Op == "in" {
155		} else if constraint.Op == "+" {
156			w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Add(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
157		} else if constraint.Op == "-" {
158			w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Sub(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
159		} else if constraint.Op == "*" {
160			w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Mul(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
161		} else if constraint.Op == "/" {
162			w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Div(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
163		}
164	}
165	return w
166}