问题
手机钱包如何验证一笔交易已经发生?
小王在咖啡馆用手机 Bitcoin 钱包给小李支付了一杯咖啡。小李的钱包如何快速确认收到了付款?
核心矛盾:Bitcoin 区块链已达数百 GB 并持续增长,手机无法下载完整数据。
解决方案:SPV
Simplified Payment Verification 简易支付验证。
SPV 客户端(手机钱包)只下载并存储所有的区块头,不需要完整的交易列表。
区块结构
每个区块包含两部分:
区块头 Block Header (80 字节)
- 版本号
- 时间戳
- 前一个区块的哈希值
- Merkle 根 Merkle Root(关键)
交易列表 Transactions
- 区块内所有交易记录
- 占据绝大部分空间
graph LR subgraph Block N-1 Header1[Header N-1] Txs1[...] 在底部添加一个不可见的锚点 anchor2( ):::hidden end subgraph Block N+1 Header3[Header N+1] Txs3[...] 连接这些不可见的锚点,而不是连接整个子图 anchor1 -- Previous Block Hash --> anchor2 -- Previous Block Hash --> anchor3 保持原有的样式 style Header1 fill:#f9f,stroke:#333,stroke-width:2px style Header2 fill:#f9f,stroke:#333,stroke-width:2px style Header3 fill:#f9f,stroke:#333,stroke-width:2px 树的顶端,是记录在区块头里的 Merkle Root MerkleRoot("Merkle Root") end subgraph "Block Transactions Tree" 中间节点 H_AB("H(H_A + H_B)") H_CD("H(H_C + H_D)") 定义从上到下的连接关系 MerkleRoot --> H_ABCD H_ABCD --> H_AB H_ABCD --> H_CD H_AB --> H_A H_AB --> H_B H_CD --> H_C H_CD --> H_D 1. 客户端已知的权威信息 subgraph "Client Knows (from Block Header)" KnownRoot("Merkle Root") style KnownRoot fill:#f9f,stroke:#333,stroke-width:2px end 从接收的数据中提取用于计算的部分 Provided_HD["Provided: H(D)"] Provided_HAB["Provided: H(H_A + H_B)"] 3. 从全节点收到的数据包 subgraph "Received from Full Node" direction LR TargetTx("Target Tx_C") MerklePath("Merkle Path<br/>1. H(D)<br/>2. H(H_A + H_B)") end 5. 最终的验证步骤 CalculatedRoot -- "Compare with" --> KnownRoot %% 为从外部接收的数据块设置样式 style TargetTx fill:#bbf,stroke:#333,stroke-width:2px style MerklePath fill:#bbf,stroke:#333,stroke-width:2px style Provided_HD fill:#bbf,stroke:#333,stroke-width:2px style Provided_HAB fill:#bbf,stroke:#333,stroke-width:2px
上图中:
- 蓝色:从全节点收到的数据
- 白色:SPV 钱包自己计算的步骤
- 紫色:区块头中的权威信息
SPV 钱包只下载目标交易和几个哈希值,数据量极小,验证极快。
总结
SPV 是 Bitcoin 白皮书中的卓越创新。只同步区块头 + Merkle 树验证,极大降低了硬件和网络门槛。这种巧妙的权衡,让轻便的移动钱包成为可能。