如何查看自己的账户资产是否在默克尔树中
什么是默克尔树
默克尔树(Merkle Tree)又叫哈希树(Hash Tree),是一种数据结构,通常是一棵二叉树,它以特定的方式从叶节点逐层向上计算hash值,直到顶部根节点。
CoinEx默克尔树定义
节点信息
每个树节点存储信息包括: 1. 节点hash值; 2. 用户资产快照覆盖的币种数量(以BTC, ETH, USDT为例)
hash值{"BTC":"BTC数量","ETH":"ETH数量","USDT":"USDT数量"}
3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"}
3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"}
Hash规则
叶子节点
hash = SHA256(nonce + balances)
如:
hash = SHA256('79b0319c0003e6b5f149525a6677f1bcb7851e9bd7bf05c7089576d38dd95efa{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"}')
其中,CoinEx会给每个用户分配一个唯一的nonce,这个nonce可以在审计数据中查询到;balances是用户资产快照覆盖的币种数量组成的json字符串,如:{"BTC":"1.023","ETH":"0","USDT":"20.2343322"},遵循如下规则:
1. json字符串为紧凑格式,无换行和空格。
2. 币种数量去除末尾无效0,保留8位精度。
3. 币种名按英文字母顺序排序。
1. json字符串为紧凑格式,无换行和空格。
2. 币种数量去除末尾无效0,保留8位精度。
3. 币种名按英文字母顺序排序。
父节点
hash = SHA256(h1 + h2 + balances)
· h1: 左边子节点的哈希值
· h2: 右边子节点的哈希值
· balances: 左边子节点的balances + 右边子节点的balances,把相同币种的余额加起来
· h2: 右边子节点的哈希值
· balances: 左边子节点的balances + 右边子节点的balances,把相同币种的余额加起来
如:
左子节点:
3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"}
右子节点:
e9fcf13c9cdae1dfab4c2ea60d8acb62603b5f8430e265bf4b3f901fc4e45fe9{"BTC":"0.48","USDT":"100.24534"}
父节点的哈希:
hash = SHA256(3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"})
填充节点规则
构建一棵完整的Merkle Tree(满二叉树)需要2^n个叶子结点数据,但实际情况数据的数量未必满足且还可能是奇数。在此种情况下,如果一个节点k没有兄弟节点,则自动填充(padding)生成一个兄弟节点k',该兄弟节点hash(k')=hash(k),节点k'的币种数量全置零。
如节点K:
3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"}
父节点的哈希:
hash = SHA256(3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d3d101072de66342c711e369e1e98f48c89c412e7246918ae6466a5c72e73003d{"BTC":"1.023","ETH":"0.56","USDT":"20.2343322"})
验证规则
1. 验证原理:根据默克尔树定义,从用户本身叶子结点开始往上计算父节点hash值,一直到根节点得出hash(root),对比根节点的hash值,如果二者相等则验证通过,不等则验证不通过。
2. 例如下面的json文本,根据用户(self)数据计算叶子节点的哈希,然后与路径(path)上的每个兄弟节点计算父节点哈希,最后得到的节点哈希应等于根节点(root)哈希,且余额(balances)也相等。(注意兄弟节点为空时表明没有兄弟节点,按填充节点规则计算父节点哈希)
2. 例如下面的json文本,根据用户(self)数据计算叶子节点的哈希,然后与路径(path)上的每个兄弟节点计算父节点哈希,最后得到的节点哈希应等于根节点(root)哈希,且余额(balances)也相等。(注意兄弟节点为空时表明没有兄弟节点,按填充节点规则计算父节点哈希)
默克尔树路径数据json文本:
{ "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" } ] }
验证步骤
1. 登录您的CoinEx账户,点击“储备金证明”,进入“储备金证明”页面,点击“复制我的审计数据”。
2. 把复制的审计数据粘贴保存到文本文件,如 merkle_proof_file.json。
3. 下载CoinEx提供的[开源验证工具]。
4. 解压验证工具,将解压后的文件和 merkle_proof_file.json 放到同一个文件夹,如 ~/Downloads/proof-of-reserves
5. 打开终端(MacOS: 终端App;Windows: 终端或PowerShell),输入 cd ~/Downloads/proof-of-reserves 进入上面的目录。
6. 输入以下命令来验证您的数据:MacOS / Linux:
./proof-of-reserves -f merkle_proof_file.json
Windows:
./proof-of-reserves.exe -f merkle_proof_file.json
7. 如果验证通过,提示 Merkle tree path validation passed,验证失败则提示 Merkle tree path validation failed。
您也可以参考本篇文档给出的说明以及[开源验证工具源码]自行编写程序验证。