From 572aa67d2ebdcc9beb377751eb147f42885622fa Mon Sep 17 00:00:00 2001 From: Pzzzzzack Date: Tue, 20 Aug 2024 18:56:07 +0800 Subject: [PATCH] init --- Makefile | 27 + README.md | 124 ++- main/main.go | 76 ++ merkle/proof.go | 68 ++ merkle/proof_types.go | 118 +++ merkle_groth16/circuit/constants.go | 8 + merkle_groth16/circuit/group_user_circuit.go | 150 ++++ merkle_groth16/circuit/types.go | 34 + merkle_groth16/circuit/utils.go | 61 ++ merkle_groth16/src/dbtool/config/config.go | 12 + merkle_groth16/src/dbtool/config/config.json | 10 + merkle_groth16/src/dbtool/main.go | 161 ++++ merkle_groth16/src/keygen/main.go | 38 + merkle_groth16/src/prover/config/config.go | 12 + merkle_groth16/src/prover/config/config.json | 9 + merkle_groth16/src/prover/main.go | 35 + .../src/prover/prover/proof_model.go | 134 +++ merkle_groth16/src/prover/prover/prover.go | 266 ++++++ merkle_groth16/src/sampledata/test_data.csv | 22 + merkle_groth16/src/userproof/config/config.go | 13 + .../src/userproof/config/config.json | 11 + .../src/userproof/config/config_wallet.json | 12 + merkle_groth16/src/userproof/main.go | 284 ++++++ .../src/userproof/model/userproof_model.go | 105 +++ merkle_groth16/src/utils/account_tree.go | 87 ++ merkle_groth16/src/utils/constants.go | 36 + merkle_groth16/src/utils/error_codes.go | 9 + merkle_groth16/src/utils/redis_lock.go | 51 ++ merkle_groth16/src/utils/secret_manager.go | 54 ++ merkle_groth16/src/utils/types.go | 96 ++ merkle_groth16/src/utils/utils.go | 439 +++++++++ merkle_groth16/src/verifier/config/config.go | 22 + .../src/verifier/config/config.json | 847 ++++++++++++++++++ merkle_groth16/src/verifier/config/proof0.csv | 21 + .../src/verifier/config/user_config.json | 44 + merkle_groth16/src/verifier/main.go | 225 +++++ merkle_groth16/src/witness/config/config.go | 13 + merkle_groth16/src/witness/config/config.json | 11 + merkle_groth16/src/witness/main.go | 47 + merkle_groth16/src/witness/witness/witness.go | 286 ++++++ .../src/witness/witness/witness_model.go | 157 ++++ 41 files changed, 4234 insertions(+), 1 deletion(-) create mode 100644 Makefile create mode 100644 main/main.go create mode 100644 merkle/proof.go create mode 100644 merkle/proof_types.go create mode 100644 merkle_groth16/circuit/constants.go create mode 100644 merkle_groth16/circuit/group_user_circuit.go create mode 100644 merkle_groth16/circuit/types.go create mode 100644 merkle_groth16/circuit/utils.go create mode 100644 merkle_groth16/src/dbtool/config/config.go create mode 100644 merkle_groth16/src/dbtool/config/config.json create mode 100644 merkle_groth16/src/dbtool/main.go create mode 100644 merkle_groth16/src/keygen/main.go create mode 100644 merkle_groth16/src/prover/config/config.go create mode 100644 merkle_groth16/src/prover/config/config.json create mode 100644 merkle_groth16/src/prover/main.go create mode 100644 merkle_groth16/src/prover/prover/proof_model.go create mode 100644 merkle_groth16/src/prover/prover/prover.go create mode 100644 merkle_groth16/src/sampledata/test_data.csv create mode 100644 merkle_groth16/src/userproof/config/config.go create mode 100644 merkle_groth16/src/userproof/config/config.json create mode 100644 merkle_groth16/src/userproof/config/config_wallet.json create mode 100644 merkle_groth16/src/userproof/main.go create mode 100644 merkle_groth16/src/userproof/model/userproof_model.go create mode 100644 merkle_groth16/src/utils/account_tree.go create mode 100644 merkle_groth16/src/utils/constants.go create mode 100644 merkle_groth16/src/utils/error_codes.go create mode 100644 merkle_groth16/src/utils/redis_lock.go create mode 100644 merkle_groth16/src/utils/secret_manager.go create mode 100644 merkle_groth16/src/utils/types.go create mode 100644 merkle_groth16/src/utils/utils.go create mode 100644 merkle_groth16/src/verifier/config/config.go create mode 100644 merkle_groth16/src/verifier/config/config.json create mode 100644 merkle_groth16/src/verifier/config/proof0.csv create mode 100644 merkle_groth16/src/verifier/config/user_config.json create mode 100644 merkle_groth16/src/verifier/main.go create mode 100644 merkle_groth16/src/witness/config/config.go create mode 100644 merkle_groth16/src/witness/config/config.json create mode 100644 merkle_groth16/src/witness/main.go create mode 100644 merkle_groth16/src/witness/witness/witness.go create mode 100644 merkle_groth16/src/witness/witness/witness_model.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6f57cc8 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +.PHONY: build-local + + + +merkle_verify: + go build -o build/MerkleVerify-macos-x64 main/main.go + +merkle_verify_linux: + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o build/MerkleVerify-linux-x64 main/main.go + +merkle_verify_windows: + CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o build/MerkleVerify-win-x64.exe main/main.go + +keygen: + go build -o build/MerkleVerify merkle_groth16/src/keygen/main.go + +prover: + go build -o build/MerkleVerify merkle_groth16/src/prover/main.go + +userproof: + go build -o build/MerkleVerify merkle_groth16/src/userproof/main.go + +verifier: + go build -o build/MerkleVerify merkle_groth16/src/verifier/main.go + +witness: + go build -o build/MerkleVerify merkle_groth16/src/witness/main.go \ No newline at end of file diff --git a/README.md b/README.md index d461a44..53c0cab 100644 --- a/README.md +++ b/README.md @@ -1 +1,123 @@ -# template-repo \ No newline at end of file +# Poloniex Merkle Verify Tool + +## Background + +Poloniex launches [Proof of Reserves (PoR)]() to improve the security and transparency of user's assets. These tools will allow +you to verify the validity of your assets in the merkle sum tree by verify merkle proof file, in order to confirm your assets in Poloniex. + +## Introduction + +### Building the source + +Download the [latest build](https://github.com/poloniex/tools-go-merkle-verify/releases) for your operating system and architecture. Also, you can build the source by yourself. + +Building this open source tool requires Go (version >= 1.16). + +Install dependencies +```shell + go mod vendor +``` + +build +```shell + make merkle_verify +``` + +run +```shell +./build/MerkleVerify --file ./merkle_sum_proof.json +``` + +# Poloniex Merkle Verify Tool V2 + +#### 1. Prover service +By using the r1cs circuit and pk and vk files generated by the keygen program, the required proof files are generated and stored in the database, allowing users to verify. The service is performed on the server side, and its built-in already includes verify, so after the prover runs, the verify will succeed as long as it runs according to the correct steps. + +Operation method: Make sure that the current working directory is under zkmerkleverify, that is, under the upper directory of src. The config file is merkle_groth16/src/prover/config/config.json + +MysqlDataSource is the dsn of you save your proofs +Redis is the source you save your treeroot +DbSuffix is the proof table suffix +ZkKeyName is corresponding to the batchsize + +(1) When only one host is used to enable the prover service, use the following command to use the prover service: +```shell + go run merkle_groth16/src/prover/main.go +``` +When prover is run correctly, it will output: +"there is no published status witness in db, so quit" +"prover run finish..." + + +(2) When multiple hosts are used to enable the prover service, all hosts are guaranteed to be in the above-mentioned working directory, and then use the same command: +```shell + go run merkle_groth16/src/prover/main.go +``` +When the command of all hosts ends, the following output will be + + "there is no published status witness in db, so quit" + "prover run finish..." + +Use the following command: +```shell + go run merkle_groth16/src/prover/main.go -rerun +``` +To check whether there is a prover program that has not been run, if there is, run it to completion, when the program runs normally, the final output shows: + + "there is no received status witness in db, so quit" + "prover rerun finish..." + +#### 2. Verifier service +Uses the verifier service to provide users with self-verification por verification services and userproof verification services. The customer uses the proof form and vk generated by the prover service to perform por verification and userproof verification. Among them, por verification is our zero-knowledge asset proof verification, and userproof is our zero-knowledge Merkle tree verification. Operation methods:Make sure that the current working directory is under zkmerkleverify, which is the upper directory of src. + +Before using this service, you need to use the following command to download the proof0.csv required by the user: +The config file of it is +merkle_groth16/load_proof_from_database/config/config.json + +MysqlDataSource is the dsn that you save your proofs +```shell + go run merkle_groth16/test/main/main.go +``` + +Use the following command to download the config.json required by the user: +The config file is merkle_groth16/src/dbtool/config/config.json + +MysqlDataSource is the dsn of you save your proofs +TreeDB is the source you save your treeroot +DbSuffix is the proof table suffix + +```shell + go run merkle_groth16/src/dbtool/main.go +``` + +Use the following command to verify por: +The config file is merkle_groth16/src/verifier/config/config.json + +```shell + go run merkle_groth16/src/verifier/main.go +``` + +If the verification is passed, it will output + + "All proofs verify passed!!!" + +otherwise output + + "proof verify failed:" +Use the following command to perform userproof verification +The config file is merkle_groth16/src/verifier/config/user_config.json + +```shell + go run merkle_groth16/src/verifier/main.go -user +``` + +If the verification is passed, the output will be: + + "verify pass!!!" + +Otherwise output: + + "verify failed..." + + + diff --git a/main/main.go b/main/main.go new file mode 100644 index 0000000..f7e5973 --- /dev/null +++ b/main/main.go @@ -0,0 +1,76 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + + "github.com/spf13/cobra" + + "merkleverifytool/merkle" +) + +func main() { + Execute() +} + +var proofJsonFile string +var failStr = "Merkle proof verify failed! " + +var rootCmd = &cobra.Command{ + Use: "MerkleValidator", + Short: "merkle tree path validation", + Long: ``, + Run: MerkleVerify, +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func init() { + cobra.OnInitialize(initConfig) + rootCmd.PersistentFlags().StringVar(&proofJsonFile, "file", "", "") +} + +func initConfig() {} + +func MerkleVerify(cmd *cobra.Command, args []string) { + log.Println("Merkle verify start") + if proofJsonFile == "" { + log.Println(failStr, "Invalid merkle proof file") + return + } + buf, err := ioutil.ReadFile(proofJsonFile) + if err != nil { + log.Println(failStr, "Invalid merkle proof file", err) + return + } + if len(buf) == 0 { + log.Println(failStr, "Empty merkle proof file") + return + } + pf := new(merkle.JsonProofPath) + if err := json.Unmarshal(buf, &pf); err != nil { + log.Println(fmt.Sprintf(failStr+"error:%s", err)) + return + } + + verified, err := merkle.VerifyProofFile(pf) + + if verified { + log.Println("Merkle proof verify passed.") + return + } else { + if err != nil { + log.Println(failStr + err.Error()) + return + } + log.Println(failStr) + } +} diff --git a/merkle/proof.go b/merkle/proof.go new file mode 100644 index 0000000..8a22833 --- /dev/null +++ b/merkle/proof.go @@ -0,0 +1,68 @@ +package merkle + +import ( + "errors" + "fmt" + "hash" +) + +func hash256(hash string, hashStrategy func() hash.Hash) ([]byte, error) { + h := hashStrategy() + if _, err := h.Write([]byte(hash)); err != nil { + return nil, err + } + + return h.Sum(nil), nil +} + +func VerifyProof(m *PathNodes) (bool, error) { + if len(m.Path) < 3 { + return false, errors.New("invalid path") + } + self := m.Path[len(m.Path)-1] + var lNode, rNode *PathNode + if self.R == 1 { + lNode, rNode = m.Path[1], self + } else { + lNode, rNode = self, m.Path[1] + } + + node, err := NewPath(lNode, rNode) + if err != nil { + return false, err + } + + for i := 2; i < len(m.Path)-1; i++ { + if m.Path[i].R == 1 { + lNode, rNode = node, m.Path[i] + } else { + lNode, rNode = m.Path[i], node + } + node, err = NewPath(lNode, rNode) + if err != nil { + return false, err + } + } + + root := m.Path[0] + + for i := 0; i < Length; i++ { + fmt.Printf("Rebuild root %s balance : %s, root %s balance in proof file : %s \n", CoinList[i], node.Ub.Coins[CoinList[i]], CoinList[i], root.Ub.Coins[CoinList[i]]) + } + + fmt.Printf("Rebuild root hash: %s, root hash in proof file: %s \n", node.Hash, root.Hash) + + if node.Hash != root.Hash || !node.Ub.Equal(root.Ub) { + return false, err + } + return true, nil +} + +func VerifyProofFile(pf *JsonProofPath) (bool, error) { + verified, err := VerifyProof(pf.JsonProofPathToPathNodes()) + if verified { + return true, nil + } + + return false, err +} diff --git a/merkle/proof_types.go b/merkle/proof_types.go new file mode 100644 index 0000000..d6984c4 --- /dev/null +++ b/merkle/proof_types.go @@ -0,0 +1,118 @@ +package merkle + +import ( + "crypto/sha1" + "crypto/sha256" + "encoding/hex" + "fmt" + "strings" + + "github.com/shopspring/decimal" +) + +var Length int +var CoinList []string + +type JsonProofPath struct { + Path []*JsonProofNode `json:"data"` +} + +type JsonProofNode struct { + Type string `json:"type"` + Hash string `json:"hash"` + UHash string `json:"uhash"` + Depth int `json:"depth"` + R int64 `json:"r"` + Balances string `json:"balances"` +} + +type UserBalance struct { + UHash string + Coins map[string]string +} + +type PathNode struct { + Hash string + R int64 + Ub UserBalance +} + +func (t UserBalance) Equal(other UserBalance) bool { + for i := 0; i < Length; i++ { + if t.Coins[CoinList[i]] != other.Coins[CoinList[i]] { + return false + } + } + return true +} + +func (t UserBalance) Add(other UserBalance) (UserBalance, error) { + h := sha1.New() + if _, err := h.Write([]byte(t.UHash + other.UHash)); err != nil { + return UserBalance{}, err + } + resultUB := UserBalance{ + UHash: hex.EncodeToString(h.Sum(nil)), + } + resultUB.Coins = make(map[string]string) + for i := 0; i < Length; i++ { + val := CoinList[i] + v1, err := decimal.NewFromString(t.Coins[val]) + if err != nil { + return UserBalance{}, err + } + v2, err := decimal.NewFromString(other.Coins[val]) + if err != nil { + return UserBalance{}, err + } + resultUB.Coins[val] = v1.Add(v2).RoundDown(8).String() + } + + return resultUB, nil +} + +func NewPath(lNode *PathNode, rNode *PathNode) (*PathNode, error) { + a, err := lNode.Ub.Add(rNode.Ub) + if err != nil { + return nil, err + } + var hashString = "" + for i := 0; i < Length; i++ { + hashString += a.Coins[CoinList[i]] + } + hash, err := hash256(fmt.Sprintf("%s%s%s", lNode.Hash, rNode.Hash, hashString), sha256.New) + if err != nil { + return nil, err + } + + return &PathNode{Hash: hex.EncodeToString(hash), Ub: a}, nil +} + +type PathNodes struct { + Path []*PathNode +} + +func (jNode *JsonProofNode) JsonProofNodeToPathNode() *PathNode { + ret := &PathNode{} + ret.Hash = jNode.Hash + ret.R = jNode.R + ret.Ub.UHash = jNode.UHash + ret.Ub.Coins = make(map[string]string) + bss := strings.Split(jNode.Balances, ",") + Length = len(bss) + for i := 0; i < Length; i++ { + CoinName := strings.Split(bss[i], ":")[0] + CoinList = append(CoinList, CoinName) + ret.Ub.Coins[CoinName] = strings.Split(bss[i], ":")[1] + } + return ret +} + +func (jPath *JsonProofPath) JsonProofPathToPathNodes() *PathNodes { + ret := new(PathNodes) + ret.Path = make([]*PathNode, 0) + for _, jp := range jPath.Path { + ret.Path = append(ret.Path, jp.JsonProofNodeToPathNode()) + } + return ret +} diff --git a/merkle_groth16/circuit/constants.go b/merkle_groth16/circuit/constants.go new file mode 100644 index 0000000..422e874 --- /dev/null +++ b/merkle_groth16/circuit/constants.go @@ -0,0 +1,8 @@ +package circuit + +import "math/big" + +var ( + // is poseidon hash(empty account info) + EmptyAccountLeafNodeHash, _ = new(big.Int).SetString("0cc1c37a517c3b8db148653c41b15dc7f0136dc284fb2818adf26669d897298c", 16) +) diff --git a/merkle_groth16/circuit/group_user_circuit.go b/merkle_groth16/circuit/group_user_circuit.go new file mode 100644 index 0000000..47ef3ca --- /dev/null +++ b/merkle_groth16/circuit/group_user_circuit.go @@ -0,0 +1,150 @@ +package circuit + +import ( + "merkleverifytool/merkle_groth16/src/utils" + + "github.com/consensys/gnark/std/hash/poseidon" +) + +type GroupUserCircuit struct { + GroupCommitment Variable `gnark:",public"` + PreSMTRoot Variable + NextSMTRoot Variable + PreCEXCommitment Variable + NextCEXCommitment Variable + PreCexAssets []CexAssetInfo + TotalCexAssets CexAssetsInfo + UserInstructions []UserInstruction +} + +func NewVerifyBatchCreateUserCircuit(commitment []byte) *GroupUserCircuit { + var v GroupUserCircuit + v.GroupCommitment = commitment + return &v +} + +func NewBatchCreateUserCircuit(assetCounts uint32, batchCounts uint32) *GroupUserCircuit { + var circuit GroupUserCircuit + circuit.GroupCommitment = 0 + circuit.PreSMTRoot = 0 + circuit.NextSMTRoot = 0 + circuit.PreCEXCommitment = 0 + circuit.NextCEXCommitment = 0 + circuit.TotalCexAssets.PreCEXTotalEquity = 0 + circuit.TotalCexAssets.NextCEXTotalEquity = 0 + circuit.TotalCexAssets.PreCEXTotalDebt = 0 + circuit.TotalCexAssets.NextCEXTotalDebt = 0 + circuit.PreCexAssets = make([]CexAssetInfo, assetCounts) + for i := uint32(0); i < assetCounts; i++ { + circuit.PreCexAssets[i].TotalBalance = 0 + } + circuit.UserInstructions = make([]UserInstruction, batchCounts) + for i := uint32(0); i < batchCounts; i++ { + circuit.UserInstructions[i] = UserInstruction{ + PreSMTRoot: 0, + NextSMTRoot: 0, + Assets: make([]Variable, 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.UserInstructions[i].Assets[j] = 0 + } + } + return &circuit +} + +func (b GroupUserCircuit) Define(api API) error { + // verify whether GroupCommitment is computed correctly + actualBatchCommitment := poseidon.Poseidon(api, b.PreSMTRoot, b.NextSMTRoot, b.PreCEXCommitment, b.NextCEXCommitment) + api.AssertIsEqual(b.GroupCommitment, actualBatchCommitment) + cexAssets := make([]Variable, len(b.PreCexAssets)) + afterCexAssets := make([]Variable, len(b.PreCexAssets)) + for i := 0; i < len(b.PreCexAssets); i++ { + cexAssets[i] = b.PreCexAssets[i].TotalBalance + afterCexAssets[i] = b.PreCexAssets[i].TotalBalance // + } + actualCexAssetsCommitment := ComputeUserAssetsCommitment(api, cexAssets) + api.AssertIsEqual(b.PreCEXCommitment, actualCexAssetsCommitment) + + api.AssertIsEqual(b.PreSMTRoot, b.UserInstructions[0].PreSMTRoot) + api.AssertIsEqual(b.NextSMTRoot, b.UserInstructions[len(b.UserInstructions)-1].NextSMTRoot) + + tempTotalCexAssets := CexAssetsInfo{ + PreCEXTotalEquity: 0, + PreCEXTotalDebt: 0, + NextCEXTotalEquity: b.TotalCexAssets.PreCEXTotalEquity, + NextCEXTotalDebt: b.TotalCexAssets.PreCEXTotalDebt, + } + CheckValueInRange(api, b.TotalCexAssets.PreCEXTotalDebt) + CheckValueInRange(api, b.TotalCexAssets.PreCEXTotalEquity) + + for i := 0; i < len(b.UserInstructions); i++ { + accountIndexHelper := AccountIdToMerkleHelper(api, b.UserInstructions[i].AccountIndex) + VerifyMerkleProof(api, b.UserInstructions[i].PreSMTRoot, EmptyAccountLeafNodeHash, b.UserInstructions[i].AccountProof[:], accountIndexHelper) + userAssets := b.UserInstructions[i].Assets //copy + + for j := 0; j < len(userAssets); j++ { + afterCexAssets[j] = api.Add(afterCexAssets[j], userAssets[j]) + } + tempTotalCexAssets.NextCEXTotalEquity = api.Add(tempTotalCexAssets.NextCEXTotalEquity, b.UserInstructions[i].TotalEquity) + tempTotalCexAssets.NextCEXTotalDebt = api.Add(tempTotalCexAssets.NextCEXTotalDebt, b.UserInstructions[i].TotalDebt) + api.AssertIsLessOrEqual(tempTotalCexAssets.NextCEXTotalDebt, tempTotalCexAssets.NextCEXTotalEquity) + CheckValueInRange(api, b.UserInstructions[i].TotalEquity) + CheckValueInRange(api, b.UserInstructions[i].TotalDebt) + userAssetsCommitment := ComputeUserAssetsCommitment(api, userAssets) + accountHash := poseidon.Poseidon(api, b.UserInstructions[i].AccountIdHash, b.UserInstructions[i].TotalEquity, b.UserInstructions[i].TotalDebt, userAssetsCommitment) + actualAccountTreeRoot := UpdateMerkleProof(api, accountHash, b.UserInstructions[i].AccountProof[:], accountIndexHelper) + api.AssertIsEqual(actualAccountTreeRoot, b.UserInstructions[i].NextSMTRoot) + } + CheckValueInRange(api, tempTotalCexAssets.NextCEXTotalDebt) + api.AssertIsEqual(tempTotalCexAssets.NextCEXTotalEquity, b.TotalCexAssets.NextCEXTotalEquity) + api.AssertIsEqual(tempTotalCexAssets.NextCEXTotalDebt, b.TotalCexAssets.NextCEXTotalDebt) + actualAfterCEXAssetsCommitment := ComputeUserAssetsCommitment(api, afterCexAssets) + api.AssertIsEqual(actualAfterCEXAssetsCommitment, b.NextCEXCommitment) + for i := 0; i < len(b.UserInstructions)-1; i++ { + api.AssertIsEqual(b.UserInstructions[i].NextSMTRoot, b.UserInstructions[i+1].PreSMTRoot) + } + + return nil +} + +func SetBatchCreateUserCircuitWitness(batchWitness *utils.BatchCreateUserWitness) (witness *GroupUserCircuit, err error) { + witness = &GroupUserCircuit{ + GroupCommitment: batchWitness.BatchCommitment, + PreSMTRoot: batchWitness.BeforeAccountTreeRoot, + NextSMTRoot: batchWitness.AfterAccountTreeRoot, + PreCEXCommitment: batchWitness.BeforeCEXAssetsCommitment, + NextCEXCommitment: batchWitness.AfterCEXAssetsCommitment, + PreCexAssets: make([]CexAssetInfo, len(batchWitness.BeforeCexAssets)), + UserInstructions: make([]UserInstruction, len(batchWitness.CreateUserOps)), + } + witness.TotalCexAssets.PreCEXTotalEquity = batchWitness.TotalCexAssets.BeforeCEXTotalEquity + witness.TotalCexAssets.PreCEXTotalDebt = batchWitness.TotalCexAssets.BeforeCEXTotalDebt + witness.TotalCexAssets.NextCEXTotalEquity = batchWitness.TotalCexAssets.AfterCEXTotalEquity + witness.TotalCexAssets.NextCEXTotalDebt = batchWitness.TotalCexAssets.AfterCEXTotalDebt + + for i := 0; i < len(witness.PreCexAssets); i++ { + witness.PreCexAssets[i].TotalBalance = batchWitness.BeforeCexAssets[i].TotalBalance //___ + + } + for i := 0; i < len(witness.UserInstructions); i++ { + witness.UserInstructions[i].PreSMTRoot = batchWitness.CreateUserOps[i].BeforeAccountTreeRoot + witness.UserInstructions[i].NextSMTRoot = batchWitness.CreateUserOps[i].AfterAccountTreeRoot + witness.UserInstructions[i].Assets = make([]Variable, len(batchWitness.CreateUserOps[i].Assets)) + witness.UserInstructions[i].TotalEquity = batchWitness.CreateUserOps[i].TotalEquity + witness.UserInstructions[i].TotalDebt = batchWitness.CreateUserOps[i].TotalDebt + + for j := 0; j < len(batchWitness.CreateUserOps[i].Assets); j++ { + var userAsset Variable + userAsset = batchWitness.CreateUserOps[i].Assets[j].Balance + witness.UserInstructions[i].Assets[j] = userAsset + } + witness.UserInstructions[i].AccountIdHash = batchWitness.CreateUserOps[i].AccountIdHash + witness.UserInstructions[i].AccountIndex = batchWitness.CreateUserOps[i].AccountIndex + for j := 0; j < len(witness.UserInstructions[i].AccountProof); j++ { + witness.UserInstructions[i].AccountProof[j] = batchWitness.CreateUserOps[i].AccountProof[j] + } + } + return witness, nil +} diff --git a/merkle_groth16/circuit/types.go b/merkle_groth16/circuit/types.go new file mode 100644 index 0000000..f67a2fd --- /dev/null +++ b/merkle_groth16/circuit/types.go @@ -0,0 +1,34 @@ +package circuit + +import ( + "merkleverifytool/merkle_groth16/src/utils" + + "github.com/consensys/gnark/frontend" +) + +type ( + Variable = frontend.Variable + API = frontend.API +) + +type CexAssetInfo struct { + TotalBalance Variable +} + +type CexAssetsInfo struct { + PreCEXTotalEquity Variable + PreCEXTotalDebt Variable + NextCEXTotalEquity Variable + NextCEXTotalDebt Variable +} + +type UserInstruction struct { + PreSMTRoot Variable + NextSMTRoot Variable + Assets []Variable + TotalEquity Variable + TotalDebt Variable + AccountIndex Variable + AccountIdHash Variable + AccountProof [utils.AccountTreeDepth]Variable +} diff --git a/merkle_groth16/circuit/utils.go b/merkle_groth16/circuit/utils.go new file mode 100644 index 0000000..7e118fc --- /dev/null +++ b/merkle_groth16/circuit/utils.go @@ -0,0 +1,61 @@ +package circuit + +import ( + "math/big" + "merkleverifytool/merkle_groth16/src/utils" + + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/hash/poseidon" +) + +func VerifyMerkleProof(api API, merkleRoot Variable, node Variable, proofSet, helper []Variable) { + for i := 0; i < len(proofSet); i++ { + api.AssertIsBoolean(helper[i]) + d1 := api.Select(helper[i], proofSet[i], node) + d2 := api.Select(helper[i], node, proofSet[i]) + node = poseidon.Poseidon(api, d1, d2) + } + // Compare our calculated Merkle root to the desired Merkle root. + api.AssertIsEqual(merkleRoot, node) +} + +func UpdateMerkleProof(api API, node Variable, proofSet, helper []Variable) (root Variable) { + for i := 0; i < len(proofSet); i++ { + api.AssertIsBoolean(helper[i]) + d1 := api.Select(helper[i], proofSet[i], node) + d2 := api.Select(helper[i], node, proofSet[i]) + node = poseidon.Poseidon(api, d1, d2) + } + root = node + return root +} + +func AccountIdToMerkleHelper(api API, accountId Variable) []Variable { + merkleHelpers := api.ToBinary(accountId, utils.AccountTreeDepth) + return merkleHelpers +} + +// check value is in [0, 2^64-1] range +func CheckValueInRange(api API, value Variable) { + api.ToBinary(value, 64) +} + +func Abs(api frontend.API, i frontend.Variable) frontend.Variable { + // _v + 2 ** 66 - bound + // _v should be less than 2 ** 66 + // bound should be less than 2 ** 66 + temp := api.Add(i, new(big.Int).Exp(new(big.Int).SetUint64(2), new(big.Int).SetInt64(int64(68-2)), nil)) + bitsTemp := api.ToBinary(temp, 68) + cmResult := bitsTemp[68-2] + return api.Select(cmResult, i, api.Neg(i)) + +} + +func ComputeUserAssetsCommitment(api API, assets []Variable) Variable { + assets_ := make([]frontend.Variable, len(assets)) + for i := 0; i < len(assets); i++ { + assets_[i] = Abs(api, assets[i]) + } + commitment := poseidon.Poseidon(api, assets_...) + return commitment +} diff --git a/merkle_groth16/src/dbtool/config/config.go b/merkle_groth16/src/dbtool/config/config.go new file mode 100644 index 0000000..d87b7c5 --- /dev/null +++ b/merkle_groth16/src/dbtool/config/config.go @@ -0,0 +1,12 @@ +package config + +type Config struct { + MysqlDataSource string + DbSuffix string + TreeDB struct { + Driver string + Option struct { + Addr string + } + } +} diff --git a/merkle_groth16/src/dbtool/config/config.json b/merkle_groth16/src/dbtool/config/config.json new file mode 100644 index 0000000..a10db82 --- /dev/null +++ b/merkle_groth16/src/dbtool/config/config.json @@ -0,0 +1,10 @@ +{ + "MysqlDataSource" : "qwer123:796474aa@tcp(127.0.0.1:3306)/test_1?parseTime=true", + "DbSuffix": "0", + "TreeDB": { + "Driver": "redis", + "Option": { + "Addr": "127.0.0.1:6379" + } + } +} diff --git a/merkle_groth16/src/dbtool/main.go b/merkle_groth16/src/dbtool/main.go new file mode 100644 index 0000000..2505848 --- /dev/null +++ b/merkle_groth16/src/dbtool/main.go @@ -0,0 +1,161 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "merkleverifytool/merkle_groth16/src/dbtool/config" + "merkleverifytool/merkle_groth16/src/prover/prover" + "merkleverifytool/merkle_groth16/src/userproof/model" + "merkleverifytool/merkle_groth16/src/utils" + "merkleverifytool/merkle_groth16/src/witness/witness" + "os" + "time" + + "github.com/go-redis/redis/v8" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +func main() { + dbtoolConfig := &config.Config{} + content, err := ioutil.ReadFile("src/dbtool/config/config.json") + if err != nil { + panic(err.Error()) + } + err = json.Unmarshal(content, dbtoolConfig) + if err != nil { + panic(err.Error()) + } + + onlyFlushKvrocks := flag.Bool("only_delete_kvrocks", false, "only delete kvrocks") + deleteAllData := flag.Bool("delete_all", false, "delete kvrocks and mysql data") + checkProverStatus := flag.Bool("check_prover_status", false, "check prover status") + remotePasswdConfig := flag.String("remote_password_config", "", "fetch password from aws secretsmanager") + queryCexAssetsConfig := flag.Bool("query_cex_assets", true, "query cex assets info") + + flag.Parse() + + if *remotePasswdConfig != "" { + s, err := utils.GetMysqlSource(dbtoolConfig.MysqlDataSource, *remotePasswdConfig) + if err != nil { + panic(err.Error()) + } + dbtoolConfig.MysqlDataSource = s + } + if *deleteAllData { + db, err := gorm.Open(mysql.Open(dbtoolConfig.MysqlDataSource)) + if err != nil { + panic(err.Error()) + } + witnessModel := witness.NewWitnessModel(db, dbtoolConfig.DbSuffix) + err = witnessModel.DropBatchWitnessTable() + if err != nil { + fmt.Println("drop witness table failed") + panic(err.Error()) + } + fmt.Println("drop witness table successfully") + + proofModel := prover.NewProofModel(db, dbtoolConfig.DbSuffix) + err = proofModel.DropProofTable() + if err != nil { + fmt.Println("drop proof table failed") + panic(err.Error()) + } + fmt.Println("drop proof table successfully") + + userProofModel := model.NewUserProofModel(db, dbtoolConfig.DbSuffix) + err = userProofModel.DropUserProofTable() + if err != nil { + fmt.Println("drop userproof table failed") + panic(err.Error()) + } + fmt.Println("drop userproof table successfully") + } + + if *deleteAllData || *onlyFlushKvrocks { + client := redis.NewClient(&redis.Options{ + Addr: dbtoolConfig.TreeDB.Option.Addr, + PoolSize: 500, + MaxRetries: 5, + MinRetryBackoff: 8 * time.Millisecond, + MaxRetryBackoff: 512 * time.Millisecond, + DialTimeout: 10 * time.Second, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + PoolTimeout: 15 * time.Second, + IdleTimeout: 5 * time.Minute, + }) + client.FlushAll(context.Background()) + fmt.Println("kvrocks data drop successfully") + } + + if *checkProverStatus { + newLogger := logger.New( + log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer + logger.Config{ + SlowThreshold: 60 * time.Second, // Slow SQL threshold + LogLevel: logger.Silent, // Log level + IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger + Colorful: false, // Disable color + }, + ) + db, err := gorm.Open(mysql.Open(dbtoolConfig.MysqlDataSource), &gorm.Config{ + Logger: newLogger, + }) + if err != nil { + panic(err.Error()) + } + witnessModel := witness.NewWitnessModel(db, dbtoolConfig.DbSuffix) + proofModel := prover.NewProofModel(db, dbtoolConfig.DbSuffix) + + witnessCounts, err := witnessModel.GetRowCounts() + if err != nil { + panic(err.Error()) + } + proofCounts, err := proofModel.GetRowCounts() + fmt.Printf("Total witness item %d, Published item %d, Pending item %d, Finished item %d\n", witnessCounts[0], witnessCounts[1], witnessCounts[2], witnessCounts[3]) + fmt.Println(witnessCounts[0] - proofCounts) + } + + if *queryCexAssetsConfig { + db, err := gorm.Open(mysql.Open(dbtoolConfig.MysqlDataSource)) + if err != nil { + panic(err.Error()) + } + witnessModel := witness.NewWitnessModel(db, dbtoolConfig.DbSuffix) + latestWitness, err := witnessModel.GetLatestBatchWitness() + if err != nil { + panic(err.Error()) + } + witness := utils.DecodeBatchWitness(latestWitness.WitnessData) + if witness == nil { + panic("decode invalid witness data") + } + cexAssetsInfo := utils.RecoverAfterCexAssets(witness) + var newAssetsInfo []utils.CexAssetInfo + for i := 0; i < len(cexAssetsInfo); i++ { + newAssetsInfo = append(newAssetsInfo, cexAssetsInfo[i]) + } + cexAssetsInfoBytes, _ := json.MarshalIndent(newAssetsInfo, "", " ") + fmt.Println(string(cexAssetsInfoBytes)) + var new_resutls []map[string]interface{} + err = json.Unmarshal(cexAssetsInfoBytes, &new_resutls) + content, _ = ioutil.ReadFile("src/verifier/config/config.json") + var results map[string]interface{} + err = json.Unmarshal(content, &results) + resulttable := results["ProofTable"] + resultZkname := results["ZkKeyName"] + results["ProofTable"] = resulttable + results["ZkKeyName"] = resultZkname + results["CexAssetsInfo"] = new_resutls + bytevalue, err := json.Marshal(results) + + err = ioutil.WriteFile("src/verifier/config/config.json", bytevalue, 0644) + + } +} diff --git a/merkle_groth16/src/keygen/main.go b/merkle_groth16/src/keygen/main.go new file mode 100644 index 0000000..a531ea8 --- /dev/null +++ b/merkle_groth16/src/keygen/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + "merkleverifytool/merkle_groth16/circuit" + "merkleverifytool/merkle_groth16/src/utils" + "runtime" + "strconv" + "time" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" +) + +func main() { + circuit := circuit.NewBatchCreateUserCircuit(utils.AssetCounts, utils.BatchCreateUserOpsCounts) + oR1cs, err := frontend.Compile(ecc.BN254, r1cs.NewBuilder, circuit) + if err != nil { + panic(err) + } + go func() { + for { + select { + case <-time.After(time.Second * 10): + runtime.GC() + } + } + }() + fmt.Println(oR1cs.GetNbVariables()) + zkKeyName := "zkpor" + strconv.FormatInt(utils.BatchCreateUserOpsCounts, 10) + fmt.Printf("Number of constraints: %d\n", oR1cs.GetNbConstraints()) + err = groth16.SetupLazyWithDump(oR1cs, zkKeyName) + if err != nil { + panic(err) + } +} diff --git a/merkle_groth16/src/prover/config/config.go b/merkle_groth16/src/prover/config/config.go new file mode 100644 index 0000000..4b6e1f2 --- /dev/null +++ b/merkle_groth16/src/prover/config/config.go @@ -0,0 +1,12 @@ +package config + +type Config struct { + MysqlDataSource string + DbSuffix string + Redis struct { + Host string + Type string + Password string + } + ZkKeyName string +} diff --git a/merkle_groth16/src/prover/config/config.json b/merkle_groth16/src/prover/config/config.json new file mode 100644 index 0000000..563bc8c --- /dev/null +++ b/merkle_groth16/src/prover/config/config.json @@ -0,0 +1,9 @@ +{ + "MysqlDataSource" : "admin:admin123@tcp(127.0.0.1:3306)/portest?parseTime=true", + "Redis": { + "Host": "127.0.0.1:6379", + "Type": "node" + }, + "DbSuffix": "0", + "ZkKeyName": "zkpor500" +} diff --git a/merkle_groth16/src/prover/main.go b/merkle_groth16/src/prover/main.go new file mode 100644 index 0000000..2c6a4d2 --- /dev/null +++ b/merkle_groth16/src/prover/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "encoding/json" + "flag" + "io/ioutil" + + "merkleverifytool/merkle_groth16/src/prover/config" + "merkleverifytool/merkle_groth16/src/prover/prover" + "merkleverifytool/merkle_groth16/src/utils" +) + +func main() { + proverConfig := &config.Config{} + content, err := ioutil.ReadFile("src/prover/config/config.json") + if err != nil { + panic(err.Error()) + } + err = json.Unmarshal(content, proverConfig) + if err != nil { + panic(err.Error()) + } + remotePasswdConfig := flag.String("remote_password_config", "", "fetch password from aws secretsmanager") + rerun := flag.Bool("rerun", false, "flag which indicates rerun proof generation") + flag.Parse() + if *remotePasswdConfig != "" { + s, err := utils.GetMysqlSource(proverConfig.MysqlDataSource, *remotePasswdConfig) + if err != nil { + panic(err.Error()) + } + proverConfig.MysqlDataSource = s + } + prover := prover.NewProver(proverConfig) + prover.Run(*rerun) +} diff --git a/merkle_groth16/src/prover/prover/proof_model.go b/merkle_groth16/src/prover/prover/proof_model.go new file mode 100644 index 0000000..5b76028 --- /dev/null +++ b/merkle_groth16/src/prover/prover/proof_model.go @@ -0,0 +1,134 @@ +package prover + +import ( + "merkleverifytool/merkle_groth16/src/utils" + + "gorm.io/gorm" +) + +const ( + TableNamePrefix = "proof" +) + +type ( + ProofModel interface { + CreateProofTable() error + DropProofTable() error + CreateProof(row *Proof) error + GetProofsBetween(start int64, end int64) (proofs []*Proof, err error) + GetLatestProof() (p *Proof, err error) + GetLatestConfirmedProof() (p *Proof, err error) + GetProofByBatchNumber(height int64) (p *Proof, err error) + GetProofNumber() (count int64) + GetRowCounts() (count int64, err error) + } + + defaultProofModel struct { + table string + DB *gorm.DB + } + + Proof struct { + gorm.Model + ProofInfo string + CexAssetListCommitments string + AccountTreeRoots string + BatchCommitment string + BatchNumber int64 `gorm:"index:idx_number,unique"` + } +) + +func (m *defaultProofModel) TableName() string { + return m.table +} + +func NewProofModel(db *gorm.DB, suffix string) ProofModel { + return &defaultProofModel{ + table: TableNamePrefix + suffix, + DB: db, + } +} + +func (m *defaultProofModel) CreateProofTable() error { + return m.DB.Table(m.table).AutoMigrate(Proof{}) //自动迁移 +} + +func (m *defaultProofModel) DropProofTable() error { + return m.DB.Migrator().DropTable(m.table) +} + +func (m *defaultProofModel) CreateProof(row *Proof) error { + dbTx := m.DB.Table(m.table).Create(row) + if dbTx.Error != nil { + return dbTx.Error + } + if dbTx.RowsAffected == 0 { + return utils.DbErrSqlOperation + } + return nil +} + +func (m *defaultProofModel) GetProofsBetween(start int64, end int64) (proofs []*Proof, err error) { + dbTx := m.DB.Debug().Table(m.table).Where("batch_number >= ? AND batch_number <= ?", + start, + end). + Order("batch_number"). + Find(&proofs) + + if dbTx.Error != nil { + return proofs, utils.DbErrSqlOperation + } else if dbTx.RowsAffected == 0 { + return nil, utils.DbErrNotFound + } + + return proofs, err +} + +func (m *defaultProofModel) GetLatestProof() (p *Proof, err error) { + var row *Proof + dbTx := m.DB.Table(m.table).Order("batch_number desc").Limit(1).Find(&row) + if dbTx.Error != nil { + return nil, utils.DbErrSqlOperation + } else if dbTx.RowsAffected == 0 { + return nil, utils.DbErrNotFound + } else { + return row, nil + } +} + +func (m *defaultProofModel) GetLatestConfirmedProof() (p *Proof, err error) { + var row *Proof + dbTx := m.DB.Table(m.table).Order("batch_number desc").Limit(1).Find(&row) + if dbTx.Error != nil { + return nil, utils.DbErrSqlOperation + } else if dbTx.RowsAffected == 0 { + return nil, utils.DbErrNotFound + } else { + return row, nil + } +} + +func (m *defaultProofModel) GetProofByBatchNumber(num int64) (p *Proof, err error) { + var row *Proof + dbTx := m.DB.Table(m.table).Where("batch_number = ?", num).Find(&row) + if dbTx.Error != nil { + return nil, utils.DbErrSqlOperation + } else if dbTx.RowsAffected == 0 { + return nil, utils.DbErrNotFound + } else { + return row, nil + } +} + +func (m *defaultProofModel) GetProofNumber() (count int64) { + m.DB.Raw("select count(*) from " + m.table).Count(&count) + return count +} + +func (m *defaultProofModel) GetRowCounts() (count int64, err error) { + dbTx := m.DB.Table(m.table).Count(&count) + if dbTx.Error != nil { + return 0, dbTx.Error + } + return count, nil +} diff --git a/merkle_groth16/src/prover/prover/prover.go b/merkle_groth16/src/prover/prover/prover.go new file mode 100644 index 0000000..9c7085b --- /dev/null +++ b/merkle_groth16/src/prover/prover/prover.go @@ -0,0 +1,266 @@ +package prover + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "os" + "runtime" + "time" + + "merkleverifytool/merkle_groth16/circuit" + "merkleverifytool/merkle_groth16/src/prover/config" + "merkleverifytool/merkle_groth16/src/utils" + "merkleverifytool/merkle_groth16/src/witness/witness" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/stores/redis" + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +func WithRedis(redisType string, redisPass string) redis.Option { + return func(p *redis.Redis) { + p.Type = redisType + p.Pass = redisPass + } +} + +type Prover struct { + witnessModel witness.WitnessModel + proofModel ProofModel //※※※※※※ + redisConn *redis.Redis + + VerifyingKeys groth16.VerifyingKey + ProvingKeys []groth16.ProvingKey + SessionName string + R1cs frontend.CompiledConstraintSystem +} + +func NewProver(config *config.Config) *Prover { + redisConn := redis.New(config.Redis.Host, WithRedis(config.Redis.Type, config.Redis.Password)) + db, err := gorm.Open(mysql.Open(config.MysqlDataSource)) + if err != nil { + panic(err.Error()) + } + prover := Prover{ + witnessModel: witness.NewWitnessModel(db, config.DbSuffix), + proofModel: NewProofModel(db, config.DbSuffix), + redisConn: redisConn, + SessionName: config.ZkKeyName, + } + + std.RegisterHints() //※※※※※ + fmt.Println("begin loading r1cs...") + loadR1csChan := make(chan bool) + go func() { //※※※※※ + for { + + select { + case <-loadR1csChan: + fmt.Println("load r1cs finished...... quit") + return + case <-time.After(time.Second * 10): + runtime.GC() + } + } + }() + prover.R1cs, err = groth16.LoadR1CSFromFile(config.ZkKeyName) + if err != nil { + panic("r1cs init error") + } + loadR1csChan <- true + runtime.GC() + fmt.Println("finish loading r1cs...") + // read proving and verifying keys + fmt.Println("begin loading proving key...") + prover.ProvingKeys, err = LoadProvingKey(config.ZkKeyName) + if err != nil { + panic("provingKey loading error") + } + fmt.Println("finish loading proving key...") + fmt.Println("begin loading verifying key...") + prover.VerifyingKeys, err = LoadVerifyingKey(config.ZkKeyName) + if err != nil { + panic("verifyingKey loading error") + } + fmt.Println("finish loading verifying key...") + return &prover +} + +func (p *Prover) Run(flag bool) { + p.proofModel.CreateProofTable() + batchWitnessFetch := func() (*witness.BatchWitness, error) { + lock := utils.GetRedisLockByKey(p.redisConn, utils.RedisLockKey) + err := utils.TryAcquireLock(lock) + if err != nil { + return nil, utils.GetRedisLockFailed + } + defer lock.Release() + + // Fetch unproved block witness. + blockWitness, err := p.witnessModel.GetLatestBatchWitnessByStatus(witness.StatusPublished) + if err != nil { + return nil, err + } + // Update status of block witness. + err = p.witnessModel.UpdateBatchWitnessStatus(blockWitness, witness.StatusReceived) + if err != nil { + return nil, err + } + return blockWitness, nil + } + + batchWitnessFetchForRerun := func() (*witness.BatchWitness, error) { + blockWitness, err := p.witnessModel.GetLatestBatchWitnessByStatus(witness.StatusReceived) + if err != nil { + return nil, err + } + return blockWitness, nil + } + for { + var batchWitness *witness.BatchWitness + var err error + if !flag { + batchWitness, err = batchWitnessFetch() + if errors.Is(err, utils.GetRedisLockFailed) { + fmt.Println("get redis lock failed") + continue + } + if errors.Is(err, utils.DbErrNotFound) { + fmt.Println("there is no published status witness in db, so quit") + fmt.Println("prover run finish...") + return + } + if err != nil { + fmt.Println("get batch witness failed: ", err.Error()) + return + } + } else { + batchWitness, err = batchWitnessFetchForRerun() + if errors.Is(err, utils.DbErrNotFound) { + fmt.Println("there is no received status witness in db, so quit") + fmt.Println("prover rerun finish...") + return + } + if err != nil { + fmt.Println("something wrong happened, err is ", err.Error()) + return + } + } + + witnessForCircuit := utils.DecodeBatchWitness(batchWitness.WitnessData) + cexAssetListCommitments := make([][]byte, 2) + cexAssetListCommitments[0] = witnessForCircuit.BeforeCEXAssetsCommitment + cexAssetListCommitments[1] = witnessForCircuit.AfterCEXAssetsCommitment + accountTreeRoots := make([][]byte, 2) + accountTreeRoots[0] = witnessForCircuit.BeforeAccountTreeRoot + accountTreeRoots[1] = witnessForCircuit.AfterAccountTreeRoot + cexAssetListCommitmentsSerial, err := json.Marshal(cexAssetListCommitments) + if err != nil { + fmt.Println("marshal cex asset list failed: ", err.Error()) + return + } + accountTreeRootsSerial, err := json.Marshal(accountTreeRoots) + if err != nil { + fmt.Println("marshal account tree root failed: ", err.Error()) + return + } + + proof, err := GenerateAndVerifyProof(p.R1cs, p.ProvingKeys, p.VerifyingKeys, witnessForCircuit, p.SessionName, batchWitness.Height) + if err != nil { + fmt.Println("generate and verify proof error:", err.Error()) + } + var buf bytes.Buffer + _, err = proof.WriteRawTo(&buf) + if err != nil { + fmt.Println("proof serialize failed") + return + } + proofBytes := buf.Bytes() + _, err = p.proofModel.GetProofByBatchNumber(batchWitness.Height) + if err == nil { + fmt.Printf("blockProof of height %d exists\n", batchWitness.Height) + err = p.witnessModel.UpdateBatchWitnessStatus(batchWitness, witness.StatusFinished) + if err != nil { + fmt.Println("update witness error:", err.Error()) + } + continue + } + + var row = &Proof{ + ProofInfo: base64.StdEncoding.EncodeToString(proofBytes), + BatchNumber: batchWitness.Height, + CexAssetListCommitments: string(cexAssetListCommitmentsSerial), + AccountTreeRoots: string(accountTreeRootsSerial), + BatchCommitment: base64.StdEncoding.EncodeToString(witnessForCircuit.BatchCommitment), + } + err = p.proofModel.CreateProof(row) + if err != nil { + fmt.Printf("create blockProof of height %d failed\n", batchWitness.Height) + return + } + err = p.witnessModel.UpdateBatchWitnessStatus(batchWitness, witness.StatusFinished) + if err != nil { + fmt.Println("update witness error:", err.Error()) + } + } +} + +func LoadProvingKey(filepath string) (pks []groth16.ProvingKey, err error) { + logx.Info("start reading proving key") + return groth16.ReadSegmentProveKey(filepath) +} + +func LoadVerifyingKey(filepath string) (verifyingKey groth16.VerifyingKey, err error) { + verifyingKey = groth16.NewVerifyingKey(ecc.BN254) + f, _ := os.Open(filepath + ".vk.save") + _, err = verifyingKey.ReadFrom(f) + if err != nil { + return verifyingKey, fmt.Errorf("read file error") + } + f.Close() + return verifyingKey, nil +} + +func GenerateAndVerifyProof(r1cs frontend.CompiledConstraintSystem, + provingKey []groth16.ProvingKey, + verifyingKey groth16.VerifyingKey, + batchWitness *utils.BatchCreateUserWitness, + zkKeyName string, + batchNumber int64, +) (proof groth16.Proof, err error) { + startTime := time.Now().UnixMilli() + fmt.Println("begin to generate proof for batch: ", batchNumber) + circuitWitness, _ := circuit.SetBatchCreateUserCircuitWitness(batchWitness) + verifyWitness := circuit.NewVerifyBatchCreateUserCircuit(batchWitness.BatchCommitment) + witness, err := frontend.NewWitness(circuitWitness, ecc.BN254) + if err != nil { + return proof, err + } + + vWitness, err := frontend.NewWitness(verifyWitness, ecc.BN254, frontend.PublicOnly()) + if err != nil { + return proof, err + } + proof, err = groth16.ProveRoll(r1cs, provingKey[0], provingKey[1], witness, zkKeyName) + if err != nil { + return proof, err + } + endTime := time.Now().UnixMilli() + fmt.Println("proof generation cost ", endTime-startTime, " ms") + + err = groth16.Verify(proof, verifyingKey, vWitness) + if err != nil { + return proof, err + } + endTime2 := time.Now().UnixMilli() + fmt.Println("proof verification cost ", endTime2-endTime, " ms") + return proof, nil +} diff --git a/merkle_groth16/src/sampledata/test_data.csv b/merkle_groth16/src/sampledata/test_data.csv new file mode 100644 index 0000000..0ffd168 --- /dev/null +++ b/merkle_groth16/src/sampledata/test_data.csv @@ -0,0 +1,22 @@ +index,f_uid,btc_balance,eth_balance,trx_balance,usdt_balance,ht_balance,1inch_balance,2luna_balance,aac_balance,aave_balance,ach_balance,act_balance,ada_balance,akro_balance,algo_balance,ant_balance,ape_balance,apt_balance,ar_balance,arb_balance,arix_balance,atom_balance,avax_balance,axs_balance,azero_balance,babydoge_balance,bat_balance,bbc_balance,bbf_balance,bcc_balance,berry_balance,bld_balance,blur_balance,bnb_balance,brise_balance,bsv_balance,btm_balance,btt_balance,caw_balance,chz_balance,ckb_balance,comp_balance,core_balance,coti_balance,cro_balance,cru_balance,crv_balance,cspr_balance,ctxc_balance,dai_balance,dash_balance,dbc_balance,deso_balance,dio_balance,doge_balance,dot_balance,dydx_balance,ela_balance,elf_balance,eos_balance,etc_balance,ethpow_balance,eur_balance,ever_balance,fanco_balance,fil_balance,floki_balance,flow_balance,flz_balance,ftm_balance,ftt_balance,fud_balance,galac_balance,gmt_balance,grt_balance,gt_balance,hbar_balance,hft_balance,hpt_balance,hsf_balance,husd_balance,icp_balance,imx_balance,inj_balance,iost_balance,iota_balance,jst_balance,kct_balance,krrx_balance,ksm_balance,ladys_balance,link_balance,love_balance,lovely_balance,lpt_balance,ltc_balance,luna_balance,mana_balance,mask_balance,matic_balance,mdx_balance,mina_balance,mx_balance,near_balance,neo_balance,nest_balance,nexo_balance,nft_balance,npt_balance,oland_balance,ont_balance,op_balance,ordi_balance,pando_balance,pci_balance,pepe_balance,pi_balance,poly_balance,qtum_balance,rdnt_balance,revo_balance,rndr_balance,rock_balance,rsr_balance,sand_balance,sc_balance,sdn_balance,sei_balance,shib_balance,sign_balance,snx_balance,sol_balance,strm_balance,sui_balance,sun_balance,sushi_balance,tcnh_balance,theta_balance,tomi_balance,ton_balance,tox_balance,tt_balance,tusd_balance,uni_balance,usdc_balance,usdd_balance,ust_balance,vet_balance,vidy_balance,waves_balance,wax_balance,waxl_balance,wbt_balance,wemix_balance,win_balance,wld_balance,woo_balance,wozx_balance,xch_balance,xcn_balance,xdc_balance,xec_balance,xen_balance,xfi_balance,xlm_balance,xmr_balance,xrp_balance,xtz_balance,xvg_balance,xym_balance,yfi_balance,yfii_balance,zbc_balance,zec_balance,zil_balance,user_total_quity,user_total_debt +0,2120648123,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,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,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,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,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,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,0,0,0,0,0,0,0.000162,0 +1,88898567,0.00010337,0.0000045,0.08057507,0.23690711,0.00170329,0,0,0.0192,0,0,0.00588,0.0089538,0,0,0,0,0,0,0,0,0,0,0,0,0,0.614,0,0,0.00000155,0,0,0,0,0,0.00000131,0.0083,794.38907,0,0,0,0,0,0,0,0,0,0,0,0,0.000077,0,0,0,0,0,0,0,0,0.13992363,0.000032,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0000836,0,0,0,0,0,0.0000914,0,0.00727462,0,0,0,0,0.00003342,0,0,0,0.00009101,0,0.536,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0000584,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,3.401164,0 +2,48299123,0.00000002,0.00000001,0,0.00000002,0,0,0,0,0,0,0,0.00000001,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,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.00000001,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,0,0.00000001,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00000001,0,0,0,0,0,0,0,0,0.000763,0 +3,99338456,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,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,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,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,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,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,0,0,0,0,0,0,0.000157,0 +4,2970689890,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,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,2.5,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.171358,0 +5,3762841032,0.0000008,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,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,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,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,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,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,0,0,0,0,0,0.021584,0 +6,56655979213,0,0,0,0.00018199,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,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,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,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,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,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,0,0,0.000182,0 +7,3618153223,0,0.00019632,0,1.46541431,0.00007265,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,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,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,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,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,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,0,1.79713,0 +8,15632013687,0,0.00000001,0,0.00000004,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00000001,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,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,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,0,0,0,0,0.00000001,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,0,0,0,0,0,0,0,0,0,0,0,0.000266,0 +9,28402110290,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1800,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.014508,0 +10,4250733454,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,0,0,0,0,0,0,0,940.6985479,0,0,0,0,0,0,0,0,0,85.70698144,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75.322946,0 +11,5363394087,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,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,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,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,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,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,0,0,0,0,0,0,0.000002,0 +12,3100066121,0,0,0.00175733,0.00000085,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,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,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,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,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,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,0,0,0.000238,0 +13,27887499489,0,0,0.0000001,2.7964,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,2000,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2.812599,0 +14,1428675521,0.00000001,0.00000001,0,0.00000001,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.00000001,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,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,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,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,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.00000001,0,0,0,0,0,0,0,0,0.000396,0 +15,14748055242,0.00000042,0,0,1.40317426,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,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,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,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,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,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,0,0,1.414533,0 +16,33360134098,0,0,0,0.00003,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,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,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,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,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,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,0,0,0.00003,0 +17,2014235632,0,0,0,0.01216992,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,0,0,0,0,0,0,0,0,0,0,0,336.53736,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0000208,0,0,0,0,0,0,0,0,55.64856455,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21.537509,0 +18,37858552433,0.00000042,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,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,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,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,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,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,0,0,0,0,0,0.011453,0 +19,2946364123,0.00000098,0.00007097,0,0.29648714,0.0000101,0,0.05880662,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3.05551989,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0.10223693,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,0,0,0,121861.5918,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1.649008,0 +20,2977710413,0,0,0,0.00000012,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,10120.7492,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.081576,0 \ No newline at end of file diff --git a/merkle_groth16/src/userproof/config/config.go b/merkle_groth16/src/userproof/config/config.go new file mode 100644 index 0000000..f53443f --- /dev/null +++ b/merkle_groth16/src/userproof/config/config.go @@ -0,0 +1,13 @@ +package config + +type Config struct { + MysqlDataSource string + UserDataFile string + DbSuffix string + TreeDB struct { + Driver string + Option struct { + Addr string + } + } +} diff --git a/merkle_groth16/src/userproof/config/config.json b/merkle_groth16/src/userproof/config/config.json new file mode 100644 index 0000000..8d8112c --- /dev/null +++ b/merkle_groth16/src/userproof/config/config.json @@ -0,0 +1,11 @@ +{ + "MysqlDataSource" : "qwer123:796474aa@tcp(127.0.0.1:3306)/test_1?parseTime=true", + "UserDataFile": "src/sampledata/", + "DbSuffix": "0", + "TreeDB": { + "Driver": "redis", + "Option": { + "Addr": "127.0.0.1:6379" + } + } +} diff --git a/merkle_groth16/src/userproof/config/config_wallet.json b/merkle_groth16/src/userproof/config/config_wallet.json new file mode 100644 index 0000000..9b4cf61 --- /dev/null +++ b/merkle_groth16/src/userproof/config/config_wallet.json @@ -0,0 +1,12 @@ +{ + + "MysqlDataSource" : "test:127.0.0.1:3306)/portest", + "Username" : "test", + "Password" : "123", + "Host" : "127.0.0.1", + "Port" : 3306, + "Dbname": "", + "Timeout": "", + "DbSuffix": "0", + "ZkKeyName": "/server/data/.keys/zkpor500" +} diff --git a/merkle_groth16/src/userproof/main.go b/merkle_groth16/src/userproof/main.go new file mode 100644 index 0000000..e3fd52d --- /dev/null +++ b/merkle_groth16/src/userproof/main.go @@ -0,0 +1,284 @@ +package main + +import ( + "encoding/hex" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "merkleverifytool/merkle_groth16/src/userproof/config" + "merkleverifytool/merkle_groth16/src/userproof/model" + "merkleverifytool/merkle_groth16/src/utils" + "os" + "time" + + bsmt "github.com/bnb-chain/zkbnb-smt" + "github.com/consensys/gnark-crypto/ecc/bn254/fr/poseidon" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +func HandleUserData(userProofConfig *config.Config) []utils.AccountInfo { + startTime := time.Now().UnixMilli() + accounts, _, err := utils.ParseUserDataSet(userProofConfig.UserDataFile) + if err != nil { + panic(err.Error()) + } + + endTime := time.Now().UnixMilli() + fmt.Println("handle user data cost ", endTime-startTime, " ms") + return accounts +} + +type AccountLeave struct { + hash []byte + index uint32 +} + +func ComputeAccountRootHash(userProofConfig *config.Config) { + accountTree, err := utils.NewAccountTree("memory", "") + fmt.Printf("account tree root %x\n", accountTree.Root()) + if err != nil { + panic(err.Error()) + } + accounts, _, err := utils.ParseUserDataSet(userProofConfig.UserDataFile) + if err != nil { + panic(err.Error()) + } + startTime := time.Now().UnixMilli() + totalOpsNumber := len(accounts) + fmt.Println("total ops number is ", totalOpsNumber) + chs := make(chan AccountLeave, 1000) + workers := 32 + results := make(chan bool, workers) + averageAccounts := (totalOpsNumber + workers - 1) / workers + actualWorkers := 0 + for i := 0; i < workers; i++ { + srcAccountIndex := i * averageAccounts + destAccountIndex := (i + 1) * averageAccounts + if destAccountIndex > totalOpsNumber { + destAccountIndex = totalOpsNumber + } + go CalculateAccountHash(accounts[srcAccountIndex:destAccountIndex], chs, results) + if destAccountIndex == totalOpsNumber { + actualWorkers = i + 1 + break + } + } + fmt.Println("actual workers is ", actualWorkers) + quit := make(chan bool, 1) + go CalculateAccountTreeRoot(chs, &accountTree, quit) + + for i := 0; i < actualWorkers; i++ { + <-results + } + close(chs) + <-quit + endTime := time.Now().UnixMilli() + fmt.Println("user account tree generation cost ", endTime-startTime, " ms") + fmt.Printf("account tree root %x\n", accountTree.Root()) + +} + +func CalculateAccountHash(accounts []utils.AccountInfo, chs chan<- AccountLeave, res chan<- bool) { + poseidonHasher := poseidon.NewPoseidon() + for i := 0; i < len(accounts); i++ { + chs <- AccountLeave{ + hash: utils.AccountInfoToHash(&accounts[i], &poseidonHasher), + index: accounts[i].AccountIndex, + } + } + res <- true +} + +func CalculateAccountTreeRoot(accountLeaves <-chan AccountLeave, accountTree *bsmt.SparseMerkleTree, quit chan<- bool) { + num := 0 + for accountLeaf := range accountLeaves { + (*accountTree).Set(uint64(accountLeaf.index), accountLeaf.hash) //似乎在set的时候就已经计算了root,将哈希值和索引对应设置到整个哈希树中。 + num++ + if num%100000 == 0 { + fmt.Println("for now, already set ", num, " accounts in tree") + } + } + quit <- true +} + +func main() { + memoryTreeFlag := flag.Bool("memory_tree", true, "construct memory merkle tree") + remotePasswdConfig := flag.String("remote_password_config", "", "fetch password from aws secretsmanager") + flag.Parse() + userProofConfig := &config.Config{} + content, err := ioutil.ReadFile("src/userproof/config/config.json") + if err != nil { + panic(err.Error()) + } + err = json.Unmarshal(content, userProofConfig) + if err != nil { + panic(err.Error()) + } + if *remotePasswdConfig != "" { + s, err := utils.GetMysqlSource(userProofConfig.MysqlDataSource, *remotePasswdConfig) + if err != nil { + panic(err.Error()) + } + userProofConfig.MysqlDataSource = s + } + if *memoryTreeFlag { + ComputeAccountRootHash(userProofConfig) + return + } + accountTree, err := utils.NewAccountTree(userProofConfig.TreeDB.Driver, userProofConfig.TreeDB.Option.Addr) + accounts := HandleUserData(userProofConfig) + fmt.Println("num", len(accounts)) + + userProofModel := OpenUserProofTable(userProofConfig) + latestAccountIndex, err := userProofModel.GetLatestAccountIndex() + if err != nil && err != utils.DbErrNotFound { + panic(err.Error()) + } + if err == nil { + latestAccountIndex += 1 + } + + accountTreeRoot := hex.EncodeToString(accountTree.Root()) + jobs := make(chan Job, 1000) + nums := make(chan int, 1) + results := make(chan *model.UserProof, 1000) + for i := 0; i < 1; i++ { + go worker(jobs, results, nums, accountTreeRoot) + } + quit := make(chan int, 1) + for i := 0; i < 1; i++ { + go WriteDB(results, userProofModel, quit, latestAccountIndex) + } + for i := int(latestAccountIndex); i < len(accounts); i++ { + leaf, err := accountTree.Get(uint64(i), nil) + if err != nil { + panic(err.Error()) + } + proof, err := accountTree.GetProof(uint64(accounts[i].AccountIndex)) + if err != nil { + panic(err.Error()) + } + jobs <- Job{ + account: &accounts[i], + proof: proof, + leaf: leaf, + } + } + close(jobs) + totalCounts := int(latestAccountIndex) + for i := 0; i < 1; i++ { + num := <-nums + totalCounts += num + fmt.Println("totalCounts", totalCounts) + } + if totalCounts != len(accounts) { + fmt.Println("totalCounts actual:expected", totalCounts, len(accounts)) + panic("mismatch num") + } + close(results) + for i := 0; i < 1; i++ { + <-quit + } + fmt.Println("userproof service run finished...") +} + +func WriteDB(results <-chan *model.UserProof, userProofModel model.UserProofModel, quit chan<- int, latestAccountIndex uint32) { + index := 0 + proofs := make([]model.UserProof, 100) + num := int(latestAccountIndex) + for proof := range results { + proofs[index] = *proof + index += 1 + if index%100 == 0 { + error := userProofModel.CreateUserProofs(proofs) + if error != nil { + panic(error.Error()) + } + num += 100 + if num%100000 == 0 { + fmt.Println("write ", num, "proof to db") + } + index = 0 + } + } + proofs = proofs[:index] + if index > 0 { + fmt.Println("write ", len(proofs), "proofs to db") + userProofModel.CreateUserProofs(proofs) + num += index + } + fmt.Println("total write ", num) + quit <- 0 +} + +type Job struct { + account *utils.AccountInfo + proof [][]byte + leaf []byte +} + +func worker(jobs <-chan Job, results chan<- *model.UserProof, nums chan<- int, root string) { + num := 0 + for job := range jobs { + userProof := ConvertAccount(job.account, job.leaf, job.proof, root) + results <- userProof + num += 1 + } + nums <- num +} + +func ConvertAccount(account *utils.AccountInfo, leafHash []byte, proof [][]byte, root string) *model.UserProof { + var userProof model.UserProof + var userConfig model.UserConfig + userProof.AccountIndex = account.AccountIndex + userProof.AccountId = hex.EncodeToString(account.AccountId) + userProof.AccountLeafHash = hex.EncodeToString(leafHash) + proofSerial, err := json.Marshal(proof) + userProof.Proof = string(proofSerial) + assets, err := json.Marshal(account.Assets) + if err != nil { + panic(err.Error()) + } + userProof.Assets = string(assets) + userProof.TotalDebt = account.TotalDebt.String() + userProof.TotalEquity = account.TotalEquity.String() + + userConfig.AccountIndex = account.AccountIndex + userConfig.AccountIdHash = hex.EncodeToString(account.AccountId) + userConfig.Proof = proof + userConfig.Root = root + userConfig.Assets = account.Assets + userConfig.TotalDebt = account.TotalDebt + userConfig.TotalEquity = account.TotalEquity + configSerial, err := json.Marshal(userConfig) + if err != nil { + panic(err.Error()) + } + userProof.Config = string(configSerial) + return &userProof +} + +func OpenUserProofTable(userConfig *config.Config) model.UserProofModel { + newLogger := logger.New( + log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer + logger.Config{ + SlowThreshold: 60 * time.Second, // Slow SQL threshold + LogLevel: logger.Silent, // Log level + IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger + Colorful: false, // Disable color + }, + ) + db, err := gorm.Open(mysql.Open(userConfig.MysqlDataSource), &gorm.Config{ + Logger: newLogger, + }) + if err != nil { + panic(err.Error()) + } + userProofTable := model.NewUserProofModel(db, userConfig.DbSuffix) + userProofTable.CreateUserProofTable() + return userProofTable +} diff --git a/merkle_groth16/src/userproof/model/userproof_model.go b/merkle_groth16/src/userproof/model/userproof_model.go new file mode 100644 index 0000000..dc3732f --- /dev/null +++ b/merkle_groth16/src/userproof/model/userproof_model.go @@ -0,0 +1,105 @@ +package model + +import ( + "math/big" + "merkleverifytool/merkle_groth16/src/utils" + + "gorm.io/gorm" +) + +const TableNamePreifx = "userproof" + +type ( + UserProofModel interface { + CreateUserProofTable() error + DropUserProofTable() error + CreateUserProofs(rows []UserProof) error + GetUserProofByIndex(id uint32) (*UserProof, error) + GetUserProofById(id string) (*UserProof, error) + GetLatestAccountIndex() (uint32, error) + } + + defaultUserProofModel struct { + table string + DB *gorm.DB + } + + UserProof struct { + AccountIndex uint32 `gorm:"index:idx_int,unique"` + AccountId string `gorm:"index:idx_str,unique"` + AccountLeafHash string + TotalEquity string + TotalDebt string + Assets string + Proof string + Config string + } + + UserConfig struct { + AccountIndex uint32 + AccountIdHash string + TotalEquity *big.Int + TotalDebt *big.Int + Assets []utils.AccountAsset + Root string + Proof [][]byte + } +) + +func (m *defaultUserProofModel) TableName() string { + return m.table +} + +func NewUserProofModel(db *gorm.DB, suffix string) UserProofModel { + return &defaultUserProofModel{ + table: TableNamePreifx + suffix, + DB: db, + } +} + +func (m *defaultUserProofModel) CreateUserProofTable() error { + return m.DB.Table(m.table).AutoMigrate(UserProof{}) +} + +func (m *defaultUserProofModel) DropUserProofTable() error { + return m.DB.Migrator().DropTable(m.table) +} + +func (m *defaultUserProofModel) CreateUserProofs(rows []UserProof) error { + dbTx := m.DB.Table(m.table).Create(rows) + if dbTx.Error != nil { + return dbTx.Error + } + return nil +} + +func (m *defaultUserProofModel) GetUserProofByIndex(id uint32) (userproof *UserProof, err error) { + dbTx := m.DB.Table(m.table).Where("account_index = ?", id).Find(userproof) + if dbTx.Error != nil { + return nil, dbTx.Error + } else if dbTx.RowsAffected == 0 { + return nil, utils.DbErrNotFound + } + return userproof, nil +} + +func (m *defaultUserProofModel) GetUserProofById(id string) (userproof *UserProof, err error) { + dbTx := m.DB.Table(m.table).Where("account_id = ?", id).Find(userproof) + if dbTx.Error != nil { + return nil, dbTx.Error + } else if dbTx.RowsAffected == 0 { + return nil, utils.DbErrNotFound + } + return userproof, nil +} + +func (m *defaultUserProofModel) GetLatestAccountIndex() (uint32, error) { + var row *UserProof + dbTx := m.DB.Table(m.table).Order("account_index desc").Limit(1).Find(&row) + if dbTx.Error != nil { + return 0, dbTx.Error + } else if dbTx.RowsAffected == 0 { + return 0, utils.DbErrNotFound + } + return row.AccountIndex, nil +} diff --git a/merkle_groth16/src/utils/account_tree.go b/merkle_groth16/src/utils/account_tree.go new file mode 100644 index 0000000..fd54da8 --- /dev/null +++ b/merkle_groth16/src/utils/account_tree.go @@ -0,0 +1,87 @@ +package utils + +import ( + "hash" + "time" + + bsmt "github.com/bnb-chain/zkbnb-smt" + "github.com/bnb-chain/zkbnb-smt/database" + "github.com/bnb-chain/zkbnb-smt/database/memory" + "github.com/bnb-chain/zkbnb-smt/database/redis" + "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "github.com/consensys/gnark-crypto/ecc/bn254/fr/poseidon" +) + +var ( + NilAccountHash []byte +) + +func init() { + zero := &fr.Element{0, 0, 0, 0} + poseidonHasher := poseidon.NewPoseidon() + emptyAssets := make([]AccountAsset, AssetCounts) + for i := 0; i < AssetCounts; i++ { + emptyAssets[i].Index = uint16(i) + emptyAssets[i].Balance = 0 // [new] + } + emptyAssetCommitment := ComputeUserAssetsCommitment(&poseidonHasher, emptyAssets) + tempHash := poseidon.Poseidon(zero, zero, zero, new(fr.Element).SetBytes(emptyAssetCommitment)).Bytes() + NilAccountHash = tempHash[:] +} + +func NewAccountTree(driver string, addr string) (accountTree bsmt.SparseMerkleTree, err error) { + + hasher := bsmt.NewHasherPool(func() hash.Hash { + return poseidon.NewPoseidon() + }) + + var db database.TreeDB + if driver == "memory" { + db = memory.NewMemoryDB() + } else if driver == "redis" { + redisOption := &redis.RedisConfig{} + redisOption.Addr = addr + redisOption.DialTimeout = 10 * time.Second + redisOption.ReadTimeout = 10 * time.Second + redisOption.WriteTimeout = 10 * time.Second + redisOption.PoolTimeout = 15 * time.Second + redisOption.IdleTimeout = 5 * time.Minute + redisOption.PoolSize = 500 + redisOption.MaxRetries = 5 + redisOption.MinRetryBackoff = 8 * time.Millisecond + redisOption.MaxRetryBackoff = 512 * time.Millisecond + db, err = redis.New(redisOption) + if err != nil { + return nil, err + } + } + + accountTree, err = bsmt.NewBNBSparseMerkleTree(hasher, db, AccountTreeDepth, NilAccountHash) + if err != nil { + return nil, err + } + return accountTree, nil +} + +func VerifyMerkleProof(root []byte, accountIndex uint32, proof [][]byte, node []byte) bool { + if len(proof) != AccountTreeDepth { + return false + } + hasher := poseidon.NewPoseidon() + for i := 0; i < AccountTreeDepth; i++ { + bit := accountIndex & (1 << i) + if bit == 0 { + hasher.Write(node) + hasher.Write(proof[i]) + } else { + hasher.Write(proof[i]) + hasher.Write(node) + } + node = hasher.Sum(nil) + hasher.Reset() + } + if string(node) != string(root) { + return false + } + return true +} diff --git a/merkle_groth16/src/utils/constants.go b/merkle_groth16/src/utils/constants.go new file mode 100644 index 0000000..4a29afa --- /dev/null +++ b/merkle_groth16/src/utils/constants.go @@ -0,0 +1,36 @@ +package utils + +import ( + "math/big" + + "github.com/consensys/gnark-crypto/ecc/bn254/fr" +) + +const ( + BatchCreateUserOpsCounts = 500 // batch size + AccountTreeDepth = 28 // SMT height + AssetCounts = 174 + RedisLockKey = "prover_mutex_key" +) + +var ( + ZeroBigInt = new(big.Int).SetInt64(0) + Uint64MaxValueBigInt, _ = new(big.Int).SetString("18446744073709551616", 10) // 2^64 + Uint64MaxValueBigIntSquare, _ = new(big.Int).SetString("340282366920938463463374607431768211456", 10) // 2^128 + Uint64MaxValueFr = new(fr.Element).SetBigInt(Uint64MaxValueBigInt) // 2^64 + Uint64MaxValueFrSquare = new(fr.Element).SetBigInt(Uint64MaxValueBigIntSquare) // 2^128 + AssetTypeForTwoDigits = map[string]bool{ + "ladys": true, + "brise": true, + "caw": true, + "nft": true, + "bonk": true, + "xen": true, + "lovely": true, + "btt": true, + "aidoge": true, + "babydoge": true, + "shib": true, + "pepe": true, + } +) diff --git a/merkle_groth16/src/utils/error_codes.go b/merkle_groth16/src/utils/error_codes.go new file mode 100644 index 0000000..bc9286f --- /dev/null +++ b/merkle_groth16/src/utils/error_codes.go @@ -0,0 +1,9 @@ +package utils + +import "errors" + +var ( + DbErrSqlOperation = errors.New("unknown sql operation error") + DbErrNotFound = errors.New("sql: no rows in result set") + GetRedisLockFailed = errors.New("get lock failed") +) diff --git a/merkle_groth16/src/utils/redis_lock.go b/merkle_groth16/src/utils/redis_lock.go new file mode 100644 index 0000000..b074c7c --- /dev/null +++ b/merkle_groth16/src/utils/redis_lock.go @@ -0,0 +1,51 @@ +package utils + +import ( + "errors" + "time" + + "github.com/zeromicro/go-zero/core/stores/redis" +) + +const ( + LockExpiryTime = 10 // seconds + RetryInterval = 500 * time.Millisecond + MaxRetryTimes = 3 +) + +func GetRedisLockByKey(conn *redis.Redis, keyLock string) (redisLock *redis.RedisLock) { + // get lock + redisLock = redis.NewRedisLock(conn, keyLock) + // set expiry time + redisLock.SetExpire(LockExpiryTime) + return redisLock +} + +func TryAcquireLock(redisLock *redis.RedisLock) (err error) { + // lock + ok, err := redisLock.Acquire() + if err != nil { + return err + } + // re-try for three times + if !ok { + ticker := time.NewTicker(RetryInterval) + defer ticker.Stop() + count := 0 + for { + if count > MaxRetryTimes { + return errors.New("the lock has been used, re-try later") + } + ok, err = redisLock.Acquire() + if err != nil { + return err + } + if ok { + break + } + count++ + <-ticker.C + } + } + return nil +} diff --git a/merkle_groth16/src/utils/secret_manager.go b/merkle_groth16/src/utils/secret_manager.go new file mode 100644 index 0000000..864a01e --- /dev/null +++ b/merkle_groth16/src/utils/secret_manager.go @@ -0,0 +1,54 @@ +package utils + +import ( + "context" + "encoding/json" + "errors" + "strings" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/secretsmanager" +) + +func GetSecretFromAws(secretId string) (string, error) { + region := "ap-northeast-1" + config, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(region)) + + if err != nil { + panic("Couldn't load config!") + } + conn := secretsmanager.NewFromConfig(config) + + result, err := conn.GetSecretValue(context.TODO(), &secretsmanager.GetSecretValueInput{ + SecretId: aws.String(secretId), + }) + + if err != nil { + return "", err + } + + return *result.SecretString, err +} + +// user name can't include ":" +func GetMysqlSource(source string, secretId string) (string, error) { + value, err := GetSecretFromAws(secretId) + if err != nil { + return "", err + } + + var result map[string]string + err = json.Unmarshal([]byte(value), &result) + if err != nil { + panic(err.Error()) + } + passwd := result["pg_password"] + aIndex := strings.Index(source, ":") + bIndex := strings.Index(source, "@tcp") + if aIndex == -1 || bIndex == -1 || bIndex <= aIndex { + return "", errors.New("the source format is wrong") + } + newSource := source[:aIndex+1] + passwd + source[bIndex:] + return newSource, nil +} diff --git a/merkle_groth16/src/utils/types.go b/merkle_groth16/src/utils/types.go new file mode 100644 index 0000000..052837f --- /dev/null +++ b/merkle_groth16/src/utils/types.go @@ -0,0 +1,96 @@ +package utils + +import "math/big" + +type CexAssetInfo struct { + TotalBalance int64 + Symbol string + Index uint32 +} + +type CexAssetInfo2 struct { + TotalEquity uint64 + TotalDebt uint64 + BasePrice uint64 + Symbol string + Index uint32 +} + +type AccountAsset struct { + Index uint16 + Balance int64 +} + +type AccountAsset2 struct { + Index uint16 + Equity uint64 + Debt uint64 +} + +type AccountInfo struct { + AccountIndex uint32 + AccountId []byte + TotalEquity *big.Int + TotalDebt *big.Int + Assets []AccountAsset +} + +type AccountInfo2 struct { + AccountIndex uint32 + AccountId []byte + TotalEquity *big.Int + TotalDebt *big.Int + Assets []AccountAsset +} + +type CreateUserOperation struct { + BeforeAccountTreeRoot []byte + AfterAccountTreeRoot []byte + Assets []AccountAsset + AccountIndex uint32 + AccountIdHash []byte + AccountProof [AccountTreeDepth][]byte + TotalEquity uint64 + TotalDebt uint64 +} + +// CreateUserOperation2 备份 +type CreateUserOperation2 struct { + BeforeAccountTreeRoot []byte + AfterAccountTreeRoot []byte + Assets []AccountAsset + AccountIndex uint32 + AccountIdHash []byte + AccountProof [AccountTreeDepth][]byte +} + +// CexAssetsTotal new define +type CexAssetsTotal struct { + BeforeCEXTotalEquity uint64 + AfterCEXTotalEquity uint64 + BeforeCEXTotalDebt uint64 + AfterCEXTotalDebt uint64 +} + +type BatchCreateUserWitness struct { + BatchCommitment []byte + BeforeAccountTreeRoot []byte + AfterAccountTreeRoot []byte + BeforeCEXAssetsCommitment []byte + AfterCEXAssetsCommitment []byte + + BeforeCexAssets []CexAssetInfo + CreateUserOps []CreateUserOperation + TotalCexAssets CexAssetsTotal +} + +type BatchCreateUserWitness2 struct { + BatchCommitment []byte + BeforeAccountTreeRoot []byte + AfterAccountTreeRoot []byte + BeforeCEXAssetsCommitment []byte + AfterCEXAssetsCommitment []byte + + BeforeCexAssets []CexAssetInfo + CreateUserOps []CreateUserOperation +} diff --git a/merkle_groth16/src/utils/utils.go b/merkle_groth16/src/utils/utils.go new file mode 100644 index 0000000..6f26971 --- /dev/null +++ b/merkle_groth16/src/utils/utils.go @@ -0,0 +1,439 @@ +package utils + +import ( + "bytes" + "crypto/sha256" + "encoding/base64" + "encoding/csv" + "encoding/gob" + "errors" + "fmt" + "hash" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "github.com/consensys/gnark-crypto/ecc/bn254/fr/poseidon" + "github.com/shopspring/decimal" + //"crypto/sha1" +) + +func ConvertAssetInfoToBytes(value any) []byte { + switch t := value.(type) { + case CexAssetInfo: + balanceBigInt := new(big.Int).SetInt64(t.TotalBalance) + return balanceBigInt.Bytes() + default: + panic("not supported type") + } +} + +func SelectAssetValue(expectAssetIndex int, currentAssetPosition int, assets []AccountAsset) (*big.Int, bool) { + if currentAssetPosition >= len(assets) { + return ZeroBigInt, false + } else { + return new(big.Int).SetInt64(assets[currentAssetPosition].Balance), true + } +} + +func ComputeUserAssetsCommitment(hasher *hash.Hash, assets []AccountAsset) []byte { + (*hasher).Reset() + userAssets := make([]AccountAsset, AssetCounts) + for p := 0; p < len(assets); p++ { + userAssets[assets[p].Index] = assets[p] + } + for i := 0; i < AssetCounts; i++ { + (*hasher).Write(new(big.Int).SetInt64(userAssets[i].Balance).Bytes()) + } + return (*hasher).Sum(nil) +} + +func ParseUserDataSet(dirname string) ([]AccountInfo, []CexAssetInfo, error) { + userFiles, err := ioutil.ReadDir(dirname) + if err != nil { + return nil, nil, err + } + var accountInfo []AccountInfo + var cexAssetInfo []CexAssetInfo + + workersNum := 8 + userFileNames := make([]string, 0) + + type UserParseRes struct { + accounts []AccountInfo + cex []CexAssetInfo + index int + } + results := make([]chan UserParseRes, workersNum) + for i := 0; i < workersNum; i++ { + results[i] = make(chan UserParseRes, 1) + } + for _, userFile := range userFiles { + if strings.Index(userFile.Name(), ".csv") == -1 { + continue + } + userFileNames = append(userFileNames, filepath.Join(dirname, userFile.Name())) + } + for i := 0; i < workersNum; i++ { + go func(workerId int) { + for j := workerId; j < len(userFileNames); j += workersNum { + if j >= len(userFileNames) { + break + } + tmpAccountInfo, tmpCexAssetInfo, err := ReadUserDataFromCsvFile(userFileNames[j]) + if err != nil { + panic(err.Error()) + } + results[workerId] <- UserParseRes{ + accounts: tmpAccountInfo, + cex: tmpCexAssetInfo, + } + } + }(i) + } + + gcQuitChan := make(chan bool) + go func() { + for { + select { + case <-time.After(time.Second * 10): + runtime.GC() + case <-gcQuitChan: + return + } + } + }() + + quit := make(chan bool) + go func() { + for i := 0; i < len(userFileNames); i++ { + res := <-results[i%workersNum] + if i != 0 { + for j := 0; j < len(res.accounts); j++ { + res.accounts[j].AccountIndex += uint32(len(accountInfo)) + } + } + accountInfo = append(accountInfo, res.accounts...) + if len(cexAssetInfo) == 0 { + cexAssetInfo = res.cex + } + } + quit <- true + }() + <-quit + gcQuitChan <- true + return accountInfo, cexAssetInfo, nil +} + +func ParseUserDataSet2(dirname string) ([]AccountInfo, []CexAssetInfo, error) { + userFiles, err := ioutil.ReadDir(dirname) + if err != nil { + return nil, nil, err + } + var accountInfo []AccountInfo + var cexAssetInfo []CexAssetInfo + + workersNum := 8 + userFileNames := make([]string, 0) + + type UserParseRes struct { + accounts []AccountInfo + cex []CexAssetInfo + index int + } + results := make([]chan UserParseRes, workersNum) + for i := 0; i < workersNum; i++ { + results[i] = make(chan UserParseRes, 1) + } + for _, userFile := range userFiles { + if strings.Index(userFile.Name(), ".csv") == -1 { + continue + } + userFileNames = append(userFileNames, filepath.Join(dirname, userFile.Name())) + } + for i := 0; i < workersNum; i++ { + go func(workerId int) { + for j := workerId; j < len(userFileNames); j += workersNum { + if j >= len(userFileNames) { + break + } + tmpAccountInfo, tmpCexAssetInfo, err := ReadUserDataFromCsvFile(userFileNames[j]) + if err != nil { + panic(err.Error()) + } + results[workerId] <- UserParseRes{ + accounts: tmpAccountInfo, + cex: tmpCexAssetInfo, + } + } + }(i) + } + + gcQuitChan := make(chan bool) + go func() { + for { + select { + case <-time.After(time.Second * 10): + runtime.GC() + case <-gcQuitChan: + return + } + } + }() + + quit := make(chan bool) + go func() { + for i := 0; i < len(userFileNames); i++ { + res := <-results[i%workersNum] + if i != 0 { + for j := 0; j < len(res.accounts); j++ { + res.accounts[j].AccountIndex += uint32(len(accountInfo)) + } + } + accountInfo = append(accountInfo, res.accounts...) + if len(cexAssetInfo) == 0 { + cexAssetInfo = res.cex + } + } + quit <- true + }() + <-quit + gcQuitChan <- true + return accountInfo, cexAssetInfo, nil +} + +func SafeAdd(a uint64, b uint64) (c uint64) { + c = a + b + if b < 0 && a < 0 { + if c > a || c > b { + panic("overflow for balance") + } + } else if b > 0 && a > 0 { + if c < a || c < b { + panic("overflow for balance") + } + } + return c +} + +func SafeAddInt64(a int64, b int64) (c int64) { + c = a + b + if b < 0 && a < 0 { + if c > a || c > b { + panic("overflow for balance") + } + } else if b > 0 && a > 0 { + if c < a || c < b { + panic("overflow for balance") + } + } + return c +} + +func HashBytesForUID(uid string) (accountId []byte) { + hash := sha256.New() + hash.Write([]byte(uid)) + accountId = hash.Sum(nil) + return accountId +} + +func ReadUserDataFromCsvFile(name string) ([]AccountInfo, []CexAssetInfo, error) { + f, err := os.Open(name) + if err != nil { + return nil, nil, err + } + defer f.Close() + csvReader := csv.NewReader(f) + data, err := csvReader.ReadAll() + accountIndex := 0 + cexAssetsInfo := make([]CexAssetInfo, AssetCounts) + accounts := make([]AccountInfo, len(data)-1) + symbols := data[0] + data = data[1:] + for i := 0; i < AssetCounts; i++ { + cexAssetsInfo[i].Symbol = strings.Split(symbols[i+2], "_")[0] + cexAssetsInfo[i].Index = uint32(i) + } + + invalidCounts := 0 + for i := 0; i < len(data); i++ { + var account AccountInfo + assets := make([]AccountAsset, 0, 8) + account.TotalEquity = new(big.Int).SetInt64(0) // [unknown] + account.TotalDebt = new(big.Int).SetInt64(0) // [unknown] + // first element of data[i] is ID. we use accountIndex instead + account.AccountIndex = uint32(accountIndex) + accountId := HashBytesForUID(data[i][1]) // uid to hashed id + if len(accountId) != 32 { + panic("accountId is invalid: " + data[i][1]) + } + account.AccountId = new(fr.Element).SetBytes(accountId).Marshal() + var tmpAsset AccountAsset + + multiplier := int64(100000000) + for j := 0; j < AssetCounts; j++ { + var balance int64 + var err error + if AssetTypeForTwoDigits[cexAssetsInfo[j].Symbol] { + balance, err = ConvertFloatStrToInt64(data[i][j+2], 100) + } else { + balance, err = ConvertFloatStrToInt64(data[i][j+2], multiplier) + } + + if err != nil { + //fmt.Println(cexAssetsInfo) + fmt.Println("the symbol is ", cexAssetsInfo[j].Symbol) + fmt.Println("account uid:", data[i][1], "balance data wrong:", err.Error()) + invalidCounts += 1 + continue + } + + if balance != 0 { + tmpAsset.Index = uint16(j) + tmpAsset.Balance = balance + assets = append(assets, tmpAsset) + } + } + + totalEquity, err := ConvertFloatStrToUint64(data[i][AssetCounts+2], multiplier) + if err != nil { + fmt.Println("account uid:", data[i][1], "TotalEquity data wrong:", err.Error()) + invalidCounts += 1 + continue + } + account.TotalEquity = new(big.Int).SetUint64(totalEquity) + totalDebt, err := ConvertFloatStrToUint64(data[i][AssetCounts+2+1], multiplier) + if err != nil { + fmt.Println("account uid:", data[i][1], "TotalDebt data wrong:", err.Error()) + invalidCounts += 1 + continue + } + account.TotalDebt = new(big.Int).SetUint64(totalDebt) + + account.Assets = assets + if account.TotalEquity.Cmp(account.TotalDebt) >= 0 { + accounts[accountIndex] = account + accountIndex += 1 + } else { + invalidCounts += 1 + fmt.Println("account", data[i][1], "data wrong: total debt is bigger than equity:", account.TotalDebt, account.TotalEquity) + } + + if i%100000 == 0 { + runtime.GC() + } + } + accounts = accounts[:accountIndex] + fmt.Println("The invalid accounts number is ", invalidCounts) + fmt.Println("The valid accounts number is ", len(accounts)) + return accounts, cexAssetsInfo, nil +} + +func ConvertFloatStrToInt64(f string, multiplier int64) (int64, error) { + if f == "0.0" { + return 0, nil + } + numFloat, err := decimal.NewFromString(f) + if err != nil { + return 0, err + } + numFloat = numFloat.Mul(decimal.NewFromInt(multiplier)) + numBigInt := numFloat.BigInt() + if !numBigInt.IsInt64() { + return 0, errors.New("overflow uint64") + } + num := numBigInt.Int64() + return num, nil +} + +func ConvertFloatStrToUint64(f string, multiplier int64) (uint64, error) { + if f == "0.0" { + return 0, nil + } + numFloat, err := decimal.NewFromString(f) + if err != nil { + return 0, err + } + numFloat = numFloat.Mul(decimal.NewFromInt(multiplier)) + numBigInt := numFloat.BigInt() + if !numBigInt.IsUint64() { + return 0, errors.New("overflow uint64") + } + num := numBigInt.Uint64() + return num, nil +} + +func DecodeBatchWitness(data string) *BatchCreateUserWitness { + var witnessForCircuit BatchCreateUserWitness + b, err := base64.StdEncoding.DecodeString(data) + if err != nil { + fmt.Println("deserialize batch witness failed: ", err.Error()) + return nil + } + unserializeBuf := bytes.NewBuffer(b) + dec := gob.NewDecoder(unserializeBuf) + err = dec.Decode(&witnessForCircuit) + if err != nil { + fmt.Println("unmarshal batch witness failed: ", err.Error()) + return nil + } + for i := 0; i < len(witnessForCircuit.CreateUserOps); i++ { + userAssets := make([]AccountAsset, AssetCounts) + storeUserAssets := witnessForCircuit.CreateUserOps[i].Assets + for p := 0; p < len(storeUserAssets); p++ { + userAssets[storeUserAssets[p].Index] = storeUserAssets[p] + } + witnessForCircuit.CreateUserOps[i].Assets = userAssets + } + return &witnessForCircuit +} + +func AccountInfoToHash(account *AccountInfo, hasher *hash.Hash) []byte { + assetCommitment := ComputeUserAssetsCommitment(hasher, account.Assets) + (*hasher).Reset() + // compute new account leaf node hash + accountHash := poseidon.PoseidonBytes(account.AccountId, account.TotalEquity.Bytes(), account.TotalDebt.Bytes(), assetCommitment) + return accountHash +} + +func RecoverAfterCexAssets(witness *BatchCreateUserWitness) []CexAssetInfo { + cexAssets := witness.BeforeCexAssets + for i := 0; i < len(witness.CreateUserOps); i++ { + for j := 0; j < len(witness.CreateUserOps[i].Assets); j++ { + asset := &witness.CreateUserOps[i].Assets[j] + cexAssets[asset.Index].TotalBalance = SafeAddInt64(cexAssets[asset.Index].TotalBalance, asset.Balance) + } + } + // sanity check + hasher := poseidon.NewPoseidon() + for i := 0; i < len(cexAssets); i++ { + commitment := ConvertAssetInfoToBytes(cexAssets[i]) + hasher.Write(commitment) + } + cexCommitment := hasher.Sum(nil) + if string(cexCommitment) != string(witness.AfterCEXAssetsCommitment) { + panic("after cex commitment verify failed") + } + return cexAssets +} + +func RecoverAfterTotalCexAssets(witness *BatchCreateUserWitness) CexAssetsTotal { + totalCexAssets := witness.TotalCexAssets + return totalCexAssets +} + +func ComputeCexAssetsCommitment(cexAssetsInfo []CexAssetInfo) []byte { + hasher := poseidon.NewPoseidon() + emptyCexAssets := make([]CexAssetInfo, AssetCounts-len(cexAssetsInfo)) + cexAssetsInfo = append(cexAssetsInfo, emptyCexAssets...) + for i := 0; i < len(cexAssetsInfo); i++ { + commitment := ConvertAssetInfoToBytes(cexAssetsInfo[i]) + hasher.Write(commitment) + } + return hasher.Sum(nil) +} diff --git a/merkle_groth16/src/verifier/config/config.go b/merkle_groth16/src/verifier/config/config.go new file mode 100644 index 0000000..a870a0d --- /dev/null +++ b/merkle_groth16/src/verifier/config/config.go @@ -0,0 +1,22 @@ +package config + +import ( + "math/big" + "merkleverifytool/merkle_groth16/src/utils" +) + +type Config struct { + ProofTable string + ZkKeyName string + CexAssetsInfo []utils.CexAssetInfo +} + +type UserConfig struct { + AccountIndex uint32 + AccountIdHash string + TotalEquity big.Int + TotalDebt big.Int + Root string + Assets []utils.AccountAsset + Proof []string +} diff --git a/merkle_groth16/src/verifier/config/config.json b/merkle_groth16/src/verifier/config/config.json new file mode 100644 index 0000000..9135e54 --- /dev/null +++ b/merkle_groth16/src/verifier/config/config.json @@ -0,0 +1,847 @@ +{ + "ProofTable": "src/verifier/config/proof0.csv", + "ZkKeyName": "zkpor500", + "CexAssetsInfo": [ + { + "TotalBalance": 163, + "Symbol": "btc", + "Index": 0 + }, + { + "TotalBalance": 0, + "Symbol": "eth", + "Index": 1 + }, + { + "TotalBalance": 0, + "Symbol": "trx", + "Index": 2 + }, + { + "TotalBalance": 123812, + "Symbol": "usdt", + "Index": 3 + }, + { + "TotalBalance": 0, + "Symbol": "ht", + "Index": 4 + }, + { + "TotalBalance": 0, + "Symbol": "1inch", + "Index": 5 + }, + { + "TotalBalance": 0, + "Symbol": "aave", + "Index": 6 + }, + { + "TotalBalance": 0, + "Symbol": "ach", + "Index": 7 + }, + { + "TotalBalance": 0, + "Symbol": "ada", + "Index": 8 + }, + { + "TotalBalance": 0, + "Symbol": "aidoge", + "Index": 9 + }, + { + "TotalBalance": 0, + "Symbol": "algo", + "Index": 10 + }, + { + "TotalBalance": 0, + "Symbol": "alice", + "Index": 11 + }, + { + "TotalBalance": 0, + "Symbol": "amp", + "Index": 12 + }, + { + "TotalBalance": 0, + "Symbol": "ankr", + "Index": 13 + }, + { + "TotalBalance": 0, + "Symbol": "ant", + "Index": 14 + }, + { + "TotalBalance": 0, + "Symbol": "ape", + "Index": 15 + }, + { + "TotalBalance": 0, + "Symbol": "api3", + "Index": 16 + }, + { + "TotalBalance": 0, + "Symbol": "apt", + "Index": 17 + }, + { + "TotalBalance": 0, + "Symbol": "ar", + "Index": 18 + }, + { + "TotalBalance": 0, + "Symbol": "arb", + "Index": 19 + }, + { + "TotalBalance": 0, + "Symbol": "arpa", + "Index": 20 + }, + { + "TotalBalance": 0, + "Symbol": "atom", + "Index": 21 + }, + { + "TotalBalance": 0, + "Symbol": "avax", + "Index": 22 + }, + { + "TotalBalance": 0, + "Symbol": "axs", + "Index": 23 + }, + { + "TotalBalance": 0, + "Symbol": "bal", + "Index": 24 + }, + { + "TotalBalance": 0, + "Symbol": "band", + "Index": 25 + }, + { + "TotalBalance": 0, + "Symbol": "bat", + "Index": 26 + }, + { + "TotalBalance": 0, + "Symbol": "bcc", + "Index": 27 + }, + { + "TotalBalance": 0, + "Symbol": "bico", + "Index": 28 + }, + { + "TotalBalance": 0, + "Symbol": "blur", + "Index": 29 + }, + { + "TotalBalance": 0, + "Symbol": "bnb", + "Index": 30 + }, + { + "TotalBalance": 0, + "Symbol": "bnt", + "Index": 31 + }, + { + "TotalBalance": 0, + "Symbol": "bob1", + "Index": 32 + }, + { + "TotalBalance": 0, + "Symbol": "bonk", + "Index": 33 + }, + { + "TotalBalance": 0, + "Symbol": "bsv", + "Index": 34 + }, + { + "TotalBalance": 0, + "Symbol": "btm", + "Index": 35 + }, + { + "TotalBalance": 0, + "Symbol": "btt", + "Index": 36 + }, + { + "TotalBalance": 0, + "Symbol": "c98", + "Index": 37 + }, + { + "TotalBalance": 0, + "Symbol": "celo", + "Index": 38 + }, + { + "TotalBalance": 0, + "Symbol": "cere", + "Index": 39 + }, + { + "TotalBalance": 0, + "Symbol": "chr", + "Index": 40 + }, + { + "TotalBalance": 0, + "Symbol": "chz", + "Index": 41 + }, + { + "TotalBalance": 0, + "Symbol": "comp", + "Index": 42 + }, + { + "TotalBalance": 0, + "Symbol": "core", + "Index": 43 + }, + { + "TotalBalance": 0, + "Symbol": "cro", + "Index": 44 + }, + { + "TotalBalance": 0, + "Symbol": "crv", + "Index": 45 + }, + { + "TotalBalance": 0, + "Symbol": "cvc", + "Index": 46 + }, + { + "TotalBalance": 0, + "Symbol": "dash", + "Index": 47 + }, + { + "TotalBalance": 0, + "Symbol": "doge", + "Index": 48 + }, + { + "TotalBalance": 0, + "Symbol": "dot", + "Index": 49 + }, + { + "TotalBalance": 0, + "Symbol": "dta", + "Index": 50 + }, + { + "TotalBalance": 0, + "Symbol": "dydx", + "Index": 51 + }, + { + "TotalBalance": 0, + "Symbol": "egld", + "Index": 52 + }, + { + "TotalBalance": 0, + "Symbol": "ela", + "Index": 53 + }, + { + "TotalBalance": 0, + "Symbol": "elf", + "Index": 54 + }, + { + "TotalBalance": 0, + "Symbol": "enj", + "Index": 55 + }, + { + "TotalBalance": 0, + "Symbol": "ens", + "Index": 56 + }, + { + "TotalBalance": 0, + "Symbol": "eos", + "Index": 57 + }, + { + "TotalBalance": 0, + "Symbol": "etc", + "Index": 58 + }, + { + "TotalBalance": 0, + "Symbol": "ethpow", + "Index": 59 + }, + { + "TotalBalance": 0, + "Symbol": "fil", + "Index": 60 + }, + { + "TotalBalance": 0, + "Symbol": "floki", + "Index": 61 + }, + { + "TotalBalance": 0, + "Symbol": "flow", + "Index": 62 + }, + { + "TotalBalance": 0, + "Symbol": "fsn", + "Index": 63 + }, + { + "TotalBalance": 0, + "Symbol": "ftm", + "Index": 64 + }, + { + "TotalBalance": 0, + "Symbol": "ftt", + "Index": 65 + }, + { + "TotalBalance": 0, + "Symbol": "gari", + "Index": 66 + }, + { + "TotalBalance": 0, + "Symbol": "glmr", + "Index": 67 + }, + { + "TotalBalance": 0, + "Symbol": "gmt", + "Index": 68 + }, + { + "TotalBalance": 0, + "Symbol": "grt", + "Index": 69 + }, + { + "TotalBalance": 0, + "Symbol": "gst", + "Index": 70 + }, + { + "TotalBalance": 0, + "Symbol": "gxs", + "Index": 71 + }, + { + "TotalBalance": 0, + "Symbol": "hbar", + "Index": 72 + }, + { + "TotalBalance": 0, + "Symbol": "hive", + "Index": 73 + }, + { + "TotalBalance": 0, + "Symbol": "hpt", + "Index": 74 + }, + { + "TotalBalance": 0, + "Symbol": "husd", + "Index": 75 + }, + { + "TotalBalance": 0, + "Symbol": "icp", + "Index": 76 + }, + { + "TotalBalance": 0, + "Symbol": "imx", + "Index": 77 + }, + { + "TotalBalance": 0, + "Symbol": "inj", + "Index": 78 + }, + { + "TotalBalance": 0, + "Symbol": "iost", + "Index": 79 + }, + { + "TotalBalance": 0, + "Symbol": "jasmy", + "Index": 80 + }, + { + "TotalBalance": 0, + "Symbol": "jst", + "Index": 81 + }, + { + "TotalBalance": 0, + "Symbol": "kava", + "Index": 82 + }, + { + "TotalBalance": 0, + "Symbol": "knc", + "Index": 83 + }, + { + "TotalBalance": 0, + "Symbol": "kripto", + "Index": 84 + }, + { + "TotalBalance": 0, + "Symbol": "ksm", + "Index": 85 + }, + { + "TotalBalance": 0, + "Symbol": "ladys", + "Index": 86 + }, + { + "TotalBalance": 0, + "Symbol": "lamb", + "Index": 87 + }, + { + "TotalBalance": 0, + "Symbol": "ldo", + "Index": 88 + }, + { + "TotalBalance": 0, + "Symbol": "lend", + "Index": 89 + }, + { + "TotalBalance": 0, + "Symbol": "lina", + "Index": 90 + }, + { + "TotalBalance": 0, + "Symbol": "link", + "Index": 91 + }, + { + "TotalBalance": 0, + "Symbol": "lith", + "Index": 92 + }, + { + "TotalBalance": 0, + "Symbol": "looks", + "Index": 93 + }, + { + "TotalBalance": 0, + "Symbol": "lrc", + "Index": 94 + }, + { + "TotalBalance": 0, + "Symbol": "ltc", + "Index": 95 + }, + { + "TotalBalance": 0, + "Symbol": "luna", + "Index": 96 + }, + { + "TotalBalance": 0, + "Symbol": "mana", + "Index": 97 + }, + { + "TotalBalance": 0, + "Symbol": "matic", + "Index": 98 + }, + { + "TotalBalance": 0, + "Symbol": "mina", + "Index": 99 + }, + { + "TotalBalance": 0, + "Symbol": "mkr", + "Index": 100 + }, + { + "TotalBalance": 0, + "Symbol": "mln", + "Index": 101 + }, + { + "TotalBalance": 0, + "Symbol": "mono", + "Index": 102 + }, + { + "TotalBalance": 0, + "Symbol": "near", + "Index": 103 + }, + { + "TotalBalance": 0, + "Symbol": "neo", + "Index": 104 + }, + { + "TotalBalance": 0, + "Symbol": "nest", + "Index": 105 + }, + { + "TotalBalance": 0, + "Symbol": "nft", + "Index": 106 + }, + { + "TotalBalance": 0, + "Symbol": "ngl", + "Index": 107 + }, + { + "TotalBalance": 0, + "Symbol": "nym", + "Index": 108 + }, + { + "TotalBalance": 0, + "Symbol": "oas", + "Index": 109 + }, + { + "TotalBalance": 0, + "Symbol": "ocean", + "Index": 110 + }, + { + "TotalBalance": 0, + "Symbol": "ogn", + "Index": 111 + }, + { + "TotalBalance": 0, + "Symbol": "omg", + "Index": 112 + }, + { + "TotalBalance": 0, + "Symbol": "one", + "Index": 113 + }, + { + "TotalBalance": 0, + "Symbol": "ont", + "Index": 114 + }, + { + "TotalBalance": 0, + "Symbol": "op", + "Index": 115 + }, + { + "TotalBalance": 0, + "Symbol": "ordi", + "Index": 116 + }, + { + "TotalBalance": 0, + "Symbol": "people", + "Index": 117 + }, + { + "TotalBalance": 0, + "Symbol": "pepe", + "Index": 118 + }, + { + "TotalBalance": 0, + "Symbol": "phb", + "Index": 119 + }, + { + "TotalBalance": 0, + "Symbol": "pyr", + "Index": 120 + }, + { + "TotalBalance": 0, + "Symbol": "qtum", + "Index": 121 + }, + { + "TotalBalance": 0, + "Symbol": "reef", + "Index": 122 + }, + { + "TotalBalance": 0, + "Symbol": "ren", + "Index": 123 + }, + { + "TotalBalance": 0, + "Symbol": "rlc", + "Index": 124 + }, + { + "TotalBalance": 0, + "Symbol": "rndr", + "Index": 125 + }, + { + "TotalBalance": 0, + "Symbol": "rsr", + "Index": 126 + }, + { + "TotalBalance": 0, + "Symbol": "ruff", + "Index": 127 + }, + { + "TotalBalance": 0, + "Symbol": "sand", + "Index": 128 + }, + { + "TotalBalance": 0, + "Symbol": "sdn", + "Index": 129 + }, + { + "TotalBalance": 0, + "Symbol": "shib", + "Index": 130 + }, + { + "TotalBalance": 0, + "Symbol": "snx", + "Index": 131 + }, + { + "TotalBalance": 0, + "Symbol": "sol", + "Index": 132 + }, + { + "TotalBalance": 0, + "Symbol": "storj", + "Index": 133 + }, + { + "TotalBalance": 0, + "Symbol": "sui", + "Index": 134 + }, + { + "TotalBalance": 0, + "Symbol": "sun", + "Index": 135 + }, + { + "TotalBalance": 0, + "Symbol": "sushi", + "Index": 136 + }, + { + "TotalBalance": 0, + "Symbol": "sxp", + "Index": 137 + }, + { + "TotalBalance": 0, + "Symbol": "theta", + "Index": 138 + }, + { + "TotalBalance": 0, + "Symbol": "tomi", + "Index": 139 + }, + { + "TotalBalance": 0, + "Symbol": "ton", + "Index": 140 + }, + { + "TotalBalance": 0, + "Symbol": "trb", + "Index": 141 + }, + { + "TotalBalance": 0, + "Symbol": "turbo", + "Index": 142 + }, + { + "TotalBalance": 0, + "Symbol": "uma", + "Index": 143 + }, + { + "TotalBalance": 0, + "Symbol": "uni", + "Index": 144 + }, + { + "TotalBalance": 0, + "Symbol": "usdc", + "Index": 145 + }, + { + "TotalBalance": 0, + "Symbol": "usdd", + "Index": 146 + }, + { + "TotalBalance": 0, + "Symbol": "vet", + "Index": 147 + }, + { + "TotalBalance": 0, + "Symbol": "wallet", + "Index": 148 + }, + { + "TotalBalance": 0, + "Symbol": "waves", + "Index": 149 + }, + { + "TotalBalance": 0, + "Symbol": "wbt", + "Index": 150 + }, + { + "TotalBalance": 0, + "Symbol": "wicc", + "Index": 151 + }, + { + "TotalBalance": 0, + "Symbol": "win", + "Index": 152 + }, + { + "TotalBalance": 0, + "Symbol": "wnxm", + "Index": 153 + }, + { + "TotalBalance": 0, + "Symbol": "wojak", + "Index": 154 + }, + { + "TotalBalance": 0, + "Symbol": "xch", + "Index": 155 + }, + { + "TotalBalance": 0, + "Symbol": "xcn", + "Index": 156 + }, + { + "TotalBalance": 0, + "Symbol": "xem", + "Index": 157 + }, + { + "TotalBalance": 0, + "Symbol": "xlm", + "Index": 158 + }, + { + "TotalBalance": 0, + "Symbol": "xmr", + "Index": 159 + }, + { + "TotalBalance": 0, + "Symbol": "xrp", + "Index": 160 + }, + { + "TotalBalance": 0, + "Symbol": "xtz", + "Index": 161 + }, + { + "TotalBalance": 0, + "Symbol": "yfi", + "Index": 162 + }, + { + "TotalBalance": 0, + "Symbol": "yfii", + "Index": 163 + }, + { + "TotalBalance": 0, + "Symbol": "zec", + "Index": 164 + }, + { + "TotalBalance": 0, + "Symbol": "zen", + "Index": 165 + }, + { + "TotalBalance": 0, + "Symbol": "zil", + "Index": 166 + }, + { + "TotalBalance": 0, + "Symbol": "zrx", + "Index": 167 + } + ] + +} \ No newline at end of file diff --git a/merkle_groth16/src/verifier/config/proof0.csv b/merkle_groth16/src/verifier/config/proof0.csv new file mode 100644 index 0000000..0b5b0bd --- /dev/null +++ b/merkle_groth16/src/verifier/config/proof0.csv @@ -0,0 +1,21 @@ +"id","created_at","updated_at","deleted_at","proof_info","cex_asset_list_commitments","account_tree_roots","batch_commitment","batch_number" +1,"2023-08-22 03:00:47.194","2023-08-22 03:00:47.194","","MFKmZOZhjfsJcidmALgTl3PMPIAf67CeLNQCakdc/4EXdmGnhK/i6ORYZs3q+fMixb0Z2BwsasZ44WBSug54wwSCUrqaw8gAT6VyAElYvjQe/LsNqiWkD5PRqUDQhNFAFQdrmotbRoV4hefM4kXzZmYhyN781/hApqEr3fBso3MNCSOCmJqVEks3si9lhzL7kqgu1XXisH0wJ449XOtjMhgCwK26KK5y03X0FQU83MhC/0JTb956d5vF6rkPaKJxJKZ4g3bLX4r1+0MHEspOA/og6BGIRIgx2pm9H6ucYO4cG03OmfWvkZXozRkVswE6W/JIFdJcC5Z3K4SvwyjQrA==","[""EwdxN1XimXB/p/2ZeGH2e6tbA8hxeuAdjqF/NjTmkGE="",""KIuvBihGOi+wRbA7KqD7DAcM60ovwNlLLhB08JvjXZE=""]","[""CAMKVha90kipg90fmU9Vc2HHQsxK2qJhgnX7w1qb3CQ="",""HISODeM3lErvxcsnnfq9+o8IrG/OgKpOEGW6Jfb7/W4=""]","K2tGKDSsCPCcaoj8g9KJzD0XQ+QGElHmPu5S8shseoA=",0 +2,"2023-08-22 03:00:50.435","2023-08-22 03:00:50.435","","HVPmxtJuZhSl9gAOxQxrhyrA8jw/JzMklISx+IUepiIcaz7mo3cbGgdLXxaD824AiyZ48VO0p+FcB8ybeItzFABLKfFn9x+dTZdVYQzpCRfRbYY36PQuoiUADOL5cWdxDpKyKZ5Vtxykf4muKmFRmsG+vqFn7DyYt3YEHzASlqQvJCRWFjY33r2WB++UU8kGlvwrVH72rzd4V+4LdYeN7SFCELJ59Ury+jYHNKyOQDXoq1Ok0mqkA0L8GrBiAfXqKaPl+DXKIn9fVrTIxdxvMTSzU+BP62WyWAE6icHn/xIMmSSkTXNDg73Vkwx111amN2ZBNPmfJZITB5U1w68XPQ==","[""KIuvBihGOi+wRbA7KqD7DAcM60ovwNlLLhB08JvjXZE="",""FWGakQjl3ZdYNrKXHuU7AGRf114+L8WPBj3L5kcy4e0=""]","[""HISODeM3lErvxcsnnfq9+o8IrG/OgKpOEGW6Jfb7/W4="",""CNWhtctaB6yO5hPziwcf1M2Z7g+6zvhyYVF8sLTrJog=""]","JfEZEuuhGjY/MlivCH1OLM5LP4O5aGo8XrznVnS+PJg=",1 +3,"2023-08-22 03:00:53.638","2023-08-22 03:00:53.638","","A2j6VYCgquu7VJ2Rkw2na2VGE8JaYjTm0MCig473iOsb9J1vet81LKsNd8N/mGxQBeOq732ovKZQZHAxpRSGLgoqOliTJxMruEnOFfPbU3DSvUbJR80emkV06fcGseAuFd4ax76ou1QZIcWubr6HYuYKNbrb3CzWt8EpsnFKQuwHH05ohIDpGvzGoRa/49ggrDZzpEbKVwDRMu+2TqqrIw4m0ybB9kZe/v46wXjSKggo52OW1DbxEpkLBSt257BvFLqaxR4N6UCfKNd7cr2mb55H4/3leamFoKXXJd1EiUoJPU76V+R+3gtOljxnH1kkl5hsTzhATrIlDNgTwnQoMQ==","[""FWGakQjl3ZdYNrKXHuU7AGRf114+L8WPBj3L5kcy4e0="",""ChqpkOCaRRdIqKVZ8b1Fmh0clwsY1FuaGcGgqL0sbLU=""]","[""CNWhtctaB6yO5hPziwcf1M2Z7g+6zvhyYVF8sLTrJog="",""EjNiNYmxp40ki6ZQJft57SOmY/ohOXVAAiEVsKEvYiU=""]","HzOAnc5pDCTaQGbyk0BoP2Ih7F3jf3XHwHi2Ed7wmik=",2 +4,"2023-08-22 03:00:56.857","2023-08-22 03:00:56.857","","F9U583RlZnRWlEprlgF53vgbJ/Wth5YlhDIV3T29cVYriH5GqpNjSVoJxG+FrqnTzuRBls/SU0ijri4jlgYSciqxg4I/HMHNztUAAw4aMilETbx0Y27Q4TQ7ZKbkemOkAprW9YhTTbwhtdY84ugNFfSr3BRf1IMEI2lgGnMubgYkVHHsdUl2N4kbIXuYGba0fEcCGqlKo+d168xATnHP/goxFBceuUYe/1EMOuIvNn+xA22nwl+9LXoxISjwLGlSJXngbzjiSGt3K4jV3PQ2QxwApum8t+C4VVGMLMDaTMMaF0p+XGnjY+DNabYoBAlJQ8xO8zOGjI+4TsjHvoEDNQ==","[""ChqpkOCaRRdIqKVZ8b1Fmh0clwsY1FuaGcGgqL0sbLU="",""GC2IwPywPGNSQcdhFx8LzdsNRoud41+4vAuezShjn5Q=""]","[""EjNiNYmxp40ki6ZQJft57SOmY/ohOXVAAiEVsKEvYiU="",""Fgji/JbWf51Cnr6Kg+uQbHTS8ES5Cqh3R6I3CsPlP+c=""]","CeqcAeiLUvIS7Ugt+VMPZ6/1Bg9HQCpLmbRk9QDBqko=",3 +5,"2023-08-22 03:01:00.206","2023-08-22 03:01:00.206","","JRIhMZEegRsg3p91QxeckNi8tJ8//A+tjz7EpRrMOHku/+X6L+eO1K+tZo189Il5a6sgXPmxVE41tBS8GOt0Dyvs1IptQNKx8U69SoMVJdEPn1mUgs2lzY/jhVAgnT+iDPEIVkJFqZl8rz3uZaMzzkey+14MSEMZKzgRBd/t9fMuxmN31nsWui+Gp+TbUy1FKx1966ePsnZNbcDMDCTywSC0W7KGAHXsjCp/2uS1H06abQAdXTfZ+rEfTQCZSTBbBltV3p7UTlQErV42YBTvedJpeZd0eZz30JxSKILIINcZ23gtS9qHtJan7YvDVGJGcHSdQDYXgUskvDLhqqEYqw==","[""GC2IwPywPGNSQcdhFx8LzdsNRoud41+4vAuezShjn5Q="",""JvDWw5FjbvZLH+EnSMljp+Xo1Pc10WgPzpR5C7UOGHY=""]","[""Fgji/JbWf51Cnr6Kg+uQbHTS8ES5Cqh3R6I3CsPlP+c="",""CI5pI9gnPTrVgVpIq0A6JSNBNkGeL+cLn8hkiGFIuWs=""]","FngSlFdHcNiff4SdNNUGvhDaFBHwJO6Jp4yZ5DePhyA=",4 +6,"2023-08-22 03:01:03.455","2023-08-22 03:01:03.455","","FkXggcxz82/FqBUe76hTzt4LTg7uHuWpvi81RmiGK2oRKWf4BU3ytvxj10SR0wmNA6BBxKku8N9KCpDNKCCoEhWuYb8Bb4ww5hm/3OOxYs3V/m+H2UJsiZz8pr8pY1hXD883p5/960SVDdCHAu8V+x7l3MivMumDxglS0H1E4N8r/hMyHHLtYMWgyt+W0I5tLcljHGqTkz48PfWzwxh8JAHShOT5RyRaQt7HJpRmB8DWtSbqlBA5OaZ3NLQJnYPPBh0yhNhHfmygp+kVHpQ8AVBXVZeSIjmPPuD7I2iOETgaQ1pfmdxxXrfMLM81Sq6O++Y+Er3Hh5say74bd6BBow==","[""JvDWw5FjbvZLH+EnSMljp+Xo1Pc10WgPzpR5C7UOGHY="",""EPFMsLVR7r+1Uh2SS8t578JvyHg5yujcPKbJmwcgvow=""]","[""CI5pI9gnPTrVgVpIq0A6JSNBNkGeL+cLn8hkiGFIuWs="",""AOapJ2yHtCAq7feYfn4dZBl2DrEkIGkgMyl/jgjQ8J8=""]","LrBcInxwESpc1yJwsns/YF+BjZfw43QIDeAfX3TMric=",5 +7,"2023-08-22 03:01:06.820","2023-08-22 03:01:06.820","","JQCCOuwifwA9Qyg68Zk0EOfjNaFD4yv0/0hiGMRNR74jgEvY7fcDKdSRqQ23gB811exaMuoj9h5DxYtfvcpJ+xKsNrjRr0pyX2E1ssAe3NlmbjDbcVf2PRIaxmVJKgUtBCeLcA4NuDKU0kNhlqhpEb6SlyrDuxXFIWfiXC/7ehwVN40eiR5J+Nj+DZoBuyeT8abd4fUz6LEIq3M9eI1HlArTLT6cU7uZp1bgZ5hZA3YVXoKuncz2HdvQ0BrAmigxEdPeg+VCxds38kYCQ4QzHZBAw/sVbXD0rfbzrtuI5nMg0B8/PoV4Qt5KXXfjbdWDKEIACmSPyb1SzAZ8kbf6FQ==","[""EPFMsLVR7r+1Uh2SS8t578JvyHg5yujcPKbJmwcgvow="",""Hr1jAMFb/UKfs6qdAjAWIvRp5WTCD4Dnts0bu3BHkkA=""]","[""AOapJ2yHtCAq7feYfn4dZBl2DrEkIGkgMyl/jgjQ8J8="",""AWot46QPYKGP6tVUv0olEe0LuIctyZrUyHV/1ai04B8=""]","CiI2Ag8RXli3QWKqDeGrPtBk/L1xdgzFn6dvMTjz7u4=",6 +8,"2023-08-22 03:01:10.169","2023-08-22 03:01:10.169","","GRYWihOqybjhJbNIfXCL287b/i4g/BUBgDuEkc4/R9wHsra3159/K5lgLgBPxniFWH3f73DeIqYnOoW0Kgp/6SzcgaTgukoHVVFBzUM5QK2D+xody5IOqbQFOLXjpR70HC54TyymZSkUiSMqjPjeoXVt2LbCXMK95XyN1qlsDzUT91v8On+BG9k0fRbW5Y47mjfC+bUbGqEImPDMRfdCvQOjY/3/wJZRdHa+4H0QJVMrfRlHRlupKpVjc2cSspRIIEyPc1k8rPlF4UvfMt9LbsDH6W6VCT2JWjt3rBkJ468PA7CJb9qWtH7iWALZUGYp/DwYI39bkj+sk+8j/gqsqw==","[""Hr1jAMFb/UKfs6qdAjAWIvRp5WTCD4Dnts0bu3BHkkA="",""HrEpiPehi66F87npDVQXco4Gnmrj+T9QHwuY90ZEoRU=""]","[""AWot46QPYKGP6tVUv0olEe0LuIctyZrUyHV/1ai04B8="",""FDfSUPNjUKoYWqZmYgwEmph4MsqgmL4xDDRD13JQ/bk=""]","AwLfjjq31XqIio6YoHXgj/1JQp/Qtl+XRLyAj9jyC5g=",7 +9,"2023-08-22 03:01:13.463","2023-08-22 03:01:13.463","","CfaEv0ayE0uKPb7tZ61sFTVM96Gn+ZB5yxLClTQYwpYHAmWlgH2WE6yRVMJRQkP3e2y2xVP0Xs5DLfteEe5RpRmvk4baDVlUPzHSjWpf394TK5qdIcFS7/OFV2VHg51HKKGNSMmKCJQ02GOSySSa38ZhcGN2Ax+AKgbfoeT6NVcAtngN8UBA9d5w4OMkdNxHmybhd1vsBy0MIwHa9f77URp/Zfi+dQ7bh9KZtfgdVIOETujHNvv7NnPiVugcF7vABfINBl4xXG3OqtQAOkFjmO117+7p++45g06jSEpa6F0Bih2Wcgt3DzY1h/qEQtFSw7J612EHF3Oj0oSRbqayWg==","[""HrEpiPehi66F87npDVQXco4Gnmrj+T9QHwuY90ZEoRU="",""LkXi9GH4GZSBXbBXhMjrouGpIT8aNHOyHx4+tgE4Ftk=""]","[""FDfSUPNjUKoYWqZmYgwEmph4MsqgmL4xDDRD13JQ/bk="",""DCLQWdQG9DOaneNY8ipg1IlaqG/wmmnNuMdl+JnHxTs=""]","CoJS2qIm+zr8MtUcg4SiJLev4y2+m0+Fy7f0kb+kc04=",8 +10,"2023-08-22 03:01:16.623","2023-08-22 03:01:16.623","","DIIDeJwKEMSs7gl7/nBRSLee0kcWTGukq1ru1RYpOCcK6w9IlPa7SGObtX31FC8i1I311AwfXKVXXAZqmo7JCSxQPY0qkLD2ZSkwoMe4haz/FfMK0s28L1hU11GF5Fo7LgTclJKhcKso+2ZYjGR6AxbHAbcYGkuuXjt3VbO6Wt8QLqSEJ0rfBfiBbHCHt4WLOQPoUj5xsQ/yqkyV8qvNBihRdQ/ah+g42KiiGNdDNiiqqSi/NQZqTDzRG98sNVV5DJ0ohFshrEkiuB2YUy3DMPJZf/i+0yJmcoB7pf8zzp0gJpCHjLshp0lKwx/kQGeaxbqfHUVKCiZHVoTllc0cKA==","[""LkXi9GH4GZSBXbBXhMjrouGpIT8aNHOyHx4+tgE4Ftk="",""JjRjnAr8h34F5MO5bJxmUyIUN1pl7xqIypnsfRYgryw=""]","[""DCLQWdQG9DOaneNY8ipg1IlaqG/wmmnNuMdl+JnHxTs="",""Agcyv4GRlOkbn9uGjTaZS5z8xeSmxJlDLj7n6irMB94=""]","JZDaVd6WoLVLvrGHAPzUsYdRSCJjIisCZS5rpWlJ7xg=",9 +11,"2023-08-22 03:01:19.872","2023-08-22 03:01:19.872","","AFRD432AhkPNTJHwjnPDUpTV6FpjNKiJoFKNcRb4lIwNamNRJans0+zoz2RkwNgP+KLfbx0oFP1+bEdnPBAxXBJs6fOdCJkE35c+vLOLnIKXf7Ir7jHwXSZ4nVQcMeVKAYA0zjwB8qA6ieXJBuPSfloTAFAtzSS9BvsVXmG3EUki1grCnCyv9XHCU3d2nX9d3vFwPGItMopFZM4Y6nk7YRBPLmIaQCxqbRFcz7oDUnLIycWKjfEPeKZb8u1XBLM5KeYokErQn/ZCVu1ZN411Gajk1esCjjZIkftVpe8i6EUBTDBTnPM6k504I2ur9YYN+HNVwQV2mEpntk+4DeMk1w==","[""JjRjnAr8h34F5MO5bJxmUyIUN1pl7xqIypnsfRYgryw="",""DTB8FvGAL7DP36uUauArz8tTd4yeVLITxIVkUM2+J+I=""]","[""Agcyv4GRlOkbn9uGjTaZS5z8xeSmxJlDLj7n6irMB94="",""Axeqf+Zu3nPpMQ96RRCuBF8USLFbR5xwhIE9BaaOUrU=""]","EAOoSLyJILwby7242LzB1xeGJoTKWZhaNks0wcMkYjc=",10 +12,"2023-08-22 03:01:23.408","2023-08-22 03:01:23.408","","Be4AxAyEoK+V2R6PblahctybNWK3hX+xDa19o0C20XkSzhUDuZXrwSETeLwDjHRa2lnPW3EgNpXBZPWIydmzFByq+chTL5FQYsu5s9XTixU7IeeuFyg+GfGosOxM4AixHJ4FCpYAJk6f5hpUSdiOvBtsHqMiOPflEXIhauwUH/ctFzjZMWsTRN6tgkYfDSCgHvlgBVxJAnhVJ9g6xUAC9gYOLBUo/RPqTZbjvRfh2kx2KUQ9VU9RDxmG0UCnN1KWBpRh00+mhOLmC78oAibKKuOrlhCGSLHHp+3eTKHRLgUsRIMei2r6CHt/UEEkKHq5LxMTjKP4E+taDGE7qWlryQ==","[""DTB8FvGAL7DP36uUauArz8tTd4yeVLITxIVkUM2+J+I="",""JzwdKXvbcow+M73uXSBylfGiZtA2WSOwwd6IDhwQGHA=""]","[""Axeqf+Zu3nPpMQ96RRCuBF8USLFbR5xwhIE9BaaOUrU="",""JtWO1YHl9b3fRIrUKYte95Y9R/zzpb2pIABpTT/OM0U=""]","EuC1zN86El7R07YJ5j9zwgoqv+1OVX8Z1C+sk54C4zc=",11 +13,"2023-08-22 03:01:26.826","2023-08-22 03:01:26.826","","I3ZirnBK0156mP1kqFOEubuQKUgAF0b8pAsAPs7GFsYRmQDcV/2rte0y0lyZ4vuOawMLrs40Yz/g6ehMyxocNyod8a/Er/mQV3A/emupS1g6bLKxQsLpQQCZbQthtt1aLUvw7LcCAF7Hp+wWYgwzm4tzJphfQT+TeWp3V+TieDoqlWoexW/fgL6pvpAsZKOz6GWREqZDl4jc49MBfFw5TBCgCLt9jhmNVhE30cDloahVXE99Qbxl00Gn99YI5Sz5GPfoblM/e5GHodXQufRDKKhuXhQO6K/Mz0/89gLVKxYC8VzWDmMvmlhK4flPVVI3b32SbnBhMicvHviTD+cF9Q==","[""JzwdKXvbcow+M73uXSBylfGiZtA2WSOwwd6IDhwQGHA="",""KF85gTTqStFNl3fSv9LuKJxCEw9wSHfVZ5E32ib2tVA=""]","[""JtWO1YHl9b3fRIrUKYte95Y9R/zzpb2pIABpTT/OM0U="",""LnRB8i2MOrQrA8DMll3YTk+N1f8Ezo824OqTE3SLRcs=""]","BuxDIKJvG2PQVepoUckrD3diPDxtQZUB5vHXODriG9s=",12 +14,"2023-08-22 03:01:30.316","2023-08-22 03:01:30.316","","EZjILnHTiK7ESuNdCSKl7vdrW9bwIrSFvU437NfF6Lkl/zPQX4VMG+BvMMyDhriAd7hCQeujly+Kne07xNjrPxJBPB4pa7B+TRsuaXZ3HRcVz3wkK2pQPLmJPP9Wi6c+JGRoaT70cDlIhYXwsyBEfd6Fmrmw9OaeudO/kDgqjnkmdhoVPA/Sbhp/icxNtfCzi/2sCzAROacHqSeO1NvEJwHY2yWr967KeAo9zGtxyutX3icqMmyCTsLO3tpRgdz7BT8yM22bf742qG9LaTd1zrjLeTxLntwYAyzR7FBArDgDdXRV8qX8zuBG1cr0ATu3a/HmQl/m7+1H6itwFdztow==","[""KF85gTTqStFNl3fSv9LuKJxCEw9wSHfVZ5E32ib2tVA="",""Iwtf8F38+hcIqxxJai/03Z8PLDiZnruymOV5U25ueG0=""]","[""LnRB8i2MOrQrA8DMll3YTk+N1f8Ezo824OqTE3SLRcs="",""IjoopIdTJ6m9eSr6aysXAIzGkOco+9BuCcOfha5bXJI=""]","GTpGFoOb8srgC2sJlv4aOk5xO3IYFPKZjz5dqgYm1lA=",13 +15,"2023-08-22 03:01:33.849","2023-08-22 03:01:33.849","","JwzheCjowK336s9O+dKC+OCFzVApM+esUmS8R5IIww4KspE92cMWVX6RZm6zHWth1PHzyCiteMYmVHWq2HhfwQehJRu4IGXufRd+VyF4+kRjRsqzKsoQn3IwxDgEIrBXAv1quDCUH3PCG+k1nUNY+Q/xBqnIFCeBr9xu3aCjC6khP0ZGtA57TQ6dsIpQQjyinZQi5pk/XhTbkOln1iIS0BjwV7GYT2uFA5YZTZTnECI7x3XWLUMDMXJ7sfH2vFrXDv3rF9Kao+nZpIPJ6W5+4G89AdBClbOaOcYgcS7B8xcoxRUJ8V00iEmDadaEeGmGEp6OVE0SNALyHc6SDbsxoQ==","[""Iwtf8F38+hcIqxxJai/03Z8PLDiZnruymOV5U25ueG0="",""FdNA1IqzCs3GKKD+SbjDOAlURaqYAZ7TXN+KJsjbtZ8=""]","[""IjoopIdTJ6m9eSr6aysXAIzGkOco+9BuCcOfha5bXJI="",""DJSy8IX1T8fK5mUNS+Xr/dxbCN2MtfaRuggChv+0Sy8=""]","F2iEUiDKiF3k1Ofp/4GRHAfOrFsiz9JEXTNIFe3h+r0=",14 +16,"2023-08-22 03:01:37.229","2023-08-22 03:01:37.229","","B88C3a3jLI56lEbAqASpVjU/jqWGm3DuEmWnMte515ct/mMbv/7Nj06ChcKpx+v7k1FoQ6VK59WaDwOC0+mQChnoVVjVONRkm7j7pzBKrrpPv/VC+33z5TeyNfNy/zaLCEw9w9YvcVNi8oYL1R8sOgo8qnJt24Dfh7kk4IXx1XIafKDE4ifR/KAmm/DTfOOZGTglzeTbWdUQFkQEGr9HtxAz5VvGSrIkEY8ozYWdW7u9WvOLWhnZCKNwWQdq9cw0A4Wi6RSn7DD4cScfc64F8jr3wnPRIJYtFaPuY1oi0CgqUCTZC1yLcarN3UGCdM26tp7gxHgLz5KE8JrylthPxw==","[""FdNA1IqzCs3GKKD+SbjDOAlURaqYAZ7TXN+KJsjbtZ8="",""G4HYLldFkB/L/fLMk9f8WqFgqBJziGp+WK14+YCV69g=""]","[""DJSy8IX1T8fK5mUNS+Xr/dxbCN2MtfaRuggChv+0Sy8="",""KriRFQfFyVngqstiC0dVmhcpPPrn65hH+FTr6GwayzE=""]","HtnnM9/NMUCqzXzKqOcH4Q9Tgr1mDj3dFamB/GU2FW8=",15 +17,"2023-08-22 03:01:40.775","2023-08-22 03:01:40.775","","FKozciZ4OS2qYaHjbu9KwWhPjU9Tv/Wir3XtLnTg2psJXIx1ga0gORl6SubXAQtzN2Xn7AqHdb/TNKbkfbM6Gx2V7vj6nuMFfg2TbCwihjW+cNX5WiZcNY+f3t+hOz13LXJatUjvAMxX5YgGrKCX8/566zmCQO380p1bfywT+1Uml0WOaF5EJN+NlSUz8fzvDpyTQIK0cwhoN2NhH0W2SiAIxuixZcwL6Y6E0em4JHreZ8LWbojl3JQ0bEHV6KMoEP50BvNfmsOhZPV93mDfViU3u76nba3KQKoawVVFJtkvmgSJXIzmCqKPxkHkVq8ndig/etyiXivRR/uS6xwkBA==","[""G4HYLldFkB/L/fLMk9f8WqFgqBJziGp+WK14+YCV69g="",""FKst6G82OJRoBh8CghurR3+dxS7ewudq6IhR7zr6Xxw=""]","[""KriRFQfFyVngqstiC0dVmhcpPPrn65hH+FTr6GwayzE="",""BTTinkrJPqIkcz/nqh3/uyjEw+mOAkdmFxYni0h1Ts0=""]","JKBSZBqf1vQH018qnvgRyO+id2eRNaw+x3IAwM8TaPo=",16 +18,"2023-08-22 03:01:44.166","2023-08-22 03:01:44.166","","BlRpEbPA7G9e4vHOENMHSdx+Yii97LymQYLcuAm/9kImCKxGB7aNSYEM11ezk5ZXePPseZa4ECVg67DxssoXGwwt9Se7XOHKBMUiD1hupUFiGJnCNB/EqdyXIHpF5wqyLv1BCY71J0DNBOX42iC5DWr5QfGAfGCvSAINiAsn2mMp5A7u26fISByA08Ym75S0ihJ1FcIWvw36YNzrRmj9yBguz2d2FfcVXJR5VaPz20KqvQt7odNzihFrHMMEe52vFEK0444MF0P9bjeHmlJI/E8d+jmVWTkfOXkawGpw3YAjSTltP3T64zkAEL2fMbGhz7TXsHeUas98nwspyD+A9g==","[""FKst6G82OJRoBh8CghurR3+dxS7ewudq6IhR7zr6Xxw="",""DedzfcR4ZOimvu8JDysA/NGssHJR/GsN6knQJ4tr9s8=""]","[""BTTinkrJPqIkcz/nqh3/uyjEw+mOAkdmFxYni0h1Ts0="",""IZuBdmN0OQEyllPGzAtoDX9GRbHwVCzTzIIsjFp3xxM=""]","HMjPtYLQ3X0IHW4dgXEpHwhXKmLg0a7A8jTklCcY6ng=",17 +19,"2023-08-22 03:01:47.733","2023-08-22 03:01:47.733","","BfloYl0J2dw4DqUf2rtxmUnk/mrZKsLbQqz8843UblgwDrQRoThGWwpoUvwZy5GCHIxI2f46bj4Tp2NBWNRfOSFmWKsuKRCzgSBDKeuhed2uS6N3S1SmWMm8qrnRS36aHJPHoNU4fGW+Cf+rYw8zTm2iCNMKPwK23LPSD+q7KTID6QrZTxlOOhFb+mbFZBsqnVdeJJUt0nu4sEPchJmsvRHdctUUy0F20IqfK7LzqMEz1XA2K0Bai5BU8he30G9TBJHOrIkhG3AjFihGX4a+GgCNSM4DvfSRsw8pyYalAPYSpzGsje5hntaFxcWxggoAvwZlzd3QLSdQ4Qxy8/A9gg==","[""DedzfcR4ZOimvu8JDysA/NGssHJR/GsN6knQJ4tr9s8="",""CZl0HjDfwjKWZv2b+Y1vtA6FYtfXy6zg4XTR9hDzckM=""]","[""IZuBdmN0OQEyllPGzAtoDX9GRbHwVCzTzIIsjFp3xxM="",""Ep5guN0CR/R2FE3R/Ynx96bHC0Xsv3ESZSYvqdi8r3c=""]","LT+n7Vhea5dWVaZkn7KZlPfrfwxnz7G4P7duVmpkb+Q=",18 +20,"2023-08-22 03:01:51.142","2023-08-22 03:01:51.142","","CJzOjO/RAllccj9kyTtadxVnT5MlvhZDhhMjXYWr0xQJVG9xYpccXQFcOvOcSZKE3RPY9t9ifcgZXTM0sVZriQEthqQ0VOMrzXqT6Wz18cdVdVfedkNgRN+EYa1zRZ3BH1C/a6Jm92tWtW6SIKxvG/5i5QmbrtPjd4VhALZ4sB8FyUdJZcMY/IgFT60JBxQNHdyfipYT0DIFueJB/xsPDR5ICsmvY79LdMNLXpt+gT/xuzZxZStk5GF7YAWfH98yEktEsq1+Mc5dPYn5NzjD6i7p7YFRs8oObo3/ebgOzaMvwfhNsNO18uMw4EyTWLBjzGnF3YXZqwvPgdd9IxtzcQ==","[""CZl0HjDfwjKWZv2b+Y1vtA6FYtfXy6zg4XTR9hDzckM="",""LKsy8O8bgBzKLEm5mS/TbXMsiCOY3DmMGjQ/r730NKQ=""]","[""Ep5guN0CR/R2FE3R/Ynx96bHC0Xsv3ESZSYvqdi8r3c="",""HEN7Q/4Dbb/y6geKJVARQqprspLNCK825UrhUTHgS7M=""]","A8seZ49NawCO4IIefT/TJAze65MuqfdAj621nETbuQ8=",19 \ No newline at end of file diff --git a/merkle_groth16/src/verifier/config/user_config.json b/merkle_groth16/src/verifier/config/user_config.json new file mode 100644 index 0000000..68ce970 --- /dev/null +++ b/merkle_groth16/src/verifier/config/user_config.json @@ -0,0 +1,44 @@ + +{ + "AccountIndex":0, + "AccountIdHash":"10f99e0e89f3cc9b4396af4198979966266ee9e7380de0216c173878288f08e1", + "TotalEquity":867200, + "TotalDebt":0, + "Assets":[ + { + "Index":0, + "Balance":32 + } + ], + "Root":"2d56687955260b6385c0b167a96d2540d9f6f4a1982e6e5c394aebcb6f33223a", + "Proof":[ + "H9djvEUsd4ZN1H0DmRS5WyXRro6Qi8J286/Gg9xQHt8=", + "AbI0B5qWCG/+vTx4PdIK01tB6uLcn25+ptl21ErYztE=", + "BIZbR1X0Sm0XkYbfQHrWbytKlQ96R0MgP+jFJapCQpQ=", + "BOP/NWKDTJvzreDgALmCeM87OZWzAKU/dO/niGzOIdg=", + "FV6rJLWezNKjsAfQX6FWYyS5H5hvVW6igIJmpUktmy4=", + "KCW78epZBIcnQVKzkJvd/hTypMSc9D0ZqsVMx4mVibs=", + "Fw91/11i2Rl/x6od9tTRSfe6Y7ab1A8gTFnyj7fBpjk=", + "AnYQTbwAyEZF8hS+OHHbJLTZEB0Tx7Rx6EApjz9C7Gk=", + "JGYETX1nRn9nm6d05RpAI2O1EFf4PwJNj94hIJfJC/M=", + "GLEPi0EH308S1argUEjGpsfGq+bm+zBnBCh4g2NG20M=", + "BoPxK6AAUsSpHYqnudyhDM8TXF8DVQGNX3uzuX7KUvI=", + "L2Kb15pm4/eDthNIg/lq3YNTwuiIbPYSbTp+bvrec0s=", + "L1sKTUSg6XzEXypRFSYrnrA7P1P7ZG284+dAG2Os/Kg=", + "E1CWWGUaknj/EafXSXvuqsBYgQU1/0ypvV65L5WqmCI=", + "Jn9swsPiBvQDkEjOACAtak4l+MjIxGIaKz2Vd2VfJO4=", + "LvOWYC+Ueohrbq7OoALT3fOJkxj2qWdY1cpO00s+g3c=", + "I4dMEYJiAyJuaZCpdepyvznsCB4I3z2cENiSu4ApIMU=", + "KtjQmGmoWxbhIKNoRpdYoYaW8AgIRj0v+lpaX8SIEEs=", + "IkQnUP9DAodMJ6TClI4q4yQutXyWBTSme73m8w8ndGo=", + "IUtAfxD5GvROZ2NFlY++hBRUUT6r0iBagytl1f1PuAk=", + "Ehvlfma7Yq8SsskL+jh8xXWXn5OKWtEKRvhuplKaExQ=", + "A3RrdxbvDNlFja3iFLbqNY8LqM0QxQmRTXRfH3OCrYc=", + "JrFovuTyFhaTwaP/7m2Ro6o4TfNFK7Ia3uul2EyntnA=", + "KqZrw2UYCZh/i5azqzZlq5buTNvakUbu2RrP31yNjEQ=", + "DwB+WnFMTAJTElAm5TayYnFNJWeyupGikEyI/86ViS4=", + "AhGlSZfLMpZ8u4hO1XybHCjbbT5FyZxScPpNLIRYOXo=", + "HapIKZ5fQpiDS2e1Javi/qfB+0hn0Yomh4Z4nPHhkzE=", + "HN/Hzc8s/S+wkld9d2pVqBstS3HyNeiSKgOksyrgl/I=" + ] +} \ No newline at end of file diff --git a/merkle_groth16/src/verifier/main.go b/merkle_groth16/src/verifier/main.go new file mode 100644 index 0000000..e50c749 --- /dev/null +++ b/merkle_groth16/src/verifier/main.go @@ -0,0 +1,225 @@ +package main + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "merkleverifytool/merkle_groth16/circuit" + "merkleverifytool/merkle_groth16/src/prover/prover" + "merkleverifytool/merkle_groth16/src/utils" + "merkleverifytool/merkle_groth16/src/verifier/config" + "os" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark-crypto/ecc/bn254/fr/poseidon" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/frontend" + "github.com/gocarina/gocsv" +) + +func main() { + userFlag := flag.Bool("user", false, "flag which indicates user proof verification") + flag.Parse() + if *userFlag { + userConfig := &config.UserConfig{} + content, err := ioutil.ReadFile("src/verifier/config/user_config.json") + if err != nil { + panic(err.Error()) + } + err = json.Unmarshal(content, userConfig) + if err != nil { + panic(err.Error()) + } + root, err := hex.DecodeString(userConfig.Root) + if err != nil || len(root) != 32 { + panic("invalid account tree root") + } + + var proof [][]byte + for i := 0; i < len(userConfig.Proof); i++ { + p, err := base64.StdEncoding.DecodeString(userConfig.Proof[i]) + if err != nil || len(p) != 32 { + panic("invalid proof") + } + proof = append(proof, p) + } + + // padding user assets + userAssets := make([]utils.AccountAsset, utils.AssetCounts) + for i := 0; i < utils.AssetCounts; i++ { + userAssets[i].Index = uint16(i) + } + for i := 0; i < len(userConfig.Assets); i++ { + userAssets[userConfig.Assets[i].Index] = userConfig.Assets[i] + } + hasher := poseidon.NewPoseidon() + assetCommitment := utils.ComputeUserAssetsCommitment(&hasher, userAssets) + hasher.Reset() + // compute new account leaf node hash + accountIdHash, err := hex.DecodeString(userConfig.AccountIdHash) + if err != nil || len(accountIdHash) != 32 { + panic("the AccountIdHash is invalid") + } + accountHash := poseidon.PoseidonBytes(accountIdHash, userConfig.TotalEquity.Bytes(), userConfig.TotalDebt.Bytes(), assetCommitment) + fmt.Printf("merkle leave hash: %x\n", accountHash) + verifyFlag := utils.VerifyMerkleProof(root, userConfig.AccountIndex, proof, accountHash) + if verifyFlag { + fmt.Println("verify pass!!!") + } else { + fmt.Println("verify failed...") + } + } else { + verifierConfig := &config.Config{} + content, err := ioutil.ReadFile("src/verifier/config/config.json") + if err != nil { + panic(err.Error()) + } + err = json.Unmarshal(content, verifierConfig) + if err != nil { + panic(err.Error()) + } + + vk, err := prover.LoadVerifyingKey(verifierConfig.ZkKeyName) + if err != nil { + panic(err.Error()) + } + + f, err := os.Open(verifierConfig.ProofTable) + if err != nil { + panic(err.Error()) + } + defer f.Close() + // index 4: proof_info, index 5: cex_asset_list_commitments + // index 6: account_tree_roots, index 7: batch_commitment + // index 8: batch_number + type Proof struct { + BatchNumber int64 `csv:"BatchNumber"` + ZkProof string `csv:"ProofInfo"` + CexAssetCommitment []string `csv:"CexAssetListCommitments"` + AccountTreeRoots []string `csv:"AccountTreeRoots"` + BatchCommitment string `csv:"BatchCommitment"` + } + tmpProofs := []*Proof{} + + err = gocsv.UnmarshalFile(f, &tmpProofs) + if err != nil { + panic(err.Error()) + } + fmt.Println("tmpproofs=", tmpProofs[0]) + proofs := make([]Proof, len(tmpProofs)) + for i := 0; i < len(tmpProofs); i++ { + proofs[tmpProofs[i].BatchNumber] = *tmpProofs[i] + } + batchNumber := int64(0) + prevCexAssetListCommitments := make([][]byte, 2) + prevAccountTreeRoots := make([][]byte, 2) + // depth-28 empty account tree root + emptyAccountTreeRoot, err := hex.DecodeString("2787c6e55c2da2a73e57628e70386add83fd42cb4da4c401269381e918944558") + //0118925954da77d1a4b241fd163e4373e2265c515cfa60af7fcd28c8cb9ad58a + if err != nil { + fmt.Println("wrong empty empty account tree root") + return + } + + prevAccountTreeRoots[1] = emptyAccountTreeRoot + // according to asset price info to compute + cexAssetsInfo := make([]utils.CexAssetInfo, len(verifierConfig.CexAssetsInfo)) + for i := 0; i < len(verifierConfig.CexAssetsInfo); i++ { + cexAssetsInfo[verifierConfig.CexAssetsInfo[i].Index] = verifierConfig.CexAssetsInfo[i] + } + emptyCexAssetsInfo := make([]utils.CexAssetInfo, len(cexAssetsInfo)) + copy(emptyCexAssetsInfo, cexAssetsInfo) + for i := 0; i < len(emptyCexAssetsInfo); i++ { + emptyCexAssetsInfo[i].TotalBalance = 0 + } + emptyCexAssetListCommitment := utils.ComputeCexAssetsCommitment(emptyCexAssetsInfo) + expectFinalCexAssetsInfoComm := utils.ComputeCexAssetsCommitment(cexAssetsInfo) + prevCexAssetListCommitments[1] = emptyCexAssetListCommitment + var finalCexAssetsInfoComm []byte + var accountTreeRoot []byte + for i := 0; i < len(proofs); i++ { + if batchNumber != proofs[i].BatchNumber { + panic("the batch number is not monotonically increasing by 1") + } + // first deserialize proof + proof := groth16.NewProof(ecc.BN254) + var bufRaw bytes.Buffer + proofRaw, err := base64.StdEncoding.DecodeString(proofs[i].ZkProof) + if err != nil { + fmt.Println("decode proof failed:", batchNumber) + return + } + bufRaw.Write(proofRaw) + proof.ReadFrom(&bufRaw) + // deserialize cex asset list commitment and account tree root + cexAssetListCommitments := make([][]byte, 2) + accountTreeRoots := make([][]byte, 2) + + for j := 0; j < len(proofs[i].CexAssetCommitment); j++ { + cexAssetListCommitments[j], err = base64.StdEncoding.DecodeString(proofs[i].CexAssetCommitment[j]) + if err != nil { + fmt.Println("decode cex asset commitment failed") + panic(err.Error()) + } + } + for j := 0; j < len(proofs[i].AccountTreeRoots); j++ { + accountTreeRoots[j], err = base64.StdEncoding.DecodeString(proofs[i].AccountTreeRoots[j]) + if err != nil { + fmt.Println("decode account tree root failed") + panic(err.Error()) + } + } + + finalCexAssetsInfoComm = cexAssetListCommitments[1] + // verify the public input is correctly computed by cex asset list and account tree root + poseidonHasher := poseidon.NewPoseidon() + poseidonHasher.Write(accountTreeRoots[0]) + poseidonHasher.Write(accountTreeRoots[1]) + poseidonHasher.Write(cexAssetListCommitments[0]) + poseidonHasher.Write(cexAssetListCommitments[1]) + expectHash := poseidonHasher.Sum(nil) + actualHash, err := base64.StdEncoding.DecodeString(proofs[i].BatchCommitment) + if err != nil { + fmt.Println("decode batch commitment failed", batchNumber) + return + } + if string(expectHash) != string(actualHash) { + fmt.Println("public input verify failed ", batchNumber) + fmt.Printf("%x:%x\n", expectHash, actualHash) + return + } + + if string(accountTreeRoots[0]) != string(prevAccountTreeRoots[1]) || + string(cexAssetListCommitments[0]) != string(prevCexAssetListCommitments[1]) { + fmt.Println("mismatch account tree root or cex asset list commitment:", batchNumber) + return + } + prevCexAssetListCommitments = cexAssetListCommitments + prevAccountTreeRoots = accountTreeRoots + + verifyWitness := circuit.NewVerifyBatchCreateUserCircuit(actualHash) + vWitness, err := frontend.NewWitness(verifyWitness, ecc.BN254, frontend.PublicOnly()) + if err != nil { + panic(err.Error()) + } + err = groth16.Verify(proof, vk, vWitness) + if err != nil { + fmt.Println("proof verify failed:", batchNumber, err.Error()) + return + } else { + fmt.Println("proof verify success", batchNumber) + } + batchNumber++ + accountTreeRoot = accountTreeRoots[1] + } + if string(finalCexAssetsInfoComm) != string(expectFinalCexAssetsInfoComm) { + panic("Final Cex Assets Info Not Match") + } + fmt.Printf("account merkle tree root is %x\n", accountTreeRoot) + fmt.Println("All proofs verify passed!!!") + } +} diff --git a/merkle_groth16/src/witness/config/config.go b/merkle_groth16/src/witness/config/config.go new file mode 100644 index 0000000..f53443f --- /dev/null +++ b/merkle_groth16/src/witness/config/config.go @@ -0,0 +1,13 @@ +package config + +type Config struct { + MysqlDataSource string + UserDataFile string + DbSuffix string + TreeDB struct { + Driver string + Option struct { + Addr string + } + } +} diff --git a/merkle_groth16/src/witness/config/config.json b/merkle_groth16/src/witness/config/config.json new file mode 100644 index 0000000..d6dda68 --- /dev/null +++ b/merkle_groth16/src/witness/config/config.json @@ -0,0 +1,11 @@ +{ + "MysqlDataSource" : "admin:admin123@tcp(127.0.0.1:3306)/portest?parseTime=true", + "DbSuffix": "0", + "UserDataFile": "src/sampledata/", + "TreeDB": { + "Driver": "redis", + "Option": { + "Addr": "127.0.0.1:6379" + } + } +} diff --git a/merkle_groth16/src/witness/main.go b/merkle_groth16/src/witness/main.go new file mode 100644 index 0000000..cb8e80f --- /dev/null +++ b/merkle_groth16/src/witness/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + + "merkleverifytool/merkle_groth16/src/utils" + "merkleverifytool/merkle_groth16/src/witness/config" + "merkleverifytool/merkle_groth16/src/witness/witness" +) + +func main() { + remotePasswdConfig := flag.String("remote_password_config", "", "fetch password from aws secretsmanager") + flag.Parse() + witnessConfig := &config.Config{} // witness/config/config.go + content, err := ioutil.ReadFile("src/witness/config/config.json") + if err != nil { + panic(err.Error()) + } + err = json.Unmarshal(content, witnessConfig) + if err != nil { + panic(err.Error()) + } + if *remotePasswdConfig != "" { + s, err := utils.GetMysqlSource(witnessConfig.MysqlDataSource, *remotePasswdConfig) + if err != nil { + panic(err.Error()) + } + witnessConfig.MysqlDataSource = s + } + accounts, cexAssetsInfo, err := utils.ParseUserDataSet(witnessConfig.UserDataFile) + fmt.Println("account counts", len(accounts)) + if err != nil { + panic(err.Error()) + } + accountTree, err := utils.NewAccountTree(witnessConfig.TreeDB.Driver, witnessConfig.TreeDB.Option.Addr) + if err != nil { + panic(err.Error()) + } + fmt.Println("account tree init height is ", accountTree.LatestVersion()) + fmt.Printf("account tree root is %x\n", accountTree.Root()) + witnessService := witness.NewWitness(accountTree, uint32(len(accounts)), accounts, cexAssetsInfo, witnessConfig) + witnessService.Run() + fmt.Println("witness service run finished...") +} diff --git a/merkle_groth16/src/witness/witness/witness.go b/merkle_groth16/src/witness/witness/witness.go new file mode 100644 index 0000000..864064e --- /dev/null +++ b/merkle_groth16/src/witness/witness/witness.go @@ -0,0 +1,286 @@ +package witness + +import ( + "bytes" + "encoding/base64" + "encoding/gob" + "fmt" + "log" + "math/big" + "os" + "runtime" + "sync/atomic" + "time" + + "merkleverifytool/merkle_groth16/src/utils" + "merkleverifytool/merkle_groth16/src/witness/config" + + bsmt "github.com/bnb-chain/zkbnb-smt" + "github.com/consensys/gnark-crypto/ecc/bn254/fr/poseidon" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +type Witness struct { + accountTree bsmt.SparseMerkleTree + totalOpsNumber uint32 + witnessModel WitnessModel + ops []utils.AccountInfo + cexAssets []utils.CexAssetInfo + db *gorm.DB + ch chan BatchWitness + quit chan int + accountHashChan [utils.BatchCreateUserOpsCounts]chan []byte + currentBatchNumber int64 +} + +// NewWitness 创建 Witness 结构体 +func NewWitness(accountTree bsmt.SparseMerkleTree, totalOpsNumber uint32, + ops []utils.AccountInfo, cexAssets []utils.CexAssetInfo, + config *config.Config) *Witness { + newLogger := logger.New( + log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer + logger.Config{ + SlowThreshold: 60 * time.Second, // Slow SQL threshold + LogLevel: logger.Silent, // Log level + IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger + Colorful: false, // Disable color + }, + ) + db, err := gorm.Open(mysql.Open(config.MysqlDataSource), &gorm.Config{ + Logger: newLogger, + }) + fmt.Println("db=", db) + if err != nil { + panic(err.Error()) + } + return &Witness{ + accountTree: accountTree, + totalOpsNumber: totalOpsNumber, + witnessModel: NewWitnessModel(db, config.DbSuffix), + ops: ops, + cexAssets: cexAssets, + ch: make(chan BatchWitness, 100), + quit: make(chan int, 1), + currentBatchNumber: 0, + } +} + +func (w *Witness) Run() { + // create table first + w.witnessModel.CreateBatchWitnessTable() + latestWitness, err := w.witnessModel.GetLatestBatchWitness() + var height int64 + beforeTotalCexAssets := utils.CexAssetsTotal{ + BeforeCEXTotalEquity: 0, + AfterCEXTotalEquity: 0, + BeforeCEXTotalDebt: 0, + AfterCEXTotalDebt: 0, + } + if err == utils.DbErrNotFound { + height = -1 // 首次执行 + } + if err != nil && err != utils.DbErrNotFound { + panic(err.Error()) + } + if err == nil { + height = latestWitness.Height + w.cexAssets, beforeTotalCexAssets = w.GetCexAssets(latestWitness) + } + + batchNumber := (w.totalOpsNumber + utils.BatchCreateUserOpsCounts - 1) / utils.BatchCreateUserOpsCounts + if height == int64(batchNumber)-1 { + fmt.Println("already generate all accounts witness") + return + } + w.currentBatchNumber = height + fmt.Println("latest height is ", height) + + // tree version + if w.accountTree.LatestVersion() > bsmt.Version(height+1) { + rollbackVersion := bsmt.Version(height + 1) + err = w.accountTree.Rollback(rollbackVersion) + if err != nil { + fmt.Println("rollback failed ", rollbackVersion, err.Error()) + panic("rollback failed") + } else { + fmt.Printf("rollback to %x\n", w.accountTree.Root()) + } + } else if w.accountTree.LatestVersion() < bsmt.Version(height+1) { + panic("account tree version is less than current height") + } else { + fmt.Println("normal starting...") + } + + paddingAccountCounts := batchNumber*utils.BatchCreateUserOpsCounts - w.totalOpsNumber + for i := uint32(0); i < paddingAccountCounts; i++ { + emptyAccount := utils.AccountInfo{ + AccountIndex: i + w.totalOpsNumber, + TotalEquity: new(big.Int).SetInt64(0), + TotalDebt: new(big.Int).SetInt64(0), + Assets: make([]utils.AccountAsset, 0), + } + w.ops = append(w.ops, emptyAccount) + } + + poseidonHasher := poseidon.NewPoseidon() + go w.WriteBatchWitnessToDB() + for i := 0; i < utils.BatchCreateUserOpsCounts; i++ { + w.accountHashChan[i] = make(chan []byte, 1) + } + cpuCores := runtime.NumCPU() + workersNum := 1 + if cpuCores > 2 { + workersNum = cpuCores - 2 + } + averageCount := int64(utils.BatchCreateUserOpsCounts/workersNum + 1) + for i := int64(0); i < int64(workersNum); i++ { + go func(index int64) { + for j := height + 1; j < int64(batchNumber); j++ { + if index*averageCount >= utils.BatchCreateUserOpsCounts { + break + } + lowAccountIndex := index*averageCount + j*utils.BatchCreateUserOpsCounts + highAccountIndex := averageCount + lowAccountIndex + if highAccountIndex > (j+1)*utils.BatchCreateUserOpsCounts { + highAccountIndex = (j + 1) * utils.BatchCreateUserOpsCounts + } + currentAccountIndex := j * utils.BatchCreateUserOpsCounts + w.ComputeAccountHash(uint32(lowAccountIndex), uint32(highAccountIndex), uint32(currentAccountIndex)) + } + }(i) + } + + for i := height + 1; i < int64(batchNumber); i++ { + totalCexAssets := utils.CexAssetsTotal{ + BeforeCEXTotalEquity: beforeTotalCexAssets.AfterCEXTotalEquity, + AfterCEXTotalEquity: 0, + BeforeCEXTotalDebt: beforeTotalCexAssets.AfterCEXTotalDebt, + AfterCEXTotalDebt: 0, + } + batchCreateUserWit := &utils.BatchCreateUserWitness{ + BeforeAccountTreeRoot: w.accountTree.Root(), + BeforeCexAssets: make([]utils.CexAssetInfo, utils.AssetCounts), + CreateUserOps: make([]utils.CreateUserOperation, utils.BatchCreateUserOpsCounts), + TotalCexAssets: totalCexAssets, + } + copy(batchCreateUserWit.BeforeCexAssets[:], w.cexAssets[:]) + for j := 0; j < len(w.cexAssets); j++ { + commitment := utils.ConvertAssetInfoToBytes(w.cexAssets[j]) + poseidonHasher.Write(commitment) + } + batchCreateUserWit.BeforeCEXAssetsCommitment = poseidonHasher.Sum(nil) + poseidonHasher.Reset() + + batchCreateUserWit.TotalCexAssets.AfterCEXTotalEquity = batchCreateUserWit.TotalCexAssets.BeforeCEXTotalEquity + batchCreateUserWit.TotalCexAssets.AfterCEXTotalDebt = batchCreateUserWit.TotalCexAssets.BeforeCEXTotalDebt + + for j := i * utils.BatchCreateUserOpsCounts; j < (i+1)*utils.BatchCreateUserOpsCounts; j++ { + w.ExecuteBatchCreateUser(uint32(j), uint32(i), batchCreateUserWit) + index := uint32(j) - uint32(i)*utils.BatchCreateUserOpsCounts + batchCreateUserWit.TotalCexAssets.AfterCEXTotalEquity = utils.SafeAdd( + batchCreateUserWit.TotalCexAssets.AfterCEXTotalEquity, batchCreateUserWit.CreateUserOps[index].TotalEquity) + batchCreateUserWit.TotalCexAssets.AfterCEXTotalDebt = utils.SafeAdd( + batchCreateUserWit.TotalCexAssets.AfterCEXTotalDebt, batchCreateUserWit.CreateUserOps[index].TotalDebt) + } + for j := 0; j < len(w.cexAssets); j++ { + commitment := utils.ConvertAssetInfoToBytes(w.cexAssets[j]) + poseidonHasher.Write(commitment) + } + batchCreateUserWit.AfterCEXAssetsCommitment = poseidonHasher.Sum(nil) + poseidonHasher.Reset() + batchCreateUserWit.AfterAccountTreeRoot = w.accountTree.Root() + + // compute batch commitment + batchCreateUserWit.BatchCommitment = poseidon.PoseidonBytes( + batchCreateUserWit.BeforeAccountTreeRoot, + batchCreateUserWit.AfterAccountTreeRoot, + batchCreateUserWit.BeforeCEXAssetsCommitment, + batchCreateUserWit.AfterCEXAssetsCommitment) + + var serializeBuf bytes.Buffer + enc := gob.NewEncoder(&serializeBuf) + err := enc.Encode(batchCreateUserWit) + if err != nil { + panic(err.Error()) + } + witness := BatchWitness{ + Height: int64(i), + WitnessData: base64.StdEncoding.EncodeToString(serializeBuf.Bytes()), + Status: StatusPublished, + } + accPrunedVersion := bsmt.Version(atomic.LoadInt64(&w.currentBatchNumber) + 1) + ver, err := w.accountTree.Commit(&accPrunedVersion) + if err != nil { + fmt.Println("ver is ", ver) + panic(err.Error()) + } + w.ch <- witness + } + close(w.ch) + <-w.quit + fmt.Println("cex assets info is ", w.cexAssets) + fmt.Printf("witness run finished, the account tree root is %x\n", w.accountTree.Root()) +} + +func (w *Witness) GetCexAssets(wit *BatchWitness) ([]utils.CexAssetInfo, utils.CexAssetsTotal) { + witness := utils.DecodeBatchWitness(wit.WitnessData) + if witness == nil { + panic("decode invalid witness data") + } + cexAssetsInfo := utils.RecoverAfterCexAssets(witness) + totalCexAssets := utils.RecoverAfterTotalCexAssets(witness) + fmt.Println("recover cex assets successfully") + return cexAssetsInfo, totalCexAssets +} + +func (w *Witness) WriteBatchWitnessToDB() { + datas := make([]BatchWitness, 1) + for witness := range w.ch { + datas[0] = witness + err := w.witnessModel.CreateBatchWitness(datas) + if err != nil { + panic("create batch witness failed " + err.Error()) + } + atomic.StoreInt64(&w.currentBatchNumber, witness.Height) + if witness.Height%100 == 0 { + fmt.Println("save batch ", witness.Height, " to db") + } + } + w.quit <- 0 +} + +func (w *Witness) ComputeAccountHash(accountIndex uint32, highAccountIndex uint32, currentIndex uint32) { + poseidonHasher := poseidon.NewPoseidon() + for i := accountIndex; i < highAccountIndex; i++ { + w.accountHashChan[i-currentIndex] <- utils.AccountInfoToHash(&w.ops[i], &poseidonHasher) // w.ops: AccountInfo + } +} + +func (w *Witness) ExecuteBatchCreateUser(accountIndex uint32, currentNumber uint32, batchCreateUserWit *utils.BatchCreateUserWitness) { + index := accountIndex - currentNumber*utils.BatchCreateUserOpsCounts + account := w.ops[accountIndex] + batchCreateUserWit.CreateUserOps[index].BeforeAccountTreeRoot = w.accountTree.Root() + accountProof, err := w.accountTree.GetProof(uint64(account.AccountIndex)) + if err != nil { + panic(err.Error()) + } + copy(batchCreateUserWit.CreateUserOps[index].AccountProof[:], accountProof[:]) + for p := 0; p < len(account.Assets); p++ { + // update cexAssetInfo + w.cexAssets[account.Assets[p].Index].TotalBalance = utils.SafeAddInt64(w.cexAssets[account.Assets[p].Index].TotalBalance, account.Assets[p].Balance) + } + // update account tree + accountHash := <-w.accountHashChan[index] + err = w.accountTree.Set(uint64(account.AccountIndex), accountHash) + if err != nil { + panic(err.Error()) + } + batchCreateUserWit.CreateUserOps[index].AfterAccountTreeRoot = w.accountTree.Root() + batchCreateUserWit.CreateUserOps[index].AccountIndex = account.AccountIndex + batchCreateUserWit.CreateUserOps[index].AccountIdHash = account.AccountId + batchCreateUserWit.CreateUserOps[index].Assets = account.Assets + batchCreateUserWit.CreateUserOps[index].TotalEquity = account.TotalEquity.Uint64() + batchCreateUserWit.CreateUserOps[index].TotalDebt = account.TotalDebt.Uint64() +} diff --git a/merkle_groth16/src/witness/witness/witness_model.go b/merkle_groth16/src/witness/witness/witness_model.go new file mode 100644 index 0000000..6930b1b --- /dev/null +++ b/merkle_groth16/src/witness/witness/witness_model.go @@ -0,0 +1,157 @@ +package witness + +import ( + "merkleverifytool/merkle_groth16/src/utils" + "time" + + "gorm.io/gorm" +) + +const ( + StatusPublished = iota + StatusReceived + StatusFinished +) + +const ( + TableNamePrefix = `witness` +) + +type ( + WitnessModel interface { + CreateBatchWitnessTable() error + DropBatchWitnessTable() error + GetLatestBatchWitnessHeight() (height int64, err error) + GetBatchWitnessByHeight(height int64) (witness *BatchWitness, err error) + UpdateBatchWitnessStatus(witness *BatchWitness, status int64) error + GetLatestBatchWitness() (witness *BatchWitness, err error) + GetLatestBatchWitnessByStatus(status int64) (witness *BatchWitness, err error) + CreateBatchWitness(witness []BatchWitness) error + GetRowCounts() (count []int64, err error) + } + + defaultWitnessModel struct { + table string + DB *gorm.DB + } + + BatchWitness struct { + gorm.Model + Height int64 `gorm:"index:idx_height,unique"` + WitnessData string + Status int64 `gorm:"index"` + } +) + +func NewWitnessModel(db *gorm.DB, suffix string) WitnessModel { + return &defaultWitnessModel{ + table: TableNamePrefix + suffix, + DB: db, + } +} + +func (m *defaultWitnessModel) TableName() string { + return m.table +} + +func (m *defaultWitnessModel) CreateBatchWitnessTable() error { + return m.DB.Table(m.table).AutoMigrate(BatchWitness{}) +} + +func (m *defaultWitnessModel) DropBatchWitnessTable() error { + return m.DB.Migrator().DropTable(m.table) +} + +func (m *defaultWitnessModel) GetLatestBatchWitnessHeight() (batchNumber int64, err error) { + var height int64 + dbTx := m.DB.Table(m.table).Select("height").Order("height desc").Limit(1).Find(&height) + if dbTx.Error != nil { + return 0, utils.DbErrSqlOperation + } else if dbTx.RowsAffected == 0 { + return 0, utils.DbErrNotFound + } + return height, nil +} + +func (m *defaultWitnessModel) GetLatestBatchWitness() (witness *BatchWitness, err error) { + var height int64 + dbTx := m.DB.Table(m.table).Debug().Select("height").Order("height desc").Limit(1).Find(&height) + if dbTx.Error != nil { + return nil, dbTx.Error + } else if dbTx.RowsAffected == 0 { + return nil, utils.DbErrNotFound + } + + return m.GetBatchWitnessByHeight(height) +} + +func (m *defaultWitnessModel) GetLatestBatchWitnessByStatus(status int64) (witness *BatchWitness, err error) { + dbTx := m.DB.Table(m.table).Unscoped().Where("status = ?", status).Limit(1).Find(&witness) + if dbTx.Error != nil { + return nil, utils.DbErrSqlOperation + } else if dbTx.RowsAffected == 0 { + return nil, utils.DbErrNotFound + } + return witness, nil +} + +func (m *defaultWitnessModel) GetBatchWitnessByHeight(height int64) (witness *BatchWitness, err error) { + dbTx := m.DB.Table(m.table).Where("height = ?", height).Limit(1).Find(&witness) + if dbTx.Error != nil { + return nil, utils.DbErrSqlOperation + } else if dbTx.RowsAffected == 0 { + return nil, utils.DbErrNotFound + } + return witness, nil +} + +func (m *defaultWitnessModel) CreateBatchWitness(witness []BatchWitness) error { + dbTx := m.DB.Table(m.table).Create(witness) + if dbTx.Error != nil { + return utils.DbErrSqlOperation + } + return nil +} + +func (m *defaultWitnessModel) UpdateBatchWitnessStatus(witness *BatchWitness, status int64) error { + dbTx := m.DB.Table(m.table).Where("height = ?", witness.Height).Updates(BatchWitness{ + Model: gorm.Model{ + UpdatedAt: time.Now(), + }, + Status: status, + }) + if dbTx.Error != nil { + return utils.DbErrSqlOperation + } + return nil +} + +func (m *defaultWitnessModel) GetRowCounts() (counts []int64, err error) { + var count int64 + dbTx := m.DB.Table(m.table).Count(&count) + if dbTx.Error != nil { + return nil, dbTx.Error + } + counts = append(counts, count) + var publishedCount int64 + dbTx = m.DB.Table(m.table).Where("status = ?", StatusPublished).Count(&publishedCount) + if dbTx.Error != nil { + return nil, dbTx.Error + } + counts = append(counts, publishedCount) + + var pendingCount int64 + dbTx = m.DB.Table(m.table).Where("status = ?", StatusReceived).Count(&pendingCount) + if dbTx.Error != nil { + return nil, dbTx.Error + } + counts = append(counts, pendingCount) + + var finishedCount int64 + dbTx = m.DB.Table(m.table).Where("status = ?", StatusFinished).Count(&finishedCount) + if dbTx.Error != nil { + return nil, dbTx.Error + } + counts = append(counts, finishedCount) + return counts, nil +}