Skip to content

区块链硬核入门教程(上篇):从密码学到共识,拆掉所有黑盒 ​

写给两类人:刚进门想少走弯路的初学者,和写了几年合约/跑了几年节点、但底层一直半懂不懂的实战派。 本篇不灌鸡汤,不堆名词,每一个概念都拆到"你能用代码复现"的程度。


目录 ​

  1. 先破题:区块链到底解决了什么问题
  2. 密码学基石:哈希、签名与默克尔树
  3. 分布式系统的灵魂:共识机制全景对比
  4. 区块与链:数据结构的精妙
  5. P2P 网络:节点如何发现彼此并同步
  6. 钱包的真相:私钥、公钥、地址与 HD 钱包
  7. 交易的一生:从签名到上链确认
  8. 实战:用 Python 从零实现一个最小区块链
  9. 常见误区与思维升级

1. 先破题:区块链到底解决了什么问题 ​

1.1 拜占庭将军问题 ​

想象 10 支军队包围一座城市,将军们只能通过信使通信,其中可能有叛徒。如何让所有忠诚将军在"进攻还是撤退"上达成一致?

这就是 拜占庭将军问题(Byzantine Generals Problem),1982 年由 Lamport 提出。它的本质是:在存在恶意节点和不可靠通信的分布式系统中,如何达成共识?

传统分布式系统(如 ZooKeeper、etcd)假设节点只会"宕机"而非"作恶",用 Raft/Paxos 就能解决。但区块链是开放网络,任何人都能加入,节点完全可能作恶——这就需要 拜占庭容错(BFT)。

硬核知识点:Lamport 证明,在同步网络中,只要叛徒数 f < n/3,就能达成拜占庭共识。即 n ≥ 3f + 1。这就是为什么 PBFT 至少需要 4 个节点(容忍 1 个叛徒)。

1.2 双重支付问题 ​

数字文件可以无限复制。如果没有中心化机构记账,Alice 怎么证明她没有把同一笔钱同时花给 Bob 和 Charlie?

区块链的解法是:把所有交易公开排序,形成一条不可篡改的时间链。每笔交易都引用前一笔交易的输出(UTXO 模型)或修改账户余额(账户模型),全网节点共同验证,双重支付在数学上变得不可行。

1.3 信任的最小化 ​

区块链不是"去信任"(trustless),而是 最小化信任(trust-minimized)。你不需要信任银行、不需要信任交易对手,你只需要信任:

  • 密码学数学(哈希不可逆、签名不可伪造)
  • 经济激励(作恶成本 > 作恶收益)
  • 多数节点的诚实(由经济激励保证)

这三者构成了区块链的"信任三角"。


2. 密码学基石:哈希、签名与默克尔树 ​

2.1 哈希函数:区块链的指纹 ​

哈希函数把任意长度输入映射为固定长度输出。区块链用的是 密码学哈希函数,必须满足三个性质:

性质含义被打破的后果
抗原像性(Pre-image resistance)给定 hash,无法反推原文可以从地址反推私钥
抗第二原像性(2nd pre-image resistance)给定 x,找不到 y≠x 使 hash(x)=hash(y)可以伪造交易内容
抗碰撞性(Collision resistance)找不到任意 x≠y 使 hash(x)=hash(y)可以伪造整条链

比特币用 SHA-256,以太坊用 Keccak-256(注意:不是 NIST 标准的 SHA3-256,而是 SHA3 标准化前的 Keccak 版本,两者输出不同)。

实战细节:比特币的区块哈希实际是 SHA256(SHA256(header)),即双哈希。这是为了抵御长度扩展攻击(Length Extension Attack)。SHA-3 和 BLAKE 系列天然免疫长度扩展,所以不需要双哈希。

哈希速率对比(硬核数据) ​

算法输出长度典型算力(GPU)抗 ASIC 程度
SHA-256256bit~1 GH/s极低(ASIC 比 GPU 快 10 万倍)
Ethash256bit~40 MH/s中(内存依赖,ASIC 优势有限)
Scrypt256bit~1 MH/s中高(内存依赖)
RandomX256bit~2 KH/s(CPU)极高(CPU 优化,GPU/ASIC 无优势)

