数据结构与算法-默克尔树(Merkle tree)

一、定义

哈希树(hash tree;Merkle tree)又称为默克尔树,因为概念是由拉尔夫·查尔斯·默克尔 1979 年提出并申请专利。

Merkle 树是一种首先在计算机科学领域实现的数据结构。

每个叶节点均以数据块的哈希作为标签,而除了叶节点以外的节点则以其子节点标签的加密哈希作为标签 ,如下图所示

二、默克尔树的作用

      能够有效地验证集合中元素的存在,而无需透露整个集合本身;是通过生成 Merkle 证明(也称为 Merkle 路径或身份验证路径)来完成的。

       准确性:默克尔树可以轻松访问和分析,以检查数据是否准确

       低存储:它们将块数据压缩为更小的大小,而不是单独存储每个交易。

       对于区块链至关重要:它们已经成为区块链网络运营和为用户服务的重要组成部分。

        

三、应用领域

        在区块链和去中心化技术的世界中,数据完整性至关重要。确保存储在区块链上的信息准确且防篡改是一项根本挑战。

        每个区块包含一组交易,默克尔树保证它们的一致性和有效性。通过将 Merkle 根包含在区块头中,区块链技术可以有效验证区块的内容。这使得网络节点能够验证交易的完整性,而无需存储和处理块内的每个单独交易,从而增强可扩展性并减少计算开销。

3.1、区块链核心应用体现在以下几个方面:

        1、交易数据的高效存储与验证
  • 在区块链中,每一个区块包含多笔交易记录。每笔交易经过哈希运算后形成一个哈希值,作为默克尔树的叶子节点。
  • 通过不断两两组合相邻节点并再次哈希,最终形成一个唯一的顶层哈希值,即默克尔根(Merkle Root)。
  • 区块头仅需存储这个默克尔根,而非所有交易的具体内容,大大减少了存储需求,同时允许任何人通过默克尔证明(Merkle Proof)快速验证某个特定交易是否存在于区块中,且未被篡改。
       2、轻量级客户端验证
  • 轻客户端(SPV,Simple Payment Verification)不需要下载完整的区块链,仅需同步区块头和相关的默克尔证明即可验证交易的有效性。
  • 客户端可以根据交易哈希请求相应的默克尔路径(从叶子节点到根节点的路径),通过重新计算哈希并验证路径上的哈希是否与区块头中的默克尔根匹配,来确认交易的真实性。
        3、快速检测数据变化
  • 当区块链的状态或交易集合发生更改时,新的交易会生成一个新的默克尔树,因此,默克尔根也会随之改变。
  • 这使得网络参与者能够迅速识别出哪些区块发生了变动,因为只要有一个交易不同,默克尔根就会完全不同,从而实现对整个数据集合的高效完整性验证。
        4、简化共识机
  • 在一些区块链协议中,比如比特币、以太坊等,新区块的生成不仅要求包含前一块的哈希值,还包括前一块的默克尔根,这样就确保了新区块不仅继承了历史交易序列,还保证了这些交易未经篡改。
        5、跨链通信和分片技术
  • 在某些高级区块链架构中,默克尔树用于跨链通信和分片(Sharding)技术,使得不同分片或不同区块链之间的信息交互可以通过简洁且安全的方式进行验证。

3.2、其它应用领域

        1、数据库和文件系统
  • 在分布式数据库系统中,默克尔树可用于验证大规模数据集的完整性,例如CDN内容分发网络中用来验证内容没有被篡改。
  • 在分布式文件系统如Git版本控制系统中,默克尔树被用来追踪文件内容的变化,并高效地处理文件的不同版本之间的差异。

        2、认证和安全
  • 在安全领域,默克尔树可应用于构建信任网络,例如在证书透明度日志(Certificate Transparency logs)中,谷歌Chrome浏览器利用默克尔树来公开SSL/TLS证书的发行情况,以便公众监督和发现潜在的安全问题。
        3、内容寻址存储(Content Addressable Storage, CAS)
  • IPFS(InterPlanetary File System)这样的去中心化存储系统利用默克尔哈希树(Merkle DAG)来标识和定位文件,确保内容的唯一性和可验证性。
        4、零知识证明(Zero-Knowledge Proofs)
  • 默克尔树可以帮助构建零知识证明系统,允许一方证明他们知道某些信息而无需透露具体内容,这在隐私保护和身份验证中有重要价值。
        5、多方数据审计
  • 在多方协作或者云存储场景下,默克尔树可以用于数据完整性校验,让各方能够独立地验证数据的正确性,而不必完全共享全部数据。
        6、分布式账本和审计
  • 除了金融交易,默克尔树也可用于其他类型的分布式账本,比如供应链管理,药品追溯系统等,以确保记录的不可篡改性和透明性。

