Closed aayux closed 2 months ago
Closing because I now believe that this does not identify an actual issue, and the error above is just from incorrect usage. I explain my requirements more clearly in discussion #1250.
In short: I want to write some marshalled G1 elements (Groth16 verification key elements) as well as a byte string into a hash, and the wrapped hashes inside recursion don't seem to be able to handle that.
Description
I am using the
recursive.NewShort
method following this discussion, but I have an issue where, when I try toH.Write
just one extra value, the constraint check fails with a segmentation violation.I provide a minimal reproducible example below to explain what I mean. This toy example verifies a signature on a hash over a concatenated string, where part of the secret hash input is checked against a public value.
Circuit definition
```sighash_circuit.go``` ```golang package main import ( "fmt" "github.com/consensys/gnark-crypto/ecc" gc_tedwards "github.com/consensys/gnark-crypto/ecc/twistededwards" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/algebra/native/twistededwards" "github.com/consensys/gnark/std/hash/mimc" "github.com/consensys/gnark/std/recursion" "github.com/consensys/gnark/std/signature/eddsa" ) type HashCircuit struct { VerKey eddsa.PublicKey Signature eddsa.Signature Pad frontend.Variable `gnark:",public"` CurTime frontend.Variable `gnark:",public"` ExpTime frontend.Variable } func (c *HashCircuit) Define(api frontend.API) error { api.AssertIsLessOrEqual(c.CurTime, c.ExpTime) H, err := recursion.NewHash(api, ecc.BW6_761.ScalarField(), true) // mimc.NewMiMC(api) if err != nil { return fmt.Errorf("new hash: %w", err) } H.Write(c.ExpTime) H.Write(c.Pad) M := H.Sum() // construct the message signed curve, err := twistededwards.NewEdCurve(api, gc_tedwards.BW6_761) if err != nil { return err } mimc, err := mimc.NewMiMC(api) if err != nil { return err } // verify the signature in the cs return eddsa.Verify(curve, c.Signature, M, c.VerKey, &mimc) } ```Driver code
```sighash.go``` ```golang package main import ( crand "crypto/rand" "encoding/binary" "fmt" "time" "github.com/consensys/gnark-crypto/ecc" gc_tedwards "github.com/consensys/gnark-crypto/ecc/twistededwards" "github.com/consensys/gnark-crypto/hash" "github.com/consensys/gnark-crypto/signature" gc_eddsa "github.com/consensys/gnark-crypto/signature/eddsa" "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/std/recursion" ) const UnixTimeYear = 31536000 type WitnessStruct struct { VerKey signature.PublicKey Signature []byte Pad []byte CurTime []byte ExpTime []byte } func KeyGen_BW6_761() (signature.Signer, signature.PublicKey) { // create a eddsa key pair sigsk, _ := gc_eddsa.New(gc_tedwards.BW6_761, crand.Reader) sigvk := sigsk.Public() return sigsk, sigvk } func Sign_BW6_761(sigsk signature.Signer, sigvk signature.PublicKey, M []byte) []byte { // instantiate hash function H := hash.MIMC_BW6_761.New() // sign the M σ, _ := sigsk.Sign(M, H) b, _ := sigvk.Verify(σ, M, H) if !b { return nil } return σ } func Setup() (constraint.ConstraintSystem, groth16.ProvingKey, groth16.VerifyingKey) { // compile the signature verification circuit var circuit HashCircuit R, _ := frontend.Compile(ecc.BW6_761.ScalarField(), r1cs.NewBuilder, &circuit) // generating zkpk, zkvk zkpk, zkvk, _ := groth16.Setup(R) return R, zkpk, zkvk } func Prove(R constraint.ConstraintSystem, zkpk groth16.ProvingKey, witStruct WitnessStruct) (witness.Witness, groth16.Proof) { // declare the witness var circAssignment HashCircuit // signature verification key bytes sigvkBytes := witStruct.VerKey.Bytes() // assign signature verification key values circAssignment.VerKey.Assign(gc_tedwards.BW6_761, sigvkBytes[:48]) // assign signature values circAssignment.Signature.Assign(gc_tedwards.BW6_761, witStruct.Signature) // assign other values circAssignment.Pad = witStruct.Pad circAssignment.CurTime = witStruct.CurTime circAssignment.ExpTime = witStruct.ExpTime // full witness (instance + witness) W, _ := frontend.NewWitness(&circAssignment, ecc.BW6_761.ScalarField()) // generate the proof π, _ := groth16.Prove(R, zkpk, W) x, _ := W.Public() return x, π } func Verify(zkvk groth16.VerifyingKey, x witness.Witness, π groth16.Proof) error { // verify the proof return groth16.Verify(π, zkvk, x) } func main() { // set the inner proof expiration time curTime := time.Now() curUnixTime := curTime.Unix() curUnixTimeBytes := make([]byte, 8) binary.BigEndian.PutUint64(curUnixTimeBytes, uint64(curUnixTime)) expUnixTime := curUnixTime + UnixTimeYear expUnixTimeBytes := make([]byte, 8) binary.BigEndian.PutUint64(expUnixTimeBytes, uint64(expUnixTime)) // digest and sign H, _ := recursion.NewShort(ecc.BW6_761.ScalarField(), ecc.BW6_761.ScalarField()) // works when: hash.MIMC_BW6_761.New() H.Write(expUnixTimeBytes) pad := []byte("Expected Behavior
As I note in the comment near the "digest and sign" procedure in
main()
, the code above works when the hasher is instantiated using the standard MiMC instance (and similarly in the circuit). This is the behaviour I would expect.Actual Behavior
I realise that in this toy example, I do not really need to use
recursion.NewShort
, but in my actual application, I do need it and the errors are identitcal. The output on running the code above is also provided:Possible Fix
Unclear. I do not understand the source of this issue.
Steps to Reproduce
go run .
the provided code.Context
Write a second value to the hash:
and verify in the circuit
Your Environment
HEAD@develop
): latest