区块链原理与技术02:区块链的数据结构04(区块结构)

文章目录

    • [1. 区块的基本组成](#1. 区块的基本组成)
      • [1.1 为什么要分成区块头和区块体?](#1.1 为什么要分成区块头和区块体?)
    • [2. 区块头 Header 详解](#2. 区块头 Header 详解)
      • [2.1 Version:版本号](#2.1 Version:版本号)
      • [2.2 Prev-block Hash:前一区块哈希](#2.2 Prev-block Hash:前一区块哈希)
      • [2.3 Merkle-root:交易集合的总摘要](#2.3 Merkle-root:交易集合的总摘要)
      • [2.4 Timestamp:时间戳](#2.4 Timestamp:时间戳)
      • [2.5 Bits:难度目标](#2.5 Bits:难度目标)
      • [2.6 Nonce:随机数](#2.6 Nonce:随机数)
      • [2.7 小例子:工作量证明如何体现"难"?](#2.7 小例子:工作量证明如何体现“难”?)
    • [3. 区块体 Body 详解](#3. 区块体 Body 详解)
    • [4. 链式结构:区块为什么能连成链](#4. 链式结构:区块为什么能连成链)
      • [4.1 链式结构的形成过程](#4.1 链式结构的形成过程)
      • [4.2 为什么链式结构能防篡改?](#4.2 为什么链式结构能防篡改?)
      • [4.3 完整副本是什么意思?](#4.3 完整副本是什么意思?)
    • [5. 哈希列表与二叉树](#5. 哈希列表与二叉树)
      • [5.1 Hash List:哈希列表](#5.1 Hash List:哈希列表)
      • [5.2 Binary Tree:二叉树](#5.2 Binary Tree:二叉树)
    • [6. 默克尔树 Merkle Tree](#6. 默克尔树 Merkle Tree)
      • [6.1 Merkle Tree 的构造过程](#6.1 Merkle Tree 的构造过程)
      • [6.2 如果交易数量是奇数怎么办?](#6.2 如果交易数量是奇数怎么办?)
      • [6.3 Coinbase 交易是什么?](#6.3 Coinbase 交易是什么?)
      • [6.4 Merkle Proof:如何证明某笔交易在区块中?](#6.4 Merkle Proof:如何证明某笔交易在区块中?)
      • [6.5 Merkle Tree 的优势](#6.5 Merkle Tree 的优势)
    • [7. 以太坊中的 MPT:Merkle Patricia Tree](#7. 以太坊中的 MPT:Merkle Patricia Tree)
      • [7.1 为什么以太坊需要 MPT?](#7.1 为什么以太坊需要 MPT?)
      • [7.2 Trie 树是什么?](#7.2 Trie 树是什么?)
      • [7.3 Patricia Tree 是什么?](#7.3 Patricia Tree 是什么?)
      • [7.4 Merkle + Patricia + Trie 的结合](#7.4 Merkle + Patricia + Trie 的结合)
      • [7.5 MPT 中的节点类型](#7.5 MPT 中的节点类型)
      • [7.6 MPT 中 key 和 value 是什么?](#7.6 MPT 中 key 和 value 是什么?)
      • [7.7 以太坊区块中的三棵树](#7.7 以太坊区块中的三棵树)
      • [7.8 为什么说状态 MPT 会不断更新?](#7.8 为什么说状态 MPT 会不断更新?)
      • [7.9 MPT 能支持哪些查询?](#7.9 MPT 能支持哪些查询?)
    • [8. SPV:简化支付验证](#8. SPV:简化支付验证)
      • [8.1 SPV 解决什么问题?](#8.1 SPV 解决什么问题?)
      • [8.2 SPV 验证的是"支付",不是完整交易合法性](#8.2 SPV 验证的是“支付”,不是完整交易合法性)
      • [8.3 SPV 的验证思路](#8.3 SPV 的验证思路)
      • [8.4 举例:如何判断一笔交易有 6 个确认?](#8.4 举例:如何判断一笔交易有 6 个确认?)
    • [9. 布隆过滤器 Bloom Filter](#9. 布隆过滤器 Bloom Filter)
      • [9.1 布隆过滤器能回答什么问题?](#9.1 布隆过滤器能回答什么问题?)
      • [9.2 布隆过滤器的基本原理](#9.2 布隆过滤器的基本原理)
      • [9.3 为什么会出现误判?](#9.3 为什么会出现误判?)
      • [9.4 布隆过滤器在 SPV 中的作用](#9.4 布隆过滤器在 SPV 中的作用)
      • [9.5 例题:查找过去 10 天与某智能合约相关的交易](#9.5 例题:查找过去 10 天与某智能合约相关的交易)
    • [10. 综合例题与答疑](#10. 综合例题与答疑)
      • [10.1 例题一:为什么修改一笔交易会影响区块哈希?](#10.1 例题一:为什么修改一笔交易会影响区块哈希?)
      • [10.2 例题二:区块头为什么不直接保存所有交易?](#10.2 例题二:区块头为什么不直接保存所有交易?)
      • [10.3 例题三:SPV 能不能发现双花?](#10.3 例题三:SPV 能不能发现双花?)
      • [10.4 例题四:Merkle Tree 和 MPT 有什么区别?](#10.4 例题四:Merkle Tree 和 MPT 有什么区别?)
      • [10.5 例题五:布隆过滤器为什么不能删除元素?](#10.5 例题五:布隆过滤器为什么不能删除元素?)
    • [11. 复习速记表](#11. 复习速记表)
      • [11.1 区块结构速记](#11.1 区块结构速记)
      • [11.2 树结构速记](#11.2 树结构速记)
      • [11.3 高频问答](#11.3 高频问答)
        • Q1:区块链为什么难以篡改?
        • [Q2:Merkle-root 的核心意义是什么?](#Q2:Merkle-root 的核心意义是什么?)
        • [Q3:SPV 节点保存什么?](#Q3:SPV 节点保存什么?)
        • [Q4:Bloom Filter 的判断结果怎么理解?](#Q4:Bloom Filter 的判断结果怎么理解?)
        • [Q5:以太坊为什么要用 MPT?](#Q5:以太坊为什么要用 MPT?)
    • [12. 一页总结](#12. 一页总结)

1. 区块的基本组成

区块链中的每一个区块通常由两大部分组成:

text 复制代码
区块 Block
├── 区块头 Header:存放用于连接、校验、共识、索引的元数据
└── 区块体 Body:存放本区块打包的交易数据

也可以把区块理解为一本账本中的"一页":

  • 区块头像这一页账本的页眉,记录版本号、上一页编号、摘要、时间等信息;
  • 区块体像这一页账本的正文,记录实际发生的交易;
  • 前一区块哈希像页码引用,使每一页都能按顺序连接起来;
  • Merkle-root像本页所有交易内容的总摘要,用于快速证明某笔交易是否在这个区块中。

1.1 为什么要分成区块头和区块体?

区块头和区块体的分离可以同时兼顾两类需求:

需求 对应结构 作用
快速验证区块是否有效 区块头 区块头很小,便于传播、存储和验证
保存真实交易记录 区块体 存放完整交易信息
轻节点验证交易 区块头 + Merkle proof 不下载完整区块体,也能验证交易存在性
防篡改 前一区块哈希 + Merkle-root 改一笔交易会影响 Merkle-root,进一步影响区块哈希

**一句话总结:**区块头负责"证明和连接",区块体负责"记录和承载"。


2. 区块头 Header 详解

区块头是区块中非常核心的部分。以比特币为例,一个区块头通常包含以下字段:

字段 常见大小 作用
Version 4 Byte 当前区块使用的软件/协议版本
Prev-block Hash 32 Byte 前一区块的哈希值,用来把区块串成链
Merkle-root 32 Byte 本区块所有交易经过 Merkle Tree 计算后的根哈希
Timestamp 4 Byte 区块生成的大致时间
Bits 4 Byte 当前工作量证明的目标难度压缩表示
Nonce 4 Byte 挖矿时不断调整的随机数

2.1 Version:版本号

Version 表示区块遵循的协议规则或软件版本。区块链系统不断升级时,需要通过版本号识别区块使用的规则。

例子:

如果一次协议升级引入了新的交易验证规则,新节点可以通过区块头中的版本号判断:

text 复制代码
Version = 1:旧规则
Version = 2:新规则

这有点像软件版本:旧版软件可能不支持新版文件格式。

2.2 Prev-block Hash:前一区块哈希

Prev-block Hash 是区块链形成"链"的关键。每个区块头中都保存前一个区块的哈希值。

text 复制代码
Block 1 Hash → 写入 Block 2 的 Prev-block Hash
Block 2 Hash → 写入 Block 3 的 Prev-block Hash
Block 3 Hash → 写入 Block 4 的 Prev-block Hash

如果有人修改了 Block 2 的内容,Block 2 的哈希就会改变,那么 Block 3 中记录的 Prev-block Hash 就对不上,整条链都会暴露异常。

2.3 Merkle-root:交易集合的总摘要

Merkle-root 是区块体内所有交易的摘要结果。它不是直接把交易全部写进区块头,而是先把交易组织成 Merkle Tree,再把根节点哈希放到区块头里。

作用:

  1. 让区块头保持很小;
  2. 快速证明某笔交易是否属于某区块;
  3. 任意一笔交易被篡改,Merkle-root 都会变化;
  4. 支持 SPV 轻节点验证。

2.4 Timestamp:时间戳

Timestamp 表示区块生成的大致时间。它可以帮助节点判断区块顺序,也能辅助难度调整。

需要注意的是,区块时间戳不是普通意义上的精确服务器时间,而是链上节点接受范围内的近似时间。

2.5 Bits:难度目标

Bits 是工作量证明目标难度的压缩表示。矿工需要不断改变 Nonce,使区块头哈希满足难度要求。

简化理解:

text 复制代码
难度越高 → 目标值越小 → 找到合格哈希越难
难度越低 → 目标值越大 → 找到合格哈希越容易

2.6 Nonce:随机数

Nonce 是矿工挖矿时反复尝试的数。矿工不断改变 Nonce,然后计算区块头哈希,直到得到符合 Bits 难度要求的哈希。

简化过程如下:

text 复制代码
区块头 = Version + PrevHash + MerkleRoot + Timestamp + Bits + Nonce
Hash = SHA256(SHA256(区块头))
如果 Hash < 目标难度:挖矿成功
否则:Nonce + 1 后继续尝试

2.7 小例子:工作量证明如何体现"难"?

假设要求哈希值必须以 4 个 0 开头:

text 复制代码
0000a34f...  合格
00f19b3c...  不合格
1a04bccc...  不合格

矿工并不能直接"设计"一个合格哈希,只能不断改变 Nonce 试出来。因此,Nonce 可以理解为"试题答案草稿纸上的不断尝试"。


3. 区块体 Body 详解

区块体主要保存交易数据。不同区块链系统中,区块体的组织方式不同,但共同点是:它承载的是链上真正发生的业务数据。

在比特币中,区块体主要是交易列表:

text 复制代码
区块体 Body
├── 交易数量
├── 交易 1
├── 交易 2
├── 交易 3
└── ...

在以太坊中,区块不仅要记录交易,还要记录状态变化、收据等信息,因此以太坊区块头中会保存多棵树的根哈希,例如:

  • transaction root:交易树根;
  • state root:状态树根;
  • receipt root:收据树根。

区块体越大,完整节点存储和同步压力越大;区块头越小,轻节点验证越方便。


4. 链式结构:区块为什么能连成链

区块链是一个按照区块生成时间顺序连接形成的分布式数据库。连接的关键就是区块头中的 Prev-block Hash 字段。

4.1 链式结构的形成过程

假设有三个区块:

text 复制代码
Block A
Hash(A) = aaa111

Block B
Prev-block Hash = aaa111
Hash(B) = bbb222

Block C
Prev-block Hash = bbb222
Hash(C) = ccc333

最终结构为:

text 复制代码
Block A  ←  Block B  ←  Block C
 aaa111      bbb222      ccc333

其中,Block B 的区块头记录了 Block A 的哈希,Block C 的区块头记录了 Block B 的哈希。

4.2 为什么链式结构能防篡改?

如果攻击者修改 Block A 中的一笔交易:

  1. 交易内容变化;
  2. Block A 的 Merkle-root 变化;
  3. Block A 的区块头变化;
  4. Block A 的 Hash 变化;
  5. Block B 中保存的 Prev-block Hash 与新 Hash 对不上;
  6. 攻击者必须继续修改 Block B、Block C 以及后续所有区块;
  7. 还要重新完成这些区块的工作量证明。

因此,越早的区块越难篡改,因为后面叠加的区块越多,需要重做的计算量越大。

4.3 完整副本是什么意思?

PPT 中提到"形成了区块链表,也形成了一个完整的账本"。意思是:

  • 每个完整节点可以保存从创世区块到当前区块的全部数据;
  • 节点之间可以互相校验数据是否一致;
  • 任意节点不需要完全信任单个中心服务器,而是通过密码学哈希和共识规则验证账本。

5. 哈希列表与二叉树

PPT 中对比了 Hash list 和 Binary tree。

5.1 Hash List:哈希列表

Hash List 的基本思想是对每个数据块分别计算哈希,然后再把这些哈希组合成一个根哈希。

text 复制代码
DATA 0 → HASH 0
DATA 1 → HASH 1
DATA 2 → HASH 2
...
DATA n → HASH n

HASH 0 + HASH 1 + ... + HASH n → HASH root

缺点是:如果要证明某个数据在列表中,可能需要较多数据辅助验证。

5.2 Binary Tree:二叉树

二叉树是一种每个节点最多有两个子节点的数据结构。Merkle Tree 就是一种典型的哈希二叉树。

在 Merkle Tree 中:

  • 叶子节点通常是交易哈希;
  • 非叶子节点是左右子节点哈希拼接后再哈希;
  • 根节点是整棵树的摘要。

6. 默克尔树 Merkle Tree

Merkle Tree 是区块链中非常重要的数据结构。它的核心目标是:用一个根哈希代表大量交易,并支持高效的交易存在性证明。

6.1 Merkle Tree 的构造过程

假设一个区块中包含 4 笔交易:

text 复制代码
Tx1, Tx2, Tx3, Tx4

第一步:计算每笔交易的哈希。

text 复制代码
H1 = Hash(Tx1)
H2 = Hash(Tx2)
H3 = Hash(Tx3)
H4 = Hash(Tx4)

第二步:两两组合再哈希。

text 复制代码
H12 = Hash(H1 || H2)
H34 = Hash(H3 || H4)

第三步:继续组合到根节点。

text 复制代码
MerkleRoot = Hash(H12 || H34)

整体结构:

text 复制代码
                MerkleRoot
               /          \
             H12          H34
            /   \        /   \
          H1    H2     H3    H4
          |     |      |     |
         Tx1   Tx2    Tx3   Tx4

其中 || 表示拼接。

6.2 如果交易数量是奇数怎么办?

如果交易数量是奇数,常见做法是复制最后一个哈希,使其变成偶数个再继续计算。

例如有 5 笔交易:

text 复制代码
H1, H2, H3, H4, H5

可以处理为:

text 复制代码
H1, H2, H3, H4, H5, H5

然后两两组合。

6.3 Coinbase 交易是什么?

PPT 中提到 Coinbase 交易。它不是交易所 Coinbase,而是比特币区块中的特殊交易。

Coinbase 交易的作用:

  • 通常是区块中的第一笔交易;
  • 用来给矿工发放区块奖励和手续费;
  • 没有普通交易那样引用前一笔 UTXO;
  • 也会参与 Merkle Tree 的计算。

6.4 Merkle Proof:如何证明某笔交易在区块中?

假设要证明 Tx3 在区块中。完整节点不需要把所有交易都发给轻节点,只需要发送验证路径上的相邻哈希:

text 复制代码
已知:Tx3
需要:H4、H12、MerkleRoot
验证:
H3 = Hash(Tx3)
H34 = Hash(H3 || H4)
Root' = Hash(H12 || H34)
判断 Root' 是否等于区块头中的 MerkleRoot

如果相等,就能证明 Tx3 确实属于该区块。

6.5 Merkle Tree 的优势

优势 解释
高效验证 不需要下载全部交易,只需下载一条验证路径
防篡改 任意交易变化都会导致 Merkle-root 改变
节省空间 区块头只保存一个根哈希
支持轻节点 SPV 可以基于 Merkle Proof 验证交易存在性

7. 以太坊中的 MPT:Merkle Patricia Tree

PPT 中介绍了以太坊使用的 MPT,即 Merkle Patricia Tree,也称默克尔帕特里夏树。

7.1 为什么以太坊需要 MPT?

比特币主要关注交易是否存在、UTXO 是否可花费;而以太坊不只是转账系统,它还要支持:

  • 账户余额;
  • 合约代码;
  • 合约存储;
  • 交易执行后的状态变化;
  • 收据和日志。

因此,以太坊需要一种能够同时支持:

  1. 哈希校验;
  2. 快速查找;
  3. 状态更新;
  4. 空间压缩;
  5. 轻客户端证明;

的数据结构。MPT 就是为此设计的。

7.2 Trie 树是什么?

Trie 树又称字典树、前缀树,适合存储字符串或键值数据。

Trie 树的特点:

  1. 根节点通常不包含字符;
  2. 从根节点到某节点的路径表示一个字符串前缀;
  3. 公共前缀可以共享;
  4. 适合文本检索、自动补全、字典匹配等场景。

例如要存储三个词:

text 复制代码
cat
car
dog

可以共享 ca 前缀:

text 复制代码
      root
     /    \
    c      d
    |      |
    a      o
   / \     |
  t   r    g

7.3 Patricia Tree 是什么?

Patricia Tree 可以看作对 Trie 的压缩。普通 Trie 可能出现很多"只有一个子节点"的节点,浪费空间。Patricia Tree 会压缩这些单分支路径。

例如:

text 复制代码
普通 Trie:
root → a → b → c → d

Patricia 压缩后:
root → abcd

这样可以减少节点数量,降低存储成本。

7.4 Merkle + Patricia + Trie 的结合

MPT 可以理解为三种思想的组合:

结构 提供能力
Merkle 每个节点有哈希,可验证、防篡改
Patricia 压缩路径,节省空间
Trie 按 key 的路径查找,适合键值存储

所以 MPT 不是普通树,而是具有密码学验证能力的压缩前缀树。

7.5 MPT 中的节点类型

PPT 中提到,为了降低树高、降低操作复杂度,MPT 引入了几类节点:

节点类型 作用
空节点 表示空字符串或不存在的节点
扩展节点 Extension Node 压缩公共路径
分支节点 Branch Node 表示路径分叉,通常有 16 个分支
叶子节点 Leaf Node 保存最终的 key-value 数据

7.6 MPT 中 key 和 value 是什么?

在以太坊状态树中:

text 复制代码
key   = 账户地址经过编码后的路径
value = 账户状态信息

账户状态可能包括:

  • nonce:账户交易次数;
  • balance:账户余额;
  • storageRoot:合约存储树根;
  • codeHash:合约代码哈希。

在交易树中:

text 复制代码
key   = 交易在区块中的序号
value = 交易内容

在收据树中:

text 复制代码
key   = 交易在区块中的序号
value = 交易执行后的收据信息

7.7 以太坊区块中的三棵树

PPT 中提到,以太坊区块中包含交易、收据和状态三类树:

区块头中的根 主要用途
交易 MPT transaction root 证明某笔交易是否在该区块中
收据 MPT receipt root 证明交易执行结果、日志、事件是否存在
状态 MPT state root 证明某个账户余额、合约状态等全局状态

7.8 为什么说状态 MPT 会不断更新?

以太坊是账户模型。每笔交易执行后,都可能改变账户余额、合约状态或 nonce。

例如:

text 复制代码
Alice 向 Bob 转账 1 ETH

执行后:

text 复制代码
Alice 余额减少
Bob 余额增加
Alice nonce 增加
状态树更新
state root 改变

这就是为什么每个以太坊区块头都要记录新的 state root

7.9 MPT 能支持哪些查询?

PPT 中列出了轻客户端可以实现的查询。整理如下:

查询问题 由哪棵树处理
这笔交易是否包含在某个区块中? 交易 MPT
某地址过去 30 天内发出某类事件的所有实例? 收据 MPT
某账户当前余额是多少? 状态 MPT
某账户是否存在? 状态 MPT
某合约交易的输出是什么? 状态 MPT + 收据 MPT

8. SPV:简化支付验证

SPV 的英文全称是 Simplified Payment Verification,中文通常称为"简化支付验证"。

8.1 SPV 解决什么问题?

完整节点需要保存完整区块数据,数据量很大。对于手机钱包、物联网设备等资源有限的终端来说,保存完整区块链不现实。

SPV 的目标是:

不下载完整区块体,只保存区块头,也能验证某笔交易是否已经被区块链网络确认。

8.2 SPV 验证的是"支付",不是完整交易合法性

PPT 中特别强调:

text 复制代码
SPV 技术的设计目标是验证"支付",而不是验证"交易"。

这句话非常重要。

  • 交易验证:需要检查签名、余额是否足够、是否双花、脚本是否合法等;
  • 支付验证:只验证某笔交易是否已经出现在某个区块中,并且后面有足够确认数。

换句话说,SPV 用户不能像完整节点一样独立验证所有共识规则,它主要依赖:

  1. 区块头链;
  2. Merkle Proof;
  3. 网络中多数算力选择的最长有效链。

8.3 SPV 的验证思路

SPV 节点验证交易的大致流程:

  1. SPV 钱包只同步区块头;
  2. 当用户关心某笔交易时,向完整节点请求相关证明;
  3. 完整节点返回:
    • 包含该交易的区块头;
    • 该交易的 Merkle Proof;
    • 该区块之后的区块头链;
  4. SPV 节点利用 Merkle Proof 验证交易属于该区块;
  5. 再检查该区块后面有多少确认数;
  6. 确认数足够时,认为支付可信。

8.4 举例:如何判断一笔交易有 6 个确认?

假设交易 Tx100 被打包进区块高度 1000。

当前最长链高度为 1006:

text 复制代码
1000 → 1001 → 1002 → 1003 → 1004 → 1005 → 1006

那么 Tx100 所在区块后面已经追加了 6 个区块,通常可以说它有 6 个确认。

注意:不同系统、不同金额、不同风险场景对确认数要求不同。


9. 布隆过滤器 Bloom Filter

布隆过滤器是一种空间效率很高的概率型数据结构。它可以用来判断一个元素是否可能属于某集合。

9.1 布隆过滤器能回答什么问题?

布隆过滤器回答的问题是:

text 复制代码
某元素是否在集合中?

但它的回答有两个特点:

回答 含义
一定不在 结果可靠
可能在 可能是真的,也可能是假阳性

PPT 中强调:

text 复制代码
布隆过滤器不能完全确定一定在一个集合内,
而不能完全确定一定在一个集合内。

更准确地说:

布隆过滤器可以准确判断"不存在",但不能百分百确认"存在"。

9.2 布隆过滤器的基本原理

布隆过滤器由一个位数组和多个哈希函数组成。

假设位数组初始全为 0:

text 复制代码
位置:0 1 2 3 4 5 6 7 8 9
值:  0 0 0 0 0 0 0 0 0 0

插入元素 X 时,用多个哈希函数计算位置:

text 复制代码
H1(X) = 2
H2(X) = 5
H3(X) = 7

把对应位置置为 1:

text 复制代码
位置:0 1 2 3 4 5 6 7 8 9
值:  0 0 1 0 0 1 0 1 0 0

查询元素 Y 时,如果它对应的某个位置为 0,就说明它一定不在集合中。

如果所有位置都是 1,只能说明它"可能在集合中"。

9.3 为什么会出现误判?

因为不同元素可能通过哈希函数映射到相同位置。

例如:

text 复制代码
X 设置了位置 2、5、7
Z 设置了位置 1、5、8

查询 Y 时,假设 Y 对应位置刚好是:

text 复制代码
2、5、8

虽然 Y 没有插入过,但这些位置已经被其他元素置为 1,于是布隆过滤器会说:

text 复制代码
Y 可能在集合中

这就是假阳性。

9.4 布隆过滤器在 SPV 中的作用

SPV 钱包如果直接向网络询问:

text 复制代码
请告诉我和地址 A 有关的所有交易

就会暴露隐私,因为对方知道这个钱包关心地址 A。

布隆过滤器可以让 SPV 节点发送一个"模糊条件":

text 复制代码
请把可能和我有关的交易发给我

完整节点根据 Bloom Filter 过滤区块中的交易,把可能相关的数据返回给 SPV 节点。

这样可以:

  • 减少无关数据下载;
  • 提高查询效率;
  • 一定程度保护隐私;
  • 但不能完全保护隐私,因为过滤模式仍可能被分析。

9.5 例题:查找过去 10 天与某智能合约相关的交易

PPT 中的问题是:

要查找过去 10 天发生的所有和这个智能合约相关的交易,怎么找?

可以分场景回答。

情况一:完整节点

完整节点保存完整区块和状态,可以直接遍历过去 10 天内的区块:

text 复制代码
1. 找到过去 10 天对应的区块高度范围
2. 遍历这些区块中的交易
3. 筛选 to/from 地址等于目标合约地址的交易
4. 如果需要事件日志,再读取交易收据 logs
5. 汇总结果
情况二:轻节点 / SPV 思路

轻节点不保存完整交易数据,可以:

text 复制代码
1. 保存或同步区块头
2. 向完整节点请求与合约地址相关的交易/收据证明
3. 使用 Merkle Proof 或 MPT Proof 验证返回结果
4. 根据区块高度或时间戳限定过去 10 天
情况三:以太坊场景

以太坊中不仅要找交易,还可能要找合约事件。

text 复制代码
交易是否存在:交易 MPT
交易执行结果和事件日志:收据 MPT
合约当前状态:状态 MPT

如果只是找"调用该合约的交易",主要看交易中的 to 字段;如果要找"合约发出的事件",则主要看 receipt logs。


10. 综合例题与答疑

10.1 例题一:为什么修改一笔交易会影响区块哈希?

**问题:**某区块中一笔交易被篡改,为什么区块哈希会变化?

答案:

text 复制代码
交易变化
→ 交易哈希变化
→ Merkle Tree 中相关父节点哈希变化
→ Merkle-root 变化
→ 区块头变化
→ 区块哈希变化

因为区块头中保存了 Merkle-root,所以区块体中的交易变化最终会传导到区块头哈希。

10.2 例题二:区块头为什么不直接保存所有交易?

因为交易数量可能很多,直接把交易全部塞进区块头会导致区块头巨大,不利于传播和轻节点验证。使用 Merkle-root 后,区块头只需保存一个 32 字节左右的根哈希,就能代表整个交易集合。

10.3 例题三:SPV 能不能发现双花?

SPV 不能像完整节点一样独立检查所有双花情况。它主要通过:

  • 交易是否被打包进区块;
  • 区块是否属于最长链;
  • 后续确认数是否足够;

来降低双花风险。

因此,SPV 是一种轻量级验证方案,安全性依赖网络中诚实算力和完整节点返回的证明。

10.4 例题四:Merkle Tree 和 MPT 有什么区别?

对比项 Merkle Tree MPT
主要应用 比特币交易集合证明 以太坊账户、交易、收据证明
结构 哈希二叉树 压缩前缀树 + Merkle 哈希
查询方式 根据交易哈希验证存在性 根据 key 路径查找 value
是否适合频繁状态更新 一般 更适合
典型根哈希 Merkle-root state root / tx root / receipt root

10.5 例题五:布隆过滤器为什么不能删除元素?

普通布隆过滤器中,一个位置可能被多个元素共同设置为 1。如果删除某个元素时把对应位置改回 0,可能会误删其他元素的标记,导致原本存在的元素被判断为不存在。

因此,普通 Bloom Filter 不支持安全删除。若要支持删除,需要使用 Counting Bloom Filter 等变体。


11. 复习速记表

11.1 区块结构速记

概念 速记
区块 区块头 + 区块体
区块头 连接区块、验证区块、支持共识
区块体 保存交易数据
Prev-block Hash 指向前一区块,形成链
Merkle-root 本区块所有交易的总摘要
Nonce 挖矿时不断尝试的随机数
Bits 难度目标压缩表示

11.2 树结构速记

数据结构 关键词 主要用途
Hash List 顺序哈希 简单数据摘要
Binary Tree 左右子树 Merkle Tree 基础结构
Merkle Tree 根哈希、验证路径 交易存在性证明
Trie 前缀共享 字符串/键值检索
Patricia Tree 路径压缩 节省空间
MPT Merkle + Patricia + Trie 以太坊状态、交易、收据证明
Bloom Filter 一定不在、可能在 高效集合过滤

11.3 高频问答

Q1:区块链为什么难以篡改?

因为每个区块都包含前一区块哈希,且交易数据通过 Merkle-root 写入区块头。修改历史交易会导致 Merkle-root 和区块哈希变化,还会破坏后续区块的链接,攻击者必须重算后续所有区块的工作量证明。

Q2:Merkle-root 的核心意义是什么?

用一个根哈希代表整个交易集合,并支持高效验证某笔交易是否属于该区块。

Q3:SPV 节点保存什么?

主要保存区块头,不保存完整区块体。需要验证某笔交易时,再向完整节点请求 Merkle Proof。

Q4:Bloom Filter 的判断结果怎么理解?
  • 返回"不在":一定不在;
  • 返回"可能在":可能在,也可能是误判。
Q5:以太坊为什么要用 MPT?

因为以太坊不仅要记录交易,还要维护账户状态、合约状态、交易收据等复杂键值数据。MPT 同时具备快速查找、路径压缩和哈希证明能力。


12. 一页总结

区块链的区块结构可以概括为:

text 复制代码
区块 = 区块头 + 区块体
区块头 = Version + PrevHash + MerkleRoot + Timestamp + Bits + Nonce
区块体 = 交易列表 / 状态变化相关数据

区块通过 Prev-block Hash 首尾相连,形成不可轻易篡改的链式账本;交易通过 Merkle Tree 形成 Merkle-root,写入区块头,实现交易集合的高效摘要和存在性证明;轻节点通过 SPV 只保存区块头,并借助 Merkle Proof 验证支付是否存在;以太坊进一步使用 MPT 管理交易、收据和账户状态,使复杂的智能合约系统也能实现可验证的数据查询;Bloom Filter 则用于高效过滤可能相关的数据,降低轻节点的数据下载压力。

最终理解:

区块结构的本质不是简单"存数据",而是通过哈希、树结构和链式引用,把数据组织成一个可验证、可追溯、难篡改、可分布式维护的账本系统。

相关推荐
夏日听雨眠1 小时前
数据结构(循环队列)
数据结构·算法·链表
平行侠1 小时前
30MacLaren-Marsaglia算法故事文件
数据结构·算法
平行侠2 小时前
33水库抽样 - 从未知大小的流中等概率采样
数据结构·算法
白帽阿尔法3 小时前
一篇文章认识数字人民币和区块链技术
去中心化·区块链·智能合约·信任链·分布式账本
Controller-Inversion3 小时前
42. 接雨水
数据结构·算法·leetcode
Controller-Inversion3 小时前
33. 搜索旋转排序数组
数据结构·算法·leetcode
宵时待雨3 小时前
优选算法专题6:模拟
数据结构·c++·算法·leetcode·职场和发展
Liangwei Lin3 小时前
LeetCode 35. 搜索插入位置
数据结构·算法·leetcode
L_09074 小时前
【C++】STL— 封装红黑树以实现map 和 set
数据结构·c++