2.2 非对称加密与数字签名 ​

区块链用 椭圆曲线密码学(ECC) 而非 RSA。原因很简单:同样的安全强度,ECC 的密钥短得多。

安全强度RSA 密钥长度ECC 密钥长度
128 bit3072 bit256 bit
192 bit7680 bit384 bit
256 bit15360 bit512 bit

比特币和以太坊都使用 secp256k1 曲线。这条曲线的参数是精心选择的——不是随机的,而是有特殊的代数结构(Koblitz 曲线),使得点乘运算可以高效优化。

冷知识:secp256k1 的 p = 2^256 - 2^32 - 977,这个形式让模运算可以用位移代替除法,性能提升显著。

签名过程(ECDSA) ​

签名:
1. 生成随机数 k
2. 计算点 R = k * G,取 x 坐标 r
3. s = k^(-1) * (hash(m) + r * privKey) mod n
4. 签名 = (r, s)

验签:
1. u1 = hash(m) * s^(-1) mod n
2. u2 = r * s^(-1) mod n
3. 计算点 P = u1*G + u2*pubKey
4. 若 P.x == r,则签名有效

致命陷阱:k 必须是真随机且不可重复的。索尼 PS3 曾因 k 可预测导致私钥被提取。区块链钱包用 RFC 6979 确定性生成 k,用私钥和消息哈希作为种子,既避免随机数问题,又保证每次签名的 k 不同。

前沿:Schnorr 签名(BIP-340)已在比特币上线。相比 ECDSA,Schnorr 支持 签名聚合——多签可以合并成一个签名,既省空间又提升隐私。Taproot(BIP-341)就是基于 Schnorr 的升级。

2.3 默克尔树:轻客户端的基石 ​

默克尔树把大量数据压缩成一个 32 字节的根哈希,同时支持 存在性证明(Merkle Proof)。

        Root = H(H12 + H34)
       /                  \
   H12=H(H1+H2)        H34=H(H3+H4)
    /       \            /       \
 H1=H(A)  H2=H(B)   H3=H(C)   H4=H(D)

要证明交易 A 在区块中,只需要提供 H2, H34 两个哈希(路径上的兄弟节点),验证者计算 H(H(A)+H2) 得到 H12,再 H(H12+H34) 得到 Root,与区块头对比即可。

复杂度:证明大小从 O(n) 降到 O(log n)。一个含 4000 笔交易的区块,默克尔证明只需 ~12 个哈希(384 字节)。

进阶:默克尔帕特里夏树(Merkle Patricia Trie, MPT)是以太坊用的数据结构,结合了默克尔树和前缀树(Trie),支持键值对的存在性/不存在性证明。以太坊的状态树、交易树、收据树都是 MPT。

更新的方案:Celestia 和部分新链用 命名空间默克尔树(NMT),给每个叶子加命名空间前缀,支持按命名空间批量证明,是数据可用性采样的基础。


3. 分布式系统的灵魂:共识机制全景对比 ​

共识是区块链最核心、也最容易被误解的部分。这里把主流机制一次讲透。

3.1 PoW(工作量证明) ​

原理:谁先算出满足条件的哈希(找到 nonce),谁就有权出块。

挖矿本质:SHA256(区块头 + nonce) < 目标值。目标值由难度决定,难度每 2016 个区块调整一次,保证出块时间稳定在 10 分钟。

安全性来源:攻击需要控制 51% 算力。比特币全网算力约 600 EH/s(2024 年),攻击成本每天数千万美元。

优缺点:

  • ✅ 最安全、最去中心化、经过 15 年实战检验
  • ✅ 无许可加入,不需要质押
  • ❌ 能耗高(但实际能耗中 ~75% 来自可再生能源,Cambridge Bitcoin Electricity Consumption Index 数据)
  • ❌ 出块慢、吞吐量低

3.2 PoS(权益证明) ​

原理:按质押代币数量随机选择验证者出块。以太坊 2.0 用的是 Gasper(Casper FFG + LMD GHOST)。

