Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package circuit

import "math/big"

var (
// is poseidon hash(empty account info)
EmptyAccountLeafNodeHash, _ = new(big.Int).SetString("2853aadbbd06deb5d6a1389d23a89ff47658aaf4a45b287ecfd62df192bd91a4", 16)
)
73 changes: 73 additions & 0 deletions hash_verification_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package main

import (
"fmt"
"testing"

"gate-zkmerkle-proof/utils"

"hash"

"github.com/consensys/gnark-crypto/ecc/bn254/fr"
"github.com/consensys/gnark-crypto/ecc/bn254/fr/poseidon"
zk_smt "github.com/gatechain/gate-zk-smt"
"github.com/gatechain/gate-zk-smt/database/memory"
)

// 电路常量 计算空账户叶子节点哈希 TestEmptyAccountLeafNodeHash
func TestEmptyAccountLeafNodeHash(t *testing.T) {
// 1. 创建零值元素
zero := &fr.Element{0, 0, 0, 0}
fmt.Printf("1. 零值元素: %v\n", zero)

// 2. 创建 Poseidon 哈希器
poseidonHasher := poseidon.NewPoseidon()
fmt.Printf("2. 创建 Poseidon 哈希器\n")

// 3. 创建空资产列表(模拟项目中的逻辑)
emptyAssets := make([]utils.AccountAsset, utils.AssetCounts)
for i := 0; i < utils.AssetCounts; i++ {
emptyAssets[i].Index = uint16(i)
// Equity 和 Debt 默认为 0
}
fmt.Printf("3. 创建了 %d 个空资产,每个资产的 Equity=0, Debt=0\n", utils.AssetCounts)

// 4. 计算空资产的承诺(调用项目中的实际方法)
fmt.Printf("4. 调用 utils.ComputeUserAssetsCommitment 计算资产承诺...\n")
emptyAssetCommitment := utils.ComputeUserAssetsCommitment(&poseidonHasher, emptyAssets)
fmt.Printf(" 空资产承诺: %x\n", emptyAssetCommitment)

tempHash := poseidon.Poseidon(zero, zero, zero, new(fr.Element).SetBytes(emptyAssetCommitment)).Bytes()
fmt.Printf(" 计算得到的空账户哈希: %x\n", tempHash)
}

func TestEmptyAccountTreeRoot(t *testing.T) {
// 1. 计算 NilAccountHash(空账户叶子节点哈希)
fmt.Printf("1. 计算 NilAccountHash...\n")
zero := &fr.Element{0, 0, 0, 0}
poseidonHasher := poseidon.NewPoseidon()
emptyAssets := make([]utils.AccountAsset, utils.AssetCounts)
for i := 0; i < utils.AssetCounts; i++ {
emptyAssets[i].Index = uint16(i)
}
emptyAssetCommitment := utils.ComputeUserAssetsCommitment(&poseidonHasher, emptyAssets)
tempHash := poseidon.Poseidon(zero, zero, zero, new(fr.Element).SetBytes(emptyAssetCommitment)).Bytes()
nilAccountHash := tempHash[:]
fmt.Printf(" NilAccountHash: %x\n", nilAccountHash)

// 2. 创建空账户树
fmt.Printf("2. 创建深度为 %d 的空账户树...\n", utils.AccountTreeDepth)
hasher := zk_smt.NewHasherPool(func() hash.Hash {
return poseidon.NewPoseidon()
})
db := memory.NewMemoryDB()
accountTree, err := zk_smt.NewGateSparseMerkleTree(hasher, db, utils.AccountTreeDepth, nilAccountHash)
if err != nil {
t.Fatalf("创建账户树失败: %v", err)
}

// 3. 获取空树根
fmt.Printf("3. 获取空账户树根...\n")
emptyTreeRoot := accountTree.Root()
fmt.Printf(" 空账户树根: %x\n", emptyTreeRoot)
}
156 changes: 156 additions & 0 deletions users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package circuit

import (
"gate-zkmerkle-proof/utils"
"github.com/consensys/gnark/std/hash/poseidon"
)

type BatchCreateUserCircuit struct {
BatchCommitment Variable `gnark:",public"`
BeforeAccountTreeRoot Variable
AfterAccountTreeRoot Variable
BeforeCEXAssetsCommitment Variable
AfterCEXAssetsCommitment Variable
BeforeCexAssets []CexAssetInfo
CreateUserOps []CreateUserOperation
}

