go语言零知识证明gnark框架

 零知识证明:零知识证明(Zero—Knowledge Proof),是由S.Goldwasser、S.Micali及C.Rackoff在20世纪80年代初提出的。它指的是证明者能够在不向验证者提供任何有用的信息的情况下,使验证者相信某个论断是正确的。零知识证明实质上是一种涉及两方或更多方的协议,即两方或更多方完成一项任务所需采取的一系列步骤。证明者向验证者证明并使其相信自己知道或拥有某一消息,但证明过程不能向验证者泄漏任何关于被证明消息的信息。大量事实证明,零知识证明在密码学中非常有用。如果能够将零知识证明用于验证,将可以有效解决许多问题。

零知识证明 zero-knowledge proofs,简称ZKPs,我们都知道区块链本身的一个关键优势就是透明性,但是在很多情况下,智能合约应用却出于各种商业或法律原因需要保障数据隐私,比如传入真实有效的数据来触发智能合约执行,但这涉及到信息会有泄露的风险。如何避免这种情况的发生呢?零知识证明的出现很好地解决了这个问题。

零知识证明(Zero Knowledge Proof)的研究最早始于1985年,由MIT教授Shafi Goldwasser, Silvio Micali 和 密码学大师Charles在《The Knowledge Complexity of Interactive Proof-Systems》论文中提出。正是这篇文章提出了 零知识证明 这个伟大概念,并逐步成为了现代密码学理论的根基之一,而Shafi Goldwasser和Silvio Micali也于2012 年获得了有“计算机界诺贝尔奖”之称的图灵奖。零知识证明系统所要完成的任务是「证明某一个事实并且不泄露知识」。这个过程就是零知识证明。

首先介绍低版本的代码:

首先看清楚版本号,在go/mod里面:

module awesomeProject16

go 1.18

require (
	github.com/consensys/gnark v0.5.2
	github.com/consensys/gnark-crypto v0.5.3
)

在终端输入:(切换版本包的版本号)

go get github.com/consensys/gnark@v0.5.2
go get github.com/consensys/gnark-crypto@v0.5.3

下面是gnark的源码:

package main

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"github.com/consensys/gnark-crypto/ecc"
	bn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc"
	"github.com/consensys/gnark/backend"
	"github.com/consensys/gnark/backend/groth16"
	"github.com/consensys/gnark/frontend"
	"github.com/consensys/gnark/std/hash/mimc"
	"math/big"
)

type Circuit struct {
	PreImage frontend.Variable //这是需要隐藏的原文
	Hash     frontend.Variable `gnark:",public"` //这是公开的Hash值(加密后的,他人无法得知真实内容)
}

func (circuit *Circuit) Define(curveID ecc.ID, api frontend.API) error {
	mimc, _ := mimc.NewMiMC("seed", curveID, api)
	mimc.Write(circuit.PreImage)
	api.AssertIsEqual(circuit.Hash, mimc.Sum())
	return nil
}

//Hash函数,根据原文得到Hash值
func mimcHash(data []byte) string {
	f := bn254.NewMiMC("seed")
	f.Write(data)
	hash := f.Sum(nil)
	hashInt := big.NewInt(0).SetBytes(hash)
	return hashInt.String()
}

