From de5b60b82602db203f100c5f4f8f4eaa9796dddf Mon Sep 17 00:00:00 2001 From: arnaucube Date: Thu, 30 May 2019 21:39:00 +0200 Subject: [PATCH] add allow import circuits in circuits language compiler --- README.md | 2 +- circuitcompiler/circuit-test-1.circuit | 8 +++ circuitcompiler/circuit-test-2.circuit | 7 +++ circuitcompiler/circuit_test.go | 74 ++++++++++++++++++++++++ circuitcompiler/parser.go | 23 ++++++++ circuitexamples/import-example.circuit | 8 +++ circuitexamples/imported-example.circuit | 7 +++ vim-syntax/syntax/go-snark-circuit.vim | 4 ++ 8 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 circuitcompiler/circuit-test-1.circuit create mode 100644 circuitcompiler/circuit-test-2.circuit create mode 100644 circuitexamples/import-example.circuit create mode 100644 circuitexamples/imported-example.circuit diff --git a/README.md b/README.md index a8c53f3..c79fd17 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Minimal complete flow implementation: Improvements from the minimal implementation: - [x] allow to call functions in circuits language -- [ ] allow `import` in circuits language +- [x] allow `import` in circuits language - [ ] allow `for` in circuits language - [ ] move witness values calculation outside the setup phase - [ ] Groth16 diff --git a/circuitcompiler/circuit-test-1.circuit b/circuitcompiler/circuit-test-1.circuit new file mode 100644 index 0000000..1c909fa --- /dev/null +++ b/circuitcompiler/circuit-test-1.circuit @@ -0,0 +1,8 @@ +import "circuit-test-2.circuit" + +func main(private s0, public s1): + s3 = exp3(s0) + s4 = sum(s3, s0) + s5 = s4 + 5 + equals(s1, s5) + out = 1 * 1 diff --git a/circuitcompiler/circuit-test-2.circuit b/circuitcompiler/circuit-test-2.circuit new file mode 100644 index 0000000..44fb4b0 --- /dev/null +++ b/circuitcompiler/circuit-test-2.circuit @@ -0,0 +1,7 @@ +func exp3(private a): + b = a * a + c = a * b + return c +func sum(private a, private b): + c = a + b + return c diff --git a/circuitcompiler/circuit_test.go b/circuitcompiler/circuit_test.go index dcf6dab..3224752 100644 --- a/circuitcompiler/circuit_test.go +++ b/circuitcompiler/circuit_test.go @@ -1,7 +1,9 @@ package circuitcompiler import ( + "bufio" "math/big" + "os" "strings" "testing" @@ -172,3 +174,75 @@ func TestCircuitWithFuncCallsParser(t *testing.T) { assert.Equal(t, len(circuit.PublicInputs), 1) assert.Equal(t, len(circuit.PrivateInputs), 1) } + +func TestCircuitFromFileWithImports(t *testing.T) { + circuitFile, err := os.Open("./circuit-test-1.circuit") + assert.Nil(t, err) + + parser := NewParser(bufio.NewReader(circuitFile)) + circuit, err := parser.Parse() + assert.Nil(t, err) + + // flat code to R1CS + a, b, c := circuit.GenerateR1CS() + assert.Equal(t, "s0", circuit.PrivateInputs[0]) + assert.Equal(t, "s1", circuit.PublicInputs[0]) + + assert.Equal(t, []string{"one", "s1", "s0", "b0", "s3", "s4", "s5", "out"}, circuit.Signals) + + // expected result + b0 := big.NewInt(int64(0)) + b1 := big.NewInt(int64(1)) + b5 := big.NewInt(int64(5)) + aExpected := [][]*big.Int{ + []*big.Int{b0, b0, b1, b0, b0, b0, b0, b0}, + []*big.Int{b0, b0, b1, b0, b0, b0, b0, b0}, + []*big.Int{b0, b0, b1, b0, b1, b0, b0, b0}, + []*big.Int{b5, b0, b0, b0, b0, b1, b0, b0}, + []*big.Int{b0, b0, b0, b0, b0, b0, b1, b0}, + []*big.Int{b0, b1, b0, b0, b0, b0, b0, b0}, + []*big.Int{b1, b0, b0, b0, b0, b0, b0, b0}, + } + bExpected := [][]*big.Int{ + []*big.Int{b0, b0, b1, b0, b0, b0, b0, b0}, + []*big.Int{b0, b0, b0, b1, b0, b0, b0, b0}, + []*big.Int{b1, b0, b0, b0, b0, b0, b0, b0}, + []*big.Int{b1, b0, b0, b0, b0, b0, b0, b0}, + []*big.Int{b1, b0, b0, b0, b0, b0, b0, b0}, + []*big.Int{b1, b0, b0, b0, b0, b0, b0, b0}, + []*big.Int{b1, b0, b0, b0, b0, b0, b0, b0}, + } + cExpected := [][]*big.Int{ + []*big.Int{b0, b0, b0, b1, b0, b0, b0, b0}, + []*big.Int{b0, b0, b0, b0, b1, b0, b0, b0}, + []*big.Int{b0, b0, b0, b0, b0, b1, b0, b0}, + []*big.Int{b0, b0, b0, b0, b0, b0, b1, b0}, + []*big.Int{b0, b1, b0, b0, b0, b0, b0, b0}, + []*big.Int{b0, b0, b0, b0, b0, b0, b1, b0}, + []*big.Int{b0, b0, b0, b0, b0, b0, b0, b1}, + } + + assert.Equal(t, aExpected, a) + assert.Equal(t, bExpected, b) + assert.Equal(t, cExpected, c) + + b3 := big.NewInt(int64(3)) + privateInputs := []*big.Int{b3} + b35 := big.NewInt(int64(35)) + publicInputs := []*big.Int{b35} + // Calculate Witness + w, err := circuit.CalculateWitness(privateInputs, publicInputs) + assert.Nil(t, err) + b9 := big.NewInt(int64(9)) + b27 := big.NewInt(int64(27)) + b30 := big.NewInt(int64(30)) + wExpected := []*big.Int{b1, b35, b3, b9, b27, b30, b35, b1} + assert.Equal(t, wExpected, w) + + // circuitJson, _ := json.Marshal(circuit) + // fmt.Println("circuit:", string(circuitJson)) + + assert.Equal(t, circuit.NPublic, 1) + assert.Equal(t, len(circuit.PublicInputs), 1) + assert.Equal(t, len(circuit.PrivateInputs), 1) +} diff --git a/circuitcompiler/parser.go b/circuitcompiler/parser.go index 6bbf42b..478576c 100644 --- a/circuitcompiler/parser.go +++ b/circuitcompiler/parser.go @@ -1,6 +1,7 @@ package circuitcompiler import ( + "bufio" "errors" "fmt" "io" @@ -117,6 +118,19 @@ func (p *Parser) parseLine() (*Constraint, error) { c.Out = varToReturn return c, nil } + if c.Literal == "import" { + line, err := p.s.r.ReadString('\n') + if err != nil { + return c, err + } + // read string inside " " + path := strings.TrimLeft(strings.TrimRight(line, `"`), `"`) + path = strings.Replace(path, `"`, "", -1) + path = strings.Replace(path, " ", "", -1) + path = strings.Replace(path, "\n", "", -1) + c.Out = path + return c, nil + } _, lit = p.scanIgnoreWhitespace() // skip = c.Literal += lit @@ -302,6 +316,15 @@ func (p *Parser) Parse() (*Circuit, error) { continue } + if constraint.Literal == "import" { + circuitFile, err := os.Open(constraint.Out) + if err != nil { + panic(errors.New("imported path error: " + constraint.Out)) + } + parser := NewParser(bufio.NewReader(circuitFile)) + _, err = parser.Parse() // this will add the imported file funcs into the `circuits` map + continue + } circuits[currCircuit].Constraints = append(circuits[currCircuit].Constraints, *constraint) isVal, _ := isValue(constraint.V1) diff --git a/circuitexamples/import-example.circuit b/circuitexamples/import-example.circuit new file mode 100644 index 0000000..f9ace09 --- /dev/null +++ b/circuitexamples/import-example.circuit @@ -0,0 +1,8 @@ +import "imported-example.circuit" + +func main(private s0, public s1): + s3 = exp3(s0) + s4 = sum(s3, s0) + s5 = s4 + 5 + equals(s1, s5) + out = 1 * 1 diff --git a/circuitexamples/imported-example.circuit b/circuitexamples/imported-example.circuit new file mode 100644 index 0000000..44fb4b0 --- /dev/null +++ b/circuitexamples/imported-example.circuit @@ -0,0 +1,7 @@ +func exp3(private a): + b = a * a + c = a * b + return c +func sum(private a, private b): + c = a + b + return c diff --git a/vim-syntax/syntax/go-snark-circuit.vim b/vim-syntax/syntax/go-snark-circuit.vim index 0a0ea81..876950d 100644 --- a/vim-syntax/syntax/go-snark-circuit.vim +++ b/vim-syntax/syntax/go-snark-circuit.vim @@ -24,12 +24,14 @@ syn keyword goSnarkCircuitPrivatePublic private public syn keyword goSnarkCircuitOut out syn keyword goSnarkCircuitEquals equals syn keyword goSnarkCircuitFunction func +syn keyword goSnarkCircuitImport import syn match goSnarkCircuitFuncCall /\<\K\k*\ze\s*(/ syn keyword goSnarkCircuitPrivate private nextgroup=goSnarkCircuitInputName skipwhite syn keyword goSnarkCircuitPublic public nextgroup=goSnarkCircuitInputName skipwhite syn match goSnarkCircuitInputName '\i\+' contained syn match goSnarkCircuitBraces "[{}\[\]]" syn match goSnarkCircuitParens "[()]" +syn region goSnarkCircuitPath start=+"+ skip=+\\\\\|\\"+ end=+"\|$+ syn sync fromstart syn sync maxlines=100 @@ -44,12 +46,14 @@ hi def link goSnarkCircuitOpSymbols Operator hi def link goSnarkCircuitFuncCall Function hi def link goSnarkCircuitEquals Identifier hi def link goSnarkCircuitFunction Keyword +hi def link goSnarkCircuitImport Keyword hi def link goSnarkCircuitBraces Function hi def link goSnarkCircuitPrivate Keyword hi def link goSnarkCircuitPublic Keyword hi def link goSnarkCircuitInputName Special hi def link goSnarkCircuitOut Special hi def link goSnarkCircuitPrivatePublic Keyword +hi def link goSnarkCircuitPath String let b:current_syntax = "go-snark-circuit" if main_syntax == 'go-snark-circuit'