Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
}