以太坊 PoS 关键参数:

  • 验证者需质押 32 ETH
  • 每个 epoch(32 个 slot,约 6.4 分钟)投票一次
  • 惩罚机制:离线罚没(inactivity leak)、双重签名罚没(slashing)
  • 最终性(Finality):2/3 验证者投票后,区块不可回滚

硬核对比 PoW vs PoS 安全性:

  • PoW 攻击需要持续购买算力,是 外部成本,攻击越久成本越高
  • PoS 攻击需要质押代币,攻击会被罚没,是 内部成本,一次攻击可能血本无归
  • PoS 的"无利害关系"(Nothing at Stake)问题通过罚没机制解决
  • PoS 有 弱主观性(Weak Subjectivity):新节点加入需要信任最近的检查点,而 PoW 可以从创世块独立验证

3.3 DPoS(委托权益证明) ​

原理:代币持有者投票选出有限数量(如 21 个)的超级节点,轮流出块。

代表:EOS、TRON、Steem。

  • ✅ 出块快(EOS 号称 0.5s)、吞吐量高
  • ❌ 高度中心化,21 个节点容易被寡头控制
  • ❌ 投票率极低,治理问题频发

3.4 BFT 类共识 ​

PBFT(实用拜占庭容错):三阶段协议(pre-prepare → prepare → commit),n ≥ 3f+1,通信复杂度 O(n²)。

代表:Hyperledger Fabric、Ripple(变体)、Stellar。

  • ✅ 最终性强,不出块就确认
  • ✅ 适合许可链/联盟链
  • ❌ 节点数不能太多(O(n²) 通信瓶颈,通常 < 100 节点)

HotStuff:Facebook Libra/Diem 提出,三阶段简化为流水线,通信复杂度 O(n),是现代 BFT 的标杆。Aptos、Sui 都基于 HotStuff 变体。

3.5 新型共识:Avalanche ​

原理:重复随机抽样投票,通过雪崩效应达成共识。不是传统的"确定最终性",而是 概率最终性,但概率收敛极快(通常 1-2 秒内置信度 > 99.9%)。

  • ✅ 高吞吐(4500+ TPS)、低延迟、高并发
  • ✅ 支持子网(Subnet),每条子网可有自己的共识和虚拟机
  • ❌ 概率最终性在极端网络分区下有理论风险

3.6 共识机制总对比表 ​

机制代表链出块时间TPS最终性去中心化攻击成本适用场景
PoWBitcoin10min~7概率(6 区块)极高51% 算力价值存储、安全优先
PoSEthereum12s~15-20确定(2 epoch)高33% 质押通用智能合约
DPoSEOS/TRON0.5-3s1000-4000确定低收买节点高频交易、博彩
PBFTFabric<1s1000-3000确定极低(许可)攻破节点联盟链、企业级
HotStuffAptos/Sui<1s10000+确定中33% 质押高性能公链
AvalancheAvalanche1-2s4500+概率→确定中高33% 质押多子网、DeFi

实战经验:没有"最好的共识",只有"最适合场景的共识"。做金融结算要确定最终性,做社交可以接受概率最终性,做企业联盟链直接 PBFT。选型时先问:我的威胁模型是什么?需要多少去中心化?延迟要求多少?


4. 区块与链:数据结构的精妙 ​

4.1 区块结构 ​

比特币区块:

区块头(80 字节):
├── 版本号(4 字节)
├── 前一区块哈希(32 字节)
├── 默克尔根(32 字节)
├── 时间戳(4 字节)
├── 难度目标(4 字节,压缩格式)
└── Nonce(4 字节)

交易数据(可变,上限 ~4MB 含 SegWit):
├── 交易数量(VarInt)
└── 交易列表

注意:区块头只有 80 字节!这就是为什么轻客户端(SPV)只下载区块头就能验证交易——80 字节 × 80 万区块 ≈ 64MB,手机都能存。

以太坊区块:

区块头:
├── 父区块哈希
├── 叔区块哈希(Ommers)
├── 矿工地址(coinbase)
├── 状态根(State Root,MPT 根哈希)
├── 交易根
├── 收据根
├── 日志布隆过滤器(Bloom)
├── 难度(PoS 后为 0)
├── 区块号
├── Gas 上限 / Gas 已用
├── 时间戳
├── 额外数据
├── 混合哈希(PoS 后废弃)
└── 随机数(PoS 后废弃,改为 Randao)

