使用gnark v0.8.0和gnark-crypto v0.9.1实现MIMC验证失败求助
问题描述
使用gnark v0.8.0和gnark-crypto v0.9.1实现MIMC哈希验证时,代码始终验证失败,报错显示约束不满足,怀疑哈希值传入Define函数后发生变化。以下是完整代码及错误日志:
package main import ( "fmt" "github.com/consensys/gnark-crypto/ecc" bn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/frontend" r1cs2 "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/std/hash/mimc" "math/big" ) type Circuit struct { PreImage frontend.Variable Hash frontend.Variable `gnark:",public"` } func (circuit *Circuit) Define(api frontend.API) error { api.Println(circuit.Hash) api.Println(circuit.PreImage) mimc, _ := mimc.NewMiMC(api) mimc.Write(circuit.PreImage) api.Println(mimc.Sum()) api.AssertIsEqual(circuit.Hash, mimc.Sum()) return nil } func mimcHash(data []byte) string { f := bn254.NewMiMC() hash := f.Sum(data) hashInt := big.NewInt(0).SetBytes(hash) return hashInt.String() } func main() { preImage := []byte("1") fmt.Println(preImage) hash := mimcHash(preImage) fmt.Println(preImage) fmt.Printf("hash: %s\n", hash) var circuit Circuit r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs2.NewBuilder, &circuit) if err != nil { fmt.Printf("Compile failed : %v\n", err) return } pk, vk, err := groth16.Setup(r1cs) if err != nil { fmt.Printf("Setup failed\n") return } assignment := &Circuit{ PreImage: frontend.Variable(preImage), Hash: frontend.Variable(hash), } fmt.Println("assignment.Hash", assignment.Hash) witness, err := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) proof, err := groth16.Prove(r1cs, pk, witness) if err != nil { fmt.Printf("Prove failed: %v\n", err) return } publicAssignment := &Circuit{ Hash: frontend.Variable(hash), } fmt.Println(hash) publicWitness, err := frontend.NewWitness(publicAssignment, ecc.BN254.ScalarField()) err = groth16.Verify(proof, vk, publicWitness) if err != nil { fmt.Printf("verification failed: %v\n", err) return } fmt.Printf("verification succeded\n") }
错误日志:
hash: 5673812372628493575754978265425707484810229248616387637933421616387743352356864 04:08:31 INF compiling circuit 04:08:31 INF parsed circuit inputs nbPublic=1 nbSecret=1 04:08:31 INF building constraint builder nbConstraints=331 04:08:31 ERR error="unsatisfied constraint" backend=groth16 id=330 nbConstraints=331 Prove failed: constraint #330 is not satisfied: [assertIsEqual] 4757468822121293193159177404073236876202868908634742915586732064608951992061 == 21518545289977633059516571167495097594845616311010087326595465475420112732966 r1cs.(*builder).AssertIsEqual api_assertions.go:35 main.(*Circuit).Define test.go:22
原因分析
- 预图像处理逻辑不一致:外部计算哈希时直接使用字节数组
[]byte("1")作为输入,而电路中frontend.Variable(preImage)会将该字节数组解析为大整数(即ASCII码0x31对应的整数53),两者输入本质不同,导致哈希结果必然不一致。 - MIMC参数未对齐:外部使用
bn254.NewMiMC()创建的实例,与电路中默认mimc.NewMiMC(api)的参数(如轮数、常数)可能存在差异,进一步导致哈希结果不匹配。
解决方法
- 统一预图像处理逻辑:外部计算哈希时,将字节数组转换为对应大整数后再传入MIMC,与电路中的输入处理逻辑对齐;或者在电路中直接处理字节数组,确保两边输入完全一致。
- 显式指定MIMC参数:在电路中创建MIMC实例时,显式指定BN254对应的参数,保证电路内外使用相同的哈希配置。
修正后的代码
package main import ( "fmt" "github.com/consensys/gnark-crypto/ecc" bn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/frontend" r1cs2 "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/std/hash/mimc" "math/big" ) type Circuit struct { PreImage frontend.Variable Hash frontend.Variable `gnark:",public"` } func (circuit *Circuit) Define(api frontend.API) error { // 显式指定使用BN254的MIMC参数,确保与外部计算对齐 mimcHash, _ := mimc.NewMiMC(api, mimc.MIMC_BN254) mimcHash.Write(circuit.PreImage) api.AssertIsEqual(circuit.Hash, mimcHash.Sum()) return nil } // 修正哈希计算逻辑:将字节数组转为大整数后计算哈希,与电路输入逻辑对齐 func mimcHash(data []byte) *big.Int { // 将字节数组转换为大整数,和circuit中frontend.Variable(data)的处理逻辑一致 preImageInt := new(big.Int).SetBytes(data) f := bn254.NewMiMC() f.Write(preImageInt.Bytes()) hashBytes := f.Sum(nil) return new(big.Int).SetBytes(hashBytes) } func main() { preImage := []byte("1") hash := mimcHash(preImage) fmt.Printf("hash: %s\n", hash.String()) var circuit Circuit r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs2.NewBuilder, &circuit) if err != nil { fmt.Printf("Compile failed : %v\n", err) return } pk, vk, err := groth16.Setup(r1cs) if err != nil { fmt.Printf("Setup failed\n") return } // 预图像使用转换后的大整数,与哈希计算逻辑对齐 assignment := &Circuit{ PreImage: frontend.Variable(new(big.Int).SetBytes(preImage)), Hash: frontend.Variable(hash), } witness, err := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) if err != nil { fmt.Printf("NewWitness failed: %v\n", err) return } proof, err := groth16.Prove(r1cs, pk, witness) if err != nil { fmt.Printf("Prove failed: %v\n", err) return } publicAssignment := &Circuit{ Hash: frontend.Variable(hash), } publicWitness, err := frontend.NewWitness(publicAssignment, ecc.BN254.ScalarField()) if err != nil { fmt.Printf("NewPublicWitness failed: %v\n", err) return } err = groth16.Verify(proof, vk, publicWitness) if err != nil { fmt.Printf("verification failed: %v\n", err) return } fmt.Printf("verification succeeded\n") }
内容的提问来源于stack exchange,提问作者YurunChen
相关产品推荐
相关产品推荐

