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