How to examine whether my assets are in the Merkle Tree
What is Merkle Tree
Merkle Tree (Merkle Tree), also known as Hash Tree, is a data structure and usually a binary tree, which calculates the hash value layer by layer, from the leaf node to the top root node in a specific way.
CoinEx Merkle Tree Definition
Node Info
The info stored in each tree node includes: 1. Node hash value. 2. The number of coins covered by user asset snapshots (take BTC, ETH, USDT as examples).
Hash Value{"BTC":"BTC Balance","ETH":"ETH Balance","USDT":"USDT Balance"}
3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"}
3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"}
Hash Rules
Leaf Node
hash = SHA256(nonce + balances)
E.g.
hash = SHA256('79b0319c0003e6b5f149525a6677f1bcb7851e9bd7bf05c7089576d38dd95efa{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"}')
Among them, CoinEx will assign a unique nonce to each user, which can be queried in the audit data, while "balances" is a json string composed of the number of coins covered by the user's asset snapshot, such as: {"BTC":"1.023", "ETH":"0", "USDT":"20.2343322"}, follow the below rules:
1. The json string is in a compact format without newlines and spaces.
2. Remove the invalid 0 at the end of the coin amount, and retain 8 digits of precision.
3. Coin names are sorted alphabetically.
1. The json string is in a compact format without newlines and spaces.
2. Remove the invalid 0 at the end of the coin amount, and retain 8 digits of precision.
3. Coin names are sorted alphabetically.
Parent Node
hash = SHA256(h1 + h2 + balances)
· h1: The hash value of Child Node on the left
· h2: The hash value of Child Node on the right
· balances: The balances of Child Node on the left + the balances of Child Node on the right, adding up the balance of the same asset
· h2: The hash value of Child Node on the right
· balances: The balances of Child Node on the left + the balances of Child Node on the right, adding up the balance of the same asset
E.g.
Child Node on the left:
3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"}
Child Node on the right:
e9fcf13c9cdae1dfab4c2ea60d8acb62603b5f8430e265bf4b3f901fc4e45fe9{"BTC":"0.48","USDT":"100.24534"}
Hash of Parent Node:
hash = SHA256(3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"})
Padding Node Rules
Building a complete Merkle Tree (full binary) requires 2^n leaf node data, however, the actual number of data may not be sufficient or even. In this case, if a node k doesn't have a sibling node, padding will automatically generate a sibling node k', and this sibling node hash(k') = hash(k), and the number of coins in node k' will be entirely set as zero.
E.g. Node K:
3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"}
Hash of Parent Node:
hash = SHA256(3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"})
Validation Rules
1. Validation principles: According to the definition of Merkle Tree, the hash value of the parent node is calculated from the leaf node of the user itself, until the hash value of the root node is obtained, and the hash value of the root node is compared. If the two are equal, then the validation is approved; if not, the validation is failed.
2. For example, the following json text first calculates the hash of the leaf node based on the self data, and then calculates the hash of the parent node with each sibling node on the path, and the resulting node hash should be equal to the root node hash, with the equivalent balances. (Please note that there is no sibling node when the sibling node displays empty, and the parent node hash is calculated according to the padding node rules)
2. For example, the following json text first calculates the hash of the leaf node based on the self data, and then calculates the hash of the parent node with each sibling node on the path, and the resulting node hash should be equal to the root node hash, with the equivalent balances. (Please note that there is no sibling node when the sibling node displays empty, and the parent node hash is calculated according to the padding node rules)
Merkle Tree path data (json text):
{ "root": { "balances": { "CET": "14373493.24153457", "ETH": "104543541.61407674", "USDC": "2419089.97192761", "USDT": "4836955256.81519091" }, "hash": "c01a6c3b0fedde2a066f8a38968e40420c0b0742bb4ccda571a4349fb1c64f18" }, "self": { "balances": { "USDT": "3990000" }, "nonce": "9885b5df557ba3cec41a74347719a8a37d5792a1cf7f0e216510d60dd1b1fc95" }, "path": [ { "balances": { "CET": "10000.01994324", "USDC": "40000", "USDT": "1004.13066254" }, "hash": "01f94322a74bee4431b809406997cee575bed3b85ef36b4ba3b2ff9dd140f99a", "pos": "left" }, { "balances": { "CET": "1000", "ETH": "0.90765244", "USDT": "143151.30772787" }, "hash": "c99051749a3a83e60d1338454382044f9d7236928cfdc4b7fca1a7cc7450c7a6", "pos": "left" }, { "balances": { "CET": "548800.95984406", "ETH": "50000.00001068", "USDC": "9986.281143", "USDT": "62752.29303779" }, "hash": "173a9a7ef562f1b537def5d58167d7402c8e268b1423c5f8e1d806cd0c524344", "pos": "left" }, { "balances": { "CET": "10023.01105146", "ETH": "9900.74253772", "USDT": "22516389.78119662" }, "hash": "d79bd6c7a1536db199747061c119f98f86d99f9c7a8350fe63c6314ef3e8a24c", "pos": "right" }, { "balances": { "CET": "5393361.46905487", "ETH": "23711.51394236", "USDC": "201404.61667184", "USDT": "230211961.3159725" }, "hash": "115551fd3f85328d32858cc6d1bea9c1274984b0f8abba8140752f9d55e48277", "pos": "left" }, { "balances": { "CET": "1554146.8440552", "ETH": "100.0040003", "USDC": "160006.6", "USDT": "11201397.46983634" }, "hash": "7b92897456af56f473b75d5e009be090726ad64694fd27971dc46f2631db51d8", "pos": "right" }, { "balances": { "CET": "4712634.46013087", "ETH": "91469.27009748", "USDC": "1002463.00913027", "USDT": "830313049.62523756" }, "hash": "0905786187f2c582902b84175813b063c31755a2930b25dee7ba005f7c8a7cf9", "pos": "right" }, { "balances": { "CET": "2143526.47745487", "ETH": "104368359.17583576", "USDC": "1005229.4649825", "USDT": "3738515550.89151969" }, "hash": "41dc5da7477fab3ac6fe233a1bf1bec0d26d0f5dea679b5d91f2f09c488fcb2f", "pos": "right" } ] }
How to Validate
1. Log in to your CoinEx account, click "Proof of Reserve", enter the page, and click "Copy my audition".
2. Paste the copied audit data into a text file, such as merkle_proof_file.json.
3. Download [Open Source Verification Tool] provided by CoinEx.
4. Unzip the validation tool, and put the decompressed file and merkle_proof_file.json under the same folder, such as ~/Downloads/proof-of-reserves
5. Open the terminal (MacOS: Terminal App; Windows: Terminal or PowerShell), enter cd ~/Downloads/proof-of-reserves and go to the above directory.
6. Enter the following command to validate your data: MacOS / Linux:
./proof-of-reserves -f merkle_proof_file.json
Windows:
./proof-of-reserves.exe -f merkle_proof_file.json
7. If the validation is approved, the prompt will display as Merkle tree path validation passed. If not, the prompt will display as Merkle tree path validation failed.
You can also refer to the instructions given in this document and [Open Source Verification Tool Source Code] to create your own verification tool.