diff --git a/README.md b/README.md index e43ef365d..0457af494 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,13 @@ src="banner_gnark.png"> * [`gnark` User Documentation] * [`gnark` Issues] -## Issues +## Issues `gnark` issues are tracked [in the GitHub issues tab][`gnark` Issues]. If you have any questions, queries or comments, [GitHub discussions] is the place to find us. -You can also get in touch directly: zkteam@consensys.net +You can also get in touch directly: zkteam@consensys.net ## `gnark` Users @@ -49,6 +49,7 @@ which can be instantiated with the following curves - [x] BLS12-381 - [x] BLS12-377 - [x] BW6-761 +- [x] BLS24-315 ### Example @@ -95,7 +96,7 @@ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our [code of condu ## Versioning -We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/consensys/gnark/tags). +We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/consensys/gnark/tags). ## License @@ -107,4 +108,3 @@ This project is licensed under the Apache 2 License - see the [LICENSE](LICENSE) [`gnark` User Documentation]: https://docs.gnark.consensys.net [GitHub discussions]: https://github.com/ConsenSys/gnark/discussions [Proving schemes and curves]: https://docs.gnark.consensys.net - diff --git a/backend/groth16/assert.go b/backend/groth16/assert.go index f5535c277..54e271899 100644 --- a/backend/groth16/assert.go +++ b/backend/groth16/assert.go @@ -25,6 +25,8 @@ import ( witness_bls12377 "github.com/consensys/gnark/internal/backend/bls12-377/witness" backend_bls12381 "github.com/consensys/gnark/internal/backend/bls12-381/cs" witness_bls12381 "github.com/consensys/gnark/internal/backend/bls12-381/witness" + backend_bls24315 "github.com/consensys/gnark/internal/backend/bls24-315/cs" + witness_bls24315 "github.com/consensys/gnark/internal/backend/bls24-315/witness" backend_bn254 "github.com/consensys/gnark/internal/backend/bn254/cs" witness_bn254 "github.com/consensys/gnark/internal/backend/bn254/witness" backend_bw6761 "github.com/consensys/gnark/internal/backend/bw6-761/cs" @@ -171,6 +173,12 @@ func IsSolved(r1cs frontend.CompiledConstraintSystem, witness frontend.Circuit) return err } return _r1cs.IsSolved(w) + case *backend_bls24315.R1CS: + w := witness_bls24315.Witness{} + if err := w.FromFullAssignment(witness); err != nil { + return err + } + return _r1cs.IsSolved(w) default: panic("unrecognized R1CS curve type") } diff --git a/backend/groth16/groth16.go b/backend/groth16/groth16.go index 2a9651a3b..e78cec1f3 100644 --- a/backend/groth16/groth16.go +++ b/backend/groth16/groth16.go @@ -27,11 +27,13 @@ import ( "github.com/consensys/gnark/frontend" backend_bls12377 "github.com/consensys/gnark/internal/backend/bls12-377/cs" backend_bls12381 "github.com/consensys/gnark/internal/backend/bls12-381/cs" + backend_bls24315 "github.com/consensys/gnark/internal/backend/bls24-315/cs" backend_bn254 "github.com/consensys/gnark/internal/backend/bn254/cs" backend_bw6761 "github.com/consensys/gnark/internal/backend/bw6-761/cs" witness_bls12377 "github.com/consensys/gnark/internal/backend/bls12-377/witness" witness_bls12381 "github.com/consensys/gnark/internal/backend/bls12-381/witness" + witness_bls24315 "github.com/consensys/gnark/internal/backend/bls24-315/witness" witness_bn254 "github.com/consensys/gnark/internal/backend/bn254/witness" witness_bw6761 "github.com/consensys/gnark/internal/backend/bw6-761/witness" @@ -39,6 +41,7 @@ import ( groth16_bls12377 "github.com/consensys/gnark/internal/backend/bls12-377/groth16" groth16_bls12381 "github.com/consensys/gnark/internal/backend/bls12-381/groth16" + groth16_bls24315 "github.com/consensys/gnark/internal/backend/bls24-315/groth16" groth16_bn254 "github.com/consensys/gnark/internal/backend/bn254/groth16" groth16_bw6761 "github.com/consensys/gnark/internal/backend/bw6-761/groth16" ) @@ -104,6 +107,12 @@ func Verify(proof Proof, vk VerifyingKey, publicWitness frontend.Circuit) error return err } return groth16_bw6761.Verify(_proof, vk.(*groth16_bw6761.VerifyingKey), w) + case *groth16_bls24315.Proof: + w := witness_bls24315.Witness{} + if err := w.FromPublicAssignment(publicWitness); err != nil { + return err + } + return groth16_bls24315.Verify(_proof, vk.(*groth16_bls24315.VerifyingKey), w) default: panic("unrecognized R1CS curve type") } @@ -139,6 +148,12 @@ func ReadAndVerify(proof Proof, vk VerifyingKey, publicWitness io.Reader) error return err } return groth16_bw6761.Verify(proof.(*groth16_bw6761.Proof), _vk, w) + case *groth16_bls24315.VerifyingKey: + w := witness_bls24315.Witness{} + if _, err := w.LimitReadFrom(publicWitness, vk.SizePublicWitness()); err != nil { + return err + } + return groth16_bls24315.Verify(proof.(*groth16_bls24315.Proof), _vk, w) default: panic("unrecognized R1CS curve type") } @@ -180,6 +195,12 @@ func Prove(r1cs frontend.CompiledConstraintSystem, pk ProvingKey, witness fronte return nil, err } return groth16_bw6761.Prove(_r1cs, pk.(*groth16_bw6761.ProvingKey), w, _force) + case *backend_bls24315.R1CS: + w := witness_bls24315.Witness{} + if err := w.FromFullAssignment(witness); err != nil { + return nil, err + } + return groth16_bls24315.Prove(_r1cs, pk.(*groth16_bls24315.ProvingKey), w, _force) default: panic("unrecognized R1CS curve type") } @@ -222,6 +243,12 @@ func ReadAndProve(r1cs frontend.CompiledConstraintSystem, pk ProvingKey, witness return nil, err } return groth16_bw6761.Prove(_r1cs, pk.(*groth16_bw6761.ProvingKey), w, _force) + case *backend_bls24315.R1CS: + w := witness_bls24315.Witness{} + if _, err := w.LimitReadFrom(witness, expectedSize); err != nil { + return nil, err + } + return groth16_bls24315.Prove(_r1cs, pk.(*groth16_bls24315.ProvingKey), w, _force) default: panic("unrecognized R1CS curve type") } @@ -266,6 +293,13 @@ func Setup(r1cs frontend.CompiledConstraintSystem) (ProvingKey, VerifyingKey, er return nil, nil, err } return &pk, &vk, nil + case *backend_bls24315.R1CS: + var pk groth16_bls24315.ProvingKey + var vk groth16_bls24315.VerifyingKey + if err := groth16_bls24315.Setup(_r1cs, &pk, &vk); err != nil { + return nil, nil, err + } + return &pk, &vk, nil default: panic("unrecognized R1CS curve type") } @@ -299,6 +333,12 @@ func DummySetup(r1cs frontend.CompiledConstraintSystem) (ProvingKey, error) { return nil, err } return &pk, nil + case *backend_bls24315.R1CS: + var pk groth16_bls24315.ProvingKey + if err := groth16_bls24315.DummySetup(_r1cs, &pk); err != nil { + return nil, err + } + return &pk, nil default: panic("unrecognized R1CS curve type") } @@ -317,6 +357,8 @@ func NewProvingKey(curveID ecc.ID) ProvingKey { pk = &groth16_bls12381.ProvingKey{} case ecc.BW6_761: pk = &groth16_bw6761.ProvingKey{} + case ecc.BLS24_315: + pk = &groth16_bls24315.ProvingKey{} default: panic("not implemented") } @@ -336,6 +378,8 @@ func NewVerifyingKey(curveID ecc.ID) VerifyingKey { vk = &groth16_bls12381.VerifyingKey{} case ecc.BW6_761: vk = &groth16_bw6761.VerifyingKey{} + case ecc.BLS24_315: + vk = &groth16_bls24315.VerifyingKey{} default: panic("not implemented") } @@ -356,6 +400,8 @@ func NewProof(curveID ecc.ID) Proof { proof = &groth16_bls12381.Proof{} case ecc.BW6_761: proof = &groth16_bw6761.Proof{} + case ecc.BLS24_315: + proof = &groth16_bls24315.Proof{} default: panic("not implemented") } @@ -376,6 +422,8 @@ func NewCS(curveID ecc.ID) frontend.CompiledConstraintSystem { r1cs = &backend_bls12381.R1CS{} case ecc.BW6_761: r1cs = &backend_bw6761.R1CS{} + case ecc.BLS24_315: + r1cs = &backend_bls24315.R1CS{} default: panic("not implemented") } diff --git a/backend/plonk/assert.go b/backend/plonk/assert.go index dccde76ad..d13849acc 100644 --- a/backend/plonk/assert.go +++ b/backend/plonk/assert.go @@ -19,12 +19,14 @@ import ( backend_bls12377 "github.com/consensys/gnark/internal/backend/bls12-377/cs" backend_bls12381 "github.com/consensys/gnark/internal/backend/bls12-381/cs" + backend_bls24315 "github.com/consensys/gnark/internal/backend/bls24-315/cs" backend_bn254 "github.com/consensys/gnark/internal/backend/bn254/cs" backend_bw6761 "github.com/consensys/gnark/internal/backend/bw6-761/cs" "github.com/consensys/gnark/frontend" witness_bls12377 "github.com/consensys/gnark/internal/backend/bls12-377/witness" witness_bls12381 "github.com/consensys/gnark/internal/backend/bls12-381/witness" + witness_bls24315 "github.com/consensys/gnark/internal/backend/bls24-315/witness" witness_bn254 "github.com/consensys/gnark/internal/backend/bn254/witness" witness_bw6761 "github.com/consensys/gnark/internal/backend/bw6-761/witness" "github.com/stretchr/testify/require" @@ -112,6 +114,12 @@ func IsSolved(sparseR1cs frontend.CompiledConstraintSystem, witness frontend.Cir return err } return _sparseR1cs.IsSolved(w) + case *backend_bls24315.SparseR1CS: + w := witness_bls24315.Witness{} + if err := w.FromFullAssignment(witness); err != nil { + return err + } + return _sparseR1cs.IsSolved(w) default: panic("WIP") } diff --git a/backend/plonk/plonk.go b/backend/plonk/plonk.go index 9f3a9c5f5..ada9864b0 100644 --- a/backend/plonk/plonk.go +++ b/backend/plonk/plonk.go @@ -25,21 +25,25 @@ import ( mockcommitment_bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/polynomial/mockcommitment" mockcommitment_bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/polynomial/mockcommitment" + mockcommitment_bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial/mockcommitment" mockcommitment_bn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr/polynomial/mockcommitment" mockcommitment_bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/polynomial/mockcommitment" backend_bls12377 "github.com/consensys/gnark/internal/backend/bls12-377/cs" backend_bls12381 "github.com/consensys/gnark/internal/backend/bls12-381/cs" + backend_bls24315 "github.com/consensys/gnark/internal/backend/bls24-315/cs" backend_bn254 "github.com/consensys/gnark/internal/backend/bn254/cs" backend_bw6761 "github.com/consensys/gnark/internal/backend/bw6-761/cs" plonkbls12377 "github.com/consensys/gnark/internal/backend/bls12-377/plonk" plonkbls12381 "github.com/consensys/gnark/internal/backend/bls12-381/plonk" + plonkbls24315 "github.com/consensys/gnark/internal/backend/bls24-315/plonk" plonkbn254 "github.com/consensys/gnark/internal/backend/bn254/plonk" plonkbw6761 "github.com/consensys/gnark/internal/backend/bw6-761/plonk" bls12377witness "github.com/consensys/gnark/internal/backend/bls12-377/witness" bls12381witness "github.com/consensys/gnark/internal/backend/bls12-381/witness" + bls24315witness "github.com/consensys/gnark/internal/backend/bls24-315/witness" bn254witness "github.com/consensys/gnark/internal/backend/bn254/witness" bw6761witness "github.com/consensys/gnark/internal/backend/bw6-761/witness" ) @@ -93,6 +97,14 @@ func Setup(sparseR1cs frontend.CompiledConstraintSystem, polynomialCommitment po publicData := plonkbw6761.SetupRaw(_sparseR1cs, polynomialCommitment, w) return publicData, nil + case *backend_bls24315.SparseR1CS: + w := bls24315witness.Witness{} + if err := w.FromPublicAssignment(publicWitness); err != nil { + return nil, err + } + publicData := plonkbls24315.SetupRaw(_sparseR1cs, polynomialCommitment, w) + return publicData, nil + default: panic("unrecognized R1CS curve type") } @@ -139,6 +151,15 @@ func SetupDummyCommitment(sparseR1cs frontend.CompiledConstraintSystem, publicWi publicData := plonkbw6761.SetupRaw(_sparseR1cs, polynomialCommitment, w) return publicData, nil + case *backend_bls24315.SparseR1CS: + w := bls24315witness.Witness{} + if err := w.FromPublicAssignment(publicWitness); err != nil { + return nil, err + } + polynomialCommitment := &mockcommitment_bls24315.Scheme{} + publicData := plonkbls24315.SetupRaw(_sparseR1cs, polynomialCommitment, w) + return publicData, nil + default: panic("unrecognized R1CS curve type") } @@ -197,6 +218,18 @@ func Prove(sparseR1cs frontend.CompiledConstraintSystem, publicData PublicData, } return proof, nil + case *backend_bls24315.SparseR1CS: + _publicData := publicData.(*plonkbls24315.PublicRaw) + w := bls24315witness.Witness{} + if err := w.FromFullAssignment(fullWitness); err != nil { + return nil, err + } + proof, err := plonkbls24315.ProveRaw(_sparseR1cs, _publicData, w) + if err != nil { + return proof, err + } + return proof, nil + default: panic("unrecognized R1CS curve type") } @@ -239,6 +272,14 @@ func Verify(proof Proof, publicData PublicData, publicWitness frontend.Circuit) } return plonkbw6761.VerifyRaw(_proof, _publicData, w) + case *plonkbls24315.ProofRaw: + _publicData := publicData.(*plonkbls24315.PublicRaw) + w := bls24315witness.Witness{} + if err := w.FromPublicAssignment(publicWitness); err != nil { + return err + } + return plonkbls24315.VerifyRaw(_proof, _publicData, w) + default: panic("unrecognized proof type") } diff --git a/backend/witness/witness.go b/backend/witness/witness.go index 51814a042..68595183f 100644 --- a/backend/witness/witness.go +++ b/backend/witness/witness.go @@ -46,6 +46,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" witness_bls12377 "github.com/consensys/gnark/internal/backend/bls12-377/witness" witness_bls12381 "github.com/consensys/gnark/internal/backend/bls12-381/witness" + witness_bls24315 "github.com/consensys/gnark/internal/backend/bls24-315/witness" witness_bn254 "github.com/consensys/gnark/internal/backend/bn254/witness" witness_bw6761 "github.com/consensys/gnark/internal/backend/bw6-761/witness" @@ -79,6 +80,12 @@ func WriteFullTo(w io.Writer, curveID ecc.ID, witness frontend.Circuit) (int64, return 0, err } return _witness.WriteTo(w) + case ecc.BLS24_315: + _witness := &witness_bls24315.Witness{} + if err := _witness.FromFullAssignment(witness); err != nil { + return 0, err + } + return _witness.WriteTo(w) default: panic("not implemented") } @@ -111,6 +118,12 @@ func WritePublicTo(w io.Writer, curveID ecc.ID, publicWitness frontend.Circuit) return 0, err } return _witness.WriteTo(w) + case ecc.BLS24_315: + _witness := &witness_bls24315.Witness{} + if err := _witness.FromPublicAssignment(publicWitness); err != nil { + return 0, err + } + return _witness.WriteTo(w) default: panic("not implemented") } diff --git a/doc.go b/doc.go index 1606b4d92..7d7779d9d 100644 --- a/doc.go +++ b/doc.go @@ -9,6 +9,7 @@ // - BLS12_377 // - BLS12_381 // - BW6_761 +// - BLS24_315 // // User documentation // https://docs.gnark.consensys.net diff --git a/frontend/cs_api.go b/frontend/cs_api.go index 721e62fc5..bd22047a0 100644 --- a/frontend/cs_api.go +++ b/frontend/cs_api.go @@ -30,6 +30,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" frbls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" frbls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + frbls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" frbn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr" frbw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" ) @@ -271,6 +272,8 @@ func (cs *ConstraintSystem) IsZero(a Variable, id ecc.ID) Variable { expo.Set(frbls12377.Modulus()) case ecc.BW6_761: expo.Set(frbw6761.Modulus()) + case ecc.BLS24_315: + expo.Set(frbls24315.Modulus()) default: panic("not implemented") } diff --git a/frontend/cs_to_r1cs.go b/frontend/cs_to_r1cs.go index 3f3898a48..063e3eb77 100644 --- a/frontend/cs_to_r1cs.go +++ b/frontend/cs_to_r1cs.go @@ -8,6 +8,7 @@ import ( bls12377r1cs "github.com/consensys/gnark/internal/backend/bls12-377/cs" bls12381r1cs "github.com/consensys/gnark/internal/backend/bls12-381/cs" + bls24315r1cs "github.com/consensys/gnark/internal/backend/bls24-315/cs" bn254r1cs "github.com/consensys/gnark/internal/backend/bn254/cs" bw6761r1cs "github.com/consensys/gnark/internal/backend/bw6-761/cs" ) @@ -122,6 +123,8 @@ func (cs *ConstraintSystem) toR1CS(curveID ecc.ID) (CompiledConstraintSystem, er return bn254r1cs.NewR1CS(res, cs.coeffs), nil case ecc.BW6_761: return bw6761r1cs.NewR1CS(res, cs.coeffs), nil + case ecc.BLS24_315: + return bls24315r1cs.NewR1CS(res, cs.coeffs), nil case ecc.UNKNOWN: return &res, nil default: diff --git a/frontend/cs_to_r1cs_sparse.go b/frontend/cs_to_r1cs_sparse.go index 8a39d9243..dccf2b4e8 100644 --- a/frontend/cs_to_r1cs_sparse.go +++ b/frontend/cs_to_r1cs_sparse.go @@ -25,6 +25,7 @@ import ( bls12377r1cs "github.com/consensys/gnark/internal/backend/bls12-377/cs" bls12381r1cs "github.com/consensys/gnark/internal/backend/bls12-381/cs" + bls24315r1cs "github.com/consensys/gnark/internal/backend/bls24-315/cs" bn254r1cs "github.com/consensys/gnark/internal/backend/bn254/cs" bw6761r1cs "github.com/consensys/gnark/internal/backend/bw6-761/cs" ) @@ -180,6 +181,8 @@ func (cs *ConstraintSystem) toSparseR1CS(curveID ecc.ID) (CompiledConstraintSyst return bn254r1cs.NewSparseR1CS(res, res.Coeffs), nil case ecc.BW6_761: return bw6761r1cs.NewSparseR1CS(res, res.Coeffs), nil + case ecc.BLS24_315: + return bls24315r1cs.NewSparseR1CS(res, res.Coeffs), nil case ecc.UNKNOWN: return &res, nil default: diff --git a/frontend/fuzz.go b/frontend/fuzz.go index 2f9323657..0ef473a4d 100644 --- a/frontend/fuzz.go +++ b/frontend/fuzz.go @@ -12,6 +12,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" frbls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" frbls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + frbls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" frbn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr" frbw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" ) @@ -131,43 +132,41 @@ func (cs *ConstraintSystem) shuffleVariables(seed int64, withConstant bool) []in var v []interface{} n := len(cs.public.variables) + len(cs.secret.variables) + len(cs.internal.variables) if withConstant { - v = make([]interface{}, n*2+4*3) + v = make([]interface{}, 0, n*2+4*3) } else { - v = make([]interface{}, n) + v = make([]interface{}, 0, n) } - offset := 0 for i := 0; i < len(cs.public.variables); i++ { - v[i+offset] = cs.public.variables[i] + v = append(v, cs.public.variables[i]) } - offset += len(cs.public.variables) for i := 0; i < len(cs.secret.variables); i++ { - v[i+offset] = cs.secret.variables[i] + v = append(v, cs.secret.variables[i]) } - offset += len(cs.secret.variables) for i := 0; i < len(cs.internal.variables); i++ { - v[i+offset] = cs.internal.variables[i] + v = append(v, cs.internal.variables[i]) } - offset += len(cs.internal.variables) if withConstant { // let's add some constants to the mix. for i := 0; i < n; i++ { - v[i+offset] = i + v = append(v, i) } - offset += n - v[offset] = frbls12377.Modulus() - v[offset+1] = frbls12381.Modulus() - v[offset+2] = frbn254.Modulus() - v[offset+3] = frbw6761.Modulus() - v[offset+4] = new(big.Int).Sub(frbls12377.Modulus(), new(big.Int).SetUint64(1)) - v[offset+5] = new(big.Int).Sub(frbls12381.Modulus(), new(big.Int).SetUint64(1)) - v[offset+6] = new(big.Int).Sub(frbn254.Modulus(), new(big.Int).SetUint64(1)) - v[offset+7] = new(big.Int).Sub(frbw6761.Modulus(), new(big.Int).SetUint64(1)) - v[offset+8] = new(big.Int).Add(frbls12377.Modulus(), new(big.Int).SetUint64(1)) - v[offset+9] = new(big.Int).Add(frbls12381.Modulus(), new(big.Int).SetUint64(1)) - v[offset+10] = new(big.Int).Add(frbn254.Modulus(), new(big.Int).SetUint64(1)) - v[offset+11] = new(big.Int).Add(frbw6761.Modulus(), new(big.Int).SetUint64(1)) + v = append(v, frbls12377.Modulus()) + v = append(v, frbls12381.Modulus()) + v = append(v, frbn254.Modulus()) + v = append(v, frbw6761.Modulus()) + v = append(v, frbls24315.Modulus()) + v = append(v, new(big.Int).Sub(frbls12377.Modulus(), new(big.Int).SetUint64(1))) + v = append(v, new(big.Int).Sub(frbls12381.Modulus(), new(big.Int).SetUint64(1))) + v = append(v, new(big.Int).Sub(frbn254.Modulus(), new(big.Int).SetUint64(1))) + v = append(v, new(big.Int).Sub(frbw6761.Modulus(), new(big.Int).SetUint64(1))) + v = append(v, new(big.Int).Sub(frbls24315.Modulus(), new(big.Int).SetUint64(1))) + v = append(v, new(big.Int).Add(frbls12377.Modulus(), new(big.Int).SetUint64(1))) + v = append(v, new(big.Int).Add(frbls12381.Modulus(), new(big.Int).SetUint64(1))) + v = append(v, new(big.Int).Add(frbn254.Modulus(), new(big.Int).SetUint64(1))) + v = append(v, new(big.Int).Add(frbw6761.Modulus(), new(big.Int).SetUint64(1))) + v = append(v, new(big.Int).Add(frbls24315.Modulus(), new(big.Int).SetUint64(1))) } rand.Seed(seed) diff --git a/gnarkd/server/server.go b/gnarkd/server/server.go index f95155c04..465ab2c3c 100644 --- a/gnarkd/server/server.go +++ b/gnarkd/server/server.go @@ -285,7 +285,7 @@ func (s *Server) loadCircuits() error { return err } - curves := []ecc.ID{ecc.BN254, ecc.BLS12_381, ecc.BLS12_377, ecc.BW6_761} + curves := []ecc.ID{ecc.BN254, ecc.BLS12_381, ecc.BLS12_377, ecc.BW6_761, ecc.BLS24_315} for _, curve := range curves { curveDir := filepath.Join(s.circuitDir, curve.String()) diff --git a/go.mod b/go.mod index b76903dd6..8abfab0c4 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572 - github.com/consensys/gnark-crypto v0.4.1-0.20210525173051-223ad20f699b + github.com/consensys/gnark-crypto v0.4.1-0.20210608195447-78cc8bdb3077 github.com/fxamacker/cbor/v2 v2.2.0 github.com/golang/protobuf v1.5.2 // indirect github.com/google/uuid v1.2.0 diff --git a/go.sum b/go.sum index 6df94cd49..bfe524665 100644 --- a/go.sum +++ b/go.sum @@ -6,10 +6,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572 h1:+R8G1+Ftumd0DaveLgMIjrFPcAS4G8MsVXWXiyZL5BY= github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= -github.com/consensys/gnark-crypto v0.4.1-0.20210518165244-bafe2b3250bd h1:alnW18BGy69t+sO/62CtP2tuu7sWI4KmOHyn2ldLv58= -github.com/consensys/gnark-crypto v0.4.1-0.20210518165244-bafe2b3250bd/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= -github.com/consensys/gnark-crypto v0.4.1-0.20210525173051-223ad20f699b h1:i2AcGfgHDbsR9l9wu35ISL/bbHGIpybc3HRVfX3CLQE= -github.com/consensys/gnark-crypto v0.4.1-0.20210525173051-223ad20f699b/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= +github.com/consensys/gnark-crypto v0.4.1-0.20210608195447-78cc8bdb3077 h1:9dbjfPxb29oN4CPmTl8vIRYpjyatqQ1t0J0uYNhbJ2Q= +github.com/consensys/gnark-crypto v0.4.1-0.20210608195447-78cc8bdb3077/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= diff --git a/integration_test.go b/integration_test.go index cb69f77fb..5e3080146 100644 --- a/integration_test.go +++ b/integration_test.go @@ -42,7 +42,7 @@ func TestIntegrationAPI(t *testing.T) { t.Fatal(err) } - curves := []ecc.ID{ecc.BN254, ecc.BLS12_377, ecc.BLS12_381, ecc.BW6_761} + curves := []ecc.ID{ecc.BN254, ecc.BLS12_377, ecc.BLS12_381, ecc.BW6_761, ecc.BLS24_315} var buf bytes.Buffer for name, circuit := range circuits.Circuits { diff --git a/internal/backend/bls24-315/cs/r1cs.go b/internal/backend/bls24-315/cs/r1cs.go new file mode 100644 index 000000000..76d5bb3f5 --- /dev/null +++ b/internal/backend/bls24-315/cs/r1cs.go @@ -0,0 +1,394 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package cs + +import ( + "errors" + "fmt" + "io" + "math/big" + "os" + + "github.com/fxamacker/cbor/v2" + + "github.com/consensys/gnark/internal/backend/compiled" + "github.com/consensys/gnark/internal/backend/ioutils" + + "github.com/consensys/gnark-crypto/ecc" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" +) + +// ErrUnsatisfiedConstraint can be generated when solving a R1CS +var ErrUnsatisfiedConstraint = errors.New("constraint is not satisfied") + +// R1CS decsribes a set of R1CS constraint +type R1CS struct { + compiled.R1CS + Coefficients []fr.Element // R1C coefficients indexes point here + loggerOut io.Writer +} + +// NewR1CS returns a new R1CS and sets r1cs.Coefficient (fr.Element) from provided big.Int values +func NewR1CS(r1cs compiled.R1CS, coefficients []big.Int) *R1CS { + r := R1CS{ + r1cs, + make([]fr.Element, len(coefficients)), + os.Stdout, + } + for i := 0; i < len(coefficients); i++ { + r.Coefficients[i].SetBigInt(&coefficients[i]) + } + return &r +} + +// GetNbCoefficients return the number of unique coefficients needed in the R1CS +func (r1cs *R1CS) GetNbCoefficients() int { + return len(r1cs.Coefficients) +} + +// CurveID returns curve ID as defined in gnark-crypto (ecc.BLS24-315) +func (r1cs *R1CS) CurveID() ecc.ID { + return ecc.BLS24_315 +} + +// FrSize return fr.Limbs * 8, size in byte of a fr element +func (r1cs *R1CS) FrSize() int { + return fr.Limbs * 8 +} + +// SetLoggerOutput replace existing logger output with provided one +// default uses os.Stdout +// if nil is provided, logs are not printed +func (r1cs *R1CS) SetLoggerOutput(w io.Writer) { + r1cs.loggerOut = w +} + +// WriteTo encodes R1CS into provided io.Writer using cbor +func (r1cs *R1CS) WriteTo(w io.Writer) (int64, error) { + _w := ioutils.WriterCounter{W: w} // wraps writer to count the bytes written + encoder := cbor.NewEncoder(&_w) + + // encode our object + err := encoder.Encode(r1cs) + return _w.N, err +} + +// ReadFrom attempts to decode R1CS from io.Reader using cbor +func (r1cs *R1CS) ReadFrom(r io.Reader) (int64, error) { + dm, err := cbor.DecOptions{MaxArrayElements: 134217728}.DecMode() + if err != nil { + return 0, err + } + decoder := dm.NewDecoder(r) + err = decoder.Decode(r1cs) + return int64(decoder.NumBytesRead()), err +} + +// IsSolved returns nil if given witness solves the R1CS and error otherwise +// this method wraps r1cs.Solve() and allocates r1cs.Solve() inputs +func (r1cs *R1CS) IsSolved(witness []fr.Element) error { + a := make([]fr.Element, r1cs.NbConstraints) + b := make([]fr.Element, r1cs.NbConstraints) + c := make([]fr.Element, r1cs.NbConstraints) + wireValues := make([]fr.Element, r1cs.NbInternalVariables+r1cs.NbPublicVariables+r1cs.NbSecretVariables) + return r1cs.Solve(witness, a, b, c, wireValues) +} + +// Solve sets all the wires and returns the a, b, c vectors. +// the r1cs system should have been compiled before. The entries in a, b, c are in Montgomery form. +// witness: contains the input variables +// a, b, c vectors: ab-c = hz +// wireValues = [publicWires | secretWires | internalWires ] +// witness = [publicWires | secretWires] (without the ONE_WIRE !) +func (r1cs *R1CS) Solve(witness []fr.Element, a, b, c, wireValues []fr.Element) error { + if len(witness) != int(r1cs.NbPublicVariables-1+r1cs.NbSecretVariables) { // - 1 for ONE_WIRE + return fmt.Errorf("invalid witness size, got %d, expected %d = %d (public - ONE_WIRE) + %d (secret)", len(witness), int(r1cs.NbPublicVariables-1+r1cs.NbSecretVariables), r1cs.NbPublicVariables-1, r1cs.NbSecretVariables) + } + nbWires := r1cs.NbPublicVariables + r1cs.NbSecretVariables + r1cs.NbInternalVariables + // compute the wires and the a, b, c polynomials + if len(a) != int(r1cs.NbConstraints) || len(b) != int(r1cs.NbConstraints) || len(c) != int(r1cs.NbConstraints) || len(wireValues) != nbWires { + return errors.New("invalid input size: len(a, b, c) == r1cs.NbConstraints and len(wireValues) == r1cs.NbWires") + } + // keep track of wire that have a value + wireInstantiated := make([]bool, nbWires) + wireInstantiated[0] = true // ONE_WIRE + wireValues[0].SetOne() + copy(wireValues[1:], witness) // TODO factorize + for i := 0; i < len(witness); i++ { + wireInstantiated[i+1] = true + } + + // now that we know all inputs are set, defer log printing once all wireValues are computed + // (or sooner, if a constraint is not satisfied) + defer r1cs.printLogs(wireValues, wireInstantiated) + + // check if there is an inconsistant constraint + var check fr.Element + + // Loop through computational constraints (the one wwe need to solve and compute a wire in) + for i := 0; i < int(r1cs.NbCOConstraints); i++ { + + // solve the constraint, this will compute the missing wire of the gate + r1cs.solveR1C(&r1cs.Constraints[i], wireInstantiated, wireValues) + + // at this stage we are guaranteed that a[i]*b[i]=c[i] + // if not, it means there is a bug in the solver + a[i], b[i], c[i] = instantiateR1C(&r1cs.Constraints[i], r1cs, wireValues) + + check.Mul(&a[i], &b[i]) + if !check.Equal(&c[i]) { + return fmt.Errorf("%w: %s", ErrUnsatisfiedConstraint, "couldn't solve computational constraint. May happen: div by 0 or no inverse found") + } + } + + // Loop through the assertions -- here all wireValues should be instantiated + // if a[i] * b[i] != c[i]; it means the constraint is not satisfied + for i := int(r1cs.NbCOConstraints); i < len(r1cs.Constraints); i++ { + + // A this stage we are not guaranteed that a[i+sizecg]*b[i+sizecg]=c[i+sizecg] because we only query the values (computed + // at the previous step) + a[i], b[i], c[i] = instantiateR1C(&r1cs.Constraints[i], r1cs, wireValues) + + // check that the constraint is satisfied + check.Mul(&a[i], &b[i]) + if !check.Equal(&c[i]) { + debugInfo := r1cs.DebugInfo[i-int(r1cs.NbCOConstraints)] + debugInfoStr := r1cs.logValue(debugInfo, wireValues, wireInstantiated) + return fmt.Errorf("%w: %s", ErrUnsatisfiedConstraint, debugInfoStr) + } + } + + return nil +} + +func (r1cs *R1CS) logValue(entry compiled.LogEntry, wireValues []fr.Element, wireInstantiated []bool) string { + var toResolve []interface{} + for j := 0; j < len(entry.ToResolve); j++ { + wireID := entry.ToResolve[j] + if !wireInstantiated[wireID] { + toResolve = append(toResolve, "???") + } else { + toResolve = append(toResolve, wireValues[wireID].String()) + } + } + return fmt.Sprintf(entry.Format, toResolve...) +} + +func (r1cs *R1CS) printLogs(wireValues []fr.Element, wireInstantiated []bool) { + + // for each log, resolve the wire values and print the log to stdout + for i := 0; i < len(r1cs.Logs); i++ { + logLine := r1cs.logValue(r1cs.Logs[i], wireValues, wireInstantiated) + if r1cs.loggerOut != nil { + io.WriteString(r1cs.loggerOut, logLine) + } + } +} + +// AddTerm returns res += (value * term.Coefficient) +func (r1cs *R1CS) AddTerm(res *fr.Element, t compiled.Term, value fr.Element) *fr.Element { + coeffValue := t.CoeffValue() + switch coeffValue { + case 1: + return res.Add(res, &value) + case -1: + return res.Sub(res, &value) + case 0: + return res + case 2: + var buffer fr.Element + buffer.Double(&value) + return res.Add(res, &buffer) + default: + var buffer fr.Element + buffer.Mul(&r1cs.Coefficients[t.CoeffID()], &value) + return res.Add(res, &buffer) + } +} + +// mulWireByCoeff returns into.Mul(into, term.Coefficient) +func (r1cs *R1CS) mulWireByCoeff(res *fr.Element, t compiled.Term) *fr.Element { + coeffValue := t.CoeffValue() + switch coeffValue { + case 1: + return res + case -1: + return res.Neg(res) + case 0: + return res.SetZero() + case 2: + return res.Double(res) + default: + return res.Mul(res, &r1cs.Coefficients[t.CoeffID()]) + } +} + +// compute left, right, o part of a r1cs constraint +// this function is called when all the wires have been computed +// it instantiates the l, r o part of a R1C +func instantiateR1C(r *compiled.R1C, r1cs *R1CS, wireValues []fr.Element) (a, b, c fr.Element) { + + for _, t := range r.L { + r1cs.AddTerm(&a, t, wireValues[t.VariableID()]) + } + + for _, t := range r.R { + r1cs.AddTerm(&b, t, wireValues[t.VariableID()]) + } + + for _, t := range r.O { + r1cs.AddTerm(&c, t, wireValues[t.VariableID()]) + } + + return +} + +// solveR1c computes a wire by solving a r1cs +// the function searches for the unset wire (either the unset wire is +// alone, or it can be computed without ambiguity using the other computed wires +// , eg when doing a binary decomposition: either way the missing wire can +// be computed without ambiguity because the r1cs is correctly ordered) +func (r1cs *R1CS) solveR1C(r *compiled.R1C, wireInstantiated []bool, wireValues []fr.Element) { + + switch r.Solver { + + // in this case we solve a R1C by isolating the uncomputed wire + case compiled.SingleOutput: + + // the index of the non zero entry shows if L, R or O has an uninstantiated wire + // the content is the ID of the wire non instantiated + var loc uint8 + + var a, b, c fr.Element + var termToCompute compiled.Term + + processTerm := func(t compiled.Term, val *fr.Element, locValue uint8) { + cID := t.VariableID() + if wireInstantiated[cID] { + r1cs.AddTerm(val, t, wireValues[cID]) + } else { + if loc != 0 { + panic("found more than one wire to instantiate") + } + termToCompute = t + loc = locValue + } + } + + for _, t := range r.L { + processTerm(t, &a, 1) + } + + for _, t := range r.R { + processTerm(t, &b, 2) + } + + for _, t := range r.O { + processTerm(t, &c, 3) + } + + // ensure we found the unset wire + if loc == 0 { + // this wire may have been instantiated as part of moExpression already + return + } + + // we compute the wire value and instantiate it + cID := termToCompute.VariableID() + + switch loc { + case 1: + if !b.IsZero() { + wireValues[cID].Div(&c, &b). + Sub(&wireValues[cID], &a) + r1cs.mulWireByCoeff(&wireValues[cID], termToCompute) + } + case 2: + if !a.IsZero() { + wireValues[cID].Div(&c, &a). + Sub(&wireValues[cID], &b) + r1cs.mulWireByCoeff(&wireValues[cID], termToCompute) + } + case 3: + wireValues[cID].Mul(&a, &b). + Sub(&wireValues[cID], &c) + r1cs.mulWireByCoeff(&wireValues[cID], termToCompute) + } + + wireInstantiated[cID] = true + + // in the case the R1C is solved by directly computing the binary decomposition + // of the variable + case compiled.BinaryDec: + + // the binary decomposition must be called on the non Mont form of the number + var n fr.Element + for _, t := range r.O { + r1cs.AddTerm(&n, t, wireValues[t.VariableID()]) + } + var bigN big.Int + n.ToBigIntRegular(&bigN) + + nbBits := len(r.L) + + // cs.reduce() is non deterministic, so the variables are not sorted according to the bit position + // i->value of the ithbit + bitSlice := make([]uint, nbBits) + + // binary decomposition of n + for i := 0; i < nbBits; i++ { + bitSlice[i] = bigN.Bit(i) + } + + // log of c>0 where c is a power of 2 + quickLog := func(bi big.Int) int { + var bCopy, zero, checker big.Int + bCopy.Set(&bi) + res := 0 + for bCopy.Cmp(&zero) != 0 { + bCopy.Rsh(&bCopy, 1) + res++ + } + res-- + checker.SetInt64(1) + checker.Lsh(&checker, uint(res)) + // bi is not a power of 2, meaning it has been reduced mod r, + // so the bit is 0. We return the index of last entry of BitSlice, + // which is 0 + if checker.Cmp(&bi) != 0 { + return nbBits - 1 + } + return res + } + + // affecting the correct bit to the correct variable + for _, t := range r.L { + cID := t.VariableID() + coefID := t.CoeffID() + coef := r1cs.Coefficients[coefID] + var bcoef big.Int + coef.ToBigIntRegular(&bcoef) + ithBit := quickLog(bcoef) + wireValues[cID].SetUint64(uint64(bitSlice[ithBit])) + wireInstantiated[cID] = true + } + + default: + panic("unimplemented solving method") + } +} diff --git a/internal/backend/bls24-315/cs/r1cs_sparse.go b/internal/backend/bls24-315/cs/r1cs_sparse.go new file mode 100644 index 000000000..fa53f3953 --- /dev/null +++ b/internal/backend/bls24-315/cs/r1cs_sparse.go @@ -0,0 +1,265 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package cs + +import ( + "fmt" + "math/big" + + "github.com/consensys/gnark-crypto/ecc" + + "github.com/consensys/gnark/internal/backend/compiled" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" +) + +// SparseR1CS represents a Plonk like circuit +type SparseR1CS struct { + compiled.SparseR1CS + + // Coefficients in the constraints + Coefficients []fr.Element // list of unique coefficients. +} + +// NewSparseR1CS returns a new SparseR1CS and sets r1cs.Coefficient (fr.Element) from provided big.Int values +func NewSparseR1CS(r1cs compiled.SparseR1CS, coefficients []big.Int) *SparseR1CS { + cs := SparseR1CS{ + r1cs, + make([]fr.Element, len(coefficients)), + } + for i := 0; i < len(coefficients); i++ { + cs.Coefficients[i].SetBigInt(&coefficients[i]) + } + return &cs +} + +// FrSize return fr.Limbs * 8, size in byte of a fr element +func (cs *SparseR1CS) FrSize() int { + return fr.Limbs * 8 +} + +// GetNbCoefficients return the number of unique coefficients needed in the R1CS +func (cs *SparseR1CS) GetNbCoefficients() int { + return len(cs.Coefficients) +} + +// CurveID returns curve ID as defined in gnark-crypto (ecc.BLS24-315) +func (cs *SparseR1CS) CurveID() ecc.ID { + return ecc.BLS24_315 +} + +// find unsolved variable +// returns 0 if the variable to solve is L, 1 if it's R, 2 if it's O +func findUnsolvedVariable(c compiled.SparseR1C, wireInstantiated []bool) int { + if c.L.CoeffID() != 0 && !wireInstantiated[c.L.VariableID()] { + return 0 + } + if c.M[0].CoeffID() != 0 && !wireInstantiated[c.M[0].VariableID()] { + // M[0] corresponds to L by default + return 0 + } + if c.R.CoeffID() != 0 && !wireInstantiated[c.R.VariableID()] { + return 1 + } + if c.M[1].CoeffID() != 0 && !wireInstantiated[c.M[1].VariableID()] { + // M[1] corresponds to R by default + return 1 + } + // TODO panic if wire is already instantiated + // only O remains + return 2 +} + +// computeTerm computes coef*variable +func (cs *SparseR1CS) computeTerm(t compiled.Term, solution []fr.Element) fr.Element { + var res fr.Element + res.Mul(&cs.Coefficients[t.CoeffID()], &solution[t.VariableID()]) + return res +} + +// solveConstraint solves c with the help of the slices wireInstantiated +// and solution. Those are used to find which variable remains to be solved, +// and the way of solving it (binary or single value). Once the variable(s) +// is solved, solution and wireInstantiated are updated. +func (cs *SparseR1CS) solveConstraint(c compiled.SparseR1C, wireInstantiated []bool, solution []fr.Element) { + + switch c.Solver { + case compiled.SingleOutput: + + lro := findUnsolvedVariable(c, wireInstantiated) + if lro == 0 { // we solve for L: u1L+u2R+u3LR+u4O+k=0 => L(u1+u3R)+u2R+u4O+k = 0 + + var u1, u2, u3, den, num, v1, v2 fr.Element + u3.Mul(&cs.Coefficients[c.M[0].CoeffID()], &cs.Coefficients[c.M[1].CoeffID()]) + u1.Set(&cs.Coefficients[c.L.CoeffID()]) + u2.Set(&cs.Coefficients[c.R.CoeffID()]) + den.Mul(&u3, &solution[c.R.VariableID()]).Add(&den, &u1) + + v1 = cs.computeTerm(c.R, solution) + v2 = cs.computeTerm(c.O, solution) + num.Add(&v1, &v2).Add(&num, &cs.Coefficients[c.K]) + + solution[c.L.VariableID()].Div(&num, &den).Neg(&solution[c.L.VariableID()]) + wireInstantiated[c.L.VariableID()] = true + + } else if lro == 1 { // we solve for R: u1L+u2R+u3LR+u4O+k=0 => R(u2+u3L)+u1L+u4O+k = 0 + + var u1, u2, u3, den, num, v1, v2 fr.Element + u3.Mul(&cs.Coefficients[c.M[0].VariableID()], &cs.Coefficients[c.M[1].VariableID()]) + u1.Set(&cs.Coefficients[c.L.CoeffID()]) + u2.Set(&cs.Coefficients[c.R.CoeffID()]) + den.Mul(&u3, &solution[c.L.VariableID()]).Add(&den, &u2) + + v1 = cs.computeTerm(c.L, solution) + v2 = cs.computeTerm(c.O, solution) + num.Add(&v1, &v2).Add(&num, &cs.Coefficients[c.K]) + + solution[c.L.VariableID()].Div(&num, &den).Neg(&solution[c.L.VariableID()]) + wireInstantiated[c.L.VariableID()] = true + + } else { // O we solve for O + l := cs.computeTerm(c.L, solution) + r := cs.computeTerm(c.R, solution) + m := cs.computeTerm(c.M[0], solution) + _m := cs.computeTerm(c.M[1], solution) + m.Mul(&m, &_m) + m.Add(&m, &l).Add(&m, &r).Add(&m, &cs.Coefficients[c.K]) + m.Div(&m, &cs.Coefficients[c.O.CoeffID()]) + + solution[c.O.VariableID()].Neg(&m) + wireInstantiated[c.O.VariableID()] = true + } + + case compiled.BinaryDec: + // 2*L + R + O = 0, computed as a = c/2, b = c%2 + var bo, bl, br, two big.Int + o := cs.computeTerm(c.O, solution) + o.Neg(&o) + o.ToBigIntRegular(&bo) + two.SetInt64(2) + br.Mod(&bo, &two) + bl.Rsh(&bo, 1) + solution[c.L.VariableID()].SetBigInt(&bl) + solution[c.R.VariableID()].SetBigInt(&br) + wireInstantiated[c.L.VariableID()] = true + wireInstantiated[c.R.VariableID()] = true + + default: + panic("unimplemented solving method") + } + +} + +// IsSolved returns nil if given witness solves the R1CS and error otherwise +// this method wraps r1cs.Solve() and allocates r1cs.Solve() inputs +func (cs *SparseR1CS) IsSolved(witness []fr.Element) error { + _, err := cs.Solve(witness) + return err +} + +// checkConstraint verifies that the constraint holds +func (cs *SparseR1CS) checkConstraint(c compiled.SparseR1C, solution []fr.Element) error { + var res, a, b, zero fr.Element + res = cs.computeTerm(c.L, solution) + a = cs.computeTerm(c.R, solution) + res.Add(&res, &a) + a = cs.computeTerm(c.M[0], solution) + b = cs.computeTerm(c.M[1], solution) + a.Mul(&a, &b) + res.Add(&res, &a) + a = cs.computeTerm(c.O, solution) + res.Add(&res, &a) + a = cs.Coefficients[c.K] + res.Add(&res, &a) + if !res.Equal(&zero) { + return fmt.Errorf("%w", ErrUnsatisfiedConstraint) + } + return nil +} + +// Solve sets all the wires. +// wireValues = [publicInputs | secretInputs | internalVariables ] +// witness: contains the input variables +// it returns the full slice of wires +func (cs *SparseR1CS) Solve(witness []fr.Element) (solution []fr.Element, err error) { + + expectedWitnessSize := int(cs.NbPublicVariables + cs.NbSecretVariables) + if len(witness) != expectedWitnessSize { + return nil, fmt.Errorf( + "invalid witness size, got %d, expected %d = %d (public) + %d (secret)", + len(witness), + expectedWitnessSize, + cs.NbPublicVariables, + cs.NbSecretVariables, + ) + } + + // set the slices holding the solution and monitoring which variables have been solved + nbVariables := cs.NbInternalVariables + cs.NbSecretVariables + cs.NbPublicVariables + solution = make([]fr.Element, nbVariables) + wireInstantiated := make([]bool, nbVariables) + + // solution = [publicInputs | secretInputs | internalVariables ] -> we fill publicInputs | secretInputs + copy(solution, witness) + for i := 0; i < len(witness); i++ { + wireInstantiated[i] = true + } + + // defer log printing once all wireValues are computed + defer cs.printLogs(solution, wireInstantiated) + + // loop through the constraints to solve the variables + for i := 0; i < len(cs.Constraints); i++ { + cs.solveConstraint(cs.Constraints[i], wireInstantiated, solution) + err = cs.checkConstraint(cs.Constraints[i], solution) + if err != nil { + fmt.Printf("%d-th constraint\n", i) + return solution, err + } + } + + // loop through the assertions and check consistency + for i := 0; i < len(cs.Assertions); i++ { + err = cs.checkConstraint(cs.Assertions[i], solution) + if err != nil { + return solution, err + } + } + + return solution, nil + +} + +func logValue(entry compiled.LogEntry, wireValues []fr.Element, wireInstantiated []bool) string { + var toResolve []interface{} + for j := 0; j < len(entry.ToResolve); j++ { + wireID := entry.ToResolve[j] + if !wireInstantiated[wireID] { + panic("wire values was not instantiated") + } + toResolve = append(toResolve, wireValues[wireID].String()) + } + return fmt.Sprintf(entry.Format, toResolve...) +} + +func (cs *SparseR1CS) printLogs(wireValues []fr.Element, wireInstantiated []bool) { + + // for each log, resolve the wire values and print the log to stdout + for i := 0; i < len(cs.Logs); i++ { + fmt.Print(logValue(cs.Logs[i], wireValues, wireInstantiated)) + } +} diff --git a/internal/backend/bls24-315/cs/r1cs_test.go b/internal/backend/bls24-315/cs/r1cs_test.go new file mode 100644 index 000000000..51dc21d66 --- /dev/null +++ b/internal/backend/bls24-315/cs/r1cs_test.go @@ -0,0 +1,116 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package cs_test + +import ( + "bytes" + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/backend" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/backend/circuits" + "reflect" + "testing" + + "github.com/consensys/gnark/internal/backend/bls24-315/cs" +) + +func TestSerialization(t *testing.T) { + + var buffer, buffer2 bytes.Buffer + + for name, circuit := range circuits.Circuits { + + r1cs, err := frontend.Compile(ecc.BLS24_315, backend.GROTH16, circuit.Circuit) + if err != nil { + t.Fatal(err) + } + if testing.Short() && r1cs.GetNbConstraints() > 50 { + continue + } + + // copmpile a second time to ensure determinism + r1cs2, err := frontend.Compile(ecc.BLS24_315, backend.GROTH16, circuit.Circuit) + if err != nil { + t.Fatal(err) + } + + // no need to serialize. + r1cs.SetLoggerOutput(nil) + r1cs2.SetLoggerOutput(nil) + { + buffer.Reset() + t.Log(name) + var err error + var written, read int64 + written, err = r1cs.WriteTo(&buffer) + if err != nil { + t.Fatal(err) + } + var reconstructed cs.R1CS + read, err = reconstructed.ReadFrom(&buffer) + if err != nil { + t.Fatal(err) + } + if written != read { + t.Fatal("didn't read same number of bytes we wrote") + } + // compare original and reconstructed + if !reflect.DeepEqual(r1cs, &reconstructed) { + t.Fatal("round trip serialization failed") + } + } + + // ensure determinism in compilation / serialization / reconstruction + { + buffer.Reset() + n, err := r1cs.WriteTo(&buffer) + if err != nil { + t.Fatal(err) + } + if n == 0 { + t.Fatal("No bytes are written") + } + + buffer2.Reset() + _, err = r1cs2.WriteTo(&buffer2) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(buffer.Bytes(), buffer2.Bytes()) { + t.Fatal("compilation of R1CS is not deterministic") + } + + var r, r2 cs.R1CS + n, err = r.ReadFrom(&buffer) + if err != nil { + t.Fatal(nil) + } + if n == 0 { + t.Fatal("No bytes are read") + } + _, err = r2.ReadFrom(&buffer2) + if err != nil { + t.Fatal(nil) + } + + if !reflect.DeepEqual(r, r2) { + t.Fatal("compilation of R1CS is not deterministic (reconstruction)") + } + } + } +} diff --git a/internal/backend/bls24-315/groth16/groth16_test.go b/internal/backend/bls24-315/groth16/groth16_test.go new file mode 100644 index 000000000..b9954e1a6 --- /dev/null +++ b/internal/backend/bls24-315/groth16/groth16_test.go @@ -0,0 +1,338 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package groth16_test + +import ( + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + + curve "github.com/consensys/gnark-crypto/ecc/bls24-315" + + "github.com/consensys/gnark/internal/backend/bls24-315/cs" + + bls24_315witness "github.com/consensys/gnark/internal/backend/bls24-315/witness" + + bls24_315groth16 "github.com/consensys/gnark/internal/backend/bls24-315/groth16" + + "bytes" + "github.com/fxamacker/cbor/v2" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/backend" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/backend/circuits" +) + +func TestCircuits(t *testing.T) { + for name, circuit := range circuits.Circuits { + t.Run(name, func(t *testing.T) { + assert := groth16.NewAssert(t) + r1cs, err := frontend.Compile(curve.ID, backend.GROTH16, circuit.Circuit) + assert.NoError(err) + assert.ProverFailed(r1cs, circuit.Bad) + assert.ProverSucceeded(r1cs, circuit.Good) + }) + } +} + +//--------------------// +// benches // +//--------------------// + +type refCircuit struct { + nbConstraints int + X frontend.Variable + Y frontend.Variable `gnark:",public"` +} + +func (circuit *refCircuit) Define(curveID ecc.ID, cs *frontend.ConstraintSystem) error { + for i := 0; i < circuit.nbConstraints; i++ { + circuit.X = cs.Mul(circuit.X, circuit.X) + } + cs.AssertIsEqual(circuit.X, circuit.Y) + return nil +} + +func referenceCircuit() (frontend.CompiledConstraintSystem, frontend.Circuit) { + const nbConstraints = 40000 + circuit := refCircuit{ + nbConstraints: nbConstraints, + } + r1cs, err := frontend.Compile(curve.ID, backend.GROTH16, &circuit) + if err != nil { + panic(err) + } + + var good refCircuit + good.X.Assign(2) + + // compute expected Y + var expectedY fr.Element + expectedY.SetUint64(2) + + for i := 0; i < nbConstraints; i++ { + expectedY.Mul(&expectedY, &expectedY) + } + + good.Y.Assign(expectedY) + + return r1cs, &good +} + +func TestReferenceCircuit(t *testing.T) { + if testing.Short() { + t.SkipNow() + } + assert := groth16.NewAssert(t) + r1cs, witness := referenceCircuit() + assert.ProverSucceeded(r1cs, witness) +} + +func BenchmarkSetup(b *testing.B) { + r1cs, _ := referenceCircuit() + + var pk bls24_315groth16.ProvingKey + var vk bls24_315groth16.VerifyingKey + b.ResetTimer() + + b.Run("setup", func(b *testing.B) { + for i := 0; i < b.N; i++ { + bls24_315groth16.Setup(r1cs.(*cs.R1CS), &pk, &vk) + } + }) +} + +func BenchmarkProver(b *testing.B) { + r1cs, _solution := referenceCircuit() + fullWitness := bls24_315witness.Witness{} + err := fullWitness.FromFullAssignment(_solution) + if err != nil { + b.Fatal(err) + } + + var pk bls24_315groth16.ProvingKey + bls24_315groth16.DummySetup(r1cs.(*cs.R1CS), &pk) + + b.ResetTimer() + b.Run("prover", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = bls24_315groth16.Prove(r1cs.(*cs.R1CS), &pk, fullWitness, false) + } + }) +} + +func BenchmarkVerifier(b *testing.B) { + r1cs, _solution := referenceCircuit() + fullWitness := bls24_315witness.Witness{} + err := fullWitness.FromFullAssignment(_solution) + if err != nil { + b.Fatal(err) + } + publicWitness := bls24_315witness.Witness{} + err = publicWitness.FromPublicAssignment(_solution) + if err != nil { + b.Fatal(err) + } + + var pk bls24_315groth16.ProvingKey + var vk bls24_315groth16.VerifyingKey + bls24_315groth16.Setup(r1cs.(*cs.R1CS), &pk, &vk) + proof, err := bls24_315groth16.Prove(r1cs.(*cs.R1CS), &pk, fullWitness, false) + if err != nil { + panic(err) + } + + b.ResetTimer() + b.Run("verifier", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = bls24_315groth16.Verify(proof, &vk, publicWitness) + } + }) +} + +func BenchmarkSerialization(b *testing.B) { + r1cs, _solution := referenceCircuit() + fullWitness := bls24_315witness.Witness{} + err := fullWitness.FromFullAssignment(_solution) + if err != nil { + b.Fatal(err) + } + + var pk bls24_315groth16.ProvingKey + var vk bls24_315groth16.VerifyingKey + bls24_315groth16.Setup(r1cs.(*cs.R1CS), &pk, &vk) + proof, err := bls24_315groth16.Prove(r1cs.(*cs.R1CS), &pk, fullWitness, false) + if err != nil { + panic(err) + } + + b.ReportAllocs() + + // --------------------------------------------------------------------------------------------- + // bls24_315groth16.ProvingKey binary serialization + b.Run("pk: binary serialization (bls24_315groth16.ProvingKey)", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + var buf bytes.Buffer + _, _ = pk.WriteTo(&buf) + } + }) + b.Run("pk: binary deserialization (bls24_315groth16.ProvingKey)", func(b *testing.B) { + var buf bytes.Buffer + _, _ = pk.WriteTo(&buf) + var pkReconstructed bls24_315groth16.ProvingKey + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf := bytes.NewBuffer(buf.Bytes()) + _, _ = pkReconstructed.ReadFrom(buf) + } + }) + { + var buf bytes.Buffer + _, _ = pk.WriteTo(&buf) + } + + // --------------------------------------------------------------------------------------------- + // bls24_315groth16.ProvingKey binary serialization (uncompressed) + b.Run("pk: binary raw serialization (bls24_315groth16.ProvingKey)", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + var buf bytes.Buffer + _, _ = pk.WriteRawTo(&buf) + } + }) + b.Run("pk: binary raw deserialization (bls24_315groth16.ProvingKey)", func(b *testing.B) { + var buf bytes.Buffer + _, _ = pk.WriteRawTo(&buf) + var pkReconstructed bls24_315groth16.ProvingKey + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf := bytes.NewBuffer(buf.Bytes()) + _, _ = pkReconstructed.ReadFrom(buf) + } + }) + { + var buf bytes.Buffer + _, _ = pk.WriteRawTo(&buf) + } + + // --------------------------------------------------------------------------------------------- + // bls24_315groth16.ProvingKey binary serialization (cbor) + b.Run("pk: binary cbor serialization (bls24_315groth16.ProvingKey)", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + var buf bytes.Buffer + enc := cbor.NewEncoder(&buf) + enc.Encode(&pk) + } + }) + b.Run("pk: binary cbor deserialization (bls24_315groth16.ProvingKey)", func(b *testing.B) { + var buf bytes.Buffer + enc := cbor.NewEncoder(&buf) + enc.Encode(&pk) + var pkReconstructed bls24_315groth16.ProvingKey + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf := bytes.NewBuffer(buf.Bytes()) + dec := cbor.NewDecoder(buf) + dec.Decode(&pkReconstructed) + } + }) + { + var buf bytes.Buffer + enc := cbor.NewEncoder(&buf) + enc.Encode(&pk) + } + + // --------------------------------------------------------------------------------------------- + // bls24_315groth16.Proof binary serialization + b.Run("proof: binary serialization (bls24_315groth16.Proof)", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + var buf bytes.Buffer + _, _ = proof.WriteTo(&buf) + } + }) + b.Run("proof: binary deserialization (bls24_315groth16.Proof)", func(b *testing.B) { + var buf bytes.Buffer + _, _ = proof.WriteTo(&buf) + var proofReconstructed bls24_315groth16.Proof + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf := bytes.NewBuffer(buf.Bytes()) + _, _ = proofReconstructed.ReadFrom(buf) + } + }) + { + var buf bytes.Buffer + _, _ = proof.WriteTo(&buf) + } + + // --------------------------------------------------------------------------------------------- + // bls24_315groth16.Proof binary serialization (uncompressed) + b.Run("proof: binary raw serialization (bls24_315groth16.Proof)", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + var buf bytes.Buffer + _, _ = proof.WriteRawTo(&buf) + } + }) + b.Run("proof: binary raw deserialization (bls24_315groth16.Proof)", func(b *testing.B) { + var buf bytes.Buffer + _, _ = proof.WriteRawTo(&buf) + var proofReconstructed bls24_315groth16.Proof + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf := bytes.NewBuffer(buf.Bytes()) + _, _ = proofReconstructed.ReadFrom(buf) + } + }) + { + var buf bytes.Buffer + _, _ = proof.WriteRawTo(&buf) + } + + // --------------------------------------------------------------------------------------------- + // bls24_315groth16.Proof binary serialization (cbor) + b.Run("proof: binary cbor serialization (bls24_315groth16.Proof)", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + var buf bytes.Buffer + enc := cbor.NewEncoder(&buf) + enc.Encode(&proof) + } + }) + b.Run("proof: binary cbor deserialization (bls24_315groth16.Proof)", func(b *testing.B) { + var buf bytes.Buffer + enc := cbor.NewEncoder(&buf) + enc.Encode(&proof) + var proofReconstructed bls24_315groth16.Proof + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf := bytes.NewBuffer(buf.Bytes()) + dec := cbor.NewDecoder(buf) + dec.Decode(&proofReconstructed) + } + }) + { + var buf bytes.Buffer + enc := cbor.NewEncoder(&buf) + enc.Encode(&proof) + } + +} diff --git a/internal/backend/bls24-315/groth16/marshal.go b/internal/backend/bls24-315/groth16/marshal.go new file mode 100644 index 000000000..39693d084 --- /dev/null +++ b/internal/backend/bls24-315/groth16/marshal.go @@ -0,0 +1,260 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package groth16 + +import ( + curve "github.com/consensys/gnark-crypto/ecc/bls24-315" + + "io" +) + +// WriteTo writes binary encoding of the Proof elements to writer +// points are stored in compressed form Ar | Krs | Bs +// use WriteRawTo(...) to encode the proof without point compression +func (proof *Proof) WriteTo(w io.Writer) (n int64, err error) { + return proof.writeTo(w, false) +} + +// WriteRawTo writes binary encoding of the Proof elements to writer +// points are stored in uncompressed form Ar | Krs | Bs +// use WriteTo(...) to encode the proof with point compression +func (proof *Proof) WriteRawTo(w io.Writer) (n int64, err error) { + return proof.writeTo(w, true) +} + +func (proof *Proof) writeTo(w io.Writer, raw bool) (int64, error) { + var enc *curve.Encoder + if raw { + enc = curve.NewEncoder(w, curve.RawEncoding()) + } else { + enc = curve.NewEncoder(w) + } + + if err := enc.Encode(&proof.Ar); err != nil { + return enc.BytesWritten(), err + } + if err := enc.Encode(&proof.Bs); err != nil { + return enc.BytesWritten(), err + } + if err := enc.Encode(&proof.Krs); err != nil { + return enc.BytesWritten(), err + } + return enc.BytesWritten(), nil +} + +// ReadFrom attempts to decode a Proof from reader +// Proof must be encoded through WriteTo (compressed) or WriteRawTo (uncompressed) +func (proof *Proof) ReadFrom(r io.Reader) (n int64, err error) { + + dec := curve.NewDecoder(r) + + if err := dec.Decode(&proof.Ar); err != nil { + return dec.BytesRead(), err + } + if err := dec.Decode(&proof.Bs); err != nil { + return dec.BytesRead(), err + } + if err := dec.Decode(&proof.Krs); err != nil { + return dec.BytesRead(), err + } + + return dec.BytesRead(), nil +} + +// WriteTo writes binary encoding of the key elements to writer +// points are compressed +// use WriteRawTo(...) to encode the key without point compression +func (vk *VerifyingKey) WriteTo(w io.Writer) (n int64, err error) { + return vk.writeTo(w, false) +} + +// WriteRawTo writes binary encoding of the key elements to writer +// points are not compressed +// use WriteTo(...) to encode the key with point compression +func (vk *VerifyingKey) WriteRawTo(w io.Writer) (n int64, err error) { + return vk.writeTo(w, true) +} + +// writeTo serialization format: +// follows bellman format: +// https://github.com/zkcrypto/bellman/blob/fa9be45588227a8c6ec34957de3f68705f07bd92/src/groth16/mod.rs#L143 +// [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2,uint32(len(Kvk)),[Kvk]1 +func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { + var enc *curve.Encoder + if raw { + enc = curve.NewEncoder(w, curve.RawEncoding()) + } else { + enc = curve.NewEncoder(w) + } + + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + if err := enc.Encode(&vk.G1.Alpha); err != nil { + return enc.BytesWritten(), err + } + if err := enc.Encode(&vk.G1.Beta); err != nil { + return enc.BytesWritten(), err + } + if err := enc.Encode(&vk.G2.Beta); err != nil { + return enc.BytesWritten(), err + } + if err := enc.Encode(&vk.G2.Gamma); err != nil { + return enc.BytesWritten(), err + } + if err := enc.Encode(&vk.G1.Delta); err != nil { + return enc.BytesWritten(), err + } + if err := enc.Encode(&vk.G2.Delta); err != nil { + return enc.BytesWritten(), err + } + + // uint32(len(Kvk)),[Kvk]1 + if err := enc.Encode(vk.G1.K); err != nil { + return enc.BytesWritten(), err + } + return enc.BytesWritten(), nil +} + +// ReadFrom attempts to decode a VerifyingKey from reader +// VerifyingKey must be encoded through WriteTo (compressed) or WriteRawTo (uncompressed) +// serialization format: +// https://github.com/zkcrypto/bellman/blob/fa9be45588227a8c6ec34957de3f68705f07bd92/src/groth16/mod.rs#L143 +// [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2,uint32(len(Kvk)),[Kvk]1 +func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { + + dec := curve.NewDecoder(r) + + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + if err := dec.Decode(&vk.G1.Alpha); err != nil { + return dec.BytesRead(), err + } + if err := dec.Decode(&vk.G1.Beta); err != nil { + return dec.BytesRead(), err + } + if err := dec.Decode(&vk.G2.Beta); err != nil { + return dec.BytesRead(), err + } + if err := dec.Decode(&vk.G2.Gamma); err != nil { + return dec.BytesRead(), err + } + if err := dec.Decode(&vk.G1.Delta); err != nil { + return dec.BytesRead(), err + } + if err := dec.Decode(&vk.G2.Delta); err != nil { + return dec.BytesRead(), err + } + + // uint32(len(Kvk)),[Kvk]1 + if err := dec.Decode(&vk.G1.K); err != nil { + return dec.BytesRead(), err + } + + // recompute vk.e (e(α, β)) and -[δ]2, -[γ]2 + var err error + vk.e, err = curve.Pair([]curve.G1Affine{vk.G1.Alpha}, []curve.G2Affine{vk.G2.Beta}) + if err != nil { + return dec.BytesRead(), err + } + vk.G2.deltaNeg.Neg(&vk.G2.Delta) + vk.G2.gammaNeg.Neg(&vk.G2.Gamma) + + return dec.BytesRead(), nil +} + +// WriteTo writes binary encoding of the key elements to writer +// points are compressed +// use WriteRawTo(...) to encode the key without point compression +func (pk *ProvingKey) WriteTo(w io.Writer) (n int64, err error) { + return pk.writeTo(w, false) +} + +// WriteRawTo writes binary encoding of the key elements to writer +// points are not compressed +// use WriteTo(...) to encode the key with point compression +func (pk *ProvingKey) WriteRawTo(w io.Writer) (n int64, err error) { + return pk.writeTo(w, true) +} + +func (pk *ProvingKey) writeTo(w io.Writer, raw bool) (int64, error) { + n, err := pk.Domain.WriteTo(w) + if err != nil { + return n, err + } + + var enc *curve.Encoder + if raw { + enc = curve.NewEncoder(w, curve.RawEncoding()) + } else { + enc = curve.NewEncoder(w) + } + + toEncode := []interface{}{ + &pk.G1.Alpha, + &pk.G1.Beta, + &pk.G1.Delta, + pk.G1.A, + pk.G1.B, + pk.G1.Z, + pk.G1.K, + &pk.G2.Beta, + &pk.G2.Delta, + pk.G2.B, + } + + for _, v := range toEncode { + if err := enc.Encode(v); err != nil { + return n + enc.BytesWritten(), err + } + } + + return n + enc.BytesWritten(), nil + +} + +// ReadFrom attempts to decode a ProvingKey from reader +// ProvingKey must be encoded through WriteTo (compressed) or WriteRawTo (uncompressed) +// note that we don't check that the points are on the curve or in the correct subgroup at this point +// TODO while Proof points correctness is checkd in the Verifier, here may be a good place to check key +func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { + + n, err := pk.Domain.ReadFrom(r) + if err != nil { + return n, err + } + + dec := curve.NewDecoder(r) + + toDecode := []interface{}{ + &pk.G1.Alpha, + &pk.G1.Beta, + &pk.G1.Delta, + &pk.G1.A, + &pk.G1.B, + &pk.G1.Z, + &pk.G1.K, + &pk.G2.Beta, + &pk.G2.Delta, + &pk.G2.B, + } + + for _, v := range toDecode { + if err := dec.Decode(v); err != nil { + return n + dec.BytesRead(), err + } + } + + return n + dec.BytesRead(), nil +} diff --git a/internal/backend/bls24-315/groth16/marshal_test.go b/internal/backend/bls24-315/groth16/marshal_test.go new file mode 100644 index 000000000..43c30f34c --- /dev/null +++ b/internal/backend/bls24-315/groth16/marshal_test.go @@ -0,0 +1,270 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package groth16 + +import ( + curve "github.com/consensys/gnark-crypto/ecc/bls24-315" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/fft" + + "bytes" + "math/big" + "reflect" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/prop" + + "testing" +) + +func TestProofSerialization(t *testing.T) { + parameters := gopter.DefaultTestParameters() + parameters.MinSuccessfulTests = 1000 + + properties := gopter.NewProperties(parameters) + + properties.Property("Proof -> writer -> reader -> Proof should stay constant", prop.ForAll( + func(ar, krs curve.G1Affine, bs curve.G2Affine) bool { + var proof, pCompressed, pRaw Proof + + // create a random proof + proof.Ar = ar + proof.Krs = krs + proof.Bs = bs + + var bufCompressed bytes.Buffer + written, err := proof.WriteTo(&bufCompressed) + if err != nil { + return false + } + + read, err := pCompressed.ReadFrom(&bufCompressed) + if err != nil { + return false + } + + if read != written { + return false + } + + var bufRaw bytes.Buffer + written, err = proof.WriteRawTo(&bufRaw) + if err != nil { + return false + } + + read, err = pRaw.ReadFrom(&bufRaw) + if err != nil { + return false + } + + if read != written { + return false + } + + return reflect.DeepEqual(&proof, &pCompressed) && reflect.DeepEqual(&proof, &pRaw) + }, + GenG1(), + GenG1(), + GenG2(), + )) + + properties.TestingRun(t, gopter.ConsoleReporter(false)) +} + +func TestVerifyingKeySerialization(t *testing.T) { + parameters := gopter.DefaultTestParameters() + parameters.MinSuccessfulTests = 10 + + properties := gopter.NewProperties(parameters) + + properties.Property("VerifyingKey -> writer -> reader -> VerifyingKey should stay constant", prop.ForAll( + func(p1 curve.G1Affine, p2 curve.G2Affine) bool { + var vk, vkCompressed, vkRaw VerifyingKey + + // create a random vk + nbWires := 6 + + vk.G1.Alpha = p1 + vk.G1.Beta = p1 + vk.G1.Delta = p1 + + vk.G2.Gamma = p2 + vk.G2.Beta = p2 + vk.G2.Delta = p2 + + var err error + vk.e, err = curve.Pair([]curve.G1Affine{vk.G1.Alpha}, []curve.G2Affine{vk.G2.Beta}) + if err != nil { + t.Fatal(err) + return false + } + vk.G2.deltaNeg.Neg(&vk.G2.Delta) + vk.G2.gammaNeg.Neg(&vk.G2.Gamma) + + vk.G1.K = make([]curve.G1Affine, nbWires) + for i := 0; i < nbWires; i++ { + vk.G1.K[i] = p1 + } + + var bufCompressed bytes.Buffer + written, err := vk.WriteTo(&bufCompressed) + if err != nil { + t.Log(err) + return false + } + + read, err := vkCompressed.ReadFrom(&bufCompressed) + if err != nil { + t.Log(err) + return false + } + + if read != written { + t.Log("read != written") + return false + } + + var bufRaw bytes.Buffer + written, err = vk.WriteRawTo(&bufRaw) + if err != nil { + t.Log(err) + return false + } + + read, err = vkRaw.ReadFrom(&bufRaw) + if err != nil { + t.Log(err) + return false + } + + if read != written { + t.Log("read raw != written") + return false + } + + return reflect.DeepEqual(&vk, &vkCompressed) && reflect.DeepEqual(&vk, &vkRaw) + }, + GenG1(), + GenG2(), + )) + + properties.TestingRun(t, gopter.ConsoleReporter(false)) +} + +func TestProvingKeySerialization(t *testing.T) { + parameters := gopter.DefaultTestParameters() + parameters.MinSuccessfulTests = 10 + + properties := gopter.NewProperties(parameters) + + properties.Property("ProvingKey -> writer -> reader -> ProvingKey should stay constant", prop.ForAll( + func(p1 curve.G1Affine, p2 curve.G2Affine) bool { + var pk, pkCompressed, pkRaw ProvingKey + + // create a random pk + domain := fft.NewDomain(8, 1, true) + pk.Domain = *domain + + nbWires := 6 + nbPrivateWires := 4 + + // allocate our slices + pk.G1.A = make([]curve.G1Affine, nbWires) + pk.G1.B = make([]curve.G1Affine, nbWires) + pk.G1.K = make([]curve.G1Affine, nbPrivateWires) + pk.G1.Z = make([]curve.G1Affine, pk.Domain.Cardinality) + pk.G2.B = make([]curve.G2Affine, nbWires) + + pk.G1.Alpha = p1 + pk.G2.Beta = p2 + pk.G1.K[1] = p1 + pk.G1.B[0] = p1 + pk.G2.B[0] = p2 + + var bufCompressed bytes.Buffer + written, err := pk.WriteTo(&bufCompressed) + if err != nil { + t.Log(err) + return false + } + + read, err := pkCompressed.ReadFrom(&bufCompressed) + if err != nil { + t.Log(err) + return false + } + + if read != written { + t.Log("read != written") + return false + } + + var bufRaw bytes.Buffer + written, err = pk.WriteRawTo(&bufRaw) + if err != nil { + t.Log(err) + return false + } + + read, err = pkRaw.ReadFrom(&bufRaw) + if err != nil { + t.Log(err) + return false + } + + if read != written { + t.Log("read raw != written") + return false + } + + return reflect.DeepEqual(&pk, &pkCompressed) && reflect.DeepEqual(&pk, &pkRaw) + }, + GenG1(), + GenG2(), + )) + + properties.TestingRun(t, gopter.ConsoleReporter(false)) +} + +func GenG1() gopter.Gen { + _, _, g1GenAff, _ := curve.Generators() + return func(genParams *gopter.GenParameters) *gopter.GenResult { + var scalar big.Int + scalar.SetUint64(genParams.NextUint64()) + + var g1 curve.G1Affine + g1.ScalarMultiplication(&g1GenAff, &scalar) + + genResult := gopter.NewGenResult(g1, gopter.NoShrinker) + return genResult + } +} + +func GenG2() gopter.Gen { + _, _, _, g2GenAff := curve.Generators() + return func(genParams *gopter.GenParameters) *gopter.GenResult { + var scalar big.Int + scalar.SetUint64(genParams.NextUint64()) + + var g2 curve.G2Affine + g2.ScalarMultiplication(&g2GenAff, &scalar) + + genResult := gopter.NewGenResult(g2, gopter.NoShrinker) + return genResult + } +} diff --git a/internal/backend/bls24-315/groth16/prove.go b/internal/backend/bls24-315/groth16/prove.go new file mode 100644 index 000000000..7019ec216 --- /dev/null +++ b/internal/backend/bls24-315/groth16/prove.go @@ -0,0 +1,268 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package groth16 + +import ( + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + + curve "github.com/consensys/gnark-crypto/ecc/bls24-315" + + "github.com/consensys/gnark/internal/backend/bls24-315/cs" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/fft" + + bls24_315witness "github.com/consensys/gnark/internal/backend/bls24-315/witness" + + "fmt" + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/internal/utils" + "math/big" + "runtime" +) + +// Proof represents a Groth16 proof that was encoded with a ProvingKey and can be verified +// with a valid statement and a VerifyingKey +// Notation follows Figure 4. in DIZK paper https://eprint.iacr.org/2018/691.pdf +type Proof struct { + Ar, Krs curve.G1Affine + Bs curve.G2Affine +} + +// isValid ensures proof elements are in the correct subgroup +func (proof *Proof) isValid() bool { + return proof.Ar.IsInSubGroup() && proof.Krs.IsInSubGroup() && proof.Bs.IsInSubGroup() +} + +// GetCurveID returns the curveID +func (proof *Proof) GetCurveID() ecc.ID { + return curve.ID +} + +// Prove generates the proof of knoweldge of a r1cs with full witness (secret + public part). +// if force flag is set, Prove ignores R1CS solving error (ie invalid witness) and executes +// the FFTs and MultiExponentiations to compute an (invalid) Proof object +func Prove(r1cs *cs.R1CS, pk *ProvingKey, witness bls24_315witness.Witness, force bool) (*Proof, error) { + if len(witness) != int(r1cs.NbPublicVariables-1+r1cs.NbSecretVariables) { + return nil, fmt.Errorf("invalid witness size, got %d, expected %d = %d (public - ONE_WIRE) + %d (secret)", len(witness), int(r1cs.NbPublicVariables-1+r1cs.NbSecretVariables), r1cs.NbPublicVariables, r1cs.NbSecretVariables) + } + + // solve the R1CS and compute the a, b, c vectors + a := make([]fr.Element, r1cs.NbConstraints, pk.Domain.Cardinality) + b := make([]fr.Element, r1cs.NbConstraints, pk.Domain.Cardinality) + c := make([]fr.Element, r1cs.NbConstraints, pk.Domain.Cardinality) + wireValues := make([]fr.Element, r1cs.NbInternalVariables+r1cs.NbPublicVariables+r1cs.NbSecretVariables) + if err := r1cs.Solve(witness, a, b, c, wireValues); err != nil && !force { + return nil, err + } + + // set the wire values in regular form + utils.Parallelize(len(wireValues), func(start, end int) { + for i := start; i < end; i++ { + wireValues[i].FromMont() + } + }) + + // H (witness reduction / FFT part) + var h []fr.Element + chHDone := make(chan struct{}, 1) + go func() { + h = computeH(a, b, c, &pk.Domain) + a = nil + b = nil + c = nil + chHDone <- struct{}{} + }() + + // sample random r and s + var r, s big.Int + var _r, _s, _kr fr.Element + if _, err := _r.SetRandom(); err != nil { + return nil, err + } + if _, err := _s.SetRandom(); err != nil { + return nil, err + } + _kr.Mul(&_r, &_s).Neg(&_kr) + + _r.FromMont() + _s.FromMont() + _kr.FromMont() + _r.ToBigInt(&r) + _s.ToBigInt(&s) + + // computes r[δ], s[δ], kr[δ] + deltas := curve.BatchScalarMultiplicationG1(&pk.G1.Delta, []fr.Element{_r, _s, _kr}) + + proof := &Proof{} + var bs1, ar curve.G1Jac + + // using this ensures that our multiExps running in parallel won't use more than + // provided CPUs + cpuSemaphore := ecc.NewCPUSemaphore(runtime.NumCPU()) + + chBs1Done := make(chan struct{}, 1) + computeBS1 := func() { + bs1.MultiExp(pk.G1.B, wireValues, cpuSemaphore) + bs1.AddMixed(&pk.G1.Beta) + bs1.AddMixed(&deltas[1]) + chBs1Done <- struct{}{} + } + + chArDone := make(chan struct{}, 1) + computeAR1 := func() { + ar.MultiExp(pk.G1.A, wireValues, cpuSemaphore) + ar.AddMixed(&pk.G1.Alpha) + ar.AddMixed(&deltas[0]) + proof.Ar.FromJacobian(&ar) + chArDone <- struct{}{} + } + + chKrsDone := make(chan struct{}, 1) + computeKRS := func() { + // we could NOT split the Krs multiExp in 2, and just append pk.G1.K and pk.G1.Z + // however, having similar lengths for our tasks helps with parallelism + + var krs, krs2, p1 curve.G1Jac + chKrs2Done := make(chan struct{}, 1) + go func() { + krs2.MultiExp(pk.G1.Z, h, cpuSemaphore) + chKrs2Done <- struct{}{} + }() + krs.MultiExp(pk.G1.K, wireValues[r1cs.NbPublicVariables:], cpuSemaphore) + krs.AddMixed(&deltas[2]) + n := 3 + for n != 0 { + select { + case <-chKrs2Done: + krs.AddAssign(&krs2) + case <-chArDone: + p1.ScalarMultiplication(&ar, &s) + krs.AddAssign(&p1) + case <-chBs1Done: + p1.ScalarMultiplication(&bs1, &r) + krs.AddAssign(&p1) + } + n-- + } + + proof.Krs.FromJacobian(&krs) + chKrsDone <- struct{}{} + } + + computeBS2 := func() { + // Bs2 (1 multi exp G2 - size = len(wires)) + var Bs, deltaS curve.G2Jac + + // splitting Bs2 in 3 ensures all our go routines in the prover have similar running time + // and is good for parallelism. However, on a machine with limited CPUs, this may not be + // a good idea, as the MultiExp scales slightly better than linearly + bsSplit := len(pk.G2.B) / 3 + if bsSplit > 10 { + chDone1 := make(chan struct{}, 1) + chDone2 := make(chan struct{}, 1) + var bs1, bs2 curve.G2Jac + go func() { + bs1.MultiExp(pk.G2.B[:bsSplit], wireValues[:bsSplit], cpuSemaphore) + chDone1 <- struct{}{} + }() + go func() { + bs2.MultiExp(pk.G2.B[bsSplit:bsSplit*2], wireValues[bsSplit:bsSplit*2], cpuSemaphore) + chDone2 <- struct{}{} + }() + Bs.MultiExp(pk.G2.B[bsSplit*2:], wireValues[bsSplit*2:], cpuSemaphore) + + <-chDone1 + Bs.AddAssign(&bs1) + <-chDone2 + Bs.AddAssign(&bs2) + } else { + Bs.MultiExp(pk.G2.B, wireValues, cpuSemaphore) + } + + deltaS.FromAffine(&pk.G2.Delta) + deltaS.ScalarMultiplication(&deltaS, &s) + Bs.AddAssign(&deltaS) + Bs.AddMixed(&pk.G2.Beta) + + proof.Bs.FromJacobian(&Bs) + } + + // wait for FFT to end, as it uses all our CPUs + <-chHDone + + // schedule our proof part computations + go computeKRS() + go computeAR1() + go computeBS1() + computeBS2() + + // wait for all parts of the proof to be computed. + <-chKrsDone + + return proof, nil +} + +func computeH(a, b, c []fr.Element, domain *fft.Domain) []fr.Element { + // H part of Krs + // Compute H (hz=ab-c, where z=-2 on ker X^n+1 (z(x)=x^n-1)) + // 1 - _a = ifft(a), _b = ifft(b), _c = ifft(c) + // 2 - ca = fft_coset(_a), ba = fft_coset(_b), cc = fft_coset(_c) + // 3 - h = ifft_coset(ca o cb - cc) + + n := len(a) + + // add padding to ensure input length is domain cardinality + padding := make([]fr.Element, int(domain.Cardinality)-n) + a = append(a, padding...) + b = append(b, padding...) + c = append(c, padding...) + n = len(a) + + domain.FFTInverse(a, fft.DIF, 0) + domain.FFTInverse(b, fft.DIF, 0) + domain.FFTInverse(c, fft.DIF, 0) + + domain.FFT(a, fft.DIT, 1) + domain.FFT(b, fft.DIT, 1) + domain.FFT(c, fft.DIT, 1) + + var minusTwoInv fr.Element + minusTwoInv.SetUint64(2) + minusTwoInv.Neg(&minusTwoInv). + Inverse(&minusTwoInv) + + // h = ifft_coset(ca o cb - cc) + // reusing a to avoid unecessary memalloc + utils.Parallelize(n, func(start, end int) { + for i := start; i < end; i++ { + a[i].Mul(&a[i], &b[i]). + Sub(&a[i], &c[i]). + Mul(&a[i], &minusTwoInv) + } + }) + + // ifft_coset + domain.FFTInverse(a, fft.DIF, 1) + + utils.Parallelize(len(a), func(start, end int) { + for i := start; i < end; i++ { + a[i].FromMont() + } + }) + + return a +} diff --git a/internal/backend/bls24-315/groth16/setup.go b/internal/backend/bls24-315/groth16/setup.go new file mode 100644 index 000000000..88f6ed077 --- /dev/null +++ b/internal/backend/bls24-315/groth16/setup.go @@ -0,0 +1,456 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package groth16 + +import ( + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + + curve "github.com/consensys/gnark-crypto/ecc/bls24-315" + + "github.com/consensys/gnark/internal/backend/bls24-315/cs" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/fft" + + "github.com/consensys/gnark-crypto/ecc" + "math/big" + "math/bits" +) + +// ProvingKey is used by a Groth16 prover to encode a proof of a statement +// Notation follows Figure 4. in DIZK paper https://eprint.iacr.org/2018/691.pdf +type ProvingKey struct { + // domain + Domain fft.Domain + + // [α]1, [β]1, [δ]1 + // [A(t)]1, [B(t)]1, [Kpk(t)]1, [Z(t)]1 + G1 struct { + Alpha, Beta, Delta curve.G1Affine + A, B, Z []curve.G1Affine + K []curve.G1Affine // the indexes correspond to the private wires + } + + // [β]2, [δ]2, [B(t)]2 + G2 struct { + Beta, Delta curve.G2Affine + B []curve.G2Affine + } +} + +// VerifyingKey is used by a Groth16 verifier to verify the validity of a proof and a statement +// Notation follows Figure 4. in DIZK paper https://eprint.iacr.org/2018/691.pdf +type VerifyingKey struct { + // [α]1, [Kvk]1 + G1 struct { + Alpha curve.G1Affine + Beta, Delta curve.G1Affine // unused, here for compatibility purposes + K []curve.G1Affine // The indexes correspond to the public wires + } + + // [β]2, [δ]2, [γ]2, + // -[δ]2, -[γ]2: see proof.Verify() for more details + G2 struct { + Beta, Delta, Gamma curve.G2Affine + deltaNeg, gammaNeg curve.G2Affine // not serialized + } + + // e(α, β) + e curve.GT // not serialized +} + +// Setup constructs the SRS +func Setup(r1cs *cs.R1CS, pk *ProvingKey, vk *VerifyingKey) error { + + /* + Setup + ----- + To build the verifying keys: + - compile the r1cs system -> the number of gates is len(GateOrdering)+len(PureStructuralConstraints)+len(InpureStructuralConstraints) + - loop through the ordered computational constraints (=gate in r1cs system structure), eValuate A(X), B(X), C(X) with simple formula (the gate number is the current iterator) + - loop through the inpure structural constraints, eValuate A(X), B(X), C(X) with simple formula, the gate number is len(gateOrdering)+ current iterator + - loop through the pure structural constraints, eValuate A(X), B(X), C(X) with simple formula, the gate number is len(gateOrdering)+len(InpureStructuralConstraints)+current iterator + */ + + // get R1CS nb constraints, wires and public/private inputs + nbWires := r1cs.NbInternalVariables + r1cs.NbPublicVariables + r1cs.NbSecretVariables + nbPublicWires := int(r1cs.NbPublicVariables) + nbPrivateWires := r1cs.NbSecretVariables + r1cs.NbInternalVariables + + // Setting group for fft + domain := fft.NewDomain(uint64(r1cs.NbConstraints), 1, true) + + // samples toxic waste + toxicWaste, err := sampleToxicWaste() + if err != nil { + return err + } + + // Setup coeffs to compute pk.G1.A, pk.G1.B, pk.G1.K + A, B, C := setupABC(r1cs, domain, toxicWaste) + + // To fill in the Proving and Verifying keys, we need to perform a lot of ecc scalar multiplication (with generator) + // and convert the resulting points to affine + // this is done using the curve.BatchScalarMultiplicationGX API, which takes as input the base point + // (in our case the generator) and the list of scalars, and outputs a list of points (len(points) == len(scalars)) + // to use this batch call, we need to order our scalars in the same slice + // we have 1 batch call for G1 and 1 batch call for G1 + // scalars are fr.Element in non montgomery form + _, _, g1, g2 := curve.Generators() + + // --------------------------------------------------------------------------------------------- + // G1 scalars + + // the G1 scalars are ordered (arbitrary) as follow: + // + // [[α], [β], [δ], [A(i)], [B(i)], [pk.K(i)], [Z(i)], [vk.K(i)]] + // len(A) == len(B) == nbWires + // len(pk.K) == nbPrivateWires + // len(vk.K) == nbPublicWires + // len(Z) == domain.Cardinality + + // compute scalars for pkK and vkK + pkK := make([]fr.Element, nbPrivateWires) + vkK := make([]fr.Element, nbPublicWires) + + var t0, t1 fr.Element + + for i := 0; i < nbPublicWires; i++ { + t1.Mul(&A[i], &toxicWaste.beta) + t0.Mul(&B[i], &toxicWaste.alpha) + t1.Add(&t1, &t0). + Add(&t1, &C[i]). + Div(&t1, &toxicWaste.gamma) + vkK[i] = t1.ToRegular() + } + + for i := 0; i < nbPrivateWires; i++ { + t1.Mul(&A[i+nbPublicWires], &toxicWaste.beta) + t0.Mul(&B[i+nbPublicWires], &toxicWaste.alpha) + t1.Add(&t1, &t0). + Add(&t1, &C[i+nbPublicWires]). + Div(&t1, &toxicWaste.delta) + pkK[i] = t1.ToRegular() + } + + // convert A and B to regular form + for i := 0; i < int(nbWires); i++ { + A[i].FromMont() + } + for i := 0; i < int(nbWires); i++ { + B[i].FromMont() + } + + // Z part of the proving key (scalars) + Z := make([]fr.Element, domain.Cardinality) + one := fr.One() + var zdt fr.Element + + zdt.Exp(toxicWaste.t, new(big.Int).SetUint64(domain.Cardinality)). + Sub(&zdt, &one). + Div(&zdt, &toxicWaste.delta) // sets Zdt to Zdt/delta + + for i := 0; i < int(domain.Cardinality); i++ { + Z[i] = zdt.ToRegular() + zdt.Mul(&zdt, &toxicWaste.t) + } + + // compute our batch scalar multiplication with g1 elements + g1Scalars := make([]fr.Element, 0, (nbWires*3)+int(domain.Cardinality)+3) + g1Scalars = append(g1Scalars, toxicWaste.alphaReg, toxicWaste.betaReg, toxicWaste.deltaReg) + g1Scalars = append(g1Scalars, A...) + g1Scalars = append(g1Scalars, B...) + g1Scalars = append(g1Scalars, pkK...) + g1Scalars = append(g1Scalars, Z...) + g1Scalars = append(g1Scalars, vkK...) + + g1PointsAff := curve.BatchScalarMultiplicationG1(&g1, g1Scalars) + + // sets pk: [α]1, [β]1, [δ]1 + pk.G1.Alpha = g1PointsAff[0] + pk.G1.Beta = g1PointsAff[1] + pk.G1.Delta = g1PointsAff[2] + + offset := 3 + pk.G1.A = g1PointsAff[offset : offset+nbWires] + offset += nbWires + + pk.G1.B = g1PointsAff[offset : offset+nbWires] + offset += nbWires + + pk.G1.K = g1PointsAff[offset : offset+nbPrivateWires] + offset += nbPrivateWires + + pk.G1.Z = g1PointsAff[offset : offset+int(domain.Cardinality)] + bitReverse(pk.G1.Z) + + offset += int(domain.Cardinality) + + vk.G1.K = g1PointsAff[offset:] + + // --------------------------------------------------------------------------------------------- + // G2 scalars + + // the G2 scalars are ordered as follow: + // + // [[B(i)], [β], [δ], [γ]] + // len(B) == nbWires + + // compute our batch scalar multiplication with g2 elements + g2Scalars := append(B, toxicWaste.betaReg, toxicWaste.deltaReg, toxicWaste.gammaReg) + + g2PointsAff := curve.BatchScalarMultiplicationG2(&g2, g2Scalars) + + pk.G2.B = g2PointsAff[:nbWires] + + // sets pk: [β]2, [δ]2 + pk.G2.Beta = g2PointsAff[nbWires+0] + pk.G2.Delta = g2PointsAff[nbWires+1] + + // sets vk: [δ]2, [γ]2, -[δ]2, -[γ]2 + vk.G2.Delta = g2PointsAff[nbWires+1] + vk.G2.Gamma = g2PointsAff[nbWires+2] + vk.G2.deltaNeg.Neg(&vk.G2.Delta) + vk.G2.gammaNeg.Neg(&vk.G2.Gamma) + + // --------------------------------------------------------------------------------------------- + // Pairing: vk.e + vk.G1.Alpha = pk.G1.Alpha + vk.G2.Beta = pk.G2.Beta + + // unused, here for compatibility purposes + vk.G1.Beta = pk.G1.Beta + vk.G1.Delta = pk.G1.Delta + + vk.e, err = curve.Pair([]curve.G1Affine{pk.G1.Alpha}, []curve.G2Affine{pk.G2.Beta}) + if err != nil { + return err + } + // set domain + pk.Domain = *domain + + return nil +} + +func setupABC(r1cs *cs.R1CS, g *fft.Domain, toxicWaste toxicWaste) (A []fr.Element, B []fr.Element, C []fr.Element) { + + nbWires := r1cs.NbInternalVariables + r1cs.NbPublicVariables + r1cs.NbSecretVariables + + A = make([]fr.Element, nbWires) + B = make([]fr.Element, nbWires) + C = make([]fr.Element, nbWires) + + var one fr.Element + one.SetOne() + + // evaluation of the i-th lagrange polynomial at t + var ithLagrangePolt fr.Element + + // L0 = 1/n*(t^n-1)/(t-1), Li+1 = w*Li*(t-w^i)/(t-w^(i+1)) + var w, wi, tmp fr.Element + w.Set(&g.Generator) + wi.SetOne() + + // Setting L0 + ithLagrangePolt.Set(&toxicWaste.t) + ithLagrangePolt.Exp(ithLagrangePolt, new(big.Int).SetUint64(uint64(g.Cardinality))). + Sub(&ithLagrangePolt, &one) + tmp.Set(&toxicWaste.t).Sub(&tmp, &one) + ithLagrangePolt.Div(&ithLagrangePolt, &tmp). + Mul(&ithLagrangePolt, &g.CardinalityInv) + + // Constraints + for _, c := range r1cs.Constraints { + + for _, t := range c.L { + r1cs.AddTerm(&A[t.VariableID()], t, ithLagrangePolt) + } + for _, t := range c.R { + r1cs.AddTerm(&B[t.VariableID()], t, ithLagrangePolt) + } + for _, t := range c.O { + r1cs.AddTerm(&C[t.VariableID()], t, ithLagrangePolt) + } + + // Li+1 = w*Li*(t-w^i)/(t-w^(i+1)) + ithLagrangePolt.Mul(&ithLagrangePolt, &w) + tmp.Sub(&toxicWaste.t, &wi) + ithLagrangePolt.Mul(&ithLagrangePolt, &tmp) + wi.Mul(&wi, &w) + tmp.Sub(&toxicWaste.t, &wi) + ithLagrangePolt.Div(&ithLagrangePolt, &tmp) + } + return + +} + +// toxicWaste toxic waste +type toxicWaste struct { + + // Montgomery form of params + t, alpha, beta, gamma, delta fr.Element + + // Non Montgomery form of params + alphaReg, betaReg, gammaReg, deltaReg fr.Element +} + +func sampleToxicWaste() (toxicWaste, error) { + + res := toxicWaste{} + + if _, err := res.t.SetRandom(); err != nil { + return res, err + } + if _, err := res.alpha.SetRandom(); err != nil { + return res, err + } + if _, err := res.beta.SetRandom(); err != nil { + return res, err + } + if _, err := res.gamma.SetRandom(); err != nil { + return res, err + } + if _, err := res.delta.SetRandom(); err != nil { + return res, err + } + + res.alphaReg = res.alpha.ToRegular() + res.betaReg = res.beta.ToRegular() + res.gammaReg = res.gamma.ToRegular() + res.deltaReg = res.delta.ToRegular() + + return res, nil +} + +// DummySetup fills a random ProvingKey +// used for test or benchmarking purposes +func DummySetup(r1cs *cs.R1CS, pk *ProvingKey) error { + // get R1CS nb constraints, wires and public/private inputs + nbWires := r1cs.NbInternalVariables + r1cs.NbPublicVariables + r1cs.NbSecretVariables + nbConstraints := r1cs.NbConstraints + + // Setting group for fft + domain := fft.NewDomain(uint64(nbConstraints), 1, true) + + // initialize proving key + pk.G1.A = make([]curve.G1Affine, nbWires) + pk.G1.B = make([]curve.G1Affine, nbWires) + pk.G1.K = make([]curve.G1Affine, nbWires-r1cs.NbPublicVariables) + pk.G1.Z = make([]curve.G1Affine, domain.Cardinality) + pk.G2.B = make([]curve.G2Affine, nbWires) + + // samples toxic waste + toxicWaste, err := sampleToxicWaste() + if err != nil { + return err + } + + var r1Jac curve.G1Jac + var r1Aff curve.G1Affine + var b big.Int + g1, g2, _, _ := curve.Generators() + r1Jac.ScalarMultiplication(&g1, toxicWaste.alphaReg.ToBigInt(&b)) + r1Aff.FromJacobian(&r1Jac) + var r2Jac curve.G2Jac + var r2Aff curve.G2Affine + r2Jac.ScalarMultiplication(&g2, &b) + r2Aff.FromJacobian(&r2Jac) + for i := 0; i < int(nbWires); i++ { + pk.G1.A[i] = r1Aff + pk.G1.B[i] = r1Aff + pk.G2.B[i] = r2Aff + } + for i := 0; i < len(pk.G1.Z); i++ { + pk.G1.Z[i] = r1Aff + } + for i := 0; i < len(pk.G1.K); i++ { + pk.G1.K[i] = r1Aff + } + pk.G1.Alpha = r1Aff + pk.G1.Beta = r1Aff + pk.G1.Delta = r1Aff + pk.G2.Beta = r2Aff + pk.G2.Delta = r2Aff + + pk.Domain = *domain + + return nil +} + +// IsDifferent returns true if provided vk is different than self +// this is used by groth16.Assert to ensure random sampling +func (vk *VerifyingKey) IsDifferent(_other interface{}) bool { + vk2 := _other.(*VerifyingKey) + for i := 0; i < len(vk.G1.K); i++ { + if !vk.G1.K[i].IsInfinity() { + if vk.G1.K[i].Equal(&vk2.G1.K[i]) { + return false + } + } + } + + return true +} + +// IsDifferent returns true if provided pk is different than self +// this is used by groth16.Assert to ensure random sampling +func (pk *ProvingKey) IsDifferent(_other interface{}) bool { + pk2 := _other.(*ProvingKey) + + if pk.G1.Alpha.Equal(&pk2.G1.Alpha) || + pk.G1.Beta.Equal(&pk2.G1.Beta) || + pk.G1.Delta.Equal(&pk2.G1.Delta) { + return false + } + + for i := 0; i < len(pk.G1.K); i++ { + if !pk.G1.K[i].IsInfinity() { + if pk.G1.K[i].Equal(&pk2.G1.K[i]) { + return false + } + } + } + + return true +} + +// GetCurveID returns the curveID +func (pk *ProvingKey) GetCurveID() ecc.ID { + return curve.ID +} + +// GetCurveID returns the curveID +func (vk *VerifyingKey) GetCurveID() ecc.ID { + return curve.ID +} + +// SizePublicWitness returns the number of elements in the expected public witness +func (vk *VerifyingKey) SizePublicWitness() int { + return (len(vk.G1.K) - 1) +} + +// bitRerverse permutation as in fft.BitReverse , but with []curve.G1Affine +func bitReverse(a []curve.G1Affine) { + n := uint(len(a)) + nn := uint(bits.UintSize - bits.TrailingZeros(n)) + + for i := uint(0); i < n; i++ { + irev := bits.Reverse(i) >> nn + if irev > i { + a[i], a[irev] = a[irev], a[i] + } + } +} diff --git a/internal/backend/bls24-315/groth16/verify.go b/internal/backend/bls24-315/groth16/verify.go new file mode 100644 index 000000000..282603bed --- /dev/null +++ b/internal/backend/bls24-315/groth16/verify.go @@ -0,0 +1,91 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package groth16 + +import ( + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + + curve "github.com/consensys/gnark-crypto/ecc/bls24-315" + + bls24_315witness "github.com/consensys/gnark/internal/backend/bls24-315/witness" + + "errors" + "fmt" + "io" +) + +var ( + errPairingCheckFailed = errors.New("pairing doesn't match") + errCorrectSubgroupCheckFailed = errors.New("points in the proof are not in the correct subgroup") +) + +// Verify verifies a proof with given VerifyingKey and publicWitness +func Verify(proof *Proof, vk *VerifyingKey, publicWitness bls24_315witness.Witness) error { + + if len(publicWitness) != (len(vk.G1.K) - 1) { + return fmt.Errorf("invalid witness size, got %d, expected %d (public - ONE_WIRE)", len(publicWitness), len(vk.G1.K)-1) + } + + // check that the points in the proof are in the correct subgroup + if !proof.isValid() { + return errCorrectSubgroupCheckFailed + } + + var doubleML curve.GT + chDone := make(chan error, 1) + + // compute (eKrsδ, eArBs) + go func() { + var errML error + doubleML, errML = curve.MillerLoop([]curve.G1Affine{proof.Krs, proof.Ar}, []curve.G2Affine{vk.G2.deltaNeg, proof.Bs}) + chDone <- errML + close(chDone) + }() + + // compute e(Σx.[Kvk(t)]1, -[γ]2) + var kSum curve.G1Jac + __publicWitness := make([]fr.Element, len(publicWitness)) + for i := 0; i < len(publicWitness); i++ { + __publicWitness[i] = publicWitness[i].ToRegular() + } + kSum.MultiExp(vk.G1.K[1:], __publicWitness) + kSum.AddMixed(&vk.G1.K[0]) + var kSumAff curve.G1Affine + kSumAff.FromJacobian(&kSum) + + right, err := curve.MillerLoop([]curve.G1Affine{kSumAff}, []curve.G2Affine{vk.G2.gammaNeg}) + if err != nil { + return err + } + + // wait for (eKrsδ, eArBs) + err = <-chDone + if err != nil { + return err + } + + right = curve.FinalExponentiation(&right, &doubleML) + if !vk.e.Equal(&right) { + return errPairingCheckFailed + } + return nil +} + +// ExportSolidity not implemented for BLS24-315 +func (vk *VerifyingKey) ExportSolidity(w io.Writer) error { + return errors.New("not implemented") +} diff --git a/internal/backend/bls24-315/plonk/plonk_test.go b/internal/backend/bls24-315/plonk/plonk_test.go new file mode 100644 index 000000000..b4f5d8692 --- /dev/null +++ b/internal/backend/bls24-315/plonk/plonk_test.go @@ -0,0 +1,40 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package plonk_test + +import ( + "testing" + + "github.com/consensys/gnark/backend" + "github.com/consensys/gnark/backend/plonk" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/backend/circuits" + + curve "github.com/consensys/gnark-crypto/ecc/bls24-315" +) + +func TestCircuits(t *testing.T) { + for name, circuit := range circuits.Circuits { + t.Run(name, func(t *testing.T) { + assert := plonk.NewAssert(t) + pcs, err := frontend.Compile(curve.ID, backend.PLONK, circuit.Circuit) + assert.NoError(err) + assert.ProverSucceeded(pcs, circuit.Good) + assert.ProverFailed(pcs, circuit.Bad) + }) + } +} diff --git a/internal/backend/bls24-315/plonk/prove.go b/internal/backend/bls24-315/plonk/prove.go new file mode 100644 index 000000000..2214e4b7a --- /dev/null +++ b/internal/backend/bls24-315/plonk/prove.go @@ -0,0 +1,608 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package plonk + +import ( + "math/big" + + bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" + "github.com/consensys/gnark-crypto/polynomial" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/fft" + + bls24_315witness "github.com/consensys/gnark/internal/backend/bls24-315/witness" + + "github.com/consensys/gnark/internal/backend/bls24-315/cs" + + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" +) + +// ProofRaw PLONK proofs, consisting of opening proofs +type ProofRaw struct { + + // Claimed Values of L,R,O,Z,H at zeta (where H split in H1+X^m*H2+X^2m*H3, where deg(Hi)=m-1) + LROZH [7]fr.Element + + // Claimed vales of Z(zX) at zeta + ZShift fr.Element + + // Commitments to L,R,O,Z,H + CommitmentsLROZH [7]polynomial.Digest + + // batch opening proofs for L,R,O,H,Z at zeta + BatchOpenings polynomial.BatchOpeningProofSinglePoint + + // opening proof for Z at z*zeta + OpeningZShift polynomial.OpeningProof +} + +// ComputeLRO extracts the solution l, r, o, and returns it in lagrange form. +// solution = [ public | secret | internal ] +func ComputeLRO(spr *cs.SparseR1CS, publicData *PublicRaw, solution []fr.Element) (bls24315.Polynomial, bls24315.Polynomial, bls24315.Polynomial, bls24315.Polynomial) { + + s := int(publicData.DomainNum.Cardinality) + + var l, r, o, partialL bls24315.Polynomial + l = make([]fr.Element, s) + r = make([]fr.Element, s) + o = make([]fr.Element, s) + partialL = make([]fr.Element, s) + + for i := 0; i < spr.NbPublicVariables; i++ { // placeholders + l[i].Set(&solution[i]) + r[i].Set(&solution[0]) + o[i].Set(&solution[0]) + } + offset := spr.NbPublicVariables + for i := 0; i < len(spr.Constraints); i++ { // constraints + l[offset+i].Set(&solution[spr.Constraints[i].L.VariableID()]) + r[offset+i].Set(&solution[spr.Constraints[i].R.VariableID()]) + o[offset+i].Set(&solution[spr.Constraints[i].O.VariableID()]) + partialL[offset+i].Set(&l[offset+i]) + } + offset += len(spr.Constraints) + for i := 0; i < len(spr.Assertions); i++ { // assertions + l[offset+i].Set(&solution[spr.Assertions[i].L.VariableID()]) + r[offset+i].Set(&solution[spr.Assertions[i].R.VariableID()]) + o[offset+i].Set(&solution[spr.Assertions[i].O.VariableID()]) + partialL[offset+i].Set(&l[offset+i]) + } + offset += len(spr.Assertions) + for i := 0; i < s-offset; i++ { // offset to reach 2**n constraints (where the id of l,r,o is 0, so we assign solution[0]) + l[offset+i].Set(&solution[0]) + r[offset+i].Set(&solution[0]) + o[offset+i].Set(&solution[0]) + partialL[offset+i].Set(&l[offset+i]) + } + + return l, r, o, partialL + +} + +// ComputeZ computes Z (in Lagrange basis), where: +// +// * Z of degree n (domainNum.Cardinality) +// * Z(1)=1 +// (l_i+z**i+gamma)*(r_i+u*z**i+gamma)*(o_i+u**2z**i+gamma) +// * for i>0: Z(u**i) = Pi_{k z**i+1 + u[1].Mul(&u[1], &publicData.DomainNum.Generator) // u*z**i -> u*z**i+1 + u[2].Mul(&u[2], &publicData.DomainNum.Generator) // u**2*z**i -> u**2*z**i+1 + } + + return z + +} + +// evalConstraints computes the evaluation of lL+qrR+qqmL.R+qoO+k on +// the odd cosets of (Z/8mZ)/(Z/mZ), where m=nbConstraints+nbAssertions. +func evalConstraints(publicData *PublicRaw, evalL, evalR, evalO []fr.Element) []fr.Element { + + res := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + + // evaluates ql, qr, qm, qo, k on the odd cosets of (Z/8mZ)/(Z/mZ) + evalQl := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evalQr := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evalQm := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evalQo := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evalQk := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evaluateCosets(publicData.Ql, evalQl, publicData.DomainNum) + evaluateCosets(publicData.Qr, evalQr, publicData.DomainNum) + evaluateCosets(publicData.Qm, evalQm, publicData.DomainNum) + evaluateCosets(publicData.Qo, evalQo, publicData.DomainNum) + evaluateCosets(publicData.Qk, evalQk, publicData.DomainNum) + + // computes the evaluation of qrR+qlL+qmL.R+qoO+k on the odd cosets + // of (Z/8mZ)/(Z/mZ) + var acc, buf fr.Element + for i := uint64(0); i < 4*publicData.DomainNum.Cardinality; i++ { + + acc.Mul(&evalQl[i], &evalL[i]) // ql.l + + buf.Mul(&evalQr[i], &evalR[i]) + acc.Add(&acc, &buf) // ql.l + qr.r + + buf.Mul(&evalQm[i], &evalL[i]).Mul(&buf, &evalR[i]) + acc.Add(&acc, &buf) // ql.l + qr.r + qm.l.r + + buf.Mul(&evalQo[i], &evalO[i]) + acc.Add(&acc, &buf) // ql.l + qr.r + qm.l.r + qo.o + res[i].Add(&acc, &evalQk[i]) // ql.l + qr.r + qm.l.r + qo.o + k + } + + return res +} + +// evalIDCosets id, uid, u**2id on the odd cosets of (Z/8mZ)/(Z/mZ) +func evalIDCosets(publicData *PublicRaw) (id, uid, uuid bls24315.Polynomial) { + + // evaluation of id, uid, u**id on the cosets + id = make([]fr.Element, 4*publicData.DomainNum.Cardinality) + c := int(publicData.DomainNum.Cardinality) + id[0].SetOne() + id[1].SetOne() + id[2].SetOne() + id[3].SetOne() + for i := 1; i < c; i++ { + id[4*i].Mul(&id[4*(i-1)], &publicData.DomainNum.Generator) + id[4*i+1].Set(&id[4*i]) + id[4*i+2].Set(&id[4*i]) + id[4*i+3].Set(&id[4*i]) + } + // at this stage, id = [1,1,1,1,|z,z,z,z|,...,|z**n-1,z**n-1,z**n-1,z**n-1] + + var uu fr.Element + uu.Square(&publicData.DomainNum.FinerGenerator) + var u [4]fr.Element + u[0].Set(&publicData.DomainNum.FinerGenerator) // u + u[1].Mul(&u[0], &uu) // u**3 + u[2].Mul(&u[1], &uu) // u**5 + u[3].Mul(&u[2], &uu) // u**7 + uid = make([]fr.Element, 4*publicData.DomainNum.Cardinality) // shifter[0]*ID evaluated on odd cosets of (Z/8mZ)/(Z/mZ) + uuid = make([]fr.Element, 4*publicData.DomainNum.Cardinality) // shifter[1]*ID evaluated on odd cosets of (Z/8mZ)/(Z/mZ) + for i := 0; i < c; i++ { + + id[4*i].Mul(&id[4*i], &u[0]) // coset u.<1,z,..,z**n-1> + id[4*i+1].Mul(&id[4*i+1], &u[1]) // coset u**3.<1,z,..,z**n-1> + id[4*i+2].Mul(&id[4*i+2], &u[2]) // coset u**5.<1,z,..,z**n-1> + id[4*i+3].Mul(&id[4*i+3], &u[3]) // coset u**7.<1,z,..,z**n-1> + + uid[4*i].Mul(&id[4*i], &publicData.Shifter[0]) // shifter[0]*ID + uid[4*i+1].Mul(&id[4*i+1], &publicData.Shifter[0]) // shifter[0]*ID + uid[4*i+2].Mul(&id[4*i+2], &publicData.Shifter[0]) // shifter[0]*ID + uid[4*i+3].Mul(&id[4*i+3], &publicData.Shifter[0]) // shifter[0]*ID + + uuid[4*i].Mul(&id[4*i], &publicData.Shifter[1]) // shifter[1]*ID + uuid[4*i+1].Mul(&id[4*i+1], &publicData.Shifter[1]) // shifter[1]*ID + uuid[4*i+2].Mul(&id[4*i+2], &publicData.Shifter[1]) // shifter[1]*ID + uuid[4*i+3].Mul(&id[4*i+3], &publicData.Shifter[1]) // shifter[1]*ID + + } + return +} + +// evalConstraintOrdering computes the evaluation of Z(uX)g1g2g3-Z(X)f1f2f3 on the odd +// cosets of (Z/8mZ)/(Z/mZ), where m=nbConstraints+nbAssertions. +// +// z: permutation accumulator polynomial in canonical form +// l, r, o: solution, in canonical form +func evalConstraintOrdering(publicData *PublicRaw, evalZ, evalZu, evalL, evalR, evalO bls24315.Polynomial, gamma fr.Element) bls24315.Polynomial { + + // evaluation of z, zu, s1, s2, s3, on the odd cosets of (Z/8mZ)/(Z/mZ) + evalS1 := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evalS2 := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evalS3 := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evaluateCosets(publicData.CS1, evalS1, publicData.DomainNum) + evaluateCosets(publicData.CS2, evalS2, publicData.DomainNum) + evaluateCosets(publicData.CS3, evalS3, publicData.DomainNum) + + // evalutation of ID, u*ID, u**2*ID on the odd cosets of (Z/8mZ)/(Z/mZ) + evalID, evaluID, evaluuID := evalIDCosets(publicData) + + // computes Z(uX)g1g2g3l-Z(X)f1f2f3l on the odd cosets of (Z/8mZ)/(Z/mZ) + res := make(bls24315.Polynomial, 4*publicData.DomainNum.Cardinality) + + var f [3]fr.Element + var g [3]fr.Element + for i := 0; i < 4*int(publicData.DomainNum.Cardinality); i++ { + + f[0].Add(&evalL[i], &evalID[i]).Add(&f[0], &gamma) //l_i+z**i+gamma + f[1].Add(&evalR[i], &evaluID[i]).Add(&f[1], &gamma) //r_i+u*z**i+gamma + f[2].Add(&evalO[i], &evaluuID[i]).Add(&f[2], &gamma) //o_i+u**2*z**i+gamma + + g[0].Add(&evalL[i], &evalS1[i]).Add(&g[0], &gamma) //l_i+s1+gamma + g[1].Add(&evalR[i], &evalS2[i]).Add(&g[1], &gamma) //r_i+s2+gamma + g[2].Add(&evalO[i], &evalS3[i]).Add(&g[2], &gamma) //o_i+s3+gamma + + f[0].Mul(&f[0], &f[1]). + Mul(&f[0], &f[2]). + Mul(&f[0], &evalZ[i]) // z_i*(l_i+z**i+gamma)*(r_i+u*z**i+gamma)*(o_i+u**2*z**i+gamma) + + g[0].Mul(&g[0], &g[1]). + Mul(&g[0], &g[2]). + Mul(&g[0], &evalZu[i]) // u*z_i*(l_i+s1+gamma)*(r_i+s2+gamma)*(o_i+s3+gamma) + + res[i].Sub(&g[0], &f[0]) + } + + return res +} + +// evalStartsAtOne computes the evaluation of L1*(z-1) on the odd cosets +// of (Z/8mZ)/(Z/mZ). +// +// evalZ is the evaluation of z (=permutation constraint polynomial) on odd cosets of (Z/8mZ)/(Z/mZ) +func evalStartsAtOne(publicData *PublicRaw, evalZ bls24315.Polynomial) bls24315.Polynomial { + + // computes L1 (canonical form) + lOneLagrange := make([]fr.Element, publicData.DomainNum.Cardinality) + lOneLagrange[0].SetOne() + publicData.DomainNum.FFTInverse(lOneLagrange, fft.DIF, 0) + fft.BitReverse(lOneLagrange) + + // evaluates L1 on the odd cosets of (Z/8mZ)/(Z/mZ) + res := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evaluateCosets(lOneLagrange, res, publicData.DomainNum) + + // // evaluates L1*(z-1) on the odd cosets of (Z/8mZ)/(Z/mZ) + var buf, one fr.Element + one.SetOne() + for i := 0; i < 4*int(publicData.DomainNum.Cardinality); i++ { + buf.Sub(&evalZ[i], &one) + res[i].Mul(&buf, &res[i]) + } + + return res +} + +// evaluateCosets evaluates poly (canonical form) of degree m=domainNum.Cardinality on +// the 4 odd cosets of (Z/8mZ)/(Z/mZ), so it dodges Z/mZ (+Z/2kmZ), which contains the +// vanishing set of Z. +// +// Puts the result in res (of size 4*domain.Cardinality). +// +// Both sizes of poly and res are powers of 2, len(res) = 4*len(poly). +func evaluateCosets(poly, res []fr.Element, domain *fft.Domain) { + + // build a copy of poly padded with 0 so it has the length of the closest power of 2 of poly + evaluations := make([][]fr.Element, 4) + evaluations[0] = make([]fr.Element, domain.Cardinality) + evaluations[1] = make([]fr.Element, domain.Cardinality) + evaluations[2] = make([]fr.Element, domain.Cardinality) + evaluations[3] = make([]fr.Element, domain.Cardinality) + + // evaluations[i] must contain poly in the canonical basis + copy(evaluations[0], poly) + copy(evaluations[1], poly) + copy(evaluations[2], poly) + copy(evaluations[3], poly) + + domain.FFT(evaluations[0], fft.DIF, 1) + domain.FFT(evaluations[1], fft.DIF, 3) + domain.FFT(evaluations[2], fft.DIF, 5) + domain.FFT(evaluations[3], fft.DIF, 7) + fft.BitReverse(evaluations[0]) + fft.BitReverse(evaluations[1]) + fft.BitReverse(evaluations[2]) + fft.BitReverse(evaluations[3]) + + for i := uint64(0); i < domain.Cardinality; i++ { + res[4*i].Set(&evaluations[0][i]) + res[4*i+1].Set(&evaluations[1][i]) + res[4*i+2].Set(&evaluations[2][i]) + res[4*i+3].Set(&evaluations[3][i]) + } +} + +// shiftZ turns z to z(uX) (both in Lagrange basis) +func shiftZ(z bls24315.Polynomial) bls24315.Polynomial { + + res := make(bls24315.Polynomial, len(z)) + copy(res, z) + + var buf fr.Element + buf.Set(&res[0]) + for i := 0; i < len(res)-1; i++ { + res[i].Set(&res[i+1]) + } + res[len(res)-1].Set(&buf) + + return res +} + +// computeH computes h in canonical form, split as h1+X^mh2+X^2mh3 such that +// +// qlL+qrR+qmL.R+qoO+k + alpha.(zu*g1*g2*g3*l-z*f1*f2*f3*l) + alpha**2*L1*(z-1)= h.Z +// \------------------/ \------------------------/ \-----/ +// constraintsInd constraintOrdering startsAtOne +// +// constraintInd, constraintOrdering are evaluated on the odd cosets of (Z/8mZ)/(Z/mZ) +func computeH(publicData *PublicRaw, constraintsInd, constraintOrdering, startsAtOne bls24315.Polynomial, alpha fr.Element) (bls24315.Polynomial, bls24315.Polynomial, bls24315.Polynomial) { + + h := make(bls24315.Polynomial, publicData.DomainH.Cardinality) + + // evaluate Z = X**m-1 on the odd cosets of (Z/8mZ)/(Z/mZ) + var bExpo big.Int + bExpo.SetUint64(publicData.DomainNum.Cardinality) + var u [4]fr.Element + var uu fr.Element + var one fr.Element + one.SetOne() + uu.Square(&publicData.DomainNum.FinerGenerator) + u[0].Set(&publicData.DomainNum.FinerGenerator) + u[1].Mul(&u[0], &uu) + u[2].Mul(&u[1], &uu) + u[3].Mul(&u[2], &uu) + u[0].Exp(u[0], &bExpo).Sub(&u[0], &one).Inverse(&u[0]) // (X**m-1)**-1 at u + u[1].Exp(u[1], &bExpo).Sub(&u[1], &one).Inverse(&u[1]) // (X**m-1)**-1 at u**3 + u[2].Exp(u[2], &bExpo).Sub(&u[2], &one).Inverse(&u[2]) // (X**m-1)**-1 at u**5 + u[3].Exp(u[3], &bExpo).Sub(&u[3], &one).Inverse(&u[3]) // (X**m-1)**-1 at u**7 + + // evaluate qlL+qrR+qmL.R+qoO+k + alpha.(zu*g1*g2*g3*l-z*f1*f2*f3*l) + alpha**2*L1(X)(Z(X)-1) + // on the odd cosets of (Z/8mZ)/(Z/mZ) + for i := 0; i < 4*int(publicData.DomainNum.Cardinality); i++ { + h[i].Mul(&startsAtOne[i], &alpha). + Add(&h[i], &constraintOrdering[i]). + Mul(&h[i], &alpha). + Add(&h[i], &constraintsInd[i]) + } + + // evaluate qlL+qrR+qmL.R+qoO+k + alpha.(zu*g1*g2*g3*l-z*f1*f2*f3*l)/Z + // on the odd cosets of (Z/8mZ)/(Z/mZ) + for i := 0; i < int(publicData.DomainNum.Cardinality); i++ { + h[4*i].Mul(&h[4*i], &u[0]) + h[4*i+1].Mul(&h[4*i+1], &u[1]) + h[4*i+2].Mul(&h[4*i+2], &u[2]) + h[4*i+3].Mul(&h[4*i+3], &u[3]) + } + + // put h in canonical form + publicData.DomainH.FFTInverse(h, fft.DIF, 1) + fft.BitReverse(h) + + h1 := make(bls24315.Polynomial, publicData.DomainNum.Cardinality) + h2 := make(bls24315.Polynomial, publicData.DomainNum.Cardinality) + h3 := make(bls24315.Polynomial, publicData.DomainNum.Cardinality) + copy(h1, h[:publicData.DomainNum.Cardinality]) + copy(h2, h[publicData.DomainNum.Cardinality:2*publicData.DomainNum.Cardinality]) + copy(h3, h[2*publicData.DomainNum.Cardinality:3*publicData.DomainNum.Cardinality]) + + return h1, h2, h3 + +} + +// ProveRaw from the public data +// TODO add a parameter to force the resolution of the system even if a constraint does not hold +func ProveRaw(spr *cs.SparseR1CS, publicData *PublicRaw, fullWitness bls24_315witness.Witness) (*ProofRaw, error) { + + // create a transcript manager to apply Fiat Shamir + fs := fiatshamir.NewTranscript(fiatshamir.SHA256, "gamma", "alpha", "zeta") + + // result + proof := &ProofRaw{} + + // compute the solution + solution, _ := spr.Solve(fullWitness) + + // query l, r, o in Lagrange basis + ll, lr, lo, partialL := ComputeLRO(spr, publicData, solution) + + // save ll, lr, lo, and make a copy of them in canonical basis. + // We commit them and derive gamma from them. + cl := make(bls24315.Polynomial, len(ll)) + cr := make(bls24315.Polynomial, len(lr)) + co := make(bls24315.Polynomial, len(lo)) + copy(cl, ll) + copy(cr, lr) + copy(co, lo) + publicData.DomainNum.FFTInverse(cl, fft.DIF, 0) + publicData.DomainNum.FFTInverse(cr, fft.DIF, 0) + publicData.DomainNum.FFTInverse(co, fft.DIF, 0) + publicData.DomainNum.FFTInverse(partialL, fft.DIF, 0) + fft.BitReverse(cl) + fft.BitReverse(cr) + fft.BitReverse(co) + fft.BitReverse(partialL) + + // derive gamma from the Comm(l), Comm(r), Comm(o) + var err error + proof.CommitmentsLROZH[0], err = publicData.CommitmentScheme.Commit(&cl) + if err != nil { + return proof, err + } + proof.CommitmentsLROZH[1], err = publicData.CommitmentScheme.Commit(&cr) + if err != nil { + return proof, err + } + proof.CommitmentsLROZH[2], err = publicData.CommitmentScheme.Commit(&co) + if err != nil { + return proof, err + } + err = fs.Bind("gamma", proof.CommitmentsLROZH[0].Marshal()) + if err != nil { + return proof, err + } + err = fs.Bind("gamma", proof.CommitmentsLROZH[1].Marshal()) + if err != nil { + return proof, err + } + err = fs.Bind("gamma", proof.CommitmentsLROZH[2].Marshal()) + if err != nil { + return proof, err + } + bgamma, err := fs.ComputeChallenge("gamma") + if err != nil { + return proof, err + } + var gamma fr.Element + gamma.SetBytes(bgamma) + + // compute Z, the permutation accumulator polynomial, in Lagrange basis + z := ComputeZ(ll, lr, lo, publicData, gamma) + + // compute Z(uX), in Lagrange basis + zu := shiftZ(z) + + // compute the evaluations of l, r, o on odd cosets of (Z/8mZ)/(Z/mZ) + evalL := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evalR := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evalO := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evaluateCosets(cl, evalL, publicData.DomainNum) + evaluateCosets(cr, evalR, publicData.DomainNum) + evaluateCosets(co, evalO, publicData.DomainNum) + + // compute the evaluation of qlL+qrR+qmL.R+qoO+k on the odd cosets of (Z/8mZ)/(Z/mZ) + constraintsInd := evalConstraints(publicData, evalL, evalR, evalO) + + // put back z, zu in canonical basis + publicData.DomainNum.FFTInverse(z, fft.DIF, 0) + publicData.DomainNum.FFTInverse(zu, fft.DIF, 0) + fft.BitReverse(z) + fft.BitReverse(zu) + + // evaluate z, zu on the odd cosets of (Z/8mZ)/(Z/mZ) + evalZ := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evalZu := make([]fr.Element, 4*publicData.DomainNum.Cardinality) + evaluateCosets(z, evalZ, publicData.DomainNum) + evaluateCosets(zu, evalZu, publicData.DomainNum) + + // compute zu*g1*g2*g3-z*f1*f2*f3 on the odd cosets of (Z/8mZ)/(Z/mZ) + constraintsOrdering := evalConstraintOrdering(publicData, evalZ, evalZu, evalL, evalR, evalO, gamma) + + // compute L1*(z-1) on the odd cosets of (Z/8mZ)/(Z/mZ) + startsAtOne := evalStartsAtOne(publicData, evalZ) + + // commit to Z + proof.CommitmentsLROZH[3], err = publicData.CommitmentScheme.Commit(&z) + if err != nil { + return proof, err + } + + // derive alpha from the Comm(l), Comm(r), Comm(o), Com(Z) + err = fs.Bind("alpha", proof.CommitmentsLROZH[3].Marshal()) + if err != nil { + return proof, err + } + balpha, err := fs.ComputeChallenge("alpha") + if err != nil { + return proof, err + } + var alpha fr.Element + alpha.SetBytes(balpha) + + // compute h in canonical form + h1, h2, h3 := computeH(publicData, constraintsInd, constraintsOrdering, startsAtOne, alpha) + + // commit to h (3 commitments h1 + x**n*h2 + x**2n*h3) + proof.CommitmentsLROZH[4], err = publicData.CommitmentScheme.Commit(&h1) + if err != nil { + return proof, err + } + proof.CommitmentsLROZH[5], err = publicData.CommitmentScheme.Commit(&h2) + if err != nil { + return proof, err + } + proof.CommitmentsLROZH[6], err = publicData.CommitmentScheme.Commit(&h3) + if err != nil { + return proof, err + } + + // derive zeta, the point of evaluation + err = fs.Bind("zeta", proof.CommitmentsLROZH[4].Marshal()) + if err != nil { + return proof, err + } + err = fs.Bind("zeta", proof.CommitmentsLROZH[5].Marshal()) + if err != nil { + return proof, err + } + err = fs.Bind("zeta", proof.CommitmentsLROZH[6].Marshal()) + if err != nil { + return proof, err + } + bzeta, err := fs.ComputeChallenge("zeta") + if err != nil { + return proof, err + } + var zeta fr.Element + zeta.SetBytes(bzeta) + + // compute evaluations of l, r, o, z at zeta + proof.LROZH[0].SetInterface(partialL.Eval(&zeta).(fr.Element)) + proof.LROZH[1].SetInterface(cr.Eval(&zeta).(fr.Element)) + proof.LROZH[2].SetInterface(co.Eval(&zeta).(fr.Element)) + proof.LROZH[3].SetInterface(z.Eval(&zeta).(fr.Element)) + + // compute evaluations of h1, h2, h3 at zeta (so h(zeta)=h1(zeta)+zeta^m*h2(zeta)+zeta^2m*h3(zeta)) + proof.LROZH[4].SetInterface(h1.Eval(&zeta)) + proof.LROZH[5].SetInterface(h2.Eval(&zeta)) + proof.LROZH[6].SetInterface(h3.Eval(&zeta)) + + // compute evaluation of z at z*zeta + var zzeta fr.Element + zzeta.Mul(&zeta, &publicData.DomainNum.Generator) + proof.ZShift.SetInterface(z.Eval(&zzeta)) + + // compute batfch opening proof for l, r, o, h, z at zeta + polynomialsToOpenAtZeta := []polynomial.Polynomial{&cl, &cr, &co, &z, &h1, &h2, &h3} + proof.BatchOpenings, err = publicData.CommitmentScheme.BatchOpenSinglePoint(&zeta, proof.CommitmentsLROZH[:], polynomialsToOpenAtZeta) + if err != nil { + return proof, err + } + + // compute opening proof for z at z*zeta + proof.OpeningZShift, err = publicData.CommitmentScheme.Open(&zzeta, &z) + if err != nil { + return proof, err + } + + return proof, nil +} diff --git a/internal/backend/bls24-315/plonk/setup.go b/internal/backend/bls24-315/plonk/setup.go new file mode 100644 index 000000000..d367aea41 --- /dev/null +++ b/internal/backend/bls24-315/plonk/setup.go @@ -0,0 +1,275 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package plonk + +import ( + bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/polynomial" + "github.com/consensys/gnark-crypto/polynomial" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/fft" + + bls24_315witness "github.com/consensys/gnark/internal/backend/bls24-315/witness" + + "github.com/consensys/gnark/internal/backend/bls24-315/cs" +) + +// PublicRaw represents the raw public data corresponding to a circuit, +// which consists of the evaluations of the LDE of qr,ql,qm,qo,k. The compact +// version of public data consists of commitments of qr,ql,qm,qo,k. +type PublicRaw struct { + + // Commitment scheme that is used for an instantiation of PLONK + CommitmentScheme polynomial.CommitmentScheme + + // qr,ql,qm,qo,k (in canonical basis) + Ql, Qr, Qm, Qo, Qk bls24315.Polynomial + + // Domains used for the FFTs + DomainNum, DomainH *fft.Domain + + // shifters for extending the permutation set: from s=<1,z,..,z**n-1>, + // extended domain = s || shifter[0].s || shifter[1].s + Shifter [2]fr.Element + + // s1, s2, s3 (L=Lagrange basis, C=canonical basis) + LS1, LS2, LS3 bls24315.Polynomial + CS1, CS2, CS3 bls24315.Polynomial + + // position -> permuted position (position in [0,3*sizeSystem-1]) + Permutation []int +} + +// SetupRaw from a sparseR1CS +// * sets LDE+canonical basis representations of the permutations +// * sets the canonical basis of ql, qr, qm, qo, qk extended (i.e. containing also placeholders constraints -PUB_INPUT_i + qk_i=0) +// * sets the fft domains that will be needed for handling polynomials +// The publicWitness params is here to build the placeholder constraints (used in the verifier to complete the proof) +// TODO in many places this function should handle raising errors +func SetupRaw(spr *cs.SparseR1CS, polynomialCommitment polynomial.CommitmentScheme, publicWitness bls24_315witness.Witness) *PublicRaw { + + nbConstraints := len(spr.Constraints) + nbAssertions := len(spr.Assertions) + + var res PublicRaw + + // fft domains + sizeSystem := uint64(nbConstraints + nbAssertions + spr.NbPublicVariables) // spr.NbPublicVariables is for the placeholder constraints + res.DomainNum = fft.NewDomain(sizeSystem, 3, false) + res.DomainH = fft.NewDomain(4*sizeSystem, 1, false) + + // shifters + res.Shifter[0].Set(&res.DomainNum.FinerGenerator) + res.Shifter[1].Square(&res.DomainNum.FinerGenerator) + + // commitment scheme + res.CommitmentScheme = polynomialCommitment + + // public polynomials corresponding to constraints: [ placholders | constraints | assertions ] + res.Ql = make([]fr.Element, res.DomainNum.Cardinality) + res.Qr = make([]fr.Element, res.DomainNum.Cardinality) + res.Qm = make([]fr.Element, res.DomainNum.Cardinality) + res.Qo = make([]fr.Element, res.DomainNum.Cardinality) + res.Qk = make([]fr.Element, res.DomainNum.Cardinality) + + for i := 0; i < spr.NbPublicVariables; i++ { // placeholders (-PUB_INPUT_i + qk_i = 0) TODO should return error is size is inconsistant + res.Ql[i].SetOne().Neg(&res.Ql[i]) + res.Qr[i].SetZero() + res.Qm[i].SetZero() + res.Qo[i].SetZero() + res.Qk[i].Set(&publicWitness[i]) + } + offset := spr.NbPublicVariables + for i := 0; i < nbConstraints; i++ { // constraints + + res.Ql[offset+i].Set(&spr.Coefficients[spr.Constraints[i].L.CoeffID()]) + res.Qr[offset+i].Set(&spr.Coefficients[spr.Constraints[i].R.CoeffID()]) + res.Qm[offset+i].Set(&spr.Coefficients[spr.Constraints[i].M[0].CoeffID()]). + Mul(&res.Qm[offset+i], &spr.Coefficients[spr.Constraints[i].M[1].CoeffID()]) + res.Qo[offset+i].Set(&spr.Coefficients[spr.Constraints[i].O.CoeffID()]) + res.Qk[offset+i].Set(&spr.Coefficients[spr.Constraints[i].K]) + } + offset += nbConstraints + for i := 0; i < nbAssertions; i++ { // assertions + + res.Ql[offset+i].Set(&spr.Coefficients[spr.Assertions[i].L.CoeffID()]) + res.Qr[offset+i].Set(&spr.Coefficients[spr.Assertions[i].R.CoeffID()]) + res.Qm[offset+i].Set(&spr.Coefficients[spr.Assertions[i].M[0].CoeffID()]). + Mul(&res.Qm[offset+i], &spr.Coefficients[spr.Assertions[i].M[1].CoeffID()]) + res.Qo[offset+i].Set(&spr.Coefficients[spr.Assertions[i].O.CoeffID()]) + res.Qk[offset+i].Set(&spr.Coefficients[spr.Assertions[i].K]) + } + + res.DomainNum.FFTInverse(res.Ql, fft.DIF, 0) + res.DomainNum.FFTInverse(res.Qr, fft.DIF, 0) + res.DomainNum.FFTInverse(res.Qm, fft.DIF, 0) + res.DomainNum.FFTInverse(res.Qo, fft.DIF, 0) + res.DomainNum.FFTInverse(res.Qk, fft.DIF, 0) + fft.BitReverse(res.Ql) + fft.BitReverse(res.Qr) + fft.BitReverse(res.Qm) + fft.BitReverse(res.Qo) + fft.BitReverse(res.Qk) + + // build permutation. Note: at this stage, the permutation takes in account the placeholders + buildPermutation(spr, &res) + + // set s1, s2, s3 + ComputeS(&res) + + return &res +} + +// buildPermutation builds the Permutation associated with a circuit. +// +// The permutation s is composed of cycles of maximum length such that +// +// s. (l||r||o) = (l||r||o) +// +//, where l||r||o is the concatenation of the indices of l, r, o in +// ql.l+qr.r+qm.l.r+qo.O+k = 0. +// +// The permutation is encoded as a slice s of size 3*size(l), where the +// i-th entry of l||r||o is sent to the s[i]-th entry, so it acts on a tab +// like this: for i in tab: tab[i] = tab[permutation[i]] +func buildPermutation(spr *cs.SparseR1CS, publicData *PublicRaw) { + + sizeSolution := int(publicData.DomainNum.Cardinality) + + // position -> variable_ID + lro := make([]int, 3*sizeSolution) + + publicData.Permutation = make([]int, 3*sizeSolution) + for i := 0; i < spr.NbPublicVariables; i++ { // IDs of LRO associated to placeholders (only L needs to be taken care of) + + lro[i] = i + lro[sizeSolution+i] = 0 + lro[2*sizeSolution+i] = 0 + + publicData.Permutation[i] = -1 + publicData.Permutation[sizeSolution+i] = -1 + publicData.Permutation[2*sizeSolution+i] = -1 + } + offset := spr.NbPublicVariables + for i := 0; i < len(spr.Constraints); i++ { // IDs of LRO associated to constraints + + lro[offset+i] = spr.Constraints[i].L.VariableID() + lro[sizeSolution+offset+i] = spr.Constraints[i].R.VariableID() + lro[2*sizeSolution+offset+i] = spr.Constraints[i].O.VariableID() + + publicData.Permutation[i+offset] = -1 + publicData.Permutation[sizeSolution+i+offset] = -1 + publicData.Permutation[2*sizeSolution+i+offset] = -1 + } + offset += len(spr.Constraints) + for i := 0; i < len(spr.Assertions); i++ { // IDs of LRO associated to assertions + + lro[offset+i] = spr.Assertions[i].L.VariableID() + lro[offset+sizeSolution+i] = spr.Assertions[i].R.VariableID() + lro[offset+2*sizeSolution+i] = spr.Assertions[i].O.VariableID() + + publicData.Permutation[offset+i] = -1 + publicData.Permutation[offset+sizeSolution+i] = -1 + publicData.Permutation[offset+2*sizeSolution+i] = -1 + } + offset += len(spr.Assertions) + for i := 0; i < sizeSolution-offset; i++ { + + publicData.Permutation[offset+i] = -1 + publicData.Permutation[offset+sizeSolution+i] = -1 + publicData.Permutation[offset+2*sizeSolution+i] = -1 + } + + nbVariables := spr.NbInternalVariables + spr.NbPublicVariables + spr.NbSecretVariables + + // map ID -> last position the ID was seen + cycle := make([]int, nbVariables) + for i := 0; i < len(cycle); i++ { + cycle[i] = -1 + } + + for i := 0; i < 3*sizeSolution; i++ { + if cycle[lro[i]] != -1 { + publicData.Permutation[i] = cycle[lro[i]] + } + cycle[lro[i]] = i + } + + // complete the Permutation by filling the first IDs encountered + counter := nbVariables + for iter := 0; counter > 0; iter++ { + if publicData.Permutation[iter] == -1 { + publicData.Permutation[iter] = cycle[lro[iter]] + counter-- + } + } + +} + +// ComputeS computes the LDE (Lagrange basis) of the permutations +// s1, s2, s3. +// +// ex: z gen of Z/mZ, u gen of Z/8mZ, then +// +// 1 z .. z**n-1 | u uz .. u*z**n-1 | u**2 u**2*z .. u**2*z**n-1 | +// | +// | Permutation +// s11 s12 .. s1n s21 s22 .. s2n s31 s32 .. s3n v +// \---------------/ \--------------------/ \------------------------/ +// s1 (LDE) s2 (LDE) s3 (LDE) +func ComputeS(publicData *PublicRaw) { + + nbElmt := int(publicData.DomainNum.Cardinality) + + // sID = [1,z,..,z**n-1,u,uz,..,uz**n-1,u**2,u**2.z,..,u**2.z**n-1] + sID := make([]fr.Element, 3*nbElmt) + sID[0].SetOne() + sID[nbElmt].Set(&publicData.DomainNum.FinerGenerator) + sID[2*nbElmt].Square(&publicData.DomainNum.FinerGenerator) + + for i := 1; i < nbElmt; i++ { + sID[i].Mul(&sID[i-1], &publicData.DomainNum.Generator) // z**i -> z**i+1 + sID[i+nbElmt].Mul(&sID[nbElmt+i-1], &publicData.DomainNum.Generator) // u*z**i -> u*z**i+1 + sID[i+2*nbElmt].Mul(&sID[2*nbElmt+i-1], &publicData.DomainNum.Generator) // u**2*z**i -> u**2*z**i+1 + } + + // Lagrange form of S1, S2, S3 + publicData.LS1 = make(bls24315.Polynomial, nbElmt) + publicData.LS2 = make(bls24315.Polynomial, nbElmt) + publicData.LS3 = make(bls24315.Polynomial, nbElmt) + for i := 0; i < nbElmt; i++ { + publicData.LS1[i].Set(&sID[publicData.Permutation[i]]) + publicData.LS2[i].Set(&sID[publicData.Permutation[nbElmt+i]]) + publicData.LS3[i].Set(&sID[publicData.Permutation[2*nbElmt+i]]) + } + + // Canonical form of S1, S2, S3 + publicData.CS1 = make(bls24315.Polynomial, nbElmt) + publicData.CS2 = make(bls24315.Polynomial, nbElmt) + publicData.CS3 = make(bls24315.Polynomial, nbElmt) + copy(publicData.CS1, publicData.LS1) + copy(publicData.CS2, publicData.LS2) + copy(publicData.CS3, publicData.LS3) + publicData.DomainNum.FFTInverse(publicData.CS1, fft.DIF, 0) + publicData.DomainNum.FFTInverse(publicData.CS2, fft.DIF, 0) + publicData.DomainNum.FFTInverse(publicData.CS3, fft.DIF, 0) + fft.BitReverse(publicData.CS1) + fft.BitReverse(publicData.CS2) + fft.BitReverse(publicData.CS3) + +} diff --git a/internal/backend/bls24-315/plonk/verify.go b/internal/backend/bls24-315/plonk/verify.go new file mode 100644 index 000000000..6217e52bb --- /dev/null +++ b/internal/backend/bls24-315/plonk/verify.go @@ -0,0 +1,214 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package plonk + +import ( + "errors" + "math/big" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + + fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" + + bls24_315witness "github.com/consensys/gnark/internal/backend/bls24-315/witness" +) + +var ( + errLhsNeqRhs = errors.New("polynomial equality doesn't hold") +) + +// VerifyRaw verifies a PLONK proof +func VerifyRaw(proof *ProofRaw, publicData *PublicRaw, publicWitness bls24_315witness.Witness) error { + + // create a transcript manager to apply Fiat Shamir and get the challenges + fs := fiatshamir.NewTranscript(fiatshamir.SHA256, "gamma", "alpha", "zeta") + err := fs.Bind("gamma", proof.CommitmentsLROZH[0].Marshal()) + if err != nil { + return err + } + err = fs.Bind("gamma", proof.CommitmentsLROZH[1].Marshal()) + if err != nil { + return err + } + err = fs.Bind("gamma", proof.CommitmentsLROZH[2].Marshal()) + if err != nil { + return err + } + bgamma, err := fs.ComputeChallenge("gamma") + if err != nil { + return err + } + var gamma fr.Element + gamma.SetBytes(bgamma) + + err = fs.Bind("alpha", proof.CommitmentsLROZH[3].Marshal()) + if err != nil { + return err + } + balpha, err := fs.ComputeChallenge("alpha") + if err != nil { + return err + } + var alpha fr.Element + alpha.SetBytes(balpha) + + err = fs.Bind("zeta", proof.CommitmentsLROZH[4].Marshal()) + if err != nil { + return err + } + err = fs.Bind("zeta", proof.CommitmentsLROZH[5].Marshal()) + if err != nil { + return err + } + err = fs.Bind("zeta", proof.CommitmentsLROZH[6].Marshal()) + if err != nil { + return err + } + bzeta, err := fs.ComputeChallenge("zeta") + if err != nil { + return err + } + var zeta fr.Element + zeta.SetBytes(bzeta) + + // checks the opening proofs + err = publicData.CommitmentScheme.BatchVerifySinglePoint(proof.CommitmentsLROZH[:], proof.BatchOpenings) + if err != nil { + return err + } + err = publicData.CommitmentScheme.Verify(proof.CommitmentsLROZH[3], proof.OpeningZShift) + if err != nil { + return err + } + + // evaluation of ql, qr, qm, qo, qk at zeta + var ql, qr, qm, qo, qk fr.Element + ql.SetInterface(publicData.Ql.Eval(&zeta)) + qr.SetInterface(publicData.Qr.Eval(&zeta)) + qm.SetInterface(publicData.Qm.Eval(&zeta)) + qo.SetInterface(publicData.Qo.Eval(&zeta)) + qk.SetInterface(publicData.Qk.Eval(&zeta)) + + // evaluation of Z=X**m-1 at zeta + var zetaPowerM, zzeta, one fr.Element + var bExpo big.Int + one.SetOne() + bExpo.SetUint64(publicData.DomainNum.Cardinality) + zetaPowerM.Exp(zeta, &bExpo) + zzeta.Sub(&zetaPowerM, &one) + + // complete L + // TODO use batch inversion + var lCompleted, den, acc, lagrange, xiLi fr.Element + lagrange.Set(&zzeta) // L_0(zeta) = 1/m*(zeta**m-1)/(zeta-1) + acc.SetOne() + den.Sub(&zeta, &acc) + lagrange.Div(&lagrange, &den).Mul(&lagrange, &publicData.DomainNum.CardinalityInv) + for i := 0; i < len(publicWitness); i++ { + + xiLi.Mul(&lagrange, &publicWitness[i]) + lCompleted.Add(&lCompleted, &xiLi) + + // use L_i+1 = w*Li*(X-z**i)/(X-z**i+1) + lagrange.Mul(&lagrange, &publicData.DomainNum.Generator). + Mul(&lagrange, &den) + acc.Mul(&acc, &publicData.DomainNum.Generator) + den.Sub(&zeta, &acc) + lagrange.Div(&lagrange, &den) + } + lCompleted.Add(&lCompleted, &proof.LROZH[0]) + + var lroz [4]fr.Element + lroz[0].Set(&lCompleted) + lroz[1].Set(&proof.LROZH[1]) + lroz[2].Set(&proof.LROZH[2]) + lroz[3].Set(&proof.LROZH[3]) + + // hFull = h1(zeta)+zeta^m*h2(zeta)+zeta^2m*h3(zeta) + var hFull fr.Element + hFull.Mul(&proof.LROZH[6], &zetaPowerM). + Add(&hFull, &proof.LROZH[5]). + Mul(&hFull, &zetaPowerM). + Add(&hFull, &proof.LROZH[4]) + + // evaluation of qlL+qrR+qmL.R+qoO+k at zeta + var constraintInd fr.Element + var qll, qrr, qmlr, qoo fr.Element + qll.Mul(&ql, &lroz[0]) + qrr.Mul(&qr, &lroz[1]) + qmlr.Mul(&qm, &lroz[0]).Mul(&qmlr, &lroz[1]) + qoo.Mul(&qo, &lroz[2]) + constraintInd.Add(&qll, &qrr). + Add(&constraintInd, &qmlr). + Add(&constraintInd, &qoo). + Add(&constraintInd, &qk) + + // evaluation of zu*g1*g2*g3*l-z*f1*f2*f3*l at zeta + var constraintOrdering, sZeta, ssZeta fr.Element + var s, f, g [3]fr.Element + + s[0].SetInterface(publicData.CS1.Eval(&zeta)) + s[1].SetInterface(publicData.CS2.Eval(&zeta)) + s[2].SetInterface(publicData.CS3.Eval(&zeta)) + + g[0].Add(&lroz[0], &s[0]).Add(&g[0], &gamma) // l+s1+gamma + g[1].Add(&lroz[1], &s[1]).Add(&g[1], &gamma) // r+s2+gamma + g[2].Add(&lroz[2], &s[2]).Add(&g[2], &gamma) // o+s3+gamma + g[0].Mul(&g[0], &g[1]).Mul(&g[0], &g[2]) // (l+s1+gamma)*(r+s2+gamma)*(o+s3+gamma) (zeta) + + sZeta.Mul(&publicData.Shifter[0], &zeta) + ssZeta.Mul(&publicData.Shifter[1], &zeta) + + f[0].Add(&lroz[0], &zeta).Add(&f[0], &gamma) // l+zeta+gamma + f[1].Add(&lroz[1], &sZeta).Add(&f[1], &gamma) // r+u*zeta+gamma + f[2].Add(&lroz[2], &ssZeta).Add(&f[2], &gamma) // o+u*zeta+gamma + f[0].Mul(&f[0], &f[1]).Mul(&f[0], &f[2]) // (l+zeta+gamma)*(r+u*zeta+gamma)*(r+u*zeta+gamma) (zeta) + + g[0].Mul(&g[0], &proof.ZShift) + f[0].Mul(&f[0], &lroz[3]) + + constraintOrdering.Sub(&g[0], &f[0]) + + // evaluation of L1*(Z-1) at zeta (L1 = 1/m*[ (X**m-1)/(X-1) ]) + var startsAtOne, tmp, c fr.Element + c.SetUint64(publicData.DomainNum.Cardinality) + tmp.Sub(&lroz[3], &one) // Z(zeta)-1 + startsAtOne. + Sub(&zeta, &one). + Mul(&startsAtOne, &c). + Inverse(&startsAtOne). // 1/m*(zeta-1) + Mul(&startsAtOne, &zzeta). // 1/m * (zeta**m-1)/(zeta-1) + Mul(&startsAtOne, &tmp) // (Z(zeta)-1)*L1(ze) + + // lhs = qlL+qrR+qmL.R+qoO+k(zeta) + alpha*(zu*g1*g2*g3*l-z*f1*f2*f3*l)(zeta) + alpha**2*L1(Z-1)(zeta) + var lhs fr.Element + lhs.Mul(&alpha, &startsAtOne). + Add(&lhs, &constraintOrdering). + Mul(&lhs, &alpha). + Add(&lhs, &constraintInd) + + // rhs = h(zeta)(zeta**m-1) + var rhs fr.Element + rhs.Mul(&zzeta, &hFull) + + if !lhs.Equal(&rhs) { + return errLhsNeqRhs + } + + return nil + +} diff --git a/internal/backend/bls24-315/witness/witness.go b/internal/backend/bls24-315/witness/witness.go new file mode 100644 index 000000000..ba1fabbd9 --- /dev/null +++ b/internal/backend/bls24-315/witness/witness.go @@ -0,0 +1,156 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package witness + +import ( + "encoding/binary" + "errors" + "io" + "reflect" + + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/internal/backend/compiled" + "github.com/consensys/gnark/internal/parser" + + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + + curve "github.com/consensys/gnark-crypto/ecc/bls24-315" +) + +type Witness []fr.Element + +// WriteTo encodes witness to writer (implements io.WriterTo) +func (witness *Witness) WriteTo(w io.Writer) (int64, error) { + // encode slice length + if err := binary.Write(w, binary.BigEndian, uint32(len(*witness))); err != nil { + return 0, err + } + + enc := curve.NewEncoder(w) + for i := 0; i < len(*witness); i++ { + if err := enc.Encode(&(*witness)[i]); err != nil { + return enc.BytesWritten() + 4, err + } + } + return enc.BytesWritten() + 4, nil +} + +// LimitReadFrom decodes witness from reader; first 4 bytes (uint32) must equal to expectedSize +// this method won't read more than expectedSize * size(fr.Element) +func (witness *Witness) LimitReadFrom(r io.Reader, expectedSize int) (int64, error) { + + var buf [4]byte + if read, err := io.ReadFull(r, buf[:4]); err != nil { + return int64(read), err + } + sliceLen := binary.BigEndian.Uint32(buf[:4]) + if int(sliceLen) != expectedSize { + return 4, errors.New("invalid witness size") + } + + if len(*witness) != int(sliceLen) { + *witness = make([]fr.Element, sliceLen) + } + + lr := io.LimitReader(r, int64(expectedSize*fr.Limbs*8)) + dec := curve.NewDecoder(lr) + + for i := 0; i < int(sliceLen); i++ { + if err := dec.Decode(&(*witness)[i]); err != nil { + return dec.BytesRead() + 4, err + } + } + + return dec.BytesRead() + 4, nil +} + +// FromFullAssignment extracts the full witness [ public | secret ] +func (witness *Witness) FromFullAssignment(w frontend.Circuit) error { + nbSecret, nbPublic := count(w) + + if len(*witness) < (nbPublic + nbSecret) { + (*witness) = make(Witness, nbPublic+nbSecret) + } else { + (*witness) = (*witness)[:nbPublic+nbSecret] + } + + var i, j int // indexes for secret / public variables + i = nbPublic // offset + + var collectHandler parser.LeafHandler = func(visibility compiled.Visibility, name string, tInput reflect.Value) error { + v := tInput.Interface().(frontend.Variable) + + val := frontend.GetAssignedValue(v) + if val == nil { + return errors.New("variable " + name + " not assigned") + } + + if visibility == compiled.Secret { + (*witness)[i].SetInterface(val) + i++ + } else if visibility == compiled.Public { + (*witness)[j].SetInterface(val) + j++ + } + return nil + } + return parser.Visit(w, "", compiled.Unset, collectHandler, reflect.TypeOf(frontend.Variable{})) +} + +// FromPublicAssignment extracts the public part of witness +func (witness *Witness) FromPublicAssignment(w frontend.Circuit) error { + _, nbPublic := count(w) + + // note: does not contain ONE_WIRE for Groth16 + if len(*witness) < (nbPublic) { + (*witness) = make(Witness, nbPublic) + } else { + (*witness) = (*witness)[:nbPublic] + } + var j int // index for public variables + + var collectHandler parser.LeafHandler = func(visibility compiled.Visibility, name string, tInput reflect.Value) error { + if visibility == compiled.Public { + v := tInput.Interface().(frontend.Variable) + val := frontend.GetAssignedValue(v) + if val == nil { + return errors.New("variable " + name + " not assigned") + } + (*witness)[j].SetInterface(val) + j++ + } + return nil + } + return parser.Visit(w, "", compiled.Unset, collectHandler, reflect.TypeOf(frontend.Variable{})) +} + +func count(w frontend.Circuit) (nbSecret, nbPublic int) { + var collectHandler parser.LeafHandler = func(visibility compiled.Visibility, name string, tInput reflect.Value) error { + if visibility == compiled.Secret { + nbSecret++ + } else if visibility == compiled.Public { + nbPublic++ + } + return nil + } + + err := parser.Visit(w, "", compiled.Unset, collectHandler, reflect.TypeOf(frontend.Variable{})) + if err != nil { + panic("count handler doesn't return an error -- this panic should not happen") + } + return +} diff --git a/internal/generator/backend/main.go b/internal/generator/backend/main.go index 130c90efb..c3651c99c 100644 --- a/internal/generator/backend/main.go +++ b/internal/generator/backend/main.go @@ -34,15 +34,19 @@ func main() { CurveID: "BN254", Package: "bn254", } - bw6_761 := templateData{ RootPath: "../../../internal/backend/bw6-761/", Curve: "BW6-761", CurveID: "BW6_761", Package: "bw6761", } - - datas := []templateData{bls12_377, bls12_381, bn254, bw6_761} + bls24_315 := templateData{ + RootPath: "../../../internal/backend/bls24-315/", + Curve: "BLS24-315", + CurveID: "BLS24_315", + Package: "bls24315", + } + datas := []templateData{bls12_377, bls12_381, bn254, bw6_761, bls24_315} const importCurve = "../imports.go.tmpl" diff --git a/std/algebra/twistededwards/curve.go b/std/algebra/twistededwards/curve.go index 5840475eb..d4589ab12 100644 --- a/std/algebra/twistededwards/curve.go +++ b/std/algebra/twistededwards/curve.go @@ -25,6 +25,8 @@ import ( edbls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/twistededwards" frbls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" edbls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/twistededwards" + frbls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + edbls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/twistededwards" frbn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr" edbn254 "github.com/consensys/gnark-crypto/ecc/bn254/twistededwards" frbw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" @@ -46,6 +48,7 @@ func init() { newTwistedEdwards[ecc.BN254] = newEdBN254 newTwistedEdwards[ecc.BLS12_377] = newEdBLS377 newTwistedEdwards[ecc.BW6_761] = newEdBW761 + newTwistedEdwards[ecc.BLS24_315] = newEdBLS315 } // NewEdCurve returns an Edwards curve parameters @@ -139,3 +142,23 @@ func newEdBW761() EdCurve { return res } + +func newEdBLS315() EdCurve { + + edcurve := edbls24315.GetEdwardsCurve() + var cofactorReg big.Int + edcurve.Cofactor.ToBigInt(&cofactorReg) + + res := EdCurve{ + A: frontend.FromInterface(edcurve.A), + D: frontend.FromInterface(edcurve.D), + Cofactor: frontend.FromInterface(cofactorReg), + Order: frontend.FromInterface(edcurve.Order), + BaseX: frontend.FromInterface(edcurve.Base.X), + BaseY: frontend.FromInterface(edcurve.Base.Y), + ID: ecc.BLS24_315, + } + res.Modulus.Set(frbls24315.Modulus()) + + return res +} diff --git a/std/hash/mimc/encrypt.go b/std/hash/mimc/encrypt.go index 5b42d6afe..7dd15a286 100644 --- a/std/hash/mimc/encrypt.go +++ b/std/hash/mimc/encrypt.go @@ -22,6 +22,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/mimc" bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/mimc" + bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/mimc" bn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/mimc" @@ -37,12 +38,14 @@ func init() { encryptFuncs[ecc.BLS12_381] = encryptBLS381 encryptFuncs[ecc.BLS12_377] = encryptBLS377 encryptFuncs[ecc.BW6_761] = encryptBW761 + encryptFuncs[ecc.BLS24_315] = encryptBLS315 newMimc = make(map[ecc.ID]func(string) MiMC) newMimc[ecc.BN254] = newMimcBN254 newMimc[ecc.BLS12_381] = newMimcBLS381 newMimc[ecc.BLS12_377] = newMimcBLS377 newMimc[ecc.BW6_761] = newMimcBW761 + newMimc[ecc.BLS24_315] = newMimcBLS315 } // ------------------------------------------------------------------------------------------------- @@ -96,6 +99,18 @@ func newMimcBW761(seed string) MiMC { return res } +func newMimcBLS315(seed string) MiMC { + res := MiMC{} + params := bls24315.NewParams(seed) + for _, v := range params { + var cpy big.Int + v.ToBigIntRegular(&cpy) + res.params = append(res.params, cpy) + } + res.id = ecc.BLS24_315 + return res +} + // ------------------------------------------------------------------------------------------------- // encryptions functions @@ -161,3 +176,18 @@ func encryptBLS377(cs *frontend.ConstraintSystem, h MiMC, message frontend.Varia return res } + +// encryptBLS315 of a mimc run expressed as r1cs +func encryptBLS315(cs *frontend.ConstraintSystem, h MiMC, message frontend.Variable, key frontend.Variable) frontend.Variable { + res := message + for i := 0; i < len(h.params); i++ { + tmp := cs.Add(res, h.params[i], key) + // res = (res+k+c)^5 + res = cs.Mul(tmp, tmp) // square + res = cs.Mul(res, res) // square + res = cs.Mul(res, tmp) // mul + } + res = cs.Add(res, key) + return res + +} diff --git a/std/hash/mimc/mimc_test.go b/std/hash/mimc/mimc_test.go index cac235a72..2dea5a16d 100644 --- a/std/hash/mimc/mimc_test.go +++ b/std/hash/mimc/mimc_test.go @@ -55,6 +55,7 @@ func TestMimcAll(t *testing.T) { ecc.BLS12_381: hash.MIMC_BLS12_381, ecc.BLS12_377: hash.MIMC_BLS12_377, ecc.BW6_761: hash.MIMC_BW6_761, + ecc.BLS24_315: hash.MIMC_BLS24_315, } for curve, hashFunc := range curves { diff --git a/std/signature/eddsa/eddsa.go b/std/signature/eddsa/eddsa.go index d4e7b0834..dceb32e1a 100644 --- a/std/signature/eddsa/eddsa.go +++ b/std/signature/eddsa/eddsa.go @@ -73,6 +73,8 @@ func Verify(cs *frontend.ConstraintSystem, sig Signature, msg frontend.Variable, basis = cs.Constant("340282366920938463463374607431768211456") case ecc.BW6_761: basis = cs.Constant("6277101735386680763835789423207666416102355444464034512896") // 2**192 + case ecc.BLS24_315: + basis = cs.Constant("340282366920938463463374607431768211456") default: panic("curve is not supported") } diff --git a/std/signature/eddsa/eddsa_test.go b/std/signature/eddsa/eddsa_test.go index 3ac44298a..9577bdfba 100644 --- a/std/signature/eddsa/eddsa_test.go +++ b/std/signature/eddsa/eddsa_test.go @@ -26,6 +26,8 @@ import ( eddsabls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/twistededwards/eddsa" edwardsbls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/twistededwards" eddsabls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381/twistededwards/eddsa" + edwardsbls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/twistededwards" + eddsabls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315/twistededwards/eddsa" edwardsbn254 "github.com/consensys/gnark-crypto/ecc/bn254/twistededwards" eddsabn254 "github.com/consensys/gnark-crypto/ecc/bn254/twistededwards/eddsa" edwardsbw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/twistededwards" @@ -50,6 +52,7 @@ func parseSignature(id ecc.ID, buf []byte) ([]byte, []byte, []byte, []byte) { var pointbls12381 edwardsbls12381.PointAffine var pointbls12377 edwardsbls12377.PointAffine var pointbw6761 edwardsbw6761.PointAffine + var pointbls24315 edwardsbls24315.PointAffine switch id { case ecc.BN254: @@ -76,6 +79,12 @@ func parseSignature(id ecc.ID, buf []byte) ([]byte, []byte, []byte, []byte) { s1 := buf[48:72] s2 := buf[72:] return a[:], b[:], s1, s2 + case ecc.BLS24_315: + pointbls24315.SetBytes(buf[:32]) + a, b := parsePoint(id, buf) + s1 := buf[32:48] + s2 := buf[48:] + return a[:], b[:], s1, s2 default: return buf, buf, buf, buf } @@ -86,6 +95,7 @@ func parsePoint(id ecc.ID, buf []byte) ([]byte, []byte) { var pointbls12381 edwardsbls12381.PointAffine var pointbls12377 edwardsbls12377.PointAffine var pointbw6761 edwardsbw6761.PointAffine + var pointbls24315 edwardsbls24315.PointAffine switch id { case ecc.BN254: pointbn254.SetBytes(buf[:32]) @@ -107,6 +117,11 @@ func parsePoint(id ecc.ID, buf []byte) ([]byte, []byte) { a := pointbw6761.X.Bytes() b := pointbw6761.Y.Bytes() return a[:], b[:] + case ecc.BLS24_315: + pointbls24315.SetBytes(buf[:32]) + a := pointbls24315.X.Bytes() + b := pointbls24315.Y.Bytes() + return a[:], b[:] default: return buf, buf } @@ -139,12 +154,14 @@ func TestEddsa(t *testing.T) { signature.Register(signature.EDDSA_BLS12_381, eddsabls12381.GenerateKeyInterfaces) signature.Register(signature.EDDSA_BLS12_377, eddsabls12377.GenerateKeyInterfaces) signature.Register(signature.EDDSA_BW6_761, eddsabw6761.GenerateKeyInterfaces) + signature.Register(signature.EDDSA_BLS24_315, eddsabls24315.GenerateKeyInterfaces) confs := map[ecc.ID]confSig{ ecc.BN254: {hash.MIMC_BN254, signature.EDDSA_BN254}, ecc.BLS12_381: {hash.MIMC_BLS12_381, signature.EDDSA_BLS12_381}, ecc.BLS12_377: {hash.MIMC_BLS12_377, signature.EDDSA_BLS12_377}, ecc.BW6_761: {hash.MIMC_BW6_761, signature.EDDSA_BW6_761}, + ecc.BLS24_315: {hash.MIMC_BLS24_315, signature.EDDSA_BLS24_315}, } for id, conf := range confs {