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