Skip to content

Commit

Permalink
feat/ci: make CI run solidity verification with integartion test. Add…
Browse files Browse the repository at this point in the history
…ed test.WithSolidity option. (Consensys#732)

* feat: plonk + sol rebased on latest develop branch

* feat: kept only verifier template

* refactor: plonk solidity generate match expected gnark interface

* tmp: change Library to Contract and add MarshalSolidity on proof

* feat: plonk sodliity change visibility of some funcs

* feat: add SPDIX apache 2 identifier

* fix: move new solidity code into tmpl generation

* fix: use big int string in solidity generation for plonk to avoid negative numbers

* feat: add WithSolidity option in assert helper

* ci: attempt to make gnark-solidity-checker run on CI

* fix: address PR comments

---------

Co-authored-by: Thomas Piellard <[email protected]>
  • Loading branch information
gbotrel and ThomasPiellard authored Jun 23, 2023
1 parent a75adf0 commit e1332fa
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 33 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,16 @@ jobs:
restore-keys: |
${{ runner.os }}-go-
- name: install deps
run: go install golang.org/x/tools/cmd/goimports@latest && go install github.com/klauspost/asmfmt/cmd/asmfmt@latest
run: |
go install golang.org/x/tools/cmd/goimports@latest && go install github.com/klauspost/asmfmt/cmd/asmfmt@latest
go install github.com/ethereum/go-ethereum/cmd/[email protected]
go install github.com/consensys/gnark-solidity-checker@latest
sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install solc
- name: Test
run: |
go test -v -short -timeout=30m ./...
go test -v -short -tags=solccheck -timeout=30m ./...
- name: Test race
run: |
go test -v -short -race -timeout=30m
Expand Down
40 changes: 20 additions & 20 deletions backend/plonk/bn254/solidity.go

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

14 changes: 10 additions & 4 deletions backend/plonk/bn254/verify.go

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

3 changes: 2 additions & 1 deletion integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ func TestIntegrationAPI(t *testing.T) {
tData.Circuit, tData.ValidAssignments[i],
test.WithSolverOpts(solver.WithHints(tData.HintFunctions...)),
test.WithCurves(tData.Curves[0], tData.Curves[1:]...),
test.WithBackends(backends[0], backends[1:]...))
test.WithBackends(backends[0], backends[1:]...),
test.WithSolidity())
}, fmt.Sprintf("valid-%d", i))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,11 +335,17 @@ func (vk *VerifyingKey) ExportSolidity(w io.Writer) error {
"inc": func(i int) int {
return i + 1
},
"frptr": func(x fr.Element) *fr.Element {
return &x
"frstr": func(x fr.Element) string {
// we use big.Int to always get a positive string.
// not the most efficient hack, but it works better for .sol generation.
bv := new(big.Int)
x.BigInt(bv)
return bv.String()
},
"fpptr": func(x fp.Element) *fp.Element {
return &x
"fpstr": func(x fp.Element) string {
bv := new(big.Int)
x.BigInt(bv)
return bv.String()
},
"add": func(i, j int) int {
return i + j
Expand Down
14 changes: 12 additions & 2 deletions test/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ func (assert *Assert) ProverSucceeded(circuit frontend.Circuit, validAssignment
err = groth16.Verify(proof, vk, validPublicWitness)
checkError(err)

if opt.solidity && curve == ecc.BN254 && vk.NbPublicWitness() > 0 {
// check that the proof can be verified by gnark-solidity-checker
assert.solidityVerification(b, vk, proof, validPublicWitness)
}

case backend.PLONK:
srs, err := NewKZGSRS(ccs)
checkError(err)
Expand All @@ -188,12 +193,17 @@ func (assert *Assert) ProverSucceeded(circuit frontend.Circuit, validAssignment
roundTripCheck(assert.t, vk, vkReconstructed)
}

correctProof, err := plonk.Prove(ccs, pk, validWitness, opt.proverOpts...)
proof, err := plonk.Prove(ccs, pk, validWitness, opt.proverOpts...)
checkError(err)

err = plonk.Verify(correctProof, vk, validPublicWitness)
err = plonk.Verify(proof, vk, validPublicWitness)
checkError(err)

if opt.solidity && curve == ecc.BN254 {
// check that the proof can be verified by gnark-solidity-checker
assert.solidityVerification(b, vk, proof, validPublicWitness)
}

case backend.PLONKFRI:
pk, vk, err := plonkfri.Setup(ccs)
checkError(err)
Expand Down
95 changes: 95 additions & 0 deletions test/assert_solidity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package test

import (
"bytes"
"encoding/hex"
"io"
"os"
"os/exec"
"path/filepath"
"strconv"

"github.com/consensys/gnark/backend"
groth16_bn254 "github.com/consensys/gnark/backend/groth16/bn254"
plonk_bn254 "github.com/consensys/gnark/backend/plonk/bn254"
"github.com/consensys/gnark/backend/witness"
)

// solidityVerification checks that the exported solidity contract can verify the proof
// and that the proof is valid.
// It uses gnark-solidity-checker see test.WithSolidity option.
func (assert *Assert) solidityVerification(b backend.ID, vk interface {
NbPublicWitness() int
ExportSolidity(io.Writer) error
},
proof any,
validPublicWitness witness.Witness) {
if !solcCheck {
return // nothing to check, will make solc fail.
}
assert.t.Helper()

// make temp dir
tmpDir, err := os.MkdirTemp("", "gnark-solidity-check*")
assert.NoError(err)
defer os.RemoveAll(tmpDir)

// export solidity contract
fSolidity, err := os.Create(filepath.Join(tmpDir, "gnark_verifier.sol"))
assert.NoError(err)

err = vk.ExportSolidity(fSolidity)
assert.NoError(err)

err = fSolidity.Close()
assert.NoError(err)

// generate assets
// gnark-solidity-checker generate --dir tmpdir --solidity contract_g16.sol
cmd := exec.Command("gnark-solidity-checker", "generate", "--dir", tmpDir, "--solidity", "gnark_verifier.sol")
assert.t.Log("running ", cmd.String())
out, err := cmd.CombinedOutput()
assert.NoError(err, string(out))

// proof to hex
var proofStr string
var optBackend string

if b == backend.GROTH16 {
optBackend = "--groth16"
var buf bytes.Buffer
_proof := proof.(*groth16_bn254.Proof)
_, err = _proof.WriteRawTo(&buf)
assert.NoError(err)
proofStr = hex.EncodeToString(buf.Bytes())
} else if b == backend.PLONK {
optBackend = "--plonk"
_proof := proof.(*plonk_bn254.Proof)
// TODO @gbotrel make a single Marshal function for PlonK proof.
proofStr = hex.EncodeToString(_proof.MarshalSolidity())
} else {
panic("not implemented")
}

// public witness to hex
bPublicWitness, err := validPublicWitness.MarshalBinary()
assert.NoError(err)
// that's quite dirty...
// first 4 bytes -> nbPublic
// next 4 bytes -> nbSecret
// next 4 bytes -> nb elements in the vector (== nbPublic + nbSecret)
bPublicWitness = bPublicWitness[12:]
publicWitnessStr := hex.EncodeToString(bPublicWitness)

// verify proof
// gnark-solidity-checker verify --dir tmdir --groth16 --nb-public-inputs 1 --proof 1234 --public-inputs dead
cmd = exec.Command("gnark-solidity-checker", "verify",
"--dir", tmpDir,
optBackend,
"--nb-public-inputs", strconv.Itoa(vk.NbPublicWitness()),
"--proof", proofStr,
"--public-inputs", publicWitnessStr)
assert.t.Log("running ", cmd.String())
out, err = cmd.CombinedOutput()
assert.NoError(err, string(out))
}
14 changes: 14 additions & 0 deletions test/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type testingConfig struct {
proverOpts []backend.ProverOption
compileOpts []frontend.CompileOption
fuzzing bool
solidity bool
}

// WithBackends is testing option which restricts the backends the assertions are
Expand Down Expand Up @@ -103,3 +104,16 @@ func WithCompileOpts(compileOpts ...frontend.CompileOption) TestingOption {
return nil
}
}

// WithSolidity is a testing option which enables solidity tests in assertions.
// If the build tag "solccheck" is not set, this option is ignored.
// When the tag is set; this requires gnark-solidity-checker to be installed, which in turns
// requires solc and abigen to be reachable in the PATH.
//
// See https://github.com/ConsenSys/gnark-solidity-checker for more details.
func WithSolidity() TestingOption {
return func(opt *testingConfig) error {
opt.solidity = true && solcCheck
return nil
}
}
5 changes: 5 additions & 0 deletions test/solccheck_with.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build solccheck

package test

const solcCheck = true
5 changes: 5 additions & 0 deletions test/solccheck_without.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build !solccheck

package test

const solcCheck = false

0 comments on commit e1332fa

Please sign in to comment.