diff --git a/bn128/bn128.go b/bn128/bn128.go index ebef4ee..93075f2 100644 --- a/bn128/bn128.go +++ b/bn128/bn128.go @@ -185,6 +185,7 @@ func (bn128 Bn128) Pairing(p1 [3]*big.Int, p2 [3][2]*big.Int) [2][3][2]*big.Int return res } +// AteG1Precomp is ... type AteG1Precomp struct { Px *big.Int Py *big.Int @@ -199,11 +200,14 @@ func (bn128 Bn128) preComputeG1(p [3]*big.Int) AteG1Precomp { return res } +// EllCoeffs is ... type EllCoeffs struct { Ell0 [2]*big.Int EllVW [2]*big.Int EllVV [2]*big.Int } + +// AteG2Precomp is ... type AteG2Precomp struct { Qx [2]*big.Int Qy [2]*big.Int @@ -345,6 +349,7 @@ func (bn128 Bn128) g2MulByQ(p [3][2]*big.Int) [3][2]*big.Int { } } +// MillerLoop is ... func (bn128 Bn128) MillerLoop(pre1 AteG1Precomp, pre2 AteG2Precomp) [2][3][2]*big.Int { // https://cryptojedi.org/papers/dclxvi-20100714.pdf // https://eprint.iacr.org/2008/096.pdf diff --git a/bn128/g1.go b/bn128/g1.go index 0f5b9e0..87520a8 100644 --- a/bn128/g1.go +++ b/bn128/g1.go @@ -6,11 +6,13 @@ import ( "github.com/arnaucube/go-snark/fields" ) +// G1 is ... type G1 struct { F fields.Fq G [3]*big.Int } +// NewG1 is ... func NewG1(f fields.Fq, g [2]*big.Int) G1 { var g1 G1 g1.F = f @@ -22,13 +24,17 @@ func NewG1(f fields.Fq, g [2]*big.Int) G1 { return g1 } +// Zero is ... func (g1 G1) Zero() [2]*big.Int { return [2]*big.Int{g1.F.Zero(), g1.F.Zero()} } + +// IsZero is ... func (g1 G1) IsZero(p [3]*big.Int) bool { return g1.F.IsZero(p[2]) } +// Add is ... func (g1 G1) Add(p1, p2 [3]*big.Int) [3]*big.Int { // https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates @@ -88,6 +94,7 @@ func (g1 G1) Add(p1, p2 [3]*big.Int) [3]*big.Int { return [3]*big.Int{x3, y3, z3} } +// Neg is ... func (g1 G1) Neg(p [3]*big.Int) [3]*big.Int { return [3]*big.Int{ p[0], @@ -95,9 +102,13 @@ func (g1 G1) Neg(p [3]*big.Int) [3]*big.Int { p[2], } } + +// Sub is ... func (g1 G1) Sub(a, b [3]*big.Int) [3]*big.Int { return g1.Add(a, g1.Neg(b)) } + +// Double is ... func (g1 G1) Double(p [3]*big.Int) [3]*big.Int { // https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates @@ -137,6 +148,7 @@ func (g1 G1) Double(p [3]*big.Int) [3]*big.Int { return [3]*big.Int{x3, y3, z3} } +// MulScalar is ... func (g1 G1) MulScalar(p [3]*big.Int, e *big.Int) [3]*big.Int { // https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Double-and-add // for more possible implementations see g2.go file, at the function g2.MulScalar() @@ -154,6 +166,7 @@ func (g1 G1) MulScalar(p [3]*big.Int, e *big.Int) [3]*big.Int { return q } +// Affine is ... func (g1 G1) Affine(p [3]*big.Int) [2]*big.Int { if g1.IsZero(p) { return g1.Zero() @@ -169,6 +182,7 @@ func (g1 G1) Affine(p [3]*big.Int) [2]*big.Int { return [2]*big.Int{x, y} } +// Equal is ... func (g1 G1) Equal(p1, p2 [3]*big.Int) bool { if g1.IsZero(p1) { return g1.IsZero(p2) diff --git a/bn128/g2.go b/bn128/g2.go index 0c05a3a..0408476 100644 --- a/bn128/g2.go +++ b/bn128/g2.go @@ -6,11 +6,13 @@ import ( "github.com/arnaucube/go-snark/fields" ) +// G2 is ... type G2 struct { F fields.Fq2 G [3][2]*big.Int } +// NewG2 is ... func NewG2(f fields.Fq2, g [2][2]*big.Int) G2 { var g2 G2 g2.F = f @@ -22,13 +24,17 @@ func NewG2(f fields.Fq2, g [2][2]*big.Int) G2 { return g2 } +// Zero is ... func (g2 G2) Zero() [3][2]*big.Int { return [3][2]*big.Int{g2.F.Zero(), g2.F.One(), g2.F.Zero()} } + +// IsZero is ... func (g2 G2) IsZero(p [3][2]*big.Int) bool { return g2.F.IsZero(p[2]) } +// Add is ... func (g2 G2) Add(p1, p2 [3][2]*big.Int) [3][2]*big.Int { // https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates @@ -88,6 +94,7 @@ func (g2 G2) Add(p1, p2 [3][2]*big.Int) [3][2]*big.Int { return [3][2]*big.Int{x3, y3, z3} } +// Neg is ... func (g2 G2) Neg(p [3][2]*big.Int) [3][2]*big.Int { return [3][2]*big.Int{ p[0], @@ -96,10 +103,12 @@ func (g2 G2) Neg(p [3][2]*big.Int) [3][2]*big.Int { } } +// Sub is ... func (g2 G2) Sub(a, b [3][2]*big.Int) [3][2]*big.Int { return g2.Add(a, g2.Neg(b)) } +// Double is ... func (g2 G2) Double(p [3][2]*big.Int) [3][2]*big.Int { // https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates @@ -139,6 +148,7 @@ func (g2 G2) Double(p [3][2]*big.Int) [3][2]*big.Int { return [3][2]*big.Int{x3, y3, z3} } +// MulScalar is ... func (g2 G2) MulScalar(p [3][2]*big.Int, e *big.Int) [3][2]*big.Int { // https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Double-and-add @@ -180,6 +190,7 @@ func (g2 G2) MulScalar(p [3][2]*big.Int, e *big.Int) [3][2]*big.Int { return q } +// Affine is ... func (g2 G2) Affine(p [3][2]*big.Int) [3][2]*big.Int { if g2.IsZero(p) { return g2.Zero() @@ -199,6 +210,7 @@ func (g2 G2) Affine(p [3][2]*big.Int) [3][2]*big.Int { } } +// Equal is ... func (g2 G2) Equal(p1, p2 [3][2]*big.Int) bool { if g2.IsZero(p1) { return g2.IsZero(p2) diff --git a/circuitcompiler/circuit-test-1.circuit b/circuit/circuit-test-1.circuit similarity index 100% rename from circuitcompiler/circuit-test-1.circuit rename to circuit/circuit-test-1.circuit diff --git a/circuitcompiler/circuit-test-2.circuit b/circuit/circuit-test-2.circuit similarity index 100% rename from circuitcompiler/circuit-test-2.circuit rename to circuit/circuit-test-2.circuit diff --git a/circuitcompiler/circuit.go b/circuit/circuit.go similarity index 98% rename from circuitcompiler/circuit.go rename to circuit/circuit.go index 7f4b0af..de7d8a5 100644 --- a/circuitcompiler/circuit.go +++ b/circuit/circuit.go @@ -1,4 +1,4 @@ -package circuitcompiler +package circuit import ( "errors" @@ -143,11 +143,11 @@ func grabVar(signals []string, w []*big.Int, vStr string) *big.Int { vBig := big.NewInt(int64(v)) if isVal { return vBig - } else { - return w[indexInArray(signals, vStr)] } + return w[indexInArray(signals, vStr)] } +// Inputs is ... type Inputs struct { Private []*big.Int Public []*big.Int diff --git a/circuitcompiler/circuit_test.go b/circuit/circuit_test.go similarity index 99% rename from circuitcompiler/circuit_test.go rename to circuit/circuit_test.go index 3224752..ec68d4e 100644 --- a/circuitcompiler/circuit_test.go +++ b/circuit/circuit_test.go @@ -1,4 +1,4 @@ -package circuitcompiler +package circuit import ( "bufio" diff --git a/circuitcompiler/lexer.go b/circuit/lexer.go similarity index 97% rename from circuitcompiler/lexer.go rename to circuit/lexer.go index 92bc45c..7f84ac2 100644 --- a/circuitcompiler/lexer.go +++ b/circuit/lexer.go @@ -1,4 +1,4 @@ -package circuitcompiler +package circuit import ( "bufio" @@ -6,9 +6,13 @@ import ( "io" ) +// OperatorSymbol is ... type OperatorSymbol int + +// Token is ... type Token int +// Tokens const ( ILLEGAL Token = iota WS diff --git a/circuitcompiler/parser.go b/circuit/parser.go similarity index 99% rename from circuitcompiler/parser.go rename to circuit/parser.go index 478576c..5ebd903 100644 --- a/circuitcompiler/parser.go +++ b/circuit/parser.go @@ -1,4 +1,4 @@ -package circuitcompiler +package circuit import ( "bufio" diff --git a/cmd/go-snark/compile.go b/cmd/go-snark/compile.go new file mode 100644 index 0000000..e33f260 --- /dev/null +++ b/cmd/go-snark/compile.go @@ -0,0 +1,98 @@ +package main + +import ( + "bytes" + "fmt" + "log" + "math/big" + "os" + + "github.com/urfave/cli" + + "github.com/arnaucube/go-snark/circuit" + "github.com/arnaucube/go-snark/proof" + "github.com/arnaucube/go-snark/r1csqap" +) + +func compile(context *cli.Context) error { + circuitPath := context.Args().Get(0) + + // load compiled + circuitFile, err := os.Open(circuitPath) + if err != nil { + return err + } + parser := circuit.NewParser(circuitFile) + cir, err := parser.Parse() + if err != nil { + return err + } + log.Printf("circuit: %v\n", cir) + + // load inputs + var inputs circuit.Inputs + if err := loadFromFile(privateFileName, &inputs.Private); err != nil { + return err + } + if err := loadFromFile(publicFileName, &inputs.Public); err != nil { + return err + } + + // calculate witness + w, err := cir.CalculateWitness(inputs.Private, inputs.Public) + if err != nil { + return err + } + log.Printf("w: %v\n", w) + + // flat code to R1CS + a, b, c := cir.GenerateR1CS() + log.Printf("a: %v\n", a) + log.Printf("b: %v\n", b) + log.Printf("c: %v\n", c) + + // R1CS to QAP + alphas, betas, gammas, zx := proof.Utils.PF.R1CSToQAP(a, b, c) + log.Printf("alphas: %v\n", alphas) + log.Printf("betas: %v\n", betas) + log.Printf("gammas: %v\n", gammas) + log.Printf("zx: %v\n", zx) + + ax, bx, cx, px := proof.Utils.PF.CombinePolynomials(w, alphas, betas, gammas) + + hx := proof.Utils.PF.DivisorPolynomial(px, zx) + + // hx==px/zx so px==hx*zx + // assert.Equal(t, px, snark.Utils.PF.Mul(hx, zx)) + if !r1csqap.BigArraysEqual(px, proof.Utils.PF.Mul(hx, zx)) { + return fmt.Errorf("px != hx*zx") + } + + // p(x) = a(x) * b(x) - c(x) == h(x) * z(x) + abc := proof.Utils.PF.Sub(proof.Utils.PF.Mul(ax, bx), cx) + // assert.Equal(t, abc, px) + if !r1csqap.BigArraysEqual(abc, px) { + return fmt.Errorf("abc != px") + } + + hz := proof.Utils.PF.Mul(hx, zx) + if !r1csqap.BigArraysEqual(abc, hz) { + return fmt.Errorf("abc != hz") + } + // assert.Equal(t, abc, hz) + + div, rem := proof.Utils.PF.Div(px, zx) + if !r1csqap.BigArraysEqual(hx, div) { + return fmt.Errorf("hx != div") + } + // assert.Equal(t, hx, div) + // assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(4)) + for _, r := range rem { + if !bytes.Equal(r.Bytes(), big.NewInt(int64(0)).Bytes()) { + return fmt.Errorf("error:error: px/zx rem not equal to zeros") + } + } + + // save circuit + return saveToFile(compiledFileName, cir) +} diff --git a/cmd/go-snark/generate.go b/cmd/go-snark/generate.go new file mode 100644 index 0000000..2d92854 --- /dev/null +++ b/cmd/go-snark/generate.go @@ -0,0 +1,63 @@ +package main + +import ( + "log" + + "github.com/arnaucube/go-snark/circuit" + "github.com/arnaucube/go-snark/proof" + "github.com/urfave/cli" +) + +func generate(context *cli.Context) error { + // load circuit + var cir circuit.Circuit + if err := loadFromFile(compiledFileName, &cir); err != nil { + return err + } + log.Printf("circuit: %v\n", cir) + + // load inputs + var inputs circuit.Inputs + if err := loadFromFile(privateFileName, &inputs.Private); err != nil { + return err + } + if err := loadFromFile(publicFileName, &inputs.Public); err != nil { + return err + } + + // calculate witness + w, err := cir.CalculateWitness(inputs.Private, inputs.Public) + if err != nil { + return err + } + log.Printf("w: %v\n", w) + + // load setup + setup, err := newSetup() + if err != nil { + return err + } + if err := loadFromFile(setupFileName, setup); err != nil { + return err + } + log.Printf("setup: %v\n", setup) + + // R1CS to QAP + alphas, betas, gammas, _ := proof.Utils.PF.R1CSToQAP( + cir.R1CS.A, + cir.R1CS.B, + cir.R1CS.C) + _, _, _, px := proof.Utils.PF.CombinePolynomials(w, alphas, betas, gammas) + hx := proof.Utils.PF.DivisorPolynomial(px, setup.Z()) + log.Printf("hx: %v\n", hx) + + // generate proof + proof, err := setup.Generate(cir, w, px) + if err != nil { + return err + } + log.Printf("proof: %v\n", proof) + + // save proof + return saveToFile(proofFileName, proof) +} diff --git a/cmd/go-snark/loadsave.go b/cmd/go-snark/loadsave.go new file mode 100644 index 0000000..3428612 --- /dev/null +++ b/cmd/go-snark/loadsave.go @@ -0,0 +1,45 @@ +package main + +import ( + "bytes" + "encoding/json" + "io" + "os" +) + +func loadFromReader(r io.Reader, obj interface{}) error { + buf := new(bytes.Buffer) + if _, err := buf.ReadFrom(r); err != nil { + return err + } + return json.Unmarshal(buf.Bytes(), obj) +} + +func loadFromFile(path string, obj interface{}) error { + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + return loadFromReader(f, obj) +} + +func saveToWriter(w io.Writer, obj interface{}) error { + b, err := json.MarshalIndent(obj, "", " ") + if err != nil { + return err + } + if _, err := w.Write(b); err != nil { + return err + } + return nil +} + +func saveToFile(path string, obj interface{}) error { + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + return saveToWriter(f, obj) +} diff --git a/cmd/go-snark/main.go b/cmd/go-snark/main.go index 5af7539..9a79018 100644 --- a/cmd/go-snark/main.go +++ b/cmd/go-snark/main.go @@ -1,509 +1,106 @@ package main import ( - "bufio" - "bytes" - "encoding/json" - "errors" "fmt" - "io/ioutil" - "log" - "math/big" "os" - snark "github.com/arnaucube/go-snark" - "github.com/arnaucube/go-snark/circuitcompiler" - "github.com/arnaucube/go-snark/groth16" - "github.com/arnaucube/go-snark/r1csqap" "github.com/urfave/cli" + + "github.com/arnaucube/go-snark/proof" ) -func panicErr(err error) { - if err != nil { - panic(err) - } -} +const ( + compiledFileName = "compiled.json" + setupFileName = "setup.json" + privateFileName = "private.json" + publicFileName = "public.json" + proofFileName = "proof.json" +) + +const ( + proofSystemPinocchio = iota + proofSystemGroth16 +) + +var proofSystem int var commands = []cli.Command{ { Name: "compile", Aliases: []string{}, Usage: "compile a circuit", - Action: CompileCircuit, + Action: compile, }, { - Name: "trustedsetup", + Name: "setup", Aliases: []string{}, Usage: "generate trusted setup for a circuit", - Action: TrustedSetup, + Action: setup, }, { - Name: "genproofs", + Name: "generate", Aliases: []string{}, Usage: "generate the snark proofs", - Action: GenerateProofs, + Action: generate, }, { Name: "verify", Aliases: []string{}, Usage: "verify the snark proofs", - Action: VerifyProofs, - }, - { - Name: "groth16", - Aliases: []string{}, - Usage: "use groth16 protocol", - Subcommands: []cli.Command{ - { - Name: "trustedsetup", - Aliases: []string{}, - Usage: "generate trusted setup for a circuit", - Action: Groth16TrustedSetup, - }, - { - Name: "genproofs", - Aliases: []string{}, - Usage: "generate the snark proofs", - Action: Groth16GenerateProofs, - }, - { - Name: "verify", - Aliases: []string{}, - Usage: "verify the snark proofs", - Action: Groth16VerifyProofs, - }, - }, + Action: verify, }, } -func main() { - app := cli.NewApp() - app.Name = "go-snarks-cli" - app.Version = "0.0.3-alpha" - app.Flags = []cli.Flag{ - cli.StringFlag{Name: "config"}, - } - app.Commands = commands - - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } -} - -func CompileCircuit(context *cli.Context) error { - fmt.Println("cli") - - circuitPath := context.Args().Get(0) - - // read circuit file - circuitFile, err := os.Open(circuitPath) - panicErr(err) - - // parse circuit code - parser := circuitcompiler.NewParser(bufio.NewReader(circuitFile)) - circuit, err := parser.Parse() - panicErr(err) - fmt.Println("\ncircuit data:", circuit) - - // read privateInputs file - privateInputsFile, err := ioutil.ReadFile("privateInputs.json") - panicErr(err) - // read publicInputs file - publicInputsFile, err := ioutil.ReadFile("publicInputs.json") - panicErr(err) - - // parse inputs from inputsFile - var inputs circuitcompiler.Inputs - err = json.Unmarshal([]byte(string(privateInputsFile)), &inputs.Private) - panicErr(err) - err = json.Unmarshal([]byte(string(publicInputsFile)), &inputs.Public) - panicErr(err) - - // calculate wittness - w, err := circuit.CalculateWitness(inputs.Private, inputs.Public) - panicErr(err) - fmt.Println("\nwitness", w) - - // flat code to R1CS - fmt.Println("\ngenerating R1CS from flat code") - a, b, c := circuit.GenerateR1CS() - fmt.Println("\nR1CS:") - fmt.Println("a:", a) - fmt.Println("b:", b) - fmt.Println("c:", c) - - // R1CS to QAP - alphas, betas, gammas, zx := snark.Utils.PF.R1CSToQAP(a, b, c) - fmt.Println("qap") - fmt.Println(alphas) - fmt.Println(betas) - fmt.Println(gammas) - - ax, bx, cx, px := snark.Utils.PF.CombinePolynomials(w, alphas, betas, gammas) - - hx := snark.Utils.PF.DivisorPolynomial(px, zx) - - // hx==px/zx so px==hx*zx - // assert.Equal(t, px, snark.Utils.PF.Mul(hx, zx)) - if !r1csqap.BigArraysEqual(px, snark.Utils.PF.Mul(hx, zx)) { - panic(errors.New("px != hx*zx")) - } - - // p(x) = a(x) * b(x) - c(x) == h(x) * z(x) - abc := snark.Utils.PF.Sub(snark.Utils.PF.Mul(ax, bx), cx) - // assert.Equal(t, abc, px) - if !r1csqap.BigArraysEqual(abc, px) { - panic(errors.New("abc != px")) - } - hz := snark.Utils.PF.Mul(hx, zx) - if !r1csqap.BigArraysEqual(abc, hz) { - panic(errors.New("abc != hz")) - } - // assert.Equal(t, abc, hz) - - div, rem := snark.Utils.PF.Div(px, zx) - if !r1csqap.BigArraysEqual(hx, div) { - panic(errors.New("hx != div")) +func initProofSystem() error { + switch p := os.Getenv("PROOF_SYSTEM"); p { + case "", "PINOCCHIO": + proofSystem = proofSystemPinocchio + case "GROTH16": + proofSystem = proofSystemGroth16 + default: + return fmt.Errorf("proof system not supported: %v", p) } - // assert.Equal(t, hx, div) - // assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(4)) - for _, r := range rem { - if !bytes.Equal(r.Bytes(), big.NewInt(int64(0)).Bytes()) { - panic(errors.New("error:error: px/zx rem not equal to zeros")) - } - } - - // store circuit to json - jsonData, err := json.Marshal(circuit) - panicErr(err) - // store setup into file - jsonFile, err := os.Create("compiledcircuit.json") - panicErr(err) - defer jsonFile.Close() - jsonFile.Write(jsonData) - jsonFile.Close() - fmt.Println("Compiled Circuit data written to ", jsonFile.Name()) - - return nil -} - -func TrustedSetup(context *cli.Context) error { - // open compiledcircuit.json - compiledcircuitFile, err := ioutil.ReadFile("compiledcircuit.json") - panicErr(err) - var circuit circuitcompiler.Circuit - json.Unmarshal([]byte(string(compiledcircuitFile)), &circuit) - panicErr(err) - - // read privateInputs file - privateInputsFile, err := ioutil.ReadFile("privateInputs.json") - panicErr(err) - // read publicInputs file - publicInputsFile, err := ioutil.ReadFile("publicInputs.json") - panicErr(err) - - // parse inputs from inputsFile - var inputs circuitcompiler.Inputs - err = json.Unmarshal([]byte(string(privateInputsFile)), &inputs.Private) - panicErr(err) - err = json.Unmarshal([]byte(string(publicInputsFile)), &inputs.Public) - panicErr(err) - - // calculate wittness - w, err := circuit.CalculateWitness(inputs.Private, inputs.Public) - panicErr(err) - - // R1CS to QAP - alphas, betas, gammas, _ := snark.Utils.PF.R1CSToQAP(circuit.R1CS.A, circuit.R1CS.B, circuit.R1CS.C) - fmt.Println("qap") - fmt.Println(alphas) - fmt.Println(betas) - fmt.Println(gammas) - - // calculate trusted setup - setup, err := snark.GenerateTrustedSetup(len(w), circuit, alphas, betas, gammas) - panicErr(err) - fmt.Println("\nt:", setup.Toxic.T) - - // remove setup.Toxic - var tsetup snark.Setup - tsetup.Pk = setup.Pk - tsetup.Vk = setup.Vk - tsetup.G1T = setup.G1T - tsetup.G2T = setup.G2T - - // store setup to json - jsonData, err := json.Marshal(tsetup) - panicErr(err) - // store setup into file - jsonFile, err := os.Create("trustedsetup.json") - panicErr(err) - defer jsonFile.Close() - jsonFile.Write(jsonData) - jsonFile.Close() - fmt.Println("Trusted Setup data written to ", jsonFile.Name()) return nil } -func GenerateProofs(context *cli.Context) error { - // open compiledcircuit.json - compiledcircuitFile, err := ioutil.ReadFile("compiledcircuit.json") - panicErr(err) - var circuit circuitcompiler.Circuit - json.Unmarshal([]byte(string(compiledcircuitFile)), &circuit) - panicErr(err) - - // open trustedsetup.json - trustedsetupFile, err := ioutil.ReadFile("trustedsetup.json") - panicErr(err) - var trustedsetup snark.Setup - json.Unmarshal([]byte(string(trustedsetupFile)), &trustedsetup) - panicErr(err) - - // read privateInputs file - privateInputsFile, err := ioutil.ReadFile("privateInputs.json") - panicErr(err) - // read publicInputs file - publicInputsFile, err := ioutil.ReadFile("publicInputs.json") - panicErr(err) - // parse inputs from inputsFile - var inputs circuitcompiler.Inputs - err = json.Unmarshal([]byte(string(privateInputsFile)), &inputs.Private) - panicErr(err) - err = json.Unmarshal([]byte(string(publicInputsFile)), &inputs.Public) - panicErr(err) - - // calculate wittness - w, err := circuit.CalculateWitness(inputs.Private, inputs.Public) - panicErr(err) - fmt.Println("witness", w) - - // flat code to R1CS - a := circuit.R1CS.A - b := circuit.R1CS.B - c := circuit.R1CS.C - // R1CS to QAP - alphas, betas, gammas, _ := snark.Utils.PF.R1CSToQAP(a, b, c) - _, _, _, px := snark.Utils.PF.CombinePolynomials(w, alphas, betas, gammas) - hx := snark.Utils.PF.DivisorPolynomial(px, trustedsetup.Pk.Z) - - fmt.Println(circuit) - fmt.Println(trustedsetup.G1T) - fmt.Println(hx) - fmt.Println(w) - proof, err := snark.GenerateProofs(circuit, trustedsetup, w, px) - panicErr(err) - - fmt.Println("\n proofs:") - fmt.Println(proof) - - // store proofs to json - jsonData, err := json.Marshal(proof) - panicErr(err) - // store proof into file - jsonFile, err := os.Create("proofs.json") - panicErr(err) - defer jsonFile.Close() - jsonFile.Write(jsonData) - jsonFile.Close() - fmt.Println("Proofs data written to ", jsonFile.Name()) - return nil -} - -func VerifyProofs(context *cli.Context) error { - // open proofs.json - proofsFile, err := ioutil.ReadFile("proofs.json") - panicErr(err) - var proof snark.Proof - json.Unmarshal([]byte(string(proofsFile)), &proof) - panicErr(err) - - // open compiledcircuit.json - compiledcircuitFile, err := ioutil.ReadFile("compiledcircuit.json") - panicErr(err) - var circuit circuitcompiler.Circuit - json.Unmarshal([]byte(string(compiledcircuitFile)), &circuit) - panicErr(err) - - // open trustedsetup.json - trustedsetupFile, err := ioutil.ReadFile("trustedsetup.json") - panicErr(err) - var trustedsetup snark.Setup - json.Unmarshal([]byte(string(trustedsetupFile)), &trustedsetup) - panicErr(err) - - // read publicInputs file - publicInputsFile, err := ioutil.ReadFile("publicInputs.json") - panicErr(err) - var publicSignals []*big.Int - err = json.Unmarshal([]byte(string(publicInputsFile)), &publicSignals) - panicErr(err) - - verified := snark.VerifyProof(circuit, trustedsetup, proof, publicSignals, true) - if !verified { - fmt.Println("ERROR: proofs not verified") - } else { - fmt.Println("Proofs verified") +func newSetup() (proof.Setup, error) { + var s proof.Setup + switch proofSystem { + case proofSystemPinocchio: + s = &proof.PinocchioSetup{} + case proofSystemGroth16: + s = &proof.Groth16Setup{} + default: + return nil, fmt.Errorf("proof system not supported: %v", proofSystem) } - return nil -} - -func Groth16TrustedSetup(context *cli.Context) error { - // open compiledcircuit.json - compiledcircuitFile, err := ioutil.ReadFile("compiledcircuit.json") - panicErr(err) - var circuit circuitcompiler.Circuit - json.Unmarshal([]byte(string(compiledcircuitFile)), &circuit) - panicErr(err) - - // read privateInputs file - privateInputsFile, err := ioutil.ReadFile("privateInputs.json") - panicErr(err) - // read publicInputs file - publicInputsFile, err := ioutil.ReadFile("publicInputs.json") - panicErr(err) - - // parse inputs from inputsFile - var inputs circuitcompiler.Inputs - err = json.Unmarshal([]byte(string(privateInputsFile)), &inputs.Private) - panicErr(err) - err = json.Unmarshal([]byte(string(publicInputsFile)), &inputs.Public) - panicErr(err) - - // calculate wittness - w, err := circuit.CalculateWitness(inputs.Private, inputs.Public) - panicErr(err) - - // R1CS to QAP - alphas, betas, gammas, _ := snark.Utils.PF.R1CSToQAP(circuit.R1CS.A, circuit.R1CS.B, circuit.R1CS.C) - fmt.Println("qap") - fmt.Println(alphas) - fmt.Println(betas) - fmt.Println(gammas) - - // calculate trusted setup - setup, err := groth16.GenerateTrustedSetup(len(w), circuit, alphas, betas, gammas) - panicErr(err) - fmt.Println("\nt:", setup.Toxic.T) - - // remove setup.Toxic - var tsetup groth16.Setup - tsetup.Pk = setup.Pk - tsetup.Vk = setup.Vk - - // store setup to json - jsonData, err := json.Marshal(tsetup) - panicErr(err) - // store setup into file - jsonFile, err := os.Create("trustedsetup.json") - panicErr(err) - defer jsonFile.Close() - jsonFile.Write(jsonData) - jsonFile.Close() - fmt.Println("Trusted Setup data written to ", jsonFile.Name()) - return nil + return s, nil } -func Groth16GenerateProofs(context *cli.Context) error { - // open compiledcircuit.json - compiledcircuitFile, err := ioutil.ReadFile("compiledcircuit.json") - panicErr(err) - var circuit circuitcompiler.Circuit - json.Unmarshal([]byte(string(compiledcircuitFile)), &circuit) - panicErr(err) - - // open trustedsetup.json - trustedsetupFile, err := ioutil.ReadFile("trustedsetup.json") - panicErr(err) - var trustedsetup groth16.Setup - json.Unmarshal([]byte(string(trustedsetupFile)), &trustedsetup) - panicErr(err) - - // read privateInputs file - privateInputsFile, err := ioutil.ReadFile("privateInputs.json") - panicErr(err) - // read publicInputs file - publicInputsFile, err := ioutil.ReadFile("publicInputs.json") - panicErr(err) - // parse inputs from inputsFile - var inputs circuitcompiler.Inputs - err = json.Unmarshal([]byte(string(privateInputsFile)), &inputs.Private) - panicErr(err) - err = json.Unmarshal([]byte(string(publicInputsFile)), &inputs.Public) - panicErr(err) - - // calculate wittness - w, err := circuit.CalculateWitness(inputs.Private, inputs.Public) - panicErr(err) - fmt.Println("witness", w) - - // flat code to R1CS - a := circuit.R1CS.A - b := circuit.R1CS.B - c := circuit.R1CS.C - // R1CS to QAP - alphas, betas, gammas, _ := groth16.Utils.PF.R1CSToQAP(a, b, c) - _, _, _, px := groth16.Utils.PF.CombinePolynomials(w, alphas, betas, gammas) - hx := groth16.Utils.PF.DivisorPolynomial(px, trustedsetup.Pk.Z) - - fmt.Println(circuit) - fmt.Println(trustedsetup.Pk.PowersTauDelta) - fmt.Println(hx) - fmt.Println(w) - proof, err := groth16.GenerateProofs(circuit, trustedsetup, w, px) - panicErr(err) - - fmt.Println("\n proofs:") - fmt.Println(proof) - - // store proofs to json - jsonData, err := json.Marshal(proof) - panicErr(err) - // store proof into file - jsonFile, err := os.Create("proofs.json") - panicErr(err) - defer jsonFile.Close() - jsonFile.Write(jsonData) - jsonFile.Close() - fmt.Println("Proofs data written to ", jsonFile.Name()) - return nil +func newProof() (proof.Proof, error) { + var p proof.Proof + switch proofSystem { + case proofSystemPinocchio: + p = &proof.PinocchioProof{} + case proofSystemGroth16: + p = &proof.Groth16Proof{} + default: + return nil, fmt.Errorf("proof system not supported: %v", proofSystem) + } + return p, nil } -func Groth16VerifyProofs(context *cli.Context) error { - // open proofs.json - proofsFile, err := ioutil.ReadFile("proofs.json") - panicErr(err) - var proof groth16.Proof - json.Unmarshal([]byte(string(proofsFile)), &proof) - panicErr(err) - - // open compiledcircuit.json - compiledcircuitFile, err := ioutil.ReadFile("compiledcircuit.json") - panicErr(err) - var circuit circuitcompiler.Circuit - json.Unmarshal([]byte(string(compiledcircuitFile)), &circuit) - panicErr(err) - - // open trustedsetup.json - trustedsetupFile, err := ioutil.ReadFile("trustedsetup.json") - panicErr(err) - var trustedsetup groth16.Setup - json.Unmarshal([]byte(string(trustedsetupFile)), &trustedsetup) - panicErr(err) - - // read publicInputs file - publicInputsFile, err := ioutil.ReadFile("publicInputs.json") - panicErr(err) - var publicSignals []*big.Int - err = json.Unmarshal([]byte(string(publicInputsFile)), &publicSignals) - panicErr(err) - - verified := groth16.VerifyProof(circuit, trustedsetup, proof, publicSignals, true) - if !verified { - fmt.Println("ERROR: proofs not verified") - } else { - fmt.Println("Proofs verified") +func main() { + if err := initProofSystem(); err != nil { + panic(err) + } + app := cli.NewApp() + app.Name = "go-snark" + app.Version = "0.0.3-alpha" + app.Flags = []cli.Flag{ + cli.StringFlag{Name: "config"}, + } + app.Commands = commands + if err := app.Run(os.Args); err != nil { + panic(err) } - return nil } diff --git a/cmd/go-snark/setup.go b/cmd/go-snark/setup.go new file mode 100644 index 0000000..1302106 --- /dev/null +++ b/cmd/go-snark/setup.go @@ -0,0 +1,53 @@ +package main + +import ( + "log" + + "github.com/arnaucube/go-snark/circuit" + "github.com/arnaucube/go-snark/proof" + "github.com/urfave/cli" +) + +func setup(context *cli.Context) error { + // load circuit + var cir circuit.Circuit + if err := loadFromFile(compiledFileName, &cir); err != nil { + return err + } + log.Printf("circuit: %v\n", cir) + + // load inputs + var inputs circuit.Inputs + if err := loadFromFile(privateFileName, &inputs.Private); err != nil { + return err + } + if err := loadFromFile(publicFileName, &inputs.Public); err != nil { + return err + } + + // calculate witness + w, err := cir.CalculateWitness(inputs.Private, inputs.Public) + if err != nil { + return err + } + log.Printf("w: %v\n", w) + + // R1CS to QAP + alphas, betas, gammas, _ := proof.Utils.PF.R1CSToQAP( + cir.R1CS.A, + cir.R1CS.B, + cir.R1CS.C) + + // calculate trusted setup + setup, err := newSetup() + if err != nil { + return err + } + if err := setup.Init(len(w), cir, alphas, betas, gammas); err != nil { + return err + } + log.Printf("setup: %v\n", setup) + + // save setup + return saveToFile(setupFileName, setup) +} diff --git a/cmd/go-snark/verify.go b/cmd/go-snark/verify.go new file mode 100644 index 0000000..d93d708 --- /dev/null +++ b/cmd/go-snark/verify.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "log" + + "github.com/arnaucube/go-snark/circuit" + "github.com/urfave/cli" +) + +func verify(context *cli.Context) error { + // load circuit + var cir circuit.Circuit + if err := loadFromFile(compiledFileName, &cir); err != nil { + return err + } + log.Printf("circuit: %v\n", cir) + + // load inputs + var inputs circuit.Inputs + if err := loadFromFile(publicFileName, &inputs.Public); err != nil { + return err + } + + // load setup + setup, err := newSetup() + if err != nil { + return err + } + if err := loadFromFile(setupFileName, setup); err != nil { + return err + } + log.Printf("setup: %v\n", setup) + + // load proof + proof, err := newProof() + if err != nil { + return err + } + if err := loadFromFile(proofFileName, proof); err != nil { + return err + } + + // verify proof + if ok := setup.Verify(cir, proof, inputs.Public, true); !ok { + return fmt.Errorf("verif KO") + } + log.Printf("verif OK\n") + return nil +} diff --git a/fields/fq.go b/fields/fq.go index d874ff4..8f6b408 100644 --- a/fields/fq.go +++ b/fields/fq.go @@ -58,6 +58,7 @@ func (fq Fq) Mul(a, b *big.Int) *big.Int { return new(big.Int).Mod(m, fq.Q) } +// MulScalar is ... func (fq Fq) MulScalar(base, e *big.Int) *big.Int { return fq.Mul(base, e) } @@ -113,6 +114,7 @@ func (fq Fq) Exp(base *big.Int, e *big.Int) *big.Int { return res } +// Rand is ... func (fq Fq) Rand() (*big.Int, error) { // twoexp := new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(maxbits)), nil) @@ -131,14 +133,17 @@ func (fq Fq) Rand() (*big.Int, error) { return rq, nil } +// IsZero is ... func (fq Fq) IsZero(a *big.Int) bool { return bytes.Equal(a.Bytes(), fq.Zero().Bytes()) } +// Copy is ... func (fq Fq) Copy(a *big.Int) *big.Int { return new(big.Int).SetBytes(a.Bytes()) } +// Affine is ... func (fq Fq) Affine(a *big.Int) *big.Int { nq := fq.Neg(fq.Q) @@ -158,12 +163,14 @@ func (fq Fq) Affine(a *big.Int) *big.Int { return aux } +// Equal is ... func (fq Fq) Equal(a, b *big.Int) bool { aAff := fq.Affine(a) bAff := fq.Affine(b) return bytes.Equal(aAff.Bytes(), bAff.Bytes()) } +// BigIsOdd is ... func BigIsOdd(n *big.Int) bool { one := big.NewInt(int64(1)) and := new(big.Int).And(n, one) diff --git a/fields/fq12.go b/fields/fq12.go index 2095081..4a56991 100644 --- a/fields/fq12.go +++ b/fields/fq12.go @@ -83,6 +83,7 @@ func (fq12 Fq12) Mul(a, b [2][3][2]*big.Int) [2][3][2]*big.Int { } } +// MulScalar is ... func (fq12 Fq12) MulScalar(base [2][3][2]*big.Int, e *big.Int) [2][3][2]*big.Int { // for more possible implementations see g2.go file, at the function g2.MulScalar() @@ -136,6 +137,7 @@ func (fq12 Fq12) Square(a [2][3][2]*big.Int) [2][3][2]*big.Int { } } +// Exp is ... func (fq12 Fq12) Exp(base [2][3][2]*big.Int, e *big.Int) [2][3][2]*big.Int { // TODO fix bottleneck @@ -154,12 +156,16 @@ func (fq12 Fq12) Exp(base [2][3][2]*big.Int, e *big.Int) [2][3][2]*big.Int { // fmt.Println("time elapsed:", time.Since(before)) return res } + +// Affine is ... func (fq12 Fq12) Affine(a [2][3][2]*big.Int) [2][3][2]*big.Int { return [2][3][2]*big.Int{ fq12.F.Affine(a[0]), fq12.F.Affine(a[1]), } } + +// Equal is ... func (fq12 Fq12) Equal(a, b [2][3][2]*big.Int) bool { return fq12.F.Equal(a[0], b[0]) && fq12.F.Equal(a[1], b[1]) } diff --git a/fields/fq2.go b/fields/fq2.go index 901b346..84fdb1f 100644 --- a/fields/fq2.go +++ b/fields/fq2.go @@ -75,6 +75,7 @@ func (fq2 Fq2) Mul(a, b [2]*big.Int) [2]*big.Int { } } +// MulScalar is ... func (fq2 Fq2) MulScalar(p [2]*big.Int, e *big.Int) [2]*big.Int { // for more possible implementations see g2.go file, at the function g2.MulScalar() @@ -132,20 +133,25 @@ func (fq2 Fq2) Square(a [2]*big.Int) [2]*big.Int { } } +// IsZero is ... func (fq2 Fq2) IsZero(a [2]*big.Int) bool { return fq2.F.IsZero(a[0]) && fq2.F.IsZero(a[1]) } +// Affine is ... func (fq2 Fq2) Affine(a [2]*big.Int) [2]*big.Int { return [2]*big.Int{ fq2.F.Affine(a[0]), fq2.F.Affine(a[1]), } } + +// Equal is ... func (fq2 Fq2) Equal(a, b [2]*big.Int) bool { return fq2.F.Equal(a[0], b[0]) && fq2.F.Equal(a[1], b[1]) } +// Copy is ... func (fq2 Fq2) Copy(a [2]*big.Int) [2]*big.Int { return [2]*big.Int{ fq2.F.Copy(a[0]), diff --git a/fields/fq6.go b/fields/fq6.go index 0f53832..951fabd 100644 --- a/fields/fq6.go +++ b/fields/fq6.go @@ -43,6 +43,7 @@ func (fq6 Fq6) Add(a, b [3][2]*big.Int) [3][2]*big.Int { } } +// Double is ... func (fq6 Fq6) Double(a [3][2]*big.Int) [3][2]*big.Int { return fq6.Add(a, a) } @@ -94,6 +95,7 @@ func (fq6 Fq6) Mul(a, b [3][2]*big.Int) [3][2]*big.Int { } } +// MulScalar is ... func (fq6 Fq6) MulScalar(base [3][2]*big.Int, e *big.Int) [3][2]*big.Int { // for more possible implementations see g2.go file, at the function g2.MulScalar() @@ -172,6 +174,7 @@ func (fq6 Fq6) Square(a [3][2]*big.Int) [3][2]*big.Int { } } +// Affine is ... func (fq6 Fq6) Affine(a [3][2]*big.Int) [3][2]*big.Int { return [3][2]*big.Int{ fq6.F.Affine(a[0]), @@ -179,10 +182,13 @@ func (fq6 Fq6) Affine(a [3][2]*big.Int) [3][2]*big.Int { fq6.F.Affine(a[2]), } } + +// Equal is ... func (fq6 Fq6) Equal(a, b [3][2]*big.Int) bool { return fq6.F.Equal(a[0], b[0]) && fq6.F.Equal(a[1], b[1]) && fq6.F.Equal(a[2], b[2]) } +// Copy is ... func (fq6 Fq6) Copy(a [3][2]*big.Int) [3][2]*big.Int { return [3][2]*big.Int{ fq6.F.Copy(a[0]), diff --git a/groth16/groth16.go b/proof/groth16.go similarity index 80% rename from groth16/groth16.go rename to proof/groth16.go index 03518f1..b13a9b2 100644 --- a/groth16/groth16.go +++ b/proof/groth16.go @@ -1,19 +1,16 @@ // implementation of https://eprint.iacr.org/2016/260.pdf -package groth16 +package proof import ( "fmt" "math/big" - "github.com/arnaucube/go-snark/bn128" - "github.com/arnaucube/go-snark/circuitcompiler" - "github.com/arnaucube/go-snark/fields" - "github.com/arnaucube/go-snark/r1csqap" + "github.com/arnaucube/go-snark/circuit" ) -// Setup is the data structure holding the Trusted Setup data. The Setup.Toxic sub struct must be destroyed after the GenerateTrustedSetup function is completed -type Setup struct { +// Groth16Setup is the data structure holding the Trusted Groth16Setup data. +type Groth16Setup struct { Toxic struct { T *big.Int // trusted setup secret Kalpha *big.Int @@ -54,65 +51,43 @@ type Setup struct { } } -// Proof contains the parameters to proof the zkSNARK -type Proof struct { +// Groth16Proof contains the parameters to proof the zkSNARK +type Groth16Proof struct { PiA [3]*big.Int PiB [3][2]*big.Int PiC [3]*big.Int } -type utils struct { - Bn bn128.Bn128 - FqR fields.Fq - PF r1csqap.PolynomialField +// Z is ... +func (setup *Groth16Setup) Z() []*big.Int { + return setup.Pk.Z } -// Utils is the data structure holding the BN128, FqR Finite Field over R, PolynomialField, that will be used inside the snarks operations -var Utils = prepareUtils() - -func prepareUtils() utils { - bn, err := bn128.NewBn128() - if err != nil { - panic(err) - } - // new Finite Field - fqR := fields.NewFq(bn.R) - // new Polynomial Field - pf := r1csqap.NewPolynomialField(fqR) - - return utils{ - Bn: bn, - FqR: fqR, - PF: pf, - } -} - -// GenerateTrustedSetup generates the Trusted Setup from a compiled Circuit. The Setup.Toxic sub data structure must be destroyed -func GenerateTrustedSetup(witnessLength int, circuit circuitcompiler.Circuit, alphas, betas, gammas [][]*big.Int) (Setup, error) { - var setup Setup +// Init generates the Trusted Setup from a compiled Circuit. The Setup.Toxic sub data structure must be destroyed +func (setup *Groth16Setup) Init(witnessLength int, circuit circuit.Circuit, alphas, betas, gammas [][]*big.Int) error { var err error // generate random t value setup.Toxic.T, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } setup.Toxic.Kalpha, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } setup.Toxic.Kbeta, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } setup.Toxic.Kgamma, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } setup.Toxic.Kdelta, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } // z pol @@ -215,23 +190,23 @@ func GenerateTrustedSetup(witnessLength int, circuit circuitcompiler.Circuit, al setup.Vk.IC = append(setup.Vk.IC, g1ic) } - return setup, nil + return nil } -// GenerateProofs generates all the parameters to proof the zkSNARK from the Circuit, Setup and the Witness -func GenerateProofs(circuit circuitcompiler.Circuit, setup Setup, w []*big.Int, px []*big.Int) (Proof, error) { - var proof Proof +// Generate generates all the parameters to proof the zkSNARK from the Circuit, Setup and the Witness +func (setup Groth16Setup) Generate(circuit circuit.Circuit, w []*big.Int, px []*big.Int) (Proof, error) { + proof := &Groth16Proof{} proof.PiA = [3]*big.Int{Utils.Bn.G1.F.Zero(), Utils.Bn.G1.F.Zero(), Utils.Bn.G1.F.Zero()} proof.PiB = Utils.Bn.Fq6.Zero() proof.PiC = [3]*big.Int{Utils.Bn.G1.F.Zero(), Utils.Bn.G1.F.Zero(), Utils.Bn.G1.F.Zero()} r, err := Utils.FqR.Rand() if err != nil { - return Proof{}, err + return &Groth16Proof{}, err } s, err := Utils.FqR.Rand() if err != nil { - return Proof{}, err + return &Groth16Proof{}, err } // piBG1 will hold all the same than proof.PiB but in G1 curve @@ -274,8 +249,12 @@ func GenerateProofs(circuit circuitcompiler.Circuit, setup Setup, w []*big.Int, return proof, nil } -// VerifyProof verifies over the BN128 the Pairings of the Proof -func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof, publicSignals []*big.Int, debug bool) bool { +// Verify verifies over the BN128 the Pairings of the Proof +func (setup Groth16Setup) Verify(circuit circuit.Circuit, proof Proof, publicSignals []*big.Int, debug bool) bool { + pproof, ok := proof.(*Groth16Proof) + if !ok { + panic("bad proof") + } icPubl := setup.Vk.IC[0] for i := 0; i < len(publicSignals); i++ { @@ -283,12 +262,12 @@ func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof, publ } if !Utils.Bn.Fq12.Equal( - Utils.Bn.Pairing(proof.PiA, proof.PiB), + Utils.Bn.Pairing(pproof.PiA, pproof.PiB), Utils.Bn.Fq12.Mul( Utils.Bn.Pairing(setup.Vk.G1.Alpha, setup.Vk.G2.Beta), Utils.Bn.Fq12.Mul( Utils.Bn.Pairing(icPubl, setup.Vk.G2.Gamma), - Utils.Bn.Pairing(proof.PiC, setup.Vk.G2.Delta)))) { + Utils.Bn.Pairing(pproof.PiC, setup.Vk.G2.Delta)))) { if debug { fmt.Println("❌ groth16 verification not passed") } diff --git a/groth16/groth16_test.go b/proof/groth16_test.go similarity index 85% rename from groth16/groth16_test.go rename to proof/groth16_test.go index fc91226..b72af0b 100644 --- a/groth16/groth16_test.go +++ b/proof/groth16_test.go @@ -1,4 +1,4 @@ -package groth16 +package proof import ( "bytes" @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/arnaucube/go-snark/circuitcompiler" + "github.com/arnaucube/go-snark/circuit" "github.com/arnaucube/go-snark/r1csqap" "github.com/stretchr/testify/assert" ) @@ -29,7 +29,7 @@ func TestGroth16MinimalFlow(t *testing.T) { fmt.Print("\ncode of the circuit:") // parse the code - parser := circuitcompiler.NewParser(strings.NewReader(code)) + parser := circuit.NewParser(strings.NewReader(code)) circuit, err := parser.Parse() assert.Nil(t, err) @@ -70,7 +70,8 @@ func TestGroth16MinimalFlow(t *testing.T) { // --- // calculate trusted setup fmt.Println("groth") - setup, err := GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas) + setup := &Groth16Setup{} + err = setup.Init(len(w), *circuit, alphas, betas, gammas) assert.Nil(t, err) fmt.Println("\nt:", setup.Toxic.T) @@ -85,7 +86,7 @@ func TestGroth16MinimalFlow(t *testing.T) { // check length of polynomials H(x) and Z(x) assert.Equal(t, len(hx), len(px)-len(setup.Pk.Z)+1) - proof, err := GenerateProofs(*circuit, setup, w, px) + proof, err := setup.Generate(*circuit, w, px) assert.Nil(t, err) // fmt.Println("\n proofs:") @@ -97,11 +98,11 @@ func TestGroth16MinimalFlow(t *testing.T) { b35Verif := big.NewInt(int64(35)) publicSignalsVerif := []*big.Int{b35Verif} before := time.Now() - assert.True(t, VerifyProof(*circuit, setup, proof, publicSignalsVerif, true)) + assert.True(t, setup.Verify(*circuit, proof, publicSignalsVerif, true)) fmt.Println("verify proof time elapsed:", time.Since(before)) // check that with another public input the verification returns false bOtherWrongPublic := big.NewInt(int64(34)) wrongPublicSignalsVerif := []*big.Int{bOtherWrongPublic} - assert.True(t, !VerifyProof(*circuit, setup, proof, wrongPublicSignalsVerif, false)) + assert.True(t, !setup.Verify(*circuit, proof, wrongPublicSignalsVerif, false)) } diff --git a/snark.go b/proof/pinocchio.go similarity index 76% rename from snark.go rename to proof/pinocchio.go index 9bfe5cb..653b2db 100644 --- a/snark.go +++ b/proof/pinocchio.go @@ -1,20 +1,17 @@ // implementation of https://eprint.iacr.org/2013/879.pdf -package snark +package proof import ( "fmt" "math/big" - "os" - "github.com/arnaucube/go-snark/bn128" - "github.com/arnaucube/go-snark/circuitcompiler" - "github.com/arnaucube/go-snark/fields" - "github.com/arnaucube/go-snark/r1csqap" + "github.com/arnaucube/go-snark/circuit" ) -// Setup is the data structure holding the Trusted Setup data. The Setup.Toxic sub struct must be destroyed after the GenerateTrustedSetup function is completed -type Setup struct { +// PinocchioSetup is the data structure holding the Trusted Setup data. +// The Setup.Toxic sub struct must be destroyed after the Init function is completed +type PinocchioSetup struct { Toxic struct { T *big.Int // trusted setup secret Ka *big.Int // prover @@ -25,7 +22,7 @@ type Setup struct { RhoA *big.Int RhoB *big.Int RhoC *big.Int - } + } `json:"-"` // public G1T [][3]*big.Int // t encrypted in G1 curve, G1T == Pk.H @@ -52,8 +49,8 @@ type Setup struct { } } -// Proof contains the parameters to proof the zkSNARK -type Proof struct { +// PinocchioProof contains the parameters to proof the zkSNARK +type PinocchioProof struct { PiA [3]*big.Int PiAp [3]*big.Int PiB [3][2]*big.Int @@ -65,35 +62,13 @@ type Proof struct { // PublicSignals []*big.Int } -type utils struct { - Bn bn128.Bn128 - FqR fields.Fq - PF r1csqap.PolynomialField +// Z is ... +func (setup *PinocchioSetup) Z() []*big.Int { + return setup.Pk.Z } -// Utils is the data structure holding the BN128, FqR Finite Field over R, PolynomialField, that will be used inside the snarks operations -var Utils = prepareUtils() - -func prepareUtils() utils { - bn, err := bn128.NewBn128() - if err != nil { - panic(err) - } - // new Finite Field - fqR := fields.NewFq(bn.R) - // new Polynomial Field - pf := r1csqap.NewPolynomialField(fqR) - - return utils{ - Bn: bn, - FqR: fqR, - PF: pf, - } -} - -// GenerateTrustedSetup generates the Trusted Setup from a compiled Circuit. The Setup.Toxic sub data structure must be destroyed -func GenerateTrustedSetup(witnessLength int, circuit circuitcompiler.Circuit, alphas, betas, gammas [][]*big.Int) (Setup, error) { - var setup Setup +// Init generates the Trusted Setup from a compiled Circuit. The Setup.Toxic sub data structure must be destroyed +func (setup *PinocchioSetup) Init(witnessLength int, circuit circuit.Circuit, alphas, betas, gammas [][]*big.Int) error { var err error // input soundness @@ -110,41 +85,41 @@ func GenerateTrustedSetup(witnessLength int, circuit circuitcompiler.Circuit, al // generate random t value setup.Toxic.T, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } // k for calculating pi' and Vk setup.Toxic.Ka, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } setup.Toxic.Kb, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } setup.Toxic.Kc, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } // generate Kβ (Kbeta) and Kγ (Kgamma) setup.Toxic.Kbeta, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } setup.Toxic.Kgamma, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } // generate ρ (Rho): ρA, ρB, ρC setup.Toxic.RhoA, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } setup.Toxic.RhoB, err = Utils.FqR.Rand() if err != nil { - return Setup{}, err + return err } setup.Toxic.RhoC = Utils.FqR.Mul(setup.Toxic.RhoA, setup.Toxic.RhoB) @@ -203,15 +178,14 @@ func GenerateTrustedSetup(witnessLength int, circuit circuitcompiler.Circuit, al ktest := Utils.Bn.G1.Affine(Utils.Bn.G1.Add(Utils.Bn.G1.Add(a, bg1), c)) if !Utils.Bn.Fq2.Equal(k, ktest) { - os.Exit(1) - return setup, err + return err } setup.Pk.Ap = append(setup.Pk.Ap, Utils.Bn.G1.MulScalar(a, setup.Toxic.Ka)) setup.Pk.Bp = append(setup.Pk.Bp, Utils.Bn.G1.MulScalar(bg1, setup.Toxic.Kb)) setup.Pk.Cp = append(setup.Pk.Cp, Utils.Bn.G1.MulScalar(c, setup.Toxic.Kc)) - k_ := Utils.Bn.G1.MulScalar(Utils.Bn.G1.G, kt) - setup.Pk.Kp = append(setup.Pk.Kp, Utils.Bn.G1.MulScalar(k_, setup.Toxic.Kbeta)) + kk := Utils.Bn.G1.MulScalar(Utils.Bn.G1.G, kt) + setup.Pk.Kp = append(setup.Pk.Kp, Utils.Bn.G1.MulScalar(kk, setup.Toxic.Kbeta)) } // z pol @@ -244,12 +218,12 @@ func GenerateTrustedSetup(witnessLength int, circuit circuitcompiler.Circuit, al } setup.G1T = gt1 - return setup, nil + return nil } -// GenerateProofs generates all the parameters to proof the zkSNARK from the Circuit, Setup and the Witness -func GenerateProofs(circuit circuitcompiler.Circuit, setup Setup, w []*big.Int, px []*big.Int) (Proof, error) { - var proof Proof +// Generate generates all the parameters to proof the zkSNARK from the Circuit, Setup and the Witness +func (setup *PinocchioSetup) Generate(circuit circuit.Circuit, w []*big.Int, px []*big.Int) (Proof, error) { + proof := &PinocchioProof{} proof.PiA = [3]*big.Int{Utils.Bn.G1.F.Zero(), Utils.Bn.G1.F.Zero(), Utils.Bn.G1.F.Zero()} proof.PiAp = [3]*big.Int{Utils.Bn.G1.F.Zero(), Utils.Bn.G1.F.Zero(), Utils.Bn.G1.F.Zero()} proof.PiB = Utils.Bn.Fq6.Zero() @@ -285,11 +259,16 @@ func GenerateProofs(circuit circuitcompiler.Circuit, setup Setup, w []*big.Int, return proof, nil } -// VerifyProof verifies over the BN128 the Pairings of the Proof -func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof, publicSignals []*big.Int, debug bool) bool { +// Verify verifies over the BN128 the Pairings of the Proof +func (setup *PinocchioSetup) Verify(circuit circuit.Circuit, proof Proof, publicSignals []*big.Int, debug bool) bool { // e(piA, Va) == e(piA', g2) - pairingPiaVa := Utils.Bn.Pairing(proof.PiA, setup.Vk.Vka) - pairingPiapG2 := Utils.Bn.Pairing(proof.PiAp, Utils.Bn.G2.G) + + pproof, ok := proof.(*PinocchioProof) + if !ok { + panic("bad type") + } + pairingPiaVa := Utils.Bn.Pairing(pproof.PiA, setup.Vk.Vka) + pairingPiapG2 := Utils.Bn.Pairing(pproof.PiAp, Utils.Bn.G2.G) if !Utils.Bn.Fq12.Equal(pairingPiaVa, pairingPiapG2) { if debug { fmt.Println("❌ e(piA, Va) == e(piA', g2), valid knowledge commitment for A") @@ -301,8 +280,8 @@ func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof, publ } // e(Vb, piB) == e(piB', g2) - pairingVbPib := Utils.Bn.Pairing(setup.Vk.Vkb, proof.PiB) - pairingPibpG2 := Utils.Bn.Pairing(proof.PiBp, Utils.Bn.G2.G) + pairingVbPib := Utils.Bn.Pairing(setup.Vk.Vkb, pproof.PiB) + pairingPibpG2 := Utils.Bn.Pairing(pproof.PiBp, Utils.Bn.G2.G) if !Utils.Bn.Fq12.Equal(pairingVbPib, pairingPibpG2) { if debug { fmt.Println("❌ e(Vb, piB) == e(piB', g2), valid knowledge commitment for B") @@ -314,8 +293,8 @@ func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof, publ } // e(piC, Vc) == e(piC', g2) - pairingPicVc := Utils.Bn.Pairing(proof.PiC, setup.Vk.Vkc) - pairingPicpG2 := Utils.Bn.Pairing(proof.PiCp, Utils.Bn.G2.G) + pairingPicVc := Utils.Bn.Pairing(pproof.PiC, setup.Vk.Vkc) + pairingPicpG2 := Utils.Bn.Pairing(pproof.PiCp, Utils.Bn.G2.G) if !Utils.Bn.Fq12.Equal(pairingPicVc, pairingPicpG2) { if debug { fmt.Println("❌ e(piC, Vc) == e(piC', g2), valid knowledge commitment for C") @@ -334,10 +313,10 @@ func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof, publ // e(Vkx+piA, piB) == e(piH, Vkz) * e(piC, g2) if !Utils.Bn.Fq12.Equal( - Utils.Bn.Pairing(Utils.Bn.G1.Add(vkxpia, proof.PiA), proof.PiB), // TODO Add(vkxpia, proof.PiA) can go outside in order to save computation, as is reused later + Utils.Bn.Pairing(Utils.Bn.G1.Add(vkxpia, pproof.PiA), pproof.PiB), // TODO Add(vkxpia, proof.PiA) can go outside in order to save computation, as is reused later Utils.Bn.Fq12.Mul( - Utils.Bn.Pairing(proof.PiH, setup.Vk.Vkz), - Utils.Bn.Pairing(proof.PiC, Utils.Bn.G2.G))) { + Utils.Bn.Pairing(pproof.PiH, setup.Vk.Vkz), + Utils.Bn.Pairing(pproof.PiC, Utils.Bn.G2.G))) { if debug { fmt.Println("❌ e(Vkx+piA, piB) == e(piH, Vkz) * e(piC, g2), QAP disibility checked") } @@ -349,11 +328,11 @@ func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof, publ // e(Vkx+piA+piC, g2KbetaKgamma) * e(g1KbetaKgamma, piB) // == e(piK, g2Kgamma) - piApiC := Utils.Bn.G1.Add(Utils.Bn.G1.Add(vkxpia, proof.PiA), proof.PiC) - pairingPiACG2Kbg := Utils.Bn.Pairing(piApiC, setup.Vk.G2Kbg) - pairingG1KbgPiB := Utils.Bn.Pairing(setup.Vk.G1Kbg, proof.PiB) + piapic := Utils.Bn.G1.Add(Utils.Bn.G1.Add(vkxpia, pproof.PiA), pproof.PiC) + pairingPiACG2Kbg := Utils.Bn.Pairing(piapic, setup.Vk.G2Kbg) + pairingG1KbgPiB := Utils.Bn.Pairing(setup.Vk.G1Kbg, pproof.PiB) pairingL := Utils.Bn.Fq12.Mul(pairingPiACG2Kbg, pairingG1KbgPiB) - pairingR := Utils.Bn.Pairing(proof.PiKp, setup.Vk.G2Kg) + pairingR := Utils.Bn.Pairing(pproof.PiKp, setup.Vk.G2Kg) if !Utils.Bn.Fq12.Equal(pairingL, pairingR) { fmt.Println("❌ e(Vkx+piA+piC, g2KbetaKgamma) * e(g1KbetaKgamma, piB) == e(piK, g2Kgamma)") return false diff --git a/snark_test.go b/proof/pinocchio_test.go similarity index 70% rename from snark_test.go rename to proof/pinocchio_test.go index 4cb4a85..37f7baa 100644 --- a/snark_test.go +++ b/proof/pinocchio_test.go @@ -1,4 +1,4 @@ -package snark +package proof import ( "bytes" @@ -8,104 +8,11 @@ import ( "testing" "time" - "github.com/arnaucube/go-snark/circuitcompiler" - "github.com/arnaucube/go-snark/groth16" - "github.com/arnaucube/go-snark/r1csqap" "github.com/stretchr/testify/assert" -) - -func TestGroth16MinimalFlow(t *testing.T) { - fmt.Println("testing Groth16 minimal flow") - // circuit function - // y = x^3 + x + 5 - code := ` - func main(private s0, public s1): - s2 = s0 * s0 - s3 = s2 * s0 - s4 = s3 + s0 - s5 = s4 + 5 - equals(s1, s5) - out = 1 * 1 - ` - fmt.Print("\ncode of the circuit:") - // parse the code - parser := circuitcompiler.NewParser(strings.NewReader(code)) - circuit, err := parser.Parse() - assert.Nil(t, err) - - b3 := big.NewInt(int64(3)) - privateInputs := []*big.Int{b3} - b35 := big.NewInt(int64(35)) - publicSignals := []*big.Int{b35} - - // wittness - w, err := circuit.CalculateWitness(privateInputs, publicSignals) - assert.Nil(t, err) - - // code to R1CS - fmt.Println("\ngenerating R1CS from code") - a, b, c := circuit.GenerateR1CS() - fmt.Println("\nR1CS:") - fmt.Println("a:", a) - fmt.Println("b:", b) - fmt.Println("c:", c) - - // R1CS to QAP - // TODO zxQAP is not used and is an old impl, TODO remove - alphas, betas, gammas, _ := Utils.PF.R1CSToQAP(a, b, c) - fmt.Println("qap") - assert.Equal(t, 8, len(alphas)) - assert.Equal(t, 8, len(alphas)) - assert.Equal(t, 8, len(alphas)) - assert.True(t, !bytes.Equal(alphas[1][1].Bytes(), big.NewInt(int64(0)).Bytes())) - - ax, bx, cx, px := Utils.PF.CombinePolynomials(w, alphas, betas, gammas) - assert.Equal(t, 7, len(ax)) - assert.Equal(t, 7, len(bx)) - assert.Equal(t, 7, len(cx)) - assert.Equal(t, 13, len(px)) - - // --- - // from here is the GROTH16 - // --- - // calculate trusted setup - fmt.Println("groth") - setup, err := groth16.GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas) - assert.Nil(t, err) - fmt.Println("\nt:", setup.Toxic.T) - - hx := Utils.PF.DivisorPolynomial(px, setup.Pk.Z) - div, rem := Utils.PF.Div(px, setup.Pk.Z) - assert.Equal(t, hx, div) - assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(6)) - - // hx==px/zx so px==hx*zx - assert.Equal(t, px, Utils.PF.Mul(hx, setup.Pk.Z)) - - // check length of polynomials H(x) and Z(x) - assert.Equal(t, len(hx), len(px)-len(setup.Pk.Z)+1) - - proof, err := groth16.GenerateProofs(*circuit, setup, w, px) - assert.Nil(t, err) - - // fmt.Println("\n proofs:") - // fmt.Println(proof) - - // fmt.Println("public signals:", proof.PublicSignals) - fmt.Println("\nsignals:", circuit.Signals) - fmt.Println("witness:", w) - b35Verif := big.NewInt(int64(35)) - publicSignalsVerif := []*big.Int{b35Verif} - before := time.Now() - assert.True(t, groth16.VerifyProof(*circuit, setup, proof, publicSignalsVerif, true)) - fmt.Println("verify proof time elapsed:", time.Since(before)) - - // check that with another public input the verification returns false - bOtherWrongPublic := big.NewInt(int64(34)) - wrongPublicSignalsVerif := []*big.Int{bOtherWrongPublic} - assert.True(t, !groth16.VerifyProof(*circuit, setup, proof, wrongPublicSignalsVerif, false)) -} + "github.com/arnaucube/go-snark/circuit" + "github.com/arnaucube/go-snark/r1csqap" +) func TestZkFromFlatCircuitCode(t *testing.T) { // compile circuit and get the R1CS @@ -142,7 +49,7 @@ func TestZkFromFlatCircuitCode(t *testing.T) { fmt.Println(code) // parse the code - parser := circuitcompiler.NewParser(strings.NewReader(code)) + parser := circuit.NewParser(strings.NewReader(code)) circuit, err := parser.Parse() assert.Nil(t, err) // fmt.Println("\ncircuit data:", circuit) @@ -199,7 +106,8 @@ func TestZkFromFlatCircuitCode(t *testing.T) { assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(6)) // calculate trusted setup - setup, err := GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas) + setup := &PinocchioSetup{} + err = setup.Init(len(w), *circuit, alphas, betas, gammas) assert.Nil(t, err) fmt.Println("\nt:", setup.Toxic.T) @@ -221,7 +129,7 @@ func TestZkFromFlatCircuitCode(t *testing.T) { assert.Equal(t, len(hx), len(px)-len(setup.Pk.Z)+1) assert.Equal(t, len(hxQAP), len(px)-len(zxQAP)+1) - proof, err := GenerateProofs(*circuit, setup, w, px) + proof, err := setup.Generate(*circuit, w, px) assert.Nil(t, err) // fmt.Println("\n proofs:") @@ -233,13 +141,13 @@ func TestZkFromFlatCircuitCode(t *testing.T) { b35Verif := big.NewInt(int64(35)) publicSignalsVerif := []*big.Int{b35Verif} before := time.Now() - assert.True(t, VerifyProof(*circuit, setup, proof, publicSignalsVerif, true)) + assert.True(t, setup.Verify(*circuit, proof, publicSignalsVerif, true)) fmt.Println("verify proof time elapsed:", time.Since(before)) // check that with another public input the verification returns false bOtherWrongPublic := big.NewInt(int64(34)) wrongPublicSignalsVerif := []*big.Int{bOtherWrongPublic} - assert.True(t, !VerifyProof(*circuit, setup, proof, wrongPublicSignalsVerif, false)) + assert.True(t, !setup.Verify(*circuit, proof, wrongPublicSignalsVerif, false)) } func TestZkMultiplication(t *testing.T) { @@ -252,7 +160,7 @@ func TestZkMultiplication(t *testing.T) { fmt.Println("code", code) // parse the code - parser := circuitcompiler.NewParser(strings.NewReader(code)) + parser := circuit.NewParser(strings.NewReader(code)) circuit, err := parser.Parse() assert.Nil(t, err) @@ -306,7 +214,8 @@ func TestZkMultiplication(t *testing.T) { assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(4)) // calculate trusted setup - setup, err := GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas) + setup := &PinocchioSetup{} + err = setup.Init(len(w), *circuit, alphas, betas, gammas) assert.Nil(t, err) // fmt.Println("\nt:", setup.Toxic.T) @@ -329,7 +238,7 @@ func TestZkMultiplication(t *testing.T) { assert.Equal(t, len(hx), len(px)-len(setup.Pk.Z)+1) assert.Equal(t, len(hxQAP), len(px)-len(zxQAP)+1) - proof, err := GenerateProofs(*circuit, setup, w, px) + proof, err := setup.Generate(*circuit, w, px) assert.Nil(t, err) // fmt.Println("\n proofs:") @@ -341,13 +250,13 @@ func TestZkMultiplication(t *testing.T) { b12Verif := big.NewInt(int64(12)) publicSignalsVerif := []*big.Int{b12Verif} before := time.Now() - assert.True(t, VerifyProof(*circuit, setup, proof, publicSignalsVerif, true)) + assert.True(t, setup.Verify(*circuit, proof, publicSignalsVerif, true)) fmt.Println("verify proof time elapsed:", time.Since(before)) // check that with another public input the verification returns false bOtherWrongPublic := big.NewInt(int64(11)) wrongPublicSignalsVerif := []*big.Int{bOtherWrongPublic} - assert.True(t, !VerifyProof(*circuit, setup, proof, wrongPublicSignalsVerif, false)) + assert.True(t, !setup.Verify(*circuit, proof, wrongPublicSignalsVerif, false)) } func TestMinimalFlow(t *testing.T) { @@ -366,7 +275,7 @@ func TestMinimalFlow(t *testing.T) { fmt.Println(code) // parse the code - parser := circuitcompiler.NewParser(strings.NewReader(code)) + parser := circuit.NewParser(strings.NewReader(code)) circuit, err := parser.Parse() assert.Nil(t, err) @@ -403,7 +312,8 @@ func TestMinimalFlow(t *testing.T) { assert.Equal(t, 13, len(px)) // calculate trusted setup - setup, err := GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas) + setup := &PinocchioSetup{} + err = setup.Init(len(w), *circuit, alphas, betas, gammas) assert.Nil(t, err) fmt.Println("\nt:", setup.Toxic.T) @@ -418,7 +328,7 @@ func TestMinimalFlow(t *testing.T) { // check length of polynomials H(x) and Z(x) assert.Equal(t, len(hx), len(px)-len(setup.Pk.Z)+1) - proof, err := GenerateProofs(*circuit, setup, w, px) + proof, err := setup.Generate(*circuit, w, px) assert.Nil(t, err) // fmt.Println("\n proofs:") @@ -430,11 +340,11 @@ func TestMinimalFlow(t *testing.T) { b35Verif := big.NewInt(int64(35)) publicSignalsVerif := []*big.Int{b35Verif} before := time.Now() - assert.True(t, VerifyProof(*circuit, setup, proof, publicSignalsVerif, true)) + assert.True(t, setup.Verify(*circuit, proof, publicSignalsVerif, true)) fmt.Println("verify proof time elapsed:", time.Since(before)) // check that with another public input the verification returns false bOtherWrongPublic := big.NewInt(int64(34)) wrongPublicSignalsVerif := []*big.Int{bOtherWrongPublic} - assert.True(t, !VerifyProof(*circuit, setup, proof, wrongPublicSignalsVerif, false)) + assert.True(t, !setup.Verify(*circuit, proof, wrongPublicSignalsVerif, false)) } diff --git a/proof/proof.go b/proof/proof.go new file mode 100644 index 0000000..baa824d --- /dev/null +++ b/proof/proof.go @@ -0,0 +1,47 @@ +package proof + +import ( + "math/big" + + "github.com/arnaucube/go-snark/bn128" + "github.com/arnaucube/go-snark/circuit" + "github.com/arnaucube/go-snark/fields" + "github.com/arnaucube/go-snark/r1csqap" +) + +type utils struct { + Bn bn128.Bn128 + FqR fields.Fq + PF r1csqap.PolynomialField +} + +// Utils is the data structure holding the BN128, FqR Finite Field over R, PolynomialField, that will be used inside the snarks operations +var Utils = prepareUtils() + +func prepareUtils() utils { + bn, err := bn128.NewBn128() + if err != nil { + panic(err) + } + // new Finite Field + fqR := fields.NewFq(bn.R) + // new Polynomial Field + pf := r1csqap.NewPolynomialField(fqR) + + return utils{ + Bn: bn, + FqR: fqR, + PF: pf, + } +} + +// Proof is +type Proof interface{} + +// Setup is +type Setup interface { + Z() []*big.Int + Init(witnessLength int, circuit circuit.Circuit, alphas, betas, gammas [][]*big.Int) error + Generate(circuit circuit.Circuit, w []*big.Int, px []*big.Int) (Proof, error) + Verify(circuit circuit.Circuit, proof Proof, publicSignals []*big.Int, debug bool) bool +} diff --git a/r1csqap/r1csqap.go b/r1csqap/r1csqap.go index 7429b74..72803a4 100644 --- a/r1csqap/r1csqap.go +++ b/r1csqap/r1csqap.go @@ -29,6 +29,8 @@ func ArrayOfBigZeros(num int) []*big.Int { } return r } + +// BigArraysEqual is ... func BigArraysEqual(a, b []*big.Int) bool { if len(a) != len(b) { return false diff --git a/r1csqapFloat/r1csqapFloat.go b/r1csqapFloat/r1csqapFloat.go deleted file mode 100644 index b5be28f..0000000 --- a/r1csqapFloat/r1csqapFloat.go +++ /dev/null @@ -1,185 +0,0 @@ -package r1csqapFloat - -import "math/big" - -func Transpose(matrix [][]*big.Float) [][]*big.Float { - var r [][]*big.Float - for i := 0; i < len(matrix[0]); i++ { - var row []*big.Float - for j := 0; j < len(matrix); j++ { - row = append(row, matrix[j][i]) - } - r = append(r, row) - } - return r -} - -func ArrayOfBigZeros(num int) []*big.Float { - bigZero := big.NewFloat(float64(0)) - var r []*big.Float - for i := 0; i < num; i++ { - r = append(r, bigZero) - } - return r -} - -func PolMul(a, b []*big.Float) []*big.Float { - r := ArrayOfBigZeros(len(a) + len(b) - 1) - for i := 0; i < len(a); i++ { - for j := 0; j < len(b); j++ { - r[i+j] = new(big.Float).Add( - r[i+j], - new(big.Float).Mul(a[i], b[j])) - } - } - return r -} - -func PolDiv(a, b []*big.Float) ([]*big.Float, []*big.Float) { - // https://en.wikipedia.org/wiki/Division_algorithm - r := ArrayOfBigZeros(len(a) - len(b) + 1) - rem := a - for len(rem) >= len(b) { - l := new(big.Float).Quo(rem[len(rem)-1], b[len(b)-1]) - pos := len(rem) - len(b) - r[pos] = l - aux := ArrayOfBigZeros(pos) - aux1 := append(aux, l) - aux2 := PolSub(rem, PolMul(b, aux1)) - rem = aux2[:len(aux2)-1] - } - return r, rem -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} - -func PolAdd(a, b []*big.Float) []*big.Float { - r := ArrayOfBigZeros(max(len(a), len(b))) - for i := 0; i < len(a); i++ { - r[i] = new(big.Float).Add(r[i], a[i]) - } - for i := 0; i < len(b); i++ { - r[i] = new(big.Float).Add(r[i], b[i]) - } - return r -} - -func PolSub(a, b []*big.Float) []*big.Float { - r := ArrayOfBigZeros(max(len(a), len(b))) - for i := 0; i < len(a); i++ { - r[i] = new(big.Float).Add(r[i], a[i]) - } - for i := 0; i < len(b); i++ { - bneg := new(big.Float).Mul(b[i], big.NewFloat(float64(-1))) - r[i] = new(big.Float).Add(r[i], bneg) - } - return r - -} - -func FloatPow(a *big.Float, e int) *big.Float { - if e == 0 { - return big.NewFloat(float64(1)) - } - result := new(big.Float).Copy(a) - for i := 0; i < e-1; i++ { - result = new(big.Float).Mul(result, a) - } - return result -} - -func PolEval(v []*big.Float, x *big.Float) *big.Float { - r := big.NewFloat(float64(0)) - for i := 0; i < len(v); i++ { - xi := FloatPow(x, i) - elem := new(big.Float).Mul(v[i], xi) - r = new(big.Float).Add(r, elem) - } - return r -} - -func NewPolZeroAt(pointPos, totalPoints int, height *big.Float) []*big.Float { - fac := 1 - for i := 1; i < totalPoints+1; i++ { - if i != pointPos { - fac = fac * (pointPos - i) - } - } - facBig := big.NewFloat(float64(fac)) - hf := new(big.Float).Quo(height, facBig) - r := []*big.Float{hf} - for i := 1; i < totalPoints+1; i++ { - if i != pointPos { - ineg := big.NewFloat(float64(-i)) - b1 := big.NewFloat(float64(1)) - r = PolMul(r, []*big.Float{ineg, b1}) - } - } - return r -} - -func LagrangeInterpolation(v []*big.Float) []*big.Float { - // https://en.wikipedia.org/wiki/Lagrange_polynomial - var r []*big.Float - for i := 0; i < len(v); i++ { - r = PolAdd(r, NewPolZeroAt(i+1, len(v), v[i])) - } - // - return r -} - -func R1CSToQAP(a, b, c [][]*big.Float) ([][]*big.Float, [][]*big.Float, [][]*big.Float, []*big.Float) { - aT := Transpose(a) - bT := Transpose(b) - cT := Transpose(c) - var alphas [][]*big.Float - for i := 0; i < len(aT); i++ { - alphas = append(alphas, LagrangeInterpolation(aT[i])) - } - var betas [][]*big.Float - for i := 0; i < len(bT); i++ { - betas = append(betas, LagrangeInterpolation(bT[i])) - } - var gammas [][]*big.Float - for i := 0; i < len(cT); i++ { - gammas = append(gammas, LagrangeInterpolation(cT[i])) - } - z := []*big.Float{big.NewFloat(float64(1))} - for i := 1; i < len(aT[0])+1; i++ { - ineg := big.NewFloat(float64(-i)) - b1 := big.NewFloat(float64(1)) - z = PolMul(z, []*big.Float{ineg, b1}) - } - return alphas, betas, gammas, z -} - -func CombinePolynomials(r []*big.Float, ap, bp, cp [][]*big.Float) ([]*big.Float, []*big.Float, []*big.Float, []*big.Float) { - var alpha []*big.Float - for i := 0; i < len(r); i++ { - m := PolMul([]*big.Float{r[i]}, ap[i]) - alpha = PolAdd(alpha, m) - } - var beta []*big.Float - for i := 0; i < len(r); i++ { - m := PolMul([]*big.Float{r[i]}, bp[i]) - beta = PolAdd(beta, m) - } - var gamma []*big.Float - for i := 0; i < len(r); i++ { - m := PolMul([]*big.Float{r[i]}, cp[i]) - gamma = PolAdd(gamma, m) - } - - px := PolSub(PolMul(alpha, beta), gamma) - return alpha, beta, gamma, px -} - -func DivisorPolinomial(px, z []*big.Float) []*big.Float { - quo, _ := PolDiv(px, z) - return quo -} diff --git a/r1csqapFloat/r1csqapFloat_test.go b/r1csqapFloat/r1csqapFloat_test.go deleted file mode 100644 index b48d9b6..0000000 --- a/r1csqapFloat/r1csqapFloat_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package r1csqapFloat - -import ( - "fmt" - "math/big" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestTranspose(t *testing.T) { - b0 := big.NewFloat(float64(0)) - b1 := big.NewFloat(float64(1)) - bFive := big.NewFloat(float64(5)) - a := [][]*big.Float{ - []*big.Float{b0, b1, b0, b0, b0, b0}, - []*big.Float{b0, b0, b0, b1, b0, b0}, - []*big.Float{b0, b1, b0, b0, b1, b0}, - []*big.Float{bFive, b0, b0, b0, b0, b1}, - } - aT := Transpose(a) - assert.Equal(t, aT, [][]*big.Float{ - []*big.Float{b0, b0, b0, bFive}, - []*big.Float{b1, b0, b1, b0}, - []*big.Float{b0, b0, b0, b0}, - []*big.Float{b0, b1, b0, b0}, - []*big.Float{b0, b0, b1, b0}, - []*big.Float{b0, b0, b0, b1}, - }) -} - -func TestPol(t *testing.T) { - b0 := big.NewFloat(float64(0)) - b1 := big.NewFloat(float64(1)) - // b1neg := big.NewFloat(float64(-1)) - // b2 := big.NewFloat(float64(2)) - b2neg := big.NewFloat(float64(-2)) - b3 := big.NewFloat(float64(3)) - b4 := big.NewFloat(float64(4)) - b5 := big.NewFloat(float64(5)) - b6 := big.NewFloat(float64(6)) - b16 := big.NewFloat(float64(16)) - - a := []*big.Float{b1, b0, b5} - b := []*big.Float{b3, b0, b1} - - // polynomial multiplication - c := PolMul(a, b) - assert.Equal(t, c, []*big.Float{b3, b0, b16, b0, b5}) - - // polynomial addition - c = PolAdd(a, b) - assert.Equal(t, c, []*big.Float{b4, b0, b6}) - - // polynomial subtraction - c = PolSub(a, b) - assert.Equal(t, c, []*big.Float{b2neg, b0, b4}) - - // FloatPow - p := FloatPow(big.NewFloat(float64(5)), 3) - assert.Equal(t, p, big.NewFloat(float64(125))) - p = FloatPow(big.NewFloat(float64(5)), 0) - assert.Equal(t, p, big.NewFloat(float64(1))) - - // NewPolZeroAt - r := NewPolZeroAt(3, 4, b4) - assert.Equal(t, PolEval(r, big.NewFloat(3)), b4) - r = NewPolZeroAt(2, 4, b3) - assert.Equal(t, PolEval(r, big.NewFloat(2)), b3) -} - -func TestLagrangeInterpolation(t *testing.T) { - b0 := big.NewFloat(float64(0)) - b5 := big.NewFloat(float64(5)) - a := []*big.Float{b0, b0, b0, b5} - alpha := LagrangeInterpolation(a) - - assert.Equal(t, PolEval(alpha, big.NewFloat(4)), b5) - aux, _ := PolEval(alpha, big.NewFloat(3)).Int64() - assert.Equal(t, aux, int64(0)) - -} - -func TestR1CSToQAP(t *testing.T) { - b0 := big.NewFloat(float64(0)) - b1 := big.NewFloat(float64(1)) - b3 := big.NewFloat(float64(3)) - b5 := big.NewFloat(float64(5)) - b9 := big.NewFloat(float64(9)) - b27 := big.NewFloat(float64(27)) - b30 := big.NewFloat(float64(30)) - b35 := big.NewFloat(float64(35)) - a := [][]*big.Float{ - []*big.Float{b0, b1, b0, b0, b0, b0}, - []*big.Float{b0, b0, b0, b1, b0, b0}, - []*big.Float{b0, b1, b0, b0, b1, b0}, - []*big.Float{b5, b0, b0, b0, b0, b1}, - } - b := [][]*big.Float{ - []*big.Float{b0, b1, b0, b0, b0, b0}, - []*big.Float{b0, b1, b0, b0, b0, b0}, - []*big.Float{b1, b0, b0, b0, b0, b0}, - []*big.Float{b1, b0, b0, b0, b0, b0}, - } - c := [][]*big.Float{ - []*big.Float{b0, b0, b0, b1, b0, b0}, - []*big.Float{b0, b0, b0, b0, b1, b0}, - []*big.Float{b0, b0, b0, b0, b0, b1}, - []*big.Float{b0, b0, b1, b0, b0, b0}, - } - // alphas, betas, gammas - alphas, betas, gammas, zx := R1CSToQAP(a, b, c) - fmt.Println(alphas) - fmt.Println(betas) - fmt.Println(gammas) - fmt.Print("Z(x): ") - fmt.Println(zx) - zexpected := []*big.Float{big.NewFloat(float64(24)), big.NewFloat(float64(-50)), big.NewFloat(float64(35)), big.NewFloat(float64(-10)), big.NewFloat(float64(1))} - assert.Equal(t, zx, zexpected) - - // witness - w := []*big.Float{b1, b3, b35, b9, b27, b30} - fmt.Print("w: ") - fmt.Println(w) - // QAP A(x), B(x), C(x) - ax, bx, cx, px := CombinePolynomials(w, alphas, betas, gammas) - fmt.Print("A(x), B(x), C(x), P(x): ") - fmt.Println(ax) - fmt.Println(bx) - fmt.Println(cx) - fmt.Println(px) - - hx := DivisorPolinomial(px, zx) - fmt.Print("H(x): ") - fmt.Println(hx) - -} diff --git a/circuitexamples/factor.circuit b/res/examples/factor.circuit similarity index 100% rename from circuitexamples/factor.circuit rename to res/examples/factor.circuit diff --git a/circuitexamples/function.circuit b/res/examples/function.circuit similarity index 100% rename from circuitexamples/function.circuit rename to res/examples/function.circuit diff --git a/circuitexamples/import-example.circuit b/res/examples/import-example.circuit similarity index 100% rename from circuitexamples/import-example.circuit rename to res/examples/import-example.circuit diff --git a/circuitexamples/imported-example.circuit b/res/examples/imported-example.circuit similarity index 100% rename from circuitexamples/imported-example.circuit rename to res/examples/imported-example.circuit diff --git a/vim-syntax/README.md b/res/vim-syntax/README.md similarity index 100% rename from vim-syntax/README.md rename to res/vim-syntax/README.md diff --git a/vim-syntax/ftdetect/go-snark-circuit.vim b/res/vim-syntax/ftdetect/go-snark-circuit.vim similarity index 100% rename from vim-syntax/ftdetect/go-snark-circuit.vim rename to res/vim-syntax/ftdetect/go-snark-circuit.vim diff --git a/vim-syntax/screenshot.png b/res/vim-syntax/screenshot.png similarity index 100% rename from vim-syntax/screenshot.png rename to res/vim-syntax/screenshot.png diff --git a/vim-syntax/syntax/go-snark-circuit.vim b/res/vim-syntax/syntax/go-snark-circuit.vim similarity index 100% rename from vim-syntax/syntax/go-snark-circuit.vim rename to res/vim-syntax/syntax/go-snark-circuit.vim