diff --git a/constants.go b/constants.go new file mode 100644 index 0000000..d9887f0 --- /dev/null +++ b/constants.go @@ -0,0 +1,8 @@ +package circuit + +import "math/big" + +var ( + // is poseidon hash(empty account info) + EmptyAccountLeafNodeHash, _ = new(big.Int).SetString("2853aadbbd06deb5d6a1389d23a89ff47658aaf4a45b287ecfd62df192bd91a4", 16) +) diff --git a/hash_verification_test.go b/hash_verification_test.go new file mode 100644 index 0000000..8879898 --- /dev/null +++ b/hash_verification_test.go @@ -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) +} diff --git a/users.go b/users.go new file mode 100644 index 0000000..ce6e9f8 --- /dev/null +++ b/users.go @@ -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 +}