From d42dffff22e48b2428769d4922afbfbcc78692fd Mon Sep 17 00:00:00 2001 From: arnaucube Date: Mon, 24 Dec 2018 16:53:38 +0100 Subject: [PATCH] starting circuitcompiler, lexer and parser (simple version) --- README.md | 1 + circuitcompiler/circuit.go | 10 +++ circuitcompiler/circuit_test.go | 36 ++++++++ circuitcompiler/lexer.go | 144 ++++++++++++++++++++++++++++++++ circuitcompiler/parser.go | 92 ++++++++++++++++++++ compiler/circuit.go | 7 -- go.mod | 7 +- go.sum | 12 ++- snark.go | 12 +-- snark_test.go | 6 +- 10 files changed, 300 insertions(+), 27 deletions(-) create mode 100644 circuitcompiler/circuit.go create mode 100644 circuitcompiler/circuit_test.go create mode 100644 circuitcompiler/lexer.go create mode 100644 circuitcompiler/parser.go delete mode 100644 compiler/circuit.go diff --git a/README.md b/README.md index 6222d3c..bfac522 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ zkSNARK library implementation in Go - [![GoDoc](https://godoc.org/github.com/arnaucube/go-snark/bn128?status.svg)](https://godoc.org/github.com/arnaucube/go-snark/bn128) bn128 (more details: https://github.com/arnaucube/go-snark/tree/master/bn128) - [![GoDoc](https://godoc.org/github.com/arnaucube/go-snark/fields?status.svg)](https://godoc.org/github.com/arnaucube/go-snark/fields) Finite Fields operations - [![GoDoc](https://godoc.org/github.com/arnaucube/go-snark/r1csqap?status.svg)](https://godoc.org/github.com/arnaucube/go-snark/r1csqap) R1CS to QAP (more details: https://github.com/arnaucube/go-snark/tree/master/r1csqap) +- [![GoDoc](https://godoc.org/github.com/arnaucube/go-snark/circuitcompiler?status.svg)](https://godoc.org/github.com/arnaucube/go-snark/circuitcompiler) Circuit Compiler Example: ```go diff --git a/circuitcompiler/circuit.go b/circuitcompiler/circuit.go new file mode 100644 index 0000000..a70ce2b --- /dev/null +++ b/circuitcompiler/circuit.go @@ -0,0 +1,10 @@ +package circuitcompiler + +type Circuit struct { + NVars int + NPublic int + NSignals int + Inputs []int + Witness []int + Constraints []Constraint +} diff --git a/circuitcompiler/circuit_test.go b/circuitcompiler/circuit_test.go new file mode 100644 index 0000000..70decd9 --- /dev/null +++ b/circuitcompiler/circuit_test.go @@ -0,0 +1,36 @@ +package circuitcompiler + +import ( + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCircuitParser(t *testing.T) { + /* + input: + def test(): + y = x**3 + return x + y + 5 + + flattened: + m1 = s1 * s1 + m2 = m1 * s1 + m3 = m2 + s1 + out = m3 + 5 + */ + raw := ` + y = x^x + z = x + y + out = z + 5 + ` + parser := NewParser(strings.NewReader(raw)) + res, err := parser.Parse() + assert.Nil(t, err) + fmt.Println(res) + + // flat code + // flat code to R1CS +} diff --git a/circuitcompiler/lexer.go b/circuitcompiler/lexer.go new file mode 100644 index 0000000..75be8cd --- /dev/null +++ b/circuitcompiler/lexer.go @@ -0,0 +1,144 @@ +package circuitcompiler + +import ( + "bufio" + "bytes" + "io" +) + +type OperatorSymbol int +type Token int + +const ( + ILLEGAL Token = iota + WS + EOF + + IDENT // val + + VAR // var + CONST // const value + + EQ // = + PLUS // + + MINUS // - + MULTIPLY // * + DIVIDE // / + EXP // ^ + + OUT +) + +var eof = rune(0) + +func isWhitespace(ch rune) bool { + return ch == ' ' || ch == '\t' || ch == '\n' +} + +func isLetter(ch rune) bool { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') +} +func isDigit(ch rune) bool { + return (ch >= '0' && ch <= '9') +} + +type Scanner struct { + r *bufio.Reader +} + +func NewScanner(r io.Reader) *Scanner { + return &Scanner{r: bufio.NewReader(r)} +} + +func (s *Scanner) read() rune { + ch, _, err := s.r.ReadRune() + if err != nil { + return eof + } + return ch +} + +func (s *Scanner) unread() { + _ = s.r.UnreadRune() +} + +func (s *Scanner) Scan() (tok Token, lit string) { + ch := s.read() + + if isWhitespace(ch) { + // space + s.unread() + return s.scanWhitespace() + } else if isLetter(ch) { + // letter + s.unread() + return s.scanIndent() + } else if isDigit(ch) { + s.unread() + return s.scanIndent() + } + + switch ch { + case eof: + return EOF, "" + case '=': + return EQ, "=" + case '+': + return PLUS, "+" + case '-': + return MINUS, "-" + case '*': + return MULTIPLY, "*" + case '/': + return DIVIDE, "/" + case '^': + return EXP, "^" + } + + return ILLEGAL, string(ch) +} + +func (s *Scanner) scanWhitespace() (token Token, lit string) { + var buf bytes.Buffer + buf.WriteRune(s.read()) + + for { + if ch := s.read(); ch == eof { + break + } else if !isWhitespace(ch) { + s.unread() + break + } else { + _, _ = buf.WriteRune(ch) + } + } + return WS, buf.String() +} + +func (s *Scanner) scanIndent() (tok Token, lit string) { + var buf bytes.Buffer + buf.WriteRune(s.read()) + + for { + if ch := s.read(); ch == eof { + break + } else if !isLetter(ch) && !isDigit(ch) { + s.unread() + break + } else { + _, _ = buf.WriteRune(ch) + } + } + switch buf.String() { + case "var": + return VAR, buf.String() + } + + if len(buf.String()) == 1 { + return Token(rune(buf.String()[0])), buf.String() + } + if buf.String() == "out" { + return OUT, buf.String() + } + return IDENT, buf.String() +} diff --git a/circuitcompiler/parser.go b/circuitcompiler/parser.go new file mode 100644 index 0000000..033d2d4 --- /dev/null +++ b/circuitcompiler/parser.go @@ -0,0 +1,92 @@ +package circuitcompiler + +import ( + "errors" + "io" +) + +type Parser struct { + s *Scanner + buf struct { + tok Token // last read token + lit string // last read literal + n int // buffer size (max=1) + } +} + +type Constraint struct { + // v1 op v2 = out + Op Token + V1 Token + V2 Token + Out Token + Literal string +} + +func NewParser(r io.Reader) *Parser { + return &Parser{s: NewScanner(r)} +} + +func (p *Parser) scan() (tok Token, lit string) { + // if there is a token in the buffer return it + if p.buf.n != 0 { + p.buf.n = 0 + return p.buf.tok, p.buf.lit + } + tok, lit = p.s.Scan() + + p.buf.tok, p.buf.lit = tok, lit + + return +} + +func (p *Parser) unscan() { + p.buf.n = 1 +} + +func (p *Parser) scanIgnoreWhitespace() (tok Token, lit string) { + tok, lit = p.scan() + if tok == WS { + tok, lit = p.scan() + } + return +} + +func (p *Parser) ParseLine() (*Constraint, error) { + /* + in this version, + line will be for example s3 = s1 * s4 + this is: + val op val op val + ident op ident op ident + */ + c := &Constraint{} + var lit string + c.Out, lit = p.scanIgnoreWhitespace() + c.Literal += lit + _, lit = p.scanIgnoreWhitespace() // skip = + c.Literal += lit + c.V1, lit = p.scanIgnoreWhitespace() + c.Literal += lit + c.Op, lit = p.scanIgnoreWhitespace() + c.Literal += lit + c.V2, lit = p.scanIgnoreWhitespace() + c.Literal += lit + if c.Out == EOF { + return nil, errors.New("eof in parseline") + } + return c, nil +} + +func (p *Parser) Parse() (*Circuit, error) { + circuit := &Circuit{} + for { + constraint, err := p.ParseLine() + if err != nil { + // return circuit, err + break + } + circuit.Constraints = append(circuit.Constraints, *constraint) + } + return circuit, nil +} diff --git a/compiler/circuit.go b/compiler/circuit.go deleted file mode 100644 index e6987f1..0000000 --- a/compiler/circuit.go +++ /dev/null @@ -1,7 +0,0 @@ -package compiler - -type Circuit struct { - NVars int - NPublic int - NSignals int -} diff --git a/go.mod b/go.mod index dbc095e..6bc629d 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,8 @@ module github.com/arnaucube/go-snark require ( - github.com/arnaucube/cryptofun v0.0.0-20181124004321-9b11ae8280bd - github.com/fatih/color v1.7.0 - github.com/mattn/go-colorable v0.0.9 // indirect - github.com/mattn/go-isatty v0.0.4 // indirect + github.com/arnaucube/cryptofun v0.0.0-20181210231954-f5d913b6a74c + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 ) diff --git a/go.sum b/go.sum index 460a134..a6c76b7 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,11 @@ -github.com/arnaucube/cryptofun v0.0.0-20181124004321-9b11ae8280bd h1:NDpNBTFeHNE2IHya+msmKlCzIPGzn8qN3Z2jtegFYT0= +github.com/arnaucube/bn128 v0.0.0-20181124004642-3bb6b68ddbe4/go.mod h1:bIKGJe1ZaHy7HYQML7Me4q38pOpggn3JO1VPPyMe3zI= +github.com/arnaucube/cryptofun v0.0.0-20181124001128-d55d875d7a54/go.mod h1:PZE8kKpHPD1UMrS3mTfAMmEEinGtijSwjxLRqRcD64A= github.com/arnaucube/cryptofun v0.0.0-20181124004321-9b11ae8280bd/go.mod h1:PZE8kKpHPD1UMrS3mTfAMmEEinGtijSwjxLRqRcD64A= +github.com/arnaucube/cryptofun v0.0.0-20181210231954-f5d913b6a74c h1:i0PwrCdHaQXw6KhbIS+VVj1x/GmqHgOs/LCXLeevE3c= +github.com/arnaucube/cryptofun v0.0.0-20181210231954-f5d913b6a74c/go.mod h1:jLYDEjM/9/Q/bpdj+9ZdKh2qDIgS2gqcywjg+93MyGI= +github.com/arnaucube/go-snark v0.0.0-20181207210027-19f7216d0e3d/go.mod h1:gLycS/B43DufBaH0jH8kqiE4A7w5FdOM8I9S416xh2Y= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= diff --git a/snark.go b/snark.go index aa35b92..3187e2a 100644 --- a/snark.go +++ b/snark.go @@ -6,7 +6,7 @@ import ( "os" "github.com/arnaucube/go-snark/bn128" - "github.com/arnaucube/go-snark/compiler" + "github.com/arnaucube/go-snark/circuitcompiler" "github.com/arnaucube/go-snark/fields" "github.com/arnaucube/go-snark/r1csqap" ) @@ -60,7 +60,7 @@ type Proof struct { PublicSignals []*big.Int } -func GenerateTrustedSetup(bn bn128.Bn128, fqR fields.Fq, pf r1csqap.PolynomialField, witnessLength int, circuit compiler.Circuit, alphas, betas, gammas [][]*big.Int, zx []*big.Int) (Setup, error) { +func GenerateTrustedSetup(bn bn128.Bn128, fqR fields.Fq, pf r1csqap.PolynomialField, witnessLength int, circuit circuitcompiler.Circuit, alphas, betas, gammas [][]*big.Int, zx []*big.Int) (Setup, error) { var setup Setup var err error // generate random t value @@ -98,7 +98,7 @@ func GenerateTrustedSetup(bn bn128.Bn128, fqR fields.Fq, pf r1csqap.PolynomialFi if err != nil { return Setup{}, err } - setup.Toxic.RhoB, err = fqR.Rand() + setup.Toxic.RhoB, err = fqR.Rand() if err != nil { return Setup{}, err } @@ -172,7 +172,7 @@ func GenerateTrustedSetup(bn bn128.Bn128, fqR fields.Fq, pf r1csqap.PolynomialFi return setup, nil } -func GenerateProofs(bn bn128.Bn128, f fields.Fq, circuit compiler.Circuit, setup Setup, hx []*big.Int, w []*big.Int) (Proof, error) { +func GenerateProofs(bn bn128.Bn128, f fields.Fq, circuit circuitcompiler.Circuit, setup Setup, hx []*big.Int, w []*big.Int) (Proof, error) { var proof Proof proof.PiA = [3]*big.Int{bn.G1.F.Zero(), bn.G1.F.Zero(), bn.G1.F.Zero()} proof.PiAp = [3]*big.Int{bn.G1.F.Zero(), bn.G1.F.Zero(), bn.G1.F.Zero()} @@ -206,7 +206,7 @@ func GenerateProofs(bn bn128.Bn128, f fields.Fq, circuit compiler.Circuit, setup return proof, nil } -func VerifyProof(bn bn128.Bn128, circuit compiler.Circuit, setup Setup, proof Proof) bool { +func VerifyProof(bn bn128.Bn128, circuit circuitcompiler.Circuit, setup Setup, proof Proof) bool { // e(piA, Va) == e(piA', g2) pairingPiaVa := bn.Pairing(proof.PiA, setup.Vk.Vka) @@ -262,7 +262,7 @@ func VerifyProof(bn bn128.Bn128, circuit compiler.Circuit, setup Setup, proof Pr if !bn.Fq12.Equal(pairingL, pairingR) { return false } else { - fmt.Println("✓ e(Vkx+piA+piC, g2KbetaKgamma) * e(g1KbetaKgamma, piB) == e(piK, g2Kgamma)") + fmt.Println("✓ e(Vkx+piA+piC, g2KbetaKgamma) * e(g1KbetaKgamma, piB) == e(piK, g2Kgamma)") } return true diff --git a/snark_test.go b/snark_test.go index b3e3f7e..29121cc 100644 --- a/snark_test.go +++ b/snark_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/arnaucube/go-snark/bn128" - "github.com/arnaucube/go-snark/compiler" + "github.com/arnaucube/go-snark/circuitcompiler" "github.com/arnaucube/go-snark/fields" "github.com/arnaucube/go-snark/r1csqap" "github.com/stretchr/testify/assert" @@ -52,7 +52,7 @@ func TestZk(t *testing.T) { // wittness = 1, 3, 35, 9, 27, 30 w := []*big.Int{b1, b3, b35, b9, b27, b30} - circuit := compiler.Circuit{ + circuit := circuitcompiler.Circuit{ NVars: 6, NPublic: 0, NSignals: len(w), @@ -69,7 +69,7 @@ func TestZk(t *testing.T) { assert.Equal(t, abc, px) hz := pf.Mul(hx, zx) assert.Equal(t, abc, hz) - + div, rem := pf.Div(px, zx) assert.Equal(t, hx, div) assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(4))