func main() {
	//preImage := []byte{0x01, 0x02, 0x03}
	preImage := []byte("我有家产三千万")
	hash := mimcHash(preImage)

	fmt.Printf("hash值是: %s\n", hash)

	var circuit Circuit
	r1cs, err := frontend.Compile(ecc.BN254, backend.GROTH16, &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
	}

	witness := &Circuit{
		PreImage: frontend.Value(preImage),
		Hash:     frontend.Value(hash),
	}
	proof, err := groth16.Prove(r1cs, pk, witness)
	if err != nil {
		fmt.Printf("Prove failed: %v\n", err)
		return
	}

	var proofBuffer bytes.Buffer
	proofBuffer.Reset()
	proof.WriteRawTo(&proofBuffer)
	proofBuffer.Bytes()

	var vkBuffer bytes.Buffer
	vkBuffer.Reset()
	vk.WriteRawTo(&vkBuffer)
	vkBuffer.Bytes()

	//fmt.Println("proof:", proof)
	//fmt.Println("vk:", vk)

	fmt.Printf("proof: %s\n", hex.EncodeToString(proofBuffer.Bytes()))
	fmt.Printf("verifykey: %s\n", hex.EncodeToString(vkBuffer.Bytes()))
	fmt.Println("proofBuffer.Bytes():", proofBuffer.Bytes())
	fmt.Println("vkBuffer.Bytes()",vkBuffer.Bytes())
	//fmt.Println(hex.DecodeString(hex.EncodeToString(proofBuffer.Bytes())))//将string类型转成[]byte
	//VerifyProof(hash, vkBuffer.Bytes(), proofBuffer.Bytes())
	//记录hash, vkbytes, proofbytes的值就可以勇于进行零知识证明验证是否是本人,本人无需出示原文件,只需出示hash, vkbytes, proofbytes即可。
	proofbytes, _ := hex.DecodeString("27ade0a7bff8814b47de7ab820d57532353145b3c0aa897a567939edee285ce1254cf5a54adfe56d860e0700481da652237137f08cd4174cefd9c23d5c60b51504873ffc3f2cefca1c4eccfcd3ddf84aa78d32a87d72768972bd1502c82284470ffa61707ee11628e7d0cf0111fd0b034f72554dc75b621bfdc2168ebc2985a221bd018617bd553849a282c9b3a30fbdb09ed6ec2cd02da18e7fe53db423d3dc0db60054a9777315fb4f618c741dc96ed7d9261e4c20a94190802cdd563bbc661b141c3fc7bfab7e753bfd39a0c9956abfa7fdab964d71de3c80ff3dfa26451112e4d650c737be6b1b8a2ffc96b32061894619f299e6db5ad30427cafc76433e")
    vkbytes, _ := hex.DecodeString("06a16d1f49bbe0640132ae85e86b10aa774d0f2ed1b89da4ea5199648679fa270af8f1b1a0a3d0bebe9a2ab0e9f9053c54fd8b609f38c9f851aba0f96d7620702bc330523ca579d1220c870ef25409525989ba9209536cdc9a113d9cb4fe403f2d75022f0e837699e4a0a060a0433536206d7804b0df5372e2bfd2ce43b130972bd6680f6abfbafec1b4551cb2990d51403d79a0199775ade4adf5bf8ce83e882685a7134549e3d5497c68de8015508268c9c7539941e6cfba86de1a1d28b4570657b89357a4e69b1c558b0d89a9c8e2752585de6d2e59f519c52f0b328bee4d182229f307b208e32fb8bb29e238f28543744d1aa065511769c728d06f9524e118b7c6aaa0e6fd70b480997b8c522168b6f98f1de0737d5f4f1d2ea27d3a751d0917b17234b0344a44b5af524267db7c82d9bebf319462bc2de49fe30d77645325fbdb28fddd7f6eb3d97ff2b284e66dcebbc3cbc1cc00881e37112cba41786b2bcc29687d4e807f7ed07f6bcd02159b9540f3ef55eb3463aeb1a25c6cc98bb8273bf06ff874eadc7d80418e894876eb14c69d152867130593f60f13bfba5f7f2c30d0ee29d461f03a448fface2bbdb4f5e28cc52d4ef7ccf78f0848d2cc37b40b709b1590fcd243d314acd2efe08e978f2c61662a1ee17d8f06ec3d69e476c22aedaf1d8f4841f4bfb7b43191d5390f1640f4c9bd6635a6c85799366b00e6f12810e7ffed58937d642bafd614174ca3a41e78340a438976d067e91368f682a42ff42c2d000f79c72c8b1a76739db73498c85f9bd958659645ad21626ce8e7320000000229b22d6ea68903dfda226c235df272a1cc6403c78318ffbc84f7b4623a5ad6de28ba81762e8739d9957d285d20cca76e82672236640e65e99395141e7613eefb1a4f24786381aff9c6ae29b165913e75144983f261d4c31558c7b08440f108a12780267560766d9b627b895d1aeae7841810ed19799bd32f04f395e9bdb1866c")
	VerifyProof(hash, vkbytes, proofbytes)
	//fmt.Println("proof:", proofBuffer.String())
	//fmt.Println("vkBuffer.:", vkBuffer.String())
	//VerifyProof(hash, proof, vk)
	/*
		publicWitness := &Circuit{
			Hash: frontend.Value(hash),
		}
		//进行零知识证明校验
		err = groth16.Verify(proof, vk, publicWitness) //这里用到了proof和vk需要记录下来
		if err != nil {
			fmt.Printf("零知识证明认证失败! %v\n", err)
			return
		} else {
			fmt.Printf("零知识证明认证成功!\n")
		}
	*/
}