四、默克尔树剖析

        Merkle Tree 具有简单的结构,可以通过检查其组件来理解。

  • 叶节点:这些是树的最底部节点,代表各个数据片段。在区块链上下文中,每个叶节点可以代表一个事务或一个块。

  • 内部节点:这些是树中的非叶节点。每个内部节点都与两个子节点相关联。内部节点的哈希值是根据其子节点的串联哈希值计算的。

  • 根节点:默克尔树的顶部是根节点。它代表了整个数据集的完整性。根节点的哈希值是根据其直接子节点的哈希值计算得出的,并且它充当整个数据集的紧凑表示。

五、默克尔树如何工作

        看一个简化的示例:

        假设有一组四个数据元素:A、B、C 和 D。要为这些数据元素创建 Merkle 树,遵循步骤

  1. 创建叶节点:为每个数据元素创建叶节点,计算每个数据元素添加到树中时的哈希值。
  2. 配对和哈希:将相邻的叶节点配对并计算每对的哈希值。在我们的示例中,我们将 A 和 B 配对来创建 AB_hash,将 C 和 D 配对来创建 CD_hash。
  3. 重复:如果剩余的节点超过两个,则继续配对和散列,直到只剩下一个节点,即根节点。在我们的例子中,AB_hash 和 CD_hash 配对并散列以创建 root_hash。
  4. 根哈希:root_hash 是代表整个数据集的最终哈希。这是存储在区块链上的价值。

现在,要验证特定数据元素(假设数据元素 C)的完整性,您只需要知道 root_hash 和从 C 到根的路径。该路径由 C 和根之间的所有节点的哈希值组成。通过重新计算这条路径上的哈希值并与root_hash进行比较,可以快速验证C是否被篡改。

现在我们在solidity中创建一个合约来演示:

// SPDX-License-Identifier: MIT

// import "hardhat/console.sol";

pragma solidity ^0.8.20;

contract MerkleProof {

    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf,
        uint index
    ) public pure returns (bool) {
        bytes32 hash = leaf;

        for (uint i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];

            if (index % 2 == 0) {
                hash = keccak256(abi.encodePacked(hash, proofElement));
            } else {
                hash = keccak256(abi.encodePacked(proofElement, hash));
            }

            index = index / 2;
        }

        return hash == root;
    }
}

contract TestMerkleProof is MerkleProof {
    bytes32[] public hashes;

    constructor() {
        string[4] memory transactions = [
            "A",
            "B",
            "C",
            "D"
        ];

        for (uint i = 0; i < transactions.length; i++) {
            hashes.push(keccak256(abi.encodePacked(transactions[i])));
        }

        uint n = transactions.length;
        uint offset = 0;

        while (n > 0) {
            for (uint i = 0; i < n - 1; i += 2) {
                hashes.push(
                    keccak256(
                        abi.encodePacked(hashes[offset + i], hashes[offset + i + 1])
                    )
                );
            }
            offset += n;
            n = n / 2;
        }
    }

    function getRoot() public view returns (bytes32) {
        return hashes[hashes.length - 1];
    }

 
}

现在我想验证一下 绿色的C,是否正确

这个时候需要传递verify函数的参数为:

第三个叶子 C的 hash为:

0x017e667f4b8c174291d1543c466717566e206df1bfd6f30271055ddafdb18f72

树根:

0x4e2b32e9c7b10ee15f38192ed65b95656a84e32531157b9384ef0d3cabe95956

索引为:

2

路径证明为:上面红色的 hash(D)和hash(hash(A),hash(B))

0x6c3fd336b49dcb1c57dd4fbeaf5f898320b0da06a5ef64e798c6497600bb79f2

0x69de756fea16daddbbdccf85c849315f51c0b50d111e3d2063cab451803324a0

最终的验证结果是OK的

六、其它的默克尔树的变体

        1、Sparse Merkle Trees

        稀疏 Merkle 树是针对数据集稀疏的场景的优化,这意味着只有一小部分数据是非空的。在标准 Merkle Tree 中,为空数据创建节点会消耗存储和计算资源。稀疏 Merkle 树通过省略空数据的节点来避免这种情况,从而使它们对于稀疏数据集更加有效。

        2、Merkle Patricia Trees

Merkle Patricia 树经常在以太坊中使用,是 Merkle 树的变体,旨在存储和验证键值对。这些树允许基于密钥有效地存储和检索数据,并且非常适合在去中心化应用程序(dApp)中使用。

        3、Merkle Mountain Ranges

Merkle Mountain Ranges 是一种先进的数据结构,旨在提高数据包含证明的效率。它们在数据随时间追加的场景中特别有用,例如在区块链系统中。 Merkle Mountain Ranges 减少了包含证明的大小,使其对于大型且不断变化的数据集更具可扩展性。

        4、......等等

听说未来以太坊会使用 Verkle 树 ,未来有机会再试一下

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值