• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
fyyy94
博客园    首页    新随笔    联系   管理    订阅  订阅
比特币学习之基本数据结构

1. 哈希指针

保存区块地址及hash,两者组合称为哈希指针(具体要看实现),类似于

struct HashPointer{
  void* p_blocker_;
  s_sha256 hash_;
}

一个链表形式如下:

  1. 每个区块保存的是前一个区块的hash 指针
  2. 前一个区块的整体(不一定包含交易,但一定包含其头部的hash指针(prev prev block))也参与hash运算,作为当前区块的hash指针
    • Tamper-evident log

下图展示了区块链的结构,genesis block 称作创世区块

flowchart LR n["genesis block"] b["blk 1"] c["blk 2"] d["blk ..."] e["Most recent block"] e-->d-->c-->b-->n

上图中,如果有人篡改了blk1的数据,则blk2 中的hash指针需要修改,进而blk2 本身的hash变化,从而blk3的hash指针也需要修改,依次类推,从篡改的节点开始一直到MRB(Most recent block) 都需要修改。在分布式系统中,单个节点完成这种修改几乎不可能。这个性质也可以叫做Tamper-evident log

基于这个性质,比特币系统中的某些节点,可以只保存区块链中的一部分连续区块(完整链的一部分),比如2020.1.1 之后的区块,如果需要在这之前的区块,那么可以向比特币中的节点请求。对于请求到的数据,只需要计算hash指针即可验证数据是否被篡改

flowchart LR n["2020.1.1"] b["blk 1"] c["blk 2"] d["blk ..."] e["Most recent block"] e-->d-->c-->b-->n f["prev block"]

图中 prev block 是节点中尚未保存的区块,如果需要该区块时,节点向其他节点申请,然后计算得到的区块的hash值,和2020.1.1的区块对比,如果不一致,说明数据被篡改

2. 默克尔树 Merkle Tree

Merkle 树就是哈希树,哈希树就是除了叶子节点(最后一层)是数据外,其余的节点都是保存的hash值,hash树的优势在于验证大型数据内的部分数据没有被篡改。比如在比特币中的应用。

Merkle 树是如何解决了大型数据的验证问题呢?

  1. 传输的过程是分片的,增量的,因此Merkle tree 可以很好的进行增量验证,避免了每次传一部分数据就要整体校验(大规模数据可能十分耗时)的问题
    • 这个通常在传文件分块时,我们对文件分块校验一样,相当于分层
  2. 可以快速定位哪个区块除了问题(即便接收完后),从而可以要求对方重传
  3. 分层验证
    • 验证少量关键数据(比如比特币的SPV验证),即可确认内容是否被篡改

下图中HPL 代表左子树hash指针, HPR 代表右...

graph TD A[HPL HPR] --> B[HPL HPR] A --> C[HPL HPR] B --> D[Data Block 1] B --> E[Data Block 2] C --> F[Data Block 3] C --> G[Data Block 4]

特点:

  1. 叶子结点都是数据块,非叶子节点存储的都是hash指针,根节点取hash值叫根hash值
  2. 满二叉树?完全二叉树?
  3. 上图中的Data block 都是交易信息,即区块链中的区块是以Merkle tree的形式保存交易信息的
  4. 只要记录根hash值即可验证一颗merkle tree 中任意数据是否被篡改
graph LR A["Block header"] --> B["Blocker body"]
  1. block header 中存储了该区块中的所有交易形成merkle tree的根hash值,不包含交易具体信息
    • 提供merkle proof
  2. block body 中保存了具体的交易信息

全节点:保存Block header + block body
轻节点:保存Block header only.(如比特币钱包)

关键业务:

  1. proof of membership
  2. proof of non membership
    • 比特币中未采用

Merkle Tree和二叉树的区别:

  1. Merkle tree 中使用hash指针代替普通指针
graph LR a["bolck 1"] b["bolck 2"] c["bolck 3"] d["bolck 4"] e["bolck ..."] e--->d--->c--->b--->a
graph TD c["block 3"] c-->n00["HPL HPR"] n00-->n10["HPL HPR n10"] n10-->n20["HPL HPR"] n10-->n21["HPL HPR n21"] n20-->d00["tx 0"] n20-->d01["tx 1"] n21-->d02["tx 2"] n21-->d03["tx 3"] n00-->n11["HPL HPR"] n11-->n22["HPL HPR"] n11-->n23["HPL HPR"] n22-->d04["tx 4"] n22-->d05["tx 5"] n23-->d06["tx 6"] n23-->d07["tx 7"]

假设轻节点想要验证 tx2被包含的话,其流程图如下

sequenceDiagram participant A as 轻节点 participant B as 全节点 A-->A: 计算tx 2的hash(n21's HPL) A-->B: 请求tx 3的hash(n21's HPR) B-->A: 返回hash值 A-->A: 计算 n21 的hash A-->B: 请求上一级节点的HPL的hash B-->A: 返回hash值 A-->A: 计算n10的hash A-->B: 请求root的HPR的hash B-->A: 返回hash A-->A: 计算hash 与 block header中保存的 root hash 比较

最后的结果中,如果计算得到的hash与root hash一致,说明tx2是被包含在该区块中的,如果hash 不一致,则说明不存在。

可见轻节点由下往上逐个验证交易的合法性

问题1. 轻节点如何知道该请求哪个位置的hash?比如上图中的请求tx3 的hash这一步

参考:简单交易验证
从这个文章可知,实际实现和课程中的讲述差异还是较大的, 交易验证被称为SPV(简单交易验证),SPV实现中。

  1. 节点一次性请求包含该交易的merkle tree路径,节点沿着这个路径验证交易有效性。
  2. merkle tree 并不是直接存储在block body 中的,而是每次请求运算出来的(动态生成出来的)
  3. merkle tree 路径伪造是不可能的(hash函数性质),但是轻节点仍然需要闭环验证
posted on 2025-09-01 18:35  feiyangyy94  阅读(24)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3