//零知识证明判断函数
func VerifyProof(hash string, verifyKey []byte, proofbytes []byte) (bool, error) {
	publicWitness := &Circuit{
		Hash: frontend.Value(hash),
	}
	proof := groth16.NewProof(ecc.BN254)
	proof.ReadFrom(bytes.NewBuffer(proofbytes))

	vk := groth16.NewVerifyingKey(ecc.BN254)
	vk.ReadFrom(bytes.NewBuffer(verifyKey))
	//进行零知识证明校验
	err := groth16.Verify(proof, vk, publicWitness) //这里用到了proof和vk需要记录下来
	if err != nil {
		fmt.Printf("零知识证明认证失败! %v\n", err)
		return false, err
	} else {
		fmt.Printf("零知识证明认证成功!\n")
		return true, nil
	}
}

然后介绍最新版本的代码:

注意:go语言的版本要在1.17以上!以下是go mod中各个包的版本:

   //最新版本的gnark和gnark-crypto
	github.com/consensys/gnark v0.7.1
	github.com/consensys/gnark-crypto v0.7.0
module go_code/awesomeProject19

go 1.18

require (
    //最新版本的gnark和gnark-crypto
	github.com/consensys/gnark v0.7.1
	github.com/consensys/gnark-crypto v0.7.0
)

require (
	github.com/fxamacker/cbor/v2 v2.2.0 // indirect
	github.com/mmcloughlin/addchain v0.4.0 // indirect
	github.com/rs/zerolog v1.26.1 // indirect
	github.com/x448/float16 v0.8.4 // indirect
	golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 // indirect
	golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
)

代码:

package main

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"github.com/consensys/gnark-crypto/ecc"
	"github.com/consensys/gnark-crypto/hash"
	"github.com/consensys/gnark/backend/groth16"
	"github.com/consensys/gnark/frontend"
	"github.com/consensys/gnark/frontend/cs/r1cs"
	"github.com/consensys/gnark/std/hash/mimc"
	"math/big"
	"os"
)

type Circuit struct {
	// struct tag on a variable is optional
	// default uses variable name and secret visibility.
	PreImage frontend.Variable
	Hash     frontend.Variable `gnark:",public"`
}

// Define declares the circuit's constraints
// Hash = mimc(PreImage)
func (circuit *Circuit) Define(api frontend.API) error {
	// hash function
	mimc, _ := mimc.NewMiMC(api)

	// specify constraints
	// mimc(preImage) == hash
	mimc.Write(circuit.PreImage)
	api.AssertIsEqual(circuit.Hash, mimc.Sum())

	return nil
}

//hash函数
func hashCalc(preImage string) string {
	hi, _ := big.NewInt(0).SetString(preImage, 10)

	h := hash.MIMC_BN254.New()
	h.Write(hi.Bytes())
	rd := h.Sum(nil)
	r1 := big.NewInt(0).SetBytes(rd).String()

	return r1
}