关键区别:比特币区块头只有交易默克尔根,而以太坊有 状态根。这意味着以太坊区块头承诺了"执行完所有交易后,所有账户的状态"。这是账户模型和 UTXO 模型的根本差异之一。

4.2 链式结构与不可篡改 ​

每个区块头包含前一区块的哈希。如果修改第 N 个区块的任何一个字节:

  1. 第 N 个区块的哈希变了
  2. 第 N+1 个区块的"前一区块哈希"字段不匹配,需要重新计算
  3. 重新计算需要重新挖矿(PoW)或重新签名(PoS)
  4. 后续所有区块都要重新计算

所以篡改越旧的区块,成本越高。这就是"不可篡改"的数学基础——不是真的不能改,是改不起。

4.3 叔区块(Ommer/Uncle Blocks) ​

以太坊独有的机制。当两个矿工几乎同时出块时,一个成为主链区块,另一个成为"叔区块"。叔区块不包含交易,但会获得部分奖励(约 1/8 的区块奖励)。

为什么要有叔区块? 以太坊出块时间 12s(PoW 时代 15s),网络延迟导致孤块率高。叔区块机制减少矿工损失,同时增加了攻击难度(攻击者需要同时覆盖叔区块)。

PoS 后叔区块机制已废弃,改为 GHOST 协议选择最重子树。


5. P2P 网络:节点如何发现彼此并同步 ​

5.1 节点发现 ​

比特币用 Kademlia DHT(分布式哈希表)的变体。新节点启动时:

  1. 连接 DNS 种子节点(如 seed.bitcoin.sipa.be),获取一批节点 IP
  2. 向这些节点发送 getaddr 消息,获取更多节点
  3. 建立 8 个出站连接,最多接受 117 个入站连接

以太坊用 discv5 协议,基于 Kademlia 但增加了话题发现(Topic Discovery),可以按能力(如"支持 ETH 协议")查找节点。

5.2 区块同步模式 ​

模式下载内容验证方式磁盘占用适用场景
全节点(Full)所有区块+交易独立验证每笔交易~500GB(BTC)/~1.5TB(ETH)矿工、高安全需求
快速同步(Fast/Snap)区块头+最近状态验证头,状态从快照同步~200GB(ETH)普通用户
轻节点(SPV/Light)仅区块头默克尔证明验证交易~64MB手机钱包
归档节点(Archive)所有历史状态全验证~12TB(ETH)区块浏览器、数据分析

实战坑:以太坊全节点同步如果用 Geth 默认模式,可能需要几天。建议用 --syncmode snap,配合 SSD(必须 SSD,HDD 根本跟不上)。归档节点需要 12TB+ NVMe,普通玩家别碰。

5.3 消息传播 ​

交易和区块通过 Gossip 协议传播:节点收到新交易/区块后,转发给若干邻居,邻居再转发,像水波一样扩散全网。

比特币交易传播延迟中位数约 2 秒,90% 节点在 10 秒内收到。以太坊约 1-3 秒。

优化:FIBRE(Fast Internet Bitcoin Relay Engine)和 Falcon 网络用压缩和前向纠错把区块传播延迟降到 100ms 级别,是矿工和大交易所的刚需。


6. 钱包的真相:私钥、公钥、地址与 HD 钱包 ​

6.1 从私钥到地址 ​

私钥(256 bit 随机数)
  │ ECDSA secp256k1 点乘
  ▼
公钥(512 bit,未压缩;或 264 bit 压缩格式)
  │ SHA-256 → RIPEMD-160(比特币)
  │ Keccak-256 取后 20 字节(以太坊)
  ▼
公钥哈希(160 bit)
  │ 加版本前缀 + 校验和 + Base58Check(比特币)
  │ 加 0x 前缀 + EIP-55 大小写校验(以太坊)
  ▼
地址

比特币地址格式演变:

  • P2PKH(1 开头):最传统,公钥哈希
  • P2SH(3 开头):脚本哈希,支持多签
  • Bech32(bc1 开头,SegWit):更短、错误检测更强
  • Taproot(bc1p 开头):Schnorr 签名,隐私更好

