You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 20:07:06