func main() {
	// 外部系统生成Hash零知识证明电路
	var circuit Circuit
	ccs, err := frontend.Compile(ecc.BN254, r1cs.NewBuilder, &circuit)
	if err != nil {
		panic(err)
	}
	// groth16 zkSNARK: Setup
	pk, vk, err := groth16.Setup(ccs)
	if err != nil {
		panic(err)
	}
	//根据原文计算hash值
	//preImage := "16130099170765464552823636852555369511329944820189892919423002775646948828469"
	preImage := "1"
	hash := hashCalc(preImage)
	fmt.Println("hash:", hash)
	// witness definition
	assignment := Circuit{
		PreImage: preImage,
		Hash:     hash,
	}

	witness, err := frontend.NewWitness(&assignment, ecc.BN254)
	if err != nil {
		panic(err)
	}

	proof, err := groth16.Prove(ccs, pk, witness)
	if err != nil {
		panic(err)
	}
	var proofBuffer bytes.Buffer
	proofBuffer.Reset()
	proof.WriteRawTo(&proofBuffer)
	proofBuffer.Bytes()

	var vkBuffer bytes.Buffer
	vkBuffer.Reset()
	vk.WriteRawTo(&vkBuffer)
	vkBuffer.Bytes()

	fmt.Printf("proof: %s\n", hex.EncodeToString(proofBuffer.Bytes()))
	fmt.Printf("verifykey: %s\n", hex.EncodeToString(vkBuffer.Bytes()))
	//将上述产生的proof和verifykey字符串输出到文本中,方便复制粘贴!
	fileName := "gnark.txt"
	dstFile, err := os.Create(fileName)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer dstFile.Close()
	str := "原文:" + preImage + "\n" + "Hash:" + hash + "\nproof:" + hex.EncodeToString(proofBuffer.Bytes()) + "\n" + "verifykey:" + hex.EncodeToString(vkBuffer.Bytes())
	dstFile.WriteString(str + "\n")

	// VerifyProof 函数放到智能合约执行
	r, err := VerifyProof(hash, vkBuffer.Bytes(), proofBuffer.Bytes())
	if err != nil {
		panic(err)
	}
	if r {
		fmt.Println("验证通过!")
	} else {
		fmt.Println("验证失败!")
	}

}

func VerifyProof(hash string, verifyKey []byte, publicWitness []byte) (bool, error) {
	assignment1 := Circuit{
		Hash: hash,
	}
	publicWitness1, err := frontend.NewWitness(&assignment1, ecc.BN254, frontend.PublicOnly())
	if err != nil {
		return false, err
	}

	proof := groth16.NewProof(ecc.BN254)
	proof.ReadFrom(bytes.NewBuffer(publicWitness))

	vk := groth16.NewVerifyingKey(ecc.BN254)
	vk.ReadFrom(bytes.NewBuffer(verifyKey))

	err = groth16.Verify(proof, vk, publicWitness1)
	if err != nil {
		return false, err
	}
	return true, nil
}

顾名思义,零知识证明就是既能充分证明自己是某种权益的合法拥有者,又不把有关的信息泄露出去——即给外界的“知识”为“零”。其实,零知识证明并不是什么新东西,早在16世纪的文艺复兴时期,意大利有两位数学家为竞争一元三次方程求根公式发现者的桂冠,就采用了零知识证明的方法。当时,数学家塔尔塔里雅和菲奥都宣称自己掌握了这个求根公式,为了证明自己没有说谎,又不把公式的具体内容公布出来(可能在当时数学公式也是一种技术秘密),他们摆开了擂台:双方各出30个一元三次方程给对方解,谁能全部解出,就说明谁掌握了这个公式。比赛结果显示,塔尔塔里雅解出了菲奥出的全部30个方程,而菲奥一个也解不出。于是人们相信塔尔塔里雅是一元三次方程求根公式的真正发现者,虽然当时除了塔尔塔里雅外,谁也不知道这个公式到底是个什么样子。从这个故事,我们可以初步了解零知识证明的概念。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值