You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

186 lines
6.0 KiB

package circuitcompiler
import (
"errors"
"math/big"
"strconv"
"github.com/arnaucube/go-snark/r1csqap"
)
// Circuit is the data structure of the compiled circuit
type Circuit struct {
NVars int
NPublic int
NSignals int
PrivateInputs []string
PublicInputs []string
Signals []string
Witness []*big.Int
Constraints []Constraint
R1CS struct {
A [][]*big.Int
B [][]*big.Int
C [][]*big.Int
}
}
// Constraint is the data structure of a flat code operation
type Constraint struct {
// v1 op v2 = out
Op string
V1 string
V2 string
Out string
Literal string
PrivateInputs []string // in func declaration case
PublicInputs []string // in func declaration case
}
func indexInArray(arr []string, e string) int {
for i, a := range arr {
if a == e {
return i
}
}
return -1
}
func isValue(a string) (bool, int) {
v, err := strconv.Atoi(a)
if err != nil {
return false, 0
}
return true, v
}
func insertVar(arr []*big.Int, signals []string, v string, used map[string]bool) ([]*big.Int, map[string]bool) {
isVal, value := isValue(v)
valueBigInt := big.NewInt(int64(value))
if isVal {
arr[0] = new(big.Int).Add(arr[0], valueBigInt)
} else {
if !used[v] {
panic(errors.New("using variable before it's set"))
}
arr[indexInArray(signals, v)] = new(big.Int).Add(arr[indexInArray(signals, v)], big.NewInt(int64(1)))
}
return arr, used
}
func insertVarNeg(arr []*big.Int, signals []string, v string, used map[string]bool) ([]*big.Int, map[string]bool) {
isVal, value := isValue(v)
valueBigInt := big.NewInt(int64(value))
if isVal {
arr[0] = new(big.Int).Add(arr[0], valueBigInt)
} else {
if !used[v] {
panic(errors.New("using variable before it's set"))
}
arr[indexInArray(signals, v)] = new(big.Int).Add(arr[indexInArray(signals, v)], big.NewInt(int64(-1)))
}
return arr, used
}
// GenerateR1CS generates the R1CS polynomials from the Circuit
func (circ *Circuit) GenerateR1CS() ([][]*big.Int, [][]*big.Int, [][]*big.Int) {
// from flat code to R1CS
var a [][]*big.Int
var b [][]*big.Int
var c [][]*big.Int
used := make(map[string]bool)
for _, constraint := range circ.Constraints {
aConstraint := r1csqap.ArrayOfBigZeros(len(circ.Signals))
bConstraint := r1csqap.ArrayOfBigZeros(len(circ.Signals))
cConstraint := r1csqap.ArrayOfBigZeros(len(circ.Signals))
// if existInArray(constraint.Out) {
// if used[constraint.Out] {
// panic(errors.New("out variable already used: " + constraint.Out))
// }
used[constraint.Out] = true
if constraint.Op == "in" {
for i := 0; i <= len(circ.PublicInputs); i++ {
aConstraint[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Add(aConstraint[indexInArray(circ.Signals, constraint.Out)], big.NewInt(int64(1)))
aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.Out, used)
bConstraint[0] = big.NewInt(int64(1))
}
continue
} else if constraint.Op == "+" {
cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.V1, used)
aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.V2, used)
bConstraint[0] = big.NewInt(int64(1))
} else if constraint.Op == "-" {
cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
aConstraint, used = insertVarNeg(aConstraint, circ.Signals, constraint.V1, used)
aConstraint, used = insertVarNeg(aConstraint, circ.Signals, constraint.V2, used)
bConstraint[0] = big.NewInt(int64(1))
} else if constraint.Op == "*" {
cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
aConstraint, used = insertVar(aConstraint, circ.Signals, constraint.V1, used)
bConstraint, used = insertVar(bConstraint, circ.Signals, constraint.V2, used)
} else if constraint.Op == "/" {
cConstraint, used = insertVar(cConstraint, circ.Signals, constraint.V1, used)
cConstraint[indexInArray(circ.Signals, constraint.Out)] = big.NewInt(int64(1))
bConstraint, used = insertVar(bConstraint, circ.Signals, constraint.V2, used)
}
a = append(a, aConstraint)
b = append(b, bConstraint)
c = append(c, cConstraint)
}
circ.R1CS.A = a
circ.R1CS.B = b
circ.R1CS.C = c
return a, b, c
}
func grabVar(signals []string, w []*big.Int, vStr string) *big.Int {
isVal, v := isValue(vStr)
vBig := big.NewInt(int64(v))
if isVal {
return vBig
} else {
return w[indexInArray(signals, vStr)]
}
}
type Inputs struct {
Private []*big.Int
Public []*big.Int
}
// CalculateWitness calculates the Witness of a Circuit based on the given inputs
// witness = [ one, output, publicInputs, privateInputs, ...]
func (circ *Circuit) CalculateWitness(privateInputs []*big.Int, publicInputs []*big.Int) ([]*big.Int, error) {
if len(privateInputs) != len(circ.PrivateInputs) {
return []*big.Int{}, errors.New("given privateInputs != circuit.PublicInputs")
}
if len(publicInputs) != len(circ.PublicInputs) {
return []*big.Int{}, errors.New("given publicInputs != circuit.PublicInputs")
}
w := r1csqap.ArrayOfBigZeros(len(circ.Signals))
w[0] = big.NewInt(int64(1))
for i, input := range publicInputs {
w[i+1] = input
}
for i, input := range privateInputs {
w[i+len(publicInputs)+1] = input
}
for _, constraint := range circ.Constraints {
if constraint.Op == "in" {
} else if constraint.Op == "+" {
w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Add(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
} else if constraint.Op == "-" {
w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Sub(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
} else if constraint.Op == "*" {
w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Mul(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
} else if constraint.Op == "/" {
w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Div(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2))
}
}
return w, nil
}