区块链硬核入门教程(上篇):从密码学到共识,拆掉所有黑盒
写给两类人:刚进门想少走弯路的初学者,和写了几年合约/跑了几年节点、但底层一直半懂不懂的实战派。 本篇不灌鸡汤,不堆名词,每一个概念都拆到"你能用代码复现"的程度。
目录
- 先破题:区块链到底解决了什么问题
- 密码学基石:哈希、签名与默克尔树
- 分布式系统的灵魂:共识机制全景对比
- 区块与链:数据结构的精妙
- P2P 网络:节点如何发现彼此并同步
- 钱包的真相:私钥、公钥、地址与 HD 钱包
- 交易的一生:从签名到上链确认
- 实战:用 Python 从零实现一个最小区块链
- 常见误区与思维升级
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-256 | 256bit | ~1 GH/s | 极低(ASIC 比 GPU 快 10 万倍) |
| Ethash | 256bit | ~40 MH/s | 中(内存依赖,ASIC 优势有限) |
| Scrypt | 256bit | ~1 MH/s | 中高(内存依赖) |
| RandomX | 256bit | ~2 KH/s(CPU) | 极高(CPU 优化,GPU/ASIC 无优势) |
2.2 非对称加密与数字签名
区块链用 椭圆曲线密码学(ECC) 而非 RSA。原因很简单:同样的安全强度,ECC 的密钥短得多。
| 安全强度 | RSA 密钥长度 | ECC 密钥长度 |
|---|---|---|
| 128 bit | 3072 bit | 256 bit |
| 192 bit | 7680 bit | 384 bit |
| 256 bit | 15360 bit | 512 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 | 最终性 | 去中心化 | 攻击成本 | 适用场景 |
|---|---|---|---|---|---|---|---|
| PoW | Bitcoin | 10min | ~7 | 概率(6 区块) | 极高 | 51% 算力 | 价值存储、安全优先 |
| PoS | Ethereum | 12s | ~15-20 | 确定(2 epoch) | 高 | 33% 质押 | 通用智能合约 |
| DPoS | EOS/TRON | 0.5-3s | 1000-4000 | 确定 | 低 | 收买节点 | 高频交易、博彩 |
| PBFT | Fabric | <1s | 1000-3000 | 确定 | 极低(许可) | 攻破节点 | 联盟链、企业级 |
| HotStuff | Aptos/Sui | <1s | 10000+ | 确定 | 中 | 33% 质押 | 高性能公链 |
| Avalanche | Avalanche | 1-2s | 4500+ | 概率→确定 | 中高 | 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 个区块的任何一个字节:
- 第 N 个区块的哈希变了
- 第 N+1 个区块的"前一区块哈希"字段不匹配,需要重新计算
- 重新计算需要重新挖矿(PoW)或重新签名(PoS)
- 后续所有区块都要重新计算
所以篡改越旧的区块,成本越高。这就是"不可篡改"的数学基础——不是真的不能改,是改不起。
4.3 叔区块(Ommer/Uncle Blocks)
以太坊独有的机制。当两个矿工几乎同时出块时,一个成为主链区块,另一个成为"叔区块"。叔区块不包含交易,但会获得部分奖励(约 1/8 的区块奖励)。
为什么要有叔区块? 以太坊出块时间 12s(PoW 时代 15s),网络延迟导致孤块率高。叔区块机制减少矿工损失,同时增加了攻击难度(攻击者需要同时覆盖叔区块)。
PoS 后叔区块机制已废弃,改为 GHOST 协议选择最重子树。
5. P2P 网络:节点如何发现彼此并同步
5.1 节点发现
比特币用 Kademlia DHT(分布式哈希表)的变体。新节点启动时:
- 连接 DNS 种子节点(如
seed.bitcoin.sipa.be),获取一批节点 IP - 向这些节点发送
getaddr消息,获取更多节点 - 建立 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 完整代码
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 运行与扩展
pip install ecdsa
python minimal_blockchain.py这个实现包含了:
- ✅ 双 SHA-256 哈希
- ✅ ECDSA secp256k1 签名与验签
- ✅ 默克尔树
- ✅ PoW 挖矿与难度调整
- ✅ 交易签名验证
- ✅ 链完整性验证
- ✅ 挖矿奖励
- ✅ 篡改检测
可以继续扩展的方向:
- 实现真正的 UTXO 模型(而非简化的账户余额)
- 加入 P2P 网络层(用
asyncio或socket) - 实现 SPV 轻客户端
- 加入智能合约执行引擎(EVM 子集)
- 实现 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 可能导致全网故障
- 地理分布:节点是否集中在少数国家/云服务商
- 治理参与度:投票率、提案通过门槛
实战派思维升级:学区块链不要停留在"知道概念",要问三个问题:
- 威胁模型是什么?(谁可能攻击?攻击成本多少?)
- 经济激励是否对齐?(诚实行为是否比作恶更有利?)
- 工程复杂度是否值得?(这个场景真的需要区块链吗?用数据库是不是更简单?)
上篇小结
本篇从拜占庭将军问题出发,拆解了区块链的四大基石:密码学(哈希、签名、默克尔树)、共识(PoW/PoS/BFT 全景对比)、数据结构(区块与链)、网络与钱包。最后用 150 行 Python 实现了一个可运行的区块链。
如果你能把上面的代码跑通并解释每一行,你已经超过了 80% 自称"懂区块链"的人。
中篇预告:我们将深入生态层——比特币的 UTXO 与脚本、以太坊的 EVM 与 Solidity、主流公链横向对比、Layer2 扩容方案、跨链技术、智能合约安全、DeFi/NFT/DAO 全景,并附上合约部署实战。
下篇将覆盖:零知识证明、账户抽象、MEV、模块化区块链、RWA、工程实践与职业路径。