Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can now assign public variables in witness without triggering error for missing secret values, and force Prover execution with invalid solution #38

Merged
merged 4 commits into from
Nov 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions backend/groth16/groth16.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,31 @@ func Verify(proof Proof, vk VerifyingKey, solution interface{}) error {
}
}

// Prove generate a groth16.Proof
func Prove(r1cs r1cs.R1CS, pk ProvingKey, solution interface{}) (Proof, error) {
// Prove generates the proof of knoweldge of a r1cs with solution.
// if force flag is set, Prove ignores R1CS solving error (ie invalid solution) and executes
// the FFTs and MultiExponentiations to compute an (invalid) Proof object
func Prove(r1cs r1cs.R1CS, pk ProvingKey, solution interface{}, force ...bool) (Proof, error) {

_solution, err := frontend.ParseWitness(solution)

if err != nil {
return nil, err
}

_force := false
if len(force) > 0 {
_force = force[0]
}

switch _r1cs := r1cs.(type) {
case *backend_bls377.R1CS:
return groth16_bls377.Prove(_r1cs, pk.(*groth16_bls377.ProvingKey), _solution)
return groth16_bls377.Prove(_r1cs, pk.(*groth16_bls377.ProvingKey), _solution, _force)
case *backend_bls381.R1CS:
return groth16_bls381.Prove(_r1cs, pk.(*groth16_bls381.ProvingKey), _solution)
return groth16_bls381.Prove(_r1cs, pk.(*groth16_bls381.ProvingKey), _solution, _force)
case *backend_bn256.R1CS:
return groth16_bn256.Prove(_r1cs, pk.(*groth16_bn256.ProvingKey), _solution)
return groth16_bn256.Prove(_r1cs, pk.(*groth16_bn256.ProvingKey), _solution, _force)
case *backend_bw761.R1CS:
return groth16_bw761.Prove(_r1cs, pk.(*groth16_bw761.ProvingKey), _solution)
return groth16_bw761.Prove(_r1cs, pk.(*groth16_bw761.ProvingKey), _solution, _force)
default:
panic("unrecognized R1CS curve type")
}
Expand Down
14 changes: 9 additions & 5 deletions frontend/circuit.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func Compile(curveID gurvy.ID, circuit Circuit) (r1cs.R1CS, error) {
}

// ParseWitness will returns a map[string]interface{} to be used as input in
// in R1CS.Solve(), groth16.Prove() or groth16.Verify()
// in R1CS.Solve(), groth16.Prove()
//
// if input is not already a map[string]interface{}, it must implement frontend.Circuit
func ParseWitness(input interface{}) (map[string]interface{}, error) {
Expand All @@ -85,14 +85,18 @@ func ParseWitness(input interface{}) (map[string]interface{}, error) {
return c, nil
case Circuit:
toReturn := make(map[string]interface{})
var extractHandler leafHandler = func(visibilityToRefactor backend.Visibility, name string, tInput reflect.Value) error {

var extractHandler leafHandler = func(visibility backend.Visibility, name string, tInput reflect.Value) error {

v := tInput.Interface().(Variable)
if v.val == nil {
return errors.New(name + " has no assigned value.")

if v.val != nil {
toReturn[name] = v.val
}
toReturn[name] = v.val

return nil
}

// recursively parse through reflection the circuits members to find all inputs that need to be allOoutputcated
// (secret or public inputs)
return toReturn, parseType(c, "", backend.Unset, extractHandler)
Expand Down
104 changes: 87 additions & 17 deletions integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ import (
"path/filepath"
"testing"

"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/internal/backend/circuits"
"github.com/consensys/gnark/io"
"github.com/consensys/gurvy"
)

func TestIntegration(t *testing.T) {
func TestIntegrationCLI(t *testing.T) {

// create temporary dir for integration test
parentDir := "./integration_test"
os.RemoveAll(parentDir)
Expand All @@ -38,29 +40,49 @@ func TestIntegration(t *testing.T) {
}

// spv: setup, prove, verify
spv := func(curveID gurvy.ID, name string, _good, _bad frontend.Circuit) {
spv := func(curveID gurvy.ID, name string, _good, _bad, _public frontend.Circuit) {

t.Logf("%s circuit (%s)", name, curveID.String())

// path for files
fCircuit := filepath.Join(parentDir, name+".r1cs")
fPk := filepath.Join(parentDir, name+".pk")
fVk := filepath.Join(parentDir, name+".vk")
fProof := filepath.Join(parentDir, name+".proof")
fInputGood := filepath.Join(parentDir, name+".good.input")
fInputBad := filepath.Join(parentDir, name+".bad.input")

fInputGoodProver := filepath.Join(parentDir, name+"_prover.good.input")
fInputBadProver := filepath.Join(parentDir, name+"_prover.bad.input")

fInputVerifier := filepath.Join(parentDir, name+"_public.good.input")

// 2: input files to disk
good, err := frontend.ParseWitness(_good)

// 2.1 data for the prover
proverGood, err := frontend.ParseWitness(_good)
if err != nil {
panic("invalid good assignment:" + err.Error())
panic("invalid good secret assignment:" + err.Error())
}
bad, err := frontend.ParseWitness(_bad)
proverBad, err := frontend.ParseWitness(_bad)
if err != nil {
panic("invalid bad assignment:" + err.Error())
panic("invalid bad secret assignment:" + err.Error())
}
if err := io.WriteWitness(fInputGood, good); err != nil {

// 2.2 data for the verifier
verifier, err := frontend.ParseWitness(_public)
if err != nil {
panic("invalid good public assignment:" + err.Error())
}

// 2.3 dump prover data on disk
if err := io.WriteWitness(fInputGoodProver, proverGood); err != nil {
t.Fatal(err)
}
if err := io.WriteWitness(fInputBad, bad); err != nil {
if err := io.WriteWitness(fInputBadProver, proverBad); err != nil {
t.Fatal(err)
}

// 2.4 dump verifier data on disk
if err := io.WriteWitness(fInputVerifier, verifier); err != nil {
t.Fatal(err)
}

Expand All @@ -74,10 +96,10 @@ func TestIntegration(t *testing.T) {
}
}

pv := func(fInput string, expectedVerifyResult bool) {
pv := func(fInputProver, fInputVerifier string, expectedVerifyResult bool) {
// 4: run prove
{
cmd := exec.Command("go", "run", "main.go", "prove", fCircuit, "--pk", fPk, "--input", fInput, "--proof", fProof)
cmd := exec.Command("go", "run", "main.go", "prove", fCircuit, "--pk", fPk, "--input", fInputProver, "--proof", fProof)
out, err := cmd.CombinedOutput()
if expectedVerifyResult && err != nil {
t.Log(string(out))
Expand All @@ -89,7 +111,7 @@ func TestIntegration(t *testing.T) {

// 4: run verify
{
cmd := exec.Command("go", "run", "main.go", "verify", fProof, "--vk", fVk, "--input", fInput)
cmd := exec.Command("go", "run", "main.go", "verify", fProof, "--vk", fVk, "--input", fInputVerifier)
out, err := cmd.CombinedOutput()
if expectedVerifyResult && err != nil {
t.Log(string(out))
Expand All @@ -101,13 +123,14 @@ func TestIntegration(t *testing.T) {
}
}

pv(fInputGood, true)
pv(fInputBad, false)
pv(fInputGoodProver, fInputVerifier, true)
pv(fInputBadProver, fInputVerifier, false)
}

curves := []gurvy.ID{gurvy.BLS377, gurvy.BLS381, gurvy.BN256, gurvy.BW761}
curves := []gurvy.ID{gurvy.BN256, gurvy.BLS377, gurvy.BLS381, gurvy.BW761}

for name, circuit := range circuits.Circuits {

if testing.Short() {
if name != "lut01" && name != "frombinary" {
continue
Expand All @@ -120,7 +143,54 @@ func TestIntegration(t *testing.T) {
if err := io.WriteFile(fCircuit, typedR1CS); err != nil {
t.Fatal(err)
}
spv(curve, name, circuit.Good, circuit.Bad)
spv(curve, name, circuit.Good, circuit.Bad, circuit.Public)
}
}
}

func TestIntegrationAPI(t *testing.T) {

// create temporary dir for integration test
parentDir := "./integration_test"
os.RemoveAll(parentDir)
defer os.RemoveAll(parentDir)
if err := os.MkdirAll(parentDir, 0700); err != nil {
t.Fatal(err)
}

curves := []gurvy.ID{gurvy.BN256, gurvy.BLS377, gurvy.BLS381, gurvy.BW761}

for name, circuit := range circuits.Circuits {

if testing.Short() {
if name != "lut01" && name != "frombinary" {
continue
}
}
for _, curve := range curves {

typedR1CS := circuit.R1CS.ToR1CS(curve)

pk, vk := groth16.Setup(typedR1CS)
correctProof, err := groth16.Prove(typedR1CS, pk, circuit.Good)
if err != nil {
t.Fatal(err)
}
wrongProof, err := groth16.Prove(typedR1CS, pk, circuit.Bad, true)
if err != nil {
t.Fatal(err)
}

err = groth16.Verify(correctProof, vk, circuit.Public)
if err != nil {
t.Fatal("Verify should have succeeded")
}
err = groth16.Verify(wrongProof, vk, circuit.Public)
if err == nil {
t.Fatal("Verify should have failed")
}

}
}

}
4 changes: 2 additions & 2 deletions internal/backend/bls377/groth16/groth16_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions internal/backend/bls377/groth16/prove.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions internal/backend/bls381/groth16/groth16_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions internal/backend/bls381/groth16/prove.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions internal/backend/bn256/groth16/groth16_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions internal/backend/bn256/groth16/prove.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions internal/backend/bw761/groth16/groth16_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading