package circuitcompiler
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var variableIndicationSign = "@"
|
|
|
|
// Circuit is the data structure of the compiled circuit
|
|
type Circuit struct {
|
|
Inputs []string
|
|
Name string
|
|
root *gate
|
|
//after reducing
|
|
//constraintMap map[string]*Constraint
|
|
gateMap map[string]*gate
|
|
}
|
|
|
|
type gate struct {
|
|
index int
|
|
left *gate
|
|
right *gate
|
|
funcInputs []*gate
|
|
value *Constraint //is a pointer a good thing here??
|
|
leftIns factors //leftIns and RightIns after addition gates have been reduced. only multiplication gates remain
|
|
rightIns factors
|
|
}
|
|
|
|
func (g gate) String() string {
|
|
return fmt.Sprintf("Gate %v : %v with left %v right %v", g.index, g.value, g.leftIns, g.rightIns)
|
|
}
|
|
|
|
//type variable struct {
|
|
// val string
|
|
//}
|
|
|
|
// Constraint is the data structure of a flat code operation
|
|
type Constraint struct {
|
|
// v1 op v2 = out
|
|
Op Token
|
|
V1 string
|
|
V2 string
|
|
Out string
|
|
//fV1 *variable
|
|
//fV2 *variable
|
|
//fOut *variable
|
|
//Literal string
|
|
//TODO once i've implemented a new parser/lexer we do this differently
|
|
Inputs []string // in func declaration case
|
|
//fInputs []*variable
|
|
negate bool
|
|
invert bool
|
|
}
|
|
|
|
func (c Constraint) String() string {
|
|
if c.negate || c.invert {
|
|
return fmt.Sprintf("|%v = %v %v %v| negated: %v, inverted %v", c.Out, c.V1, c.Op, c.V2, c.negate, c.invert)
|
|
}
|
|
return fmt.Sprintf("|%v = %v %v %v|", c.Out, c.V1, c.Op, c.V2)
|
|
}
|
|
|
|
func newCircuit(name string) *Circuit {
|
|
return &Circuit{Name: name, gateMap: make(map[string]*gate)}
|
|
}
|
|
|
|
func (p *Program) addFunction(constraint *Constraint) (c *Circuit) {
|
|
name := constraint.Out
|
|
|
|
b, name2, _ := isFunction(name)
|
|
if !b {
|
|
panic(fmt.Sprintf("not a function: %v", constraint))
|
|
}
|
|
name = name2
|
|
|
|
if _, ex := p.functions[name]; ex {
|
|
panic("function already declared")
|
|
}
|
|
|
|
c = newCircuit(name)
|
|
|
|
p.functions[name] = c
|
|
|
|
renamedInputs := make([]string, len(constraint.Inputs))
|
|
//I need the inputs to be defined as input constraints for each function for later renaming conventions
|
|
//if constraint.Literal == "main" {
|
|
for i, in := range constraint.Inputs {
|
|
newConstr := &Constraint{
|
|
Op: IN,
|
|
Out: in,
|
|
}
|
|
//if name == "main" {
|
|
// p.addGlobalInput(*newConstr)
|
|
//}
|
|
c.addConstraint(newConstr)
|
|
renamedInputs[i] = newConstr.Out
|
|
}
|
|
//}
|
|
|
|
c.Inputs = renamedInputs
|
|
return
|
|
|
|
}
|
|
|
|
func (circ *Circuit) addConstraint(constraint *Constraint) {
|
|
if _, ex := circ.gateMap[constraint.Out]; ex {
|
|
panic("already used FlatConstraint")
|
|
}
|
|
gateToAdd := &gate{value: constraint}
|
|
|
|
if constraint.Op == DIVIDE {
|
|
constraint.Op = MULTIPLY
|
|
constraint.invert = true
|
|
}
|
|
if constraint.Op == MINUS {
|
|
constraint.Op = PLUS
|
|
constraint.negate = true
|
|
}
|
|
|
|
//todo this is dangerous.. if someone would use out as variable name, things would be fucked
|
|
if constraint.Out == "out" {
|
|
constraint.Out = circ.Name //composeNewFunction(circ.Name, circ.Inputs)
|
|
circ.root = gateToAdd
|
|
} else {
|
|
constraint.Out = circ.renamer(constraint.Out)
|
|
}
|
|
|
|
constraint.V1 = circ.renamer(constraint.V1)
|
|
constraint.V2 = circ.renamer(constraint.V2)
|
|
|
|
circ.gateMap[constraint.Out] = gateToAdd
|
|
}
|
|
|
|
func (circ *Circuit) currentOutputName() string {
|
|
|
|
return composeNewFunction(circ.Name, circ.currentOutputs())
|
|
}
|
|
|
|
func (circ *Circuit) currentOutputs() []string {
|
|
|
|
renamedInputs := make([]string, len(circ.Inputs))
|
|
for i, in := range circ.Inputs {
|
|
if _, ex := circ.gateMap[in]; !ex {
|
|
panic("not existing input")
|
|
}
|
|
renamedInputs[i] = circ.gateMap[in].value.Out
|
|
}
|
|
|
|
return renamedInputs
|
|
|
|
}
|
|
|
|
func (circ *Circuit) renamer(constraint string) string {
|
|
|
|
if constraint == "" {
|
|
return ""
|
|
}
|
|
|
|
if b, _ := isValue(constraint); b {
|
|
circ.gateMap[constraint] = &gate{value: &Constraint{Op: CONST, Out: constraint}}
|
|
return constraint
|
|
}
|
|
|
|
if b, name, inputs := isFunction(constraint); b {
|
|
renamedInputs := make([]string, len(inputs))
|
|
for i, in := range inputs {
|
|
renamedInputs[i] = circ.renamer(in)
|
|
}
|
|
nn := composeNewFunction(name, renamedInputs)
|
|
circ.gateMap[nn] = &gate{value: &Constraint{Op: FUNC, Out: nn, Inputs: renamedInputs}}
|
|
return nn
|
|
}
|
|
|
|
return circ.Name + variableIndicationSign + constraint
|
|
|
|
}
|
|
|
|
//func (circ *Circuit) renameInputs(inputs []string) {
|
|
// if len(inputs) != len(circ.Inputs) {
|
|
// panic("given inputs != circuit.Inputs")
|
|
// }
|
|
// mapping := make(map[string]string)
|
|
// for i := 0; i < len(inputs); i++ {
|
|
// if _, ex := circ.gateMap[inputs[i]]; ex {
|
|
//
|
|
// //this is a tricky part. So we replace former inputs with the new ones, thereby
|
|
// //it might be, that the new input name has already been used for some output inside the function
|
|
// //currently I dont know an elegant way how to handle this renaming issue
|
|
// if circ.gateMap[inputs[i]].value.Op != IN {
|
|
// panic(fmt.Sprintf("renaming collsion with %s", inputs[i]))
|
|
// }
|
|
//
|
|
// }
|
|
// mapping[circ.Inputs[i]] = inputs[i]
|
|
// }
|
|
// //fmt.Println(mapping)
|
|
// //circ.Inputs = inputs
|
|
// permute := func(in string) string {
|
|
// if out, ex := mapping[in]; ex {
|
|
// return out
|
|
// }
|
|
// return in
|
|
// }
|
|
//
|
|
// permuteListe := func(in []string) []string {
|
|
// for i := 0; i < len(in); i++ {
|
|
// in[i] = permute(in[i])
|
|
// }
|
|
// return in
|
|
// }
|
|
//
|
|
// for _, constraint := range circ.gateMap {
|
|
//
|
|
// if constraint.value.Op == IN {
|
|
// constraint.value.Out = permute(constraint.value.Out)
|
|
// continue
|
|
// }
|
|
//
|
|
// if b, n, in := isFunction(constraint.value.Out); b {
|
|
// constraint.value.Out = composeNewFunction(n, permuteListe(in))
|
|
// constraint.value.Inputs = permuteListe(in)
|
|
// }
|
|
// if b, n, in := isFunction(constraint.value.V1); b {
|
|
// constraint.value.V1 = composeNewFunction(n, permuteListe(in))
|
|
// constraint.value.Inputs = permuteListe(in)
|
|
// }
|
|
// if b, n, in := isFunction(constraint.value.V2); b {
|
|
// constraint.value.V2 = composeNewFunction(n, permuteListe(in))
|
|
// constraint.value.Inputs = permuteListe(in)
|
|
// }
|
|
//
|
|
// constraint.value.V1 = permute(constraint.value.V1)
|
|
// constraint.value.V2 = permute(constraint.value.V2)
|
|
//
|
|
// }
|
|
// return
|
|
//}
|
|
|
|
func getContextFromVariable(in string) string {
|
|
//if strings.Contains(in, variableIndicationSign) {
|
|
// return strings.Split(in, variableIndicationSign)[0]
|
|
//}
|
|
//return ""
|
|
return strings.Split(in, variableIndicationSign)[0]
|
|
}
|
|
|
|
func composeNewFunction(fname string, inputs []string) string {
|
|
builder := strings.Builder{}
|
|
builder.WriteString(fname)
|
|
builder.WriteRune('(')
|
|
for i := 0; i < len(inputs); i++ {
|
|
builder.WriteString(inputs[i])
|
|
if i < len(inputs)-1 {
|
|
builder.WriteRune(',')
|
|
}
|
|
}
|
|
builder.WriteRune(')')
|
|
return builder.String()
|
|
}
|
|
|
|
func max(a, b int) int {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func TreeDepth(g *gate) int {
|
|
return printDepth(g, 0)
|
|
}
|
|
|
|
func printDepth(g *gate, d int) int {
|
|
d = d + 1
|
|
if g.left != nil && g.right != nil {
|
|
return max(printDepth(g.left, d), printDepth(g.right, d))
|
|
} else if g.left != nil {
|
|
return printDepth(g.left, d)
|
|
} else if g.right != nil {
|
|
return printDepth(g.right, d)
|
|
}
|
|
return d
|
|
}
|
|
func CountMultiplicationGates(g *gate) int {
|
|
if g == nil {
|
|
return 0
|
|
}
|
|
if len(g.rightIns) > 0 || len(g.leftIns) > 0 {
|
|
return 1 + CountMultiplicationGates(g.left) + CountMultiplicationGates(g.right)
|
|
} else {
|
|
return CountMultiplicationGates(g.left) + CountMultiplicationGates(g.right)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
//TODO avoid printing multiple times in case of loops
|
|
func PrintTree(g *gate) {
|
|
printTree(g, 0)
|
|
}
|
|
func printTree(g *gate, d int) {
|
|
d += 1
|
|
|
|
if g.leftIns == nil || g.rightIns == nil {
|
|
fmt.Printf("Depth: %v - %s \t \t \t \t \n", d, g.value)
|
|
} else {
|
|
fmt.Printf("Depth: %v - %s \t \t \t \t with l %v and r %v\n", d, g.value, g.leftIns, g.rightIns)
|
|
}
|
|
if g.funcInputs != nil {
|
|
for _, v := range g.funcInputs {
|
|
printTree(v, d)
|
|
}
|
|
}
|
|
|
|
if g.left != nil {
|
|
printTree(g.left, d)
|
|
}
|
|
if g.right != nil {
|
|
printTree(g.right, d)
|
|
}
|
|
}
|
|
|
|
func Xor(a, b bool) bool {
|
|
return (a && !b) || (!a && b)
|
|
}
|
|
|
|
func (g *gate) ExtractValues(in []int) (er error) {
|
|
if b, v1 := isValue(g.value.V1); b {
|
|
if b2, v2 := isValue(g.value.V2); b2 {
|
|
in = append(in, v1, v2)
|
|
return nil
|
|
}
|
|
}
|
|
return errors.New(fmt.Sprintf("Gate \"%s\" has no int values", g.value))
|
|
}
|
|
|
|
func (g *gate) OperationType() Token {
|
|
return g.value.Op
|
|
}
|
|
|
|
//returns index of e if its in arr
|
|
//return -1 if e not in arr
|
|
func indexInArray(arr []string, e string) int {
|
|
for i, a := range arr {
|
|
if a == e {
|
|
return i
|
|
}
|
|
}
|
|
panic("lul")
|
|
return -1
|
|
}
|
|
func isValue(a string) (bool, int) {
|
|
v, err := strconv.Atoi(a)
|
|
if err != nil {
|
|
return false, 0
|
|
}
|
|
return true, v
|
|
}
|
|
func isFunction(a string) (tf bool, name string, inputs []string) {
|
|
|
|
if !strings.ContainsRune(a, '(') && !strings.ContainsRune(a, ')') {
|
|
return false, "", nil
|
|
}
|
|
name = strings.Split(a, "(")[0]
|
|
|
|
// read string inside ( )
|
|
rgx := regexp.MustCompile(`\((.*?)\)`)
|
|
insideParenthesis := rgx.FindStringSubmatch(a)
|
|
varsString := strings.Replace(insideParenthesis[1], " ", "", -1)
|
|
inputs = strings.Split(varsString, ",")
|
|
|
|
return true, name, inputs
|
|
}
|
|
|
|
type Inputs struct {
|
|
Private []*big.Int
|
|
Publics []*big.Int
|
|
}
|