func NewVerifyBatchCreateUserCircuit(commitment []byte) *BatchCreateUserCircuit {
var v BatchCreateUserCircuit
v.BatchCommitment = commitment
return &v
}

func NewBatchCreateUserCircuit(assetCounts uint32, batchCounts uint32) *BatchCreateUserCircuit {
var circuit BatchCreateUserCircuit
circuit.BatchCommitment = 0
circuit.BeforeAccountTreeRoot = 0
circuit.AfterAccountTreeRoot = 0
circuit.BeforeCEXAssetsCommitment = 0
circuit.AfterCEXAssetsCommitment = 0
circuit.BeforeCexAssets = make([]CexAssetInfo, assetCounts)
for i := uint32(0); i < assetCounts; i++ {
circuit.BeforeCexAssets[i] = CexAssetInfo{
TotalEquity: 0,
TotalDebt: 0,
BasePrice: 0,
}
}
circuit.CreateUserOps = make([]CreateUserOperation, batchCounts)
for i := uint32(0); i < batchCounts; i++ {
circuit.CreateUserOps[i] = CreateUserOperation{
BeforeAccountTreeRoot: 0,
AfterAccountTreeRoot: 0,
Assets: make([]UserAssetInfo, assetCounts),
AccountIndex: 0,
AccountProof: [utils.AccountTreeDepth]Variable{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
}
for j := uint32(0); j < assetCounts; j++ {
circuit.CreateUserOps[i].Assets[j].Debt = 0
circuit.CreateUserOps[i].Assets[j].Equity = 0
}
}
return &circuit
}

func (b BatchCreateUserCircuit) Define(api API) error {
// verify whether BatchCommitment is computed correctly
actualBatchCommitment := poseidon.Poseidon(api, b.BeforeAccountTreeRoot, b.AfterAccountTreeRoot, b.BeforeCEXAssetsCommitment, b.AfterCEXAssetsCommitment)
api.AssertIsEqual(b.BatchCommitment, actualBatchCommitment)
cexAssets := make([]Variable, len(b.BeforeCexAssets))
afterCexAssets := make([]CexAssetInfo, len(b.BeforeCexAssets))

// verify whether beforeCexAssetsCommitment is computed correctly
for i := 0; i < len(b.BeforeCexAssets); i++ {
CheckValueInRange(api, b.BeforeCexAssets[i].TotalEquity)
CheckValueInRange(api, b.BeforeCexAssets[i].TotalDebt)
CheckValueInRange(api, b.BeforeCexAssets[i].BasePrice)
cexAssets[i] = api.Add(api.Mul(b.BeforeCexAssets[i].TotalEquity, utils.Uint64MaxValueFrSquare),
api.Mul(b.BeforeCexAssets[i].TotalDebt, utils.Uint64MaxValueFr), b.BeforeCexAssets[i].BasePrice)
afterCexAssets[i] = b.BeforeCexAssets[i]
}
actualCexAssetsCommitment := poseidon.Poseidon(api, cexAssets...)
api.AssertIsEqual(b.BeforeCEXAssetsCommitment, actualCexAssetsCommitment)

api.AssertIsEqual(b.BeforeAccountTreeRoot, b.CreateUserOps[0].BeforeAccountTreeRoot)
api.AssertIsEqual(b.AfterAccountTreeRoot, b.CreateUserOps[len(b.CreateUserOps)-1].AfterAccountTreeRoot)

tempAfterCexAssets := make([]Variable, len(b.BeforeCexAssets))
for i := 0; i < len(b.CreateUserOps); i++ {
accountIndexHelper := AccountIdToMerkleHelper(api, b.CreateUserOps[i].AccountIndex)
VerifyMerkleProof(api, b.CreateUserOps[i].BeforeAccountTreeRoot, EmptyAccountLeafNodeHash, b.CreateUserOps[i].AccountProof[:], accountIndexHelper)
var totalUserEquity Variable = 0
var totalUserDebt Variable = 0
userAssets := b.CreateUserOps[i].Assets
for j := 0; j < len(userAssets); j++ {
CheckValueInRange(api, userAssets[j].Debt)
CheckValueInRange(api, userAssets[j].Equity)
totalUserEquity = api.Add(totalUserEquity, api.Mul(userAssets[j].Equity, b.BeforeCexAssets[j].BasePrice))
totalUserDebt = api.Add(totalUserDebt, api.Mul(userAssets[j].Debt, b.BeforeCexAssets[j].BasePrice))

afterCexAssets[j].TotalEquity = api.Add(afterCexAssets[j].TotalEquity, userAssets[j].Equity)
afterCexAssets[j].TotalDebt = api.Add(afterCexAssets[j].TotalDebt, userAssets[j].Debt)
}
// make sure user's total Equity is greater than or equal to user's total Debt
api.AssertIsLessOrEqual(totalUserDebt, totalUserEquity)

userAssetsCommitment := ComputeUserAssetsCommitment(api, userAssets)
accountHash := poseidon.Poseidon(api, b.CreateUserOps[i].AccountIdHash, totalUserEquity, totalUserDebt, userAssetsCommitment)
actualAccountTreeRoot := UpdateMerkleProof(api, accountHash, b.CreateUserOps[i].AccountProof[:], accountIndexHelper)
api.AssertIsEqual(actualAccountTreeRoot, b.CreateUserOps[i].AfterAccountTreeRoot)

}

for j := 0; j < len(tempAfterCexAssets); j++ {
CheckValueInRange(api, afterCexAssets[j].TotalEquity)
CheckValueInRange(api, afterCexAssets[j].TotalDebt)
tempAfterCexAssets[j] = api.Add(api.Mul(afterCexAssets[j].TotalEquity, utils.Uint64MaxValueFrSquare),
api.Mul(afterCexAssets[j].TotalDebt, utils.Uint64MaxValueFr), afterCexAssets[j].BasePrice)
}

// verify AfterCEXAssetsCommitment is computed correctly
actualAfterCEXAssetsCommitment := poseidon.Poseidon(api, tempAfterCexAssets...)
api.AssertIsEqual(actualAfterCEXAssetsCommitment, b.AfterCEXAssetsCommitment)

for i := 0; i < len(b.CreateUserOps)-1; i++ {
api.AssertIsEqual(b.CreateUserOps[i].AfterAccountTreeRoot, b.CreateUserOps[i+1].BeforeAccountTreeRoot)
}

return nil
}

func SetBatchCreateUserCircuitWitness(batchWitness *utils.BatchCreateUserWitness) (witness *BatchCreateUserCircuit, err error) {
witness = &BatchCreateUserCircuit{
BatchCommitment: batchWitness.BatchCommitment,
BeforeAccountTreeRoot: batchWitness.BeforeAccountTreeRoot,
AfterAccountTreeRoot: batchWitness.AfterAccountTreeRoot,
BeforeCEXAssetsCommitment: batchWitness.BeforeCEXAssetsCommitment,
AfterCEXAssetsCommitment: batchWitness.AfterCEXAssetsCommitment,
BeforeCexAssets: make([]CexAssetInfo, len(batchWitness.BeforeCexAssets)),
CreateUserOps: make([]CreateUserOperation, len(batchWitness.CreateUserOps)),
}

for i := 0; i < len(witness.BeforeCexAssets); i++ {
witness.BeforeCexAssets[i].TotalEquity = batchWitness.BeforeCexAssets[i].TotalEquity
witness.BeforeCexAssets[i].TotalDebt = batchWitness.BeforeCexAssets[i].TotalDebt
witness.BeforeCexAssets[i].BasePrice = batchWitness.BeforeCexAssets[i].BasePrice
}

for i := 0; i < len(witness.CreateUserOps); i++ {
witness.CreateUserOps[i].BeforeAccountTreeRoot = batchWitness.CreateUserOps[i].BeforeAccountTreeRoot
witness.CreateUserOps[i].AfterAccountTreeRoot = batchWitness.CreateUserOps[i].AfterAccountTreeRoot
witness.CreateUserOps[i].Assets = make([]UserAssetInfo, len(batchWitness.CreateUserOps[i].Assets))
for j := 0; j < len(batchWitness.CreateUserOps[i].Assets); j++ {
var userAsset UserAssetInfo
userAsset.Equity = batchWitness.CreateUserOps[i].Assets[j].Equity
userAsset.Debt = batchWitness.CreateUserOps[i].Assets[j].Debt
witness.CreateUserOps[i].Assets[j] = userAsset
}
witness.CreateUserOps[i].AccountIdHash = batchWitness.CreateUserOps[i].AccountIdHash
witness.CreateUserOps[i].AccountIndex = batchWitness.CreateUserOps[i].AccountIndex
for j := 0; j < len(witness.CreateUserOps[i].AccountProof); j++ {
witness.CreateUserOps[i].AccountProof[j] = batchWitness.CreateUserOps[i].AccountProof[j]
}
}
return witness, nil
}