以太坊 EIP-55 校验和:地址大小写混合,大写字母的位置由 Keccak 哈希决定。输入地址时如果大小写不对,钱包会警告。这是一种"免费"的校验机制,不占额外字节。

6.2 HD 钱包(BIP-32 / BIP-39 / BIP-44) ​

助记词(BIP-39):12/24 个英文单词,通过 PBKDF2 派生 512 bit 种子。

熵(128-256 bit)→ 加校验和 → 分词 → 助记词
助记词 + 密码(可选)→ PBKDF2-HMAC-SHA512(2048 轮)→ 种子(512 bit)

HD 派生(BIP-32):从一个种子派生出无限多子密钥。

m / purpose' / coin_type' / account' / change / address_index

以比特币为例:m/44'/0'/0'/0/0 是第一个账户的第一个收款地址。

  • 44':BIP-44 标准(硬化派生,加 ')
  • 0':比特币(以太坊是 60')
  • 0':第 0 个账户
  • 0:外部地址(0=收款,1=找零)
  • 0:地址索引

安全细节:硬化派生(带 ')的子密钥泄露不会威胁父密钥;非硬化派生则可能。所以账户层级用硬化派生,地址索引用非硬化派生(方便公钥导出地址而不需要私钥)。

6.3 钱包类型对比 ​

类型私钥存储联网安全性便利性代表
硬件钱包专用芯片签名时才连极高中Ledger, Trezor
软件钱包手机/电脑是中高MetaMask, Trust
纸钱包纸上否高(但易丢/损)低打印的私钥
脑钱包记忆中否极低(可暴力破解)高记一句话
多签钱包多方持有是极高低Safe(原 Gnosis Safe)
智能合约钱包链上合约是高高Argent, Safe(ERC-4337)

实战建议:大额资产用硬件钱包 + 多签;日常用 MetaMask 等软件钱包,但只放少量资金。永远不要把助记词存在联网设备上,不要截屏,不要用云同步。


7. 交易的一生:从签名到上链确认 ​

7.1 比特币交易(UTXO 模型) ​

交易结构:
├── 版本号
├── 输入数量
├── 输入列表:
│   ├── 引用的前一交易哈希
│   ├── 前一交易的输出索引
│   ├── 解锁脚本(签名 + 公钥)
│   └── 序列号
├── 输出数量
├── 输出列表:
│   ├── 金额(satoshi)
│   └── 锁定脚本(公钥哈希)
└── 锁时间(Locktime)

核心思想:比特币没有"账户余额",只有一堆未花费的交易输出(UTXO)。你的余额 = 所有你能解锁的 UTXO 之和。

交易就是 销毁一些 UTXO(输入),创建新的 UTXO(输出)。输入金额总和 - 输出金额总和 = 矿工费。

脚本系统:比特币的锁定/解锁脚本是一种基于栈的简单脚本语言,不是图灵完备的。操作符包括 OP_DUP、OP_HASH160、OP_EQUALVERIFY、OP_CHECKSIG 等。P2PKH 的锁定脚本是:

OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

7.2 以太坊交易(账户模型) ​

交易结构(EIP-1559 后):
├── 链 ID(防重放攻击)
├── nonce(发送者交易序号,防重放)
├── maxPriorityFeePerGas(给矿工的小费)
├── maxFeePerGas(总 Gas 费上限)
├── gasLimit(Gas 上限)
├── to(接收地址,合约创建时为空)
├── value(转账金额,wei)
├── data(合约调用数据或字节码)
├── accessList(EIP-2930,预声明访问的地址/存储键,省 Gas)
├── v, r, s(签名)

Gas 机制:每笔交易消耗 Gas,Gas × GasPrice = 手续费。EIP-1559 后,基础费(base fee)由算法动态调整,会被 销毁(通缩机制),矿工只拿优先费(priority fee)。

Nonce 的坑:nonce 必须从 0 开始连续递增。如果你提交了 nonce=5 的交易但 Gas 太低卡住了,后续 nonce=6,7,8 的交易全部排队等待。解决方法是用相同 nonce + 更高 Gas 重新提交(替换交易),或者取消(发送一笔 to 自己、value=0、相同 nonce 的交易)。

7.3 交易生命周期 ​

用户签名
  │
  ▼
本地/钱包验证(nonce、余额、Gas)
  │
  ▼
广播到 P2P 网络
  │
  ▼
节点验证后放入内存池(Mempool)
  │
  ▼
矿工/验证者从 Mempool 选择交易(按 Gas 费排序)
  │
  ▼
打包进区块
  │
  ▼
区块广播,其他节点验证
  │
  ▼
确认数增加,交易最终不可逆

Mempool 是理解区块链的关键:它不是一个统一的池子,每个节点有自己的 Mempool。你的交易可能在 A 节点的 Mempool 里,但 B 节点还没收到。这就是为什么有时候交易"已经发了但区块浏览器查不到"。


8. 实战:用 Python 从零实现一个最小区块链 ​

光说不练假把式。下面用 ~150 行 Python 实现一个可运行的区块链,包含挖矿、交易、签名、共识验证。

8.1 完整代码 ​

python
import hashlib
import json
import time
from typing import List, Dict, Any
from ecdsa import SigningKey, VerifyingKey, SECP256k1

# ============ 工具函数 ============

def sha256(data: str) -> str:
    """双 SHA-256,和比特币一致"""
    return hashlib.sha256(hashlib.sha256(data.encode()).digest()).hexdigest()

def merkle_root(items: List[str]) -> str:
    """计算默克尔根"""
    if not items:
        return sha256("")
    if len(items) == 1:
        return sha256(items[0])
    new_level = []
    for i in range(0, len(items), 2):
        left = items[i]
        right = items[i + 1] if i + 1 < len(items) else items[i]
        new_level.append(sha256(left + right))
    return merkle_root(new_level)

# ============ 钱包 ============

class Wallet:
    def __init__(self):
        self.private_key = SigningKey.generate(curve=SECP256k1)
        self.public_key = self.private_key.get_verifying_key()
        self.address = sha256(self.public_key.to_string().hex())[:40]

    def sign(self, message: str) -> str:
        return self.private_key.sign(message.encode()).hex()

    @staticmethod
    def verify(public_key_hex: str, message: str, signature_hex: str) -> bool:
        try:
            vk = VerifyingKey.from_string(bytes.fromhex(public_key_hex), curve=SECP256k1)
            return vk.verify(bytes.fromhex(signature_hex), message.encode())
        except:
            return False

# ============ 交易 ============

class Transaction:
    def __init__(self, sender: str, recipient: str, amount: float,
                 public_key: str, signature: str = ""):
        self.sender = sender
        self.recipient = recipient
        self.amount = amount
        self.public_key = public_key
        self.signature = signature
        self.txid = self.compute_txid()

    def compute_txid(self) -> str:
        content = f"{self.sender}{self.recipient}{self.amount}{self.public_key}"
        return sha256(content)

    def sign_transaction(self, wallet: Wallet):
        message = self.compute_txid()
        self.signature = wallet.sign(message)

    def is_valid(self) -> bool:
        if self.sender == "COINBASE":  # 挖矿奖励
            return True
        if not self.signature:
            return False
        message = self.compute_txid()
        return Wallet.verify(self.public_key, message, self.signature)

    def to_dict(self) -> Dict[str, Any]:
        return {
            "txid": self.txid,
            "sender": self.sender,
            "recipient": self.recipient,
            "amount": self.amount,
            "public_key": self.public_key,
            "signature": self.signature,
        }

# ============ 区块 ============

class Block:
    def __init__(self, index: int, transactions: List[Transaction],
                 previous_hash: str, timestamp: float = None):
        self.index = index
        self.transactions = transactions
        self.previous_hash = previous_hash
        self.timestamp = timestamp or time.time()
        self.nonce = 0
        self.merkle_root = merkle_root([tx.txid for tx in transactions])
        self.hash = self.compute_hash()

    def compute_hash(self) -> str:
        block_content = json.dumps({
            "index": self.index,
            "previous_hash": self.previous_hash,
            "timestamp": self.timestamp,
            "merkle_root": self.merkle_root,
            "nonce": self.nonce,
        }, sort_keys=True)
        return sha256(block_content)

    def mine(self, difficulty: int):
        """PoW 挖矿:找到 nonce 使哈希前 difficulty 位为 0"""
        target = "0" * difficulty
        while self.hash[:difficulty] != target:
            self.nonce += 1
            self.hash = self.compute_hash()
        print(f"区块 #{self.index} 挖出!nonce={self.nonce}, hash={self.hash}")

# ============ 区块链 ============

class Blockchain:
    def __init__(self, difficulty: int = 4):
        self.chain: List[Block] = []
        self.difficulty = difficulty
        self.pending_transactions: List[Transaction] = []
        self.mining_reward = 50.0
        self.create_genesis_block()

    def create_genesis_block(self):
        genesis = Block(0, [], "0")
        genesis.mine(self.difficulty)
        self.chain.append(genesis)

    def get_latest_block(self) -> Block:
        return self.chain[-1]

    def add_transaction(self, tx: Transaction) -> bool:
        if not tx.is_valid():
            print(f"交易 {tx.txid} 验证失败,拒绝加入")
            return False
        self.pending_transactions.append(tx)
        return True

    def mine_pending_transactions(self, miner_address: str):
        # 挖矿奖励交易
        reward_tx = Transaction("COINBASE", miner_address, self.mining_reward, "")
        self.pending_transactions.insert(0, reward_tx)

        block = Block(
            index=len(self.chain),
            transactions=self.pending_transactions,
            previous_hash=self.get_latest_block().hash,
        )
        block.mine(self.difficulty)
        self.chain.append(block)
        self.pending_transactions = []

    def is_chain_valid(self) -> bool:
        for i in range(1, len(self.chain)):
            current = self.chain[i]
            previous = self.chain[i - 1]

            # 检查哈希
            if current.hash != current.compute_hash():
                print(f"区块 #{i} 哈希不匹配")
                return False

            # 检查前一区块哈希
            if current.previous_hash != previous.hash:
                print(f"区块 #{i} 前一哈希不匹配")
                return False

            # 检查 PoW
            if current.hash[:self.difficulty] != "0" * self.difficulty:
                print(f"区块 #{i} 不满足难度要求")
                return False

            # 检查所有交易
            for tx in current.transactions:
                if not tx.is_valid():
                    print(f"区块 #{i} 中交易 {tx.txid} 无效")
                    return False

        return True

    def get_balance(self, address: str) -> float:
        """简化版余额计算(实际 UTXO 模型更复杂)"""
        balance = 0.0
        for block in self.chain:
            for tx in block.transactions:
                if tx.sender == address:
                    balance -= tx.amount
                if tx.recipient == address:
                    balance += tx.amount
        return balance

# ============ 演示 ============

if __name__ == "__main__":
    # 创建钱包
    alice = Wallet()
    bob = Wallet()
    miner = Wallet()

    print(f"Alice 地址: {alice.address}")
    print(f"Bob 地址: {bob.address}")
    print(f"矿工地址: {miner.address}")
    print()

    # 初始化区块链
    bc = Blockchain(difficulty=4)

    # Alice 给 Bob 转 10 个币(此时 Alice 还没有币,仅演示签名验证)
    tx1 = Transaction(alice.address, bob.address, 10.0, alice.public_key.to_string().hex())
    tx1.sign_transaction(alice)
    bc.add_transaction(tx1)

    # 挖矿(矿工获得奖励)
    bc.mine_pending_transactions(miner.address)

    # 矿工给 Alice 转 20 个币
    tx2 = Transaction(miner.address, alice.address, 20.0, miner.public_key.to_string().hex())
    tx2.sign_transaction(miner)
    bc.add_transaction(tx2)
    bc.mine_pending_transactions(miner.address)

    # 查余额
    print(f"\nAlice 余额: {bc.get_balance(alice.address)}")
    print(f"Bob 余额: {bc.get_balance(bob.address)}")
    print(f"矿工余额: {bc.get_balance(miner.address)}")

    # 验证链
    print(f"\n区块链是否有效: {bc.is_chain_valid()}")

    # 尝试篡改
    bc.chain[1].transactions[0].amount = 999
    print(f"篡改后是否有效: {bc.is_chain_valid()}")

8.2 运行与扩展 ​

bash
pip install ecdsa
python minimal_blockchain.py

这个实现包含了:

  • ✅ 双 SHA-256 哈希
  • ✅ ECDSA secp256k1 签名与验签
  • ✅ 默克尔树
  • ✅ PoW 挖矿与难度调整
  • ✅ 交易签名验证
  • ✅ 链完整性验证
  • ✅ 挖矿奖励
  • ✅ 篡改检测

可以继续扩展的方向:

  1. 实现真正的 UTXO 模型(而非简化的账户余额)
  2. 加入 P2P 网络层(用 asyncio 或 socket)
  3. 实现 SPV 轻客户端
  4. 加入智能合约执行引擎(EVM 子集)
  5. 实现 Merkle Proof 验证

学习建议:把这段代码跑起来,然后尝试篡改一个区块,观察 is_chain_valid() 如何检测到。再尝试把难度调到 6,感受挖矿时间的指数级增长。这比读十篇文章都管用。


9. 常见误区与思维升级 ​

误区 1:"区块链就是比特币" ​

比特币是区块链的第一个应用,但区块链技术远不止比特币。以太坊把区块链变成了"世界计算机",Cosmos 做跨链,Celestia 做模块化区块链。区块链是一种技术范式,不是单一产品。

误区 2:"上链就不可篡改" ​

准确说法是:篡改的经济成本极高,在理性假设下不可行。如果有人控制了 51% 算力/质押,理论上可以回滚交易。以太坊 PoS 后,33% 质押攻击会被罚没,所以攻击成本是"质押金额的 1/3"——按当前价格约数十亿美元。

误区 3:"智能合约就是智能的" ​

智能合约既不智能也不是法律意义上的合约。它是 一段自动执行的代码,代码有 bug 就会被利用。2016 年 The DAO 被盗 6000 万美元,2022 年 Wormhole 桥被盗 3.2 亿美元,都是代码 bug。

误区 4:"Gas 费高是因为网络拥堵" ​

Gas 费高的本质是 区块空间是稀缺资源,用户用 Gas 费竞价。EIP-1559 后,基础费由算法根据区块填充率动态调整,拥堵时基础费指数级上涨。Layer2 的意义就是把大部分交易搬到链下,降低对 Layer1 区块空间的需求。

误区 5:"去中心化程度可以用节点数衡量" ​

节点数只是表面。真正的去中心化要看:

  • 算力/质押分布:前 4 大矿池占比特币 50%+ 算力
  • 客户端多样性:以太坊 Geth 占 80%+,一个 bug 可能导致全网故障
  • 地理分布:节点是否集中在少数国家/云服务商
  • 治理参与度:投票率、提案通过门槛

实战派思维升级:学区块链不要停留在"知道概念",要问三个问题:

  1. 威胁模型是什么?(谁可能攻击?攻击成本多少?)
  2. 经济激励是否对齐?(诚实行为是否比作恶更有利?)
  3. 工程复杂度是否值得?(这个场景真的需要区块链吗?用数据库是不是更简单?)

上篇小结 ​

本篇从拜占庭将军问题出发,拆解了区块链的四大基石:密码学(哈希、签名、默克尔树)、共识(PoW/PoS/BFT 全景对比)、数据结构(区块与链)、网络与钱包。最后用 150 行 Python 实现了一个可运行的区块链。

如果你能把上面的代码跑通并解释每一行,你已经超过了 80% 自称"懂区块链"的人。

中篇预告:我们将深入生态层——比特币的 UTXO 与脚本、以太坊的 EVM 与 Solidity、主流公链横向对比、Layer2 扩容方案、跨链技术、智能合约安全、DeFi/NFT/DAO 全景,并附上合约部署实战。


下篇将覆盖:零知识证明、账户抽象、MEV、模块化区块链、RWA、工程实践与职业路径。

基于 Vite 强力驱动 | 纯静态轻量托管