数字信任从何而来:区块链要解决的真实问题
你在群聊里发了一句:“我给小王转了 100 块。”五分钟后,小王说没收到;你翻聊天记录,发现那句转账消息“还在”;你截图为证,他也截图为证——但你们截到的图不一样。到底谁在撒谎?还是系统在撒谎?更糟的是:就算大家都诚实,聊天记录也可能因为客户端缓存、服务器回滚、账号被盗而变得不可信。所谓“数字世界的信任”,从来不是一句“我说了算”,而是一个工程问题:在不可信的网络与不可靠的参与者之间,如何让所有人对同一件事形成可验证的一致结论。
很多人第一次听到区块链,会把它想成“更安全的数据库”或“去中心化的 Excel”。这类理解不算错,但会把你带进一个误区:你会拿它去解决所有数据共享问题,然后发现成本高、吞吐低、开发复杂。更准确的说法是:区块链是一套在不可信环境里运行的工程方案,用来实现一种特殊的分布式系统——可验证的复制状态机(Replicated State Machine, RSM)。它解决的核心不是“存数据”,而是“对状态变更达成共识,并能让任何人验证这个共识过程没有被篡改”。
> 边栏|本章标签 > > `#intro` `#threat-model` `#trust` > 读完你应当能回答:区块链到底在“信任”什么?它假设谁会作恶?它用什么机制让作恶不划算或不可行?
---
信任的两条路:机构背书 vs 密码学可验证
机构型信任:把难题交给“裁判”
现实世界里,我们经常把“信任”外包给一个中心机构:
- 银行:负责记账、清算、处理纠纷、冻结账户。
- 平台(电商、游戏、支付):负责订单、积分、道具、余额。
- CA(证书颁发机构):负责把“这个公钥属于这个域名/公司”这件事做成可验证的证书。
这种模式的本质是:我们不需要所有参与者互相相信,只需要大家相信同一个裁判。裁判可以强制执行规则:谁欠谁钱、谁拥有某个资产、谁的签名有效。
机构型信任很高效:单点决策、统一存储、便于优化。但它也带来三个成本:
1. 单点失效:系统宕机、被攻击、内部作恶都会影响所有人。 2. 权力风险:裁判可以修改规则或选择性执法(冻结、回滚、删帖、封号)。 3. 审计困难:外部参与者难以独立验证“裁判是否按规则办事”。
密码学型信任:把结论变成“可验证的事实”
密码学提供另一条路:不靠裁判背书,而靠可验证性(verifiability)来建立信任。核心思想是:
- 任何参与者都可以提出“某个状态变更”,但必须附带可验证的证据(签名、哈希承诺、证明)。
- 任何旁观者(即使不信任提出者)都能独立验证证据。
- 系统通过某种共识机制决定“哪一组状态变更成为全局历史的一部分”。
这不是说“没有中心就更安全”。相反,区块链通常假设网络里存在持续作恶者,并把“能否作恶成功”作为设计指标。它追求的不是让坏人消失,而是让坏人即使存在也难以改变系统结论。
---
这不是“万能数据库”:区块链真正要解决的是什么
把区块链看作数据库,会立刻遇到一堆“性能不行”的吐槽:TPS 低、延迟高、存储贵。要理解这些取舍,先把问题说清楚。
复制状态机(RSM):从“存数据”到“执行规则”
想象一个最简单的系统:账户余额。系统状态是一个映射:
$$\text{State} = \{ \text{Alice}\mapsto 50, \text{Bob}\mapsto 10, \ldots \}$$
一笔转账是一个状态变更函数(交易):
$$\text{State}_{t+1} = \delta(\text{State}_t, \text{Tx}_t)$$
其中 $\delta$ 是规则:检查签名、检查余额是否足够、扣款入账、写入日志。
所谓复制状态机,就是让多台机器都运行同一套规则 $\delta$,并且对交易序列 $\text{Tx}_1,\text{Tx}_2,\ldots$ 达成一致。只要交易序列一致,最终状态就一致。“同一份账本”在工程上等价于“同一串有序输入”。
区块链的关键在于:在没有中心裁判的情况下,如何让全网对这串输入序列达成一致,并且任何人能验证“这串序列没有被偷偷改过”。
为什么普通分布式数据库不够
你可能会问:我用主从复制、Raft/Paxos,不也能复制状态机吗?
可以,但它们通常依赖一个前提:参与者属于同一组织或受同一信任边界约束。也就是说,节点不会故意构造无效数据,不会同时扮演多个身份,不会在发现自己吃亏时拒绝同步。
区块链要解决的是更“脏”的场景:节点可能是匿名的、跨组织的、动机不同的,甚至一开始就冲着攻击来的。这就引出了本章最重要的工具:威胁模型(Threat Model)。
---
威胁模型:我们假设谁会怎么作恶
要写出靠谱的系统,先假设坏事会发生。对“多人共同记账”来说,典型攻击面包括:
1. 重复支付(Double Spend):同一笔钱/同一份资产被花两次。 2. 篡改历史(History Rewrite):事后改账,否认已发生的交易。 3. 伪造身份(Sybil Attack 的入口):一个人冒充很多人,影响投票/共识。 4. 选择性传播与分叉(Network Partition / Eclipse):让不同人看到不同历史。 5. 审查(Censorship):某些交易永远无法被打包进历史。
在传统系统里,很多攻击靠“管理员权限”或“法律合同”来兜底;在开放网络里,区块链必须用密码学与激励机制把这些攻击的成功概率压到可接受范围。
---
最小案例:三人记账,如何一步步走向“区块链”
下面我们用一个尽量小、但足够真实的案例,把“区块链需要哪些零件”推导出来。
场景设定:三个人共享一本账
- 参与者:Alice、Bob、Carol。
- 目标:记录彼此之间的转账,任何人都能随时计算出每个人余额。
- 初始:三人各 100 代币(只是记账单位)。
- 网络:通过互联网互发消息,消息可能延迟、丢失、乱序。
- 假设:至少有一个人可能作恶。
我们先写一份“朴素协议”:
1. 谁要转账,就在群里发:`Alice -> Bob 10`。 2. 所有人收到后,就在自己的账本末尾追加这一行。 3. 余额靠本地回放账本计算。
这个协议看上去像“去中心化”,但马上会出问题。
问题 1:消息不一致导致账本分歧
如果 Alice 同时给 Bob 和 Carol 转账,消息在不同人那里到达顺序不同:
- Bob 先看到 `Alice->Bob 10`,再看到 `Alice->Carol 90`
- Carol 先看到 `Alice->Carol 90`,再看到 `Alice->Bob 10`
若 Alice 只有 100,第一种顺序下两笔都能执行(先扣 10 再扣 90),第二种顺序下第二笔会失败(先扣 90 后余额只剩 10,不够再扣 10)。只靠广播消息,没有一个全局一致的顺序,就没有同一份账本。
> 这里第一次出现“共识”的影子:大家需要对交易顺序达成一致。
问题 2:重复支付(双花)
Alice 作恶:她同时向 Bob 和 Carol 发起两笔“把同一笔钱花掉”的交易,并且刻意控制传播路径,让 Bob 和 Carol 各自只看到对自己有利的那一笔。
在没有全局一致顺序、也没有最终裁决者的情况下,Bob 和 Carol 都可能“以为”自己收到了钱。这就是双花。
> 双花不是“钱被复制了”,而是“系统无法给出唯一历史”。区块链要做的是让历史最终收敛。
问题 3:事后篡改历史
假设大家都把账本存在本地文件里。某天 Alice 说:“我从没给 Bob 转过钱。”然后她把自己账本里那行删掉,再把“新账本”发给别人:
- 如果别人没法证明自己手里的版本更可信,就会陷入罗生门。
- 如果别人依赖 Alice 提供历史(比如 Alice 是“管理员”),那删一行就能翻案。
> 这里需要“不可篡改日志”的构件:一旦历史写入,就能被任何人检测到是否被改动。
问题 4:伪造身份与“多数”欺骗
如果我们说“少数服从多数”,那 Alice 可以注册 100 个马甲账号,组成“多数”,让自己想要的历史成为“共识”。这类攻击通常叫 Sybil Attack(女巫攻击):用廉价身份制造虚假多数。
> 因此,区块链共识不能简单按“节点数投票”,而要引入稀缺资源(算力、质押、身份成本)作为投票权重。
到这里,我们已经把区块链的四个核心零件逼出来了:
- **可验证规则**:交易是否有效?余额是否足够?签名是否正确?
- **不可篡改日志**:历史被改过能否立刻发现?
- **分布式共识**:交易顺序与历史版本如何最终统一?
- **激励与惩罚**:为什么有人愿意提供资源维护共识?作恶为什么不划算?
接下来逐个拆解。
---
密码学构件 1:哈希,让篡改“可被检测”
哈希是什么:指纹,而不是加密
加密(encryption)解决“保密”,哈希(hash)解决“完整性”。一个安全哈希函数 $H(x)$ 有几个关键性质:
- **固定长度**:不管输入多长,输出都是固定长度(如 SHA-256 输出 256 位)。
- **抗碰撞**:很难找到 $x\neq y$ 使得 $H(x)=H(y)$。
- **抗预映像**:给定 $h$,很难找到 $x$ 使得 $H(x)=h$。
- **雪崩效应**:输入改一位,输出大幅变化。
你可以把哈希当成“文件指纹”:任何篡改都会改变指纹。
动手实验:用命令行感受雪崩效应
在类 Unix 环境(macOS/Linux):
```bash printf "Alice->Bob 10\n" | shasum -a 256 printf "Alice->Bob 11\n" | shasum -a 256 ```
你会看到两次输出完全不同。差的只是 `10` 和 `11`,指纹却像换了一个宇宙。
在 Python(标准库即可):
```python import hashlib
def sha256(s: str) -> str: return hashlib.sha256(s.encode()).hexdigest()
print(sha256("Alice->Bob 10")) print(sha256("Alice->Bob 11")) ```
哈希链:把“每条记录”串成“不可悄悄改动的历史”
只有哈希还不够。Alice 可以把账本改完后重新计算新的哈希,然后说“这就是我的新指纹”。要让历史不可悄悄改动,需要把记录链接起来:
- 每条记录(或区块)包含上一条记录的哈希:`prev_hash`
- 当前记录的哈希由 `prev_hash + data` 计算
这样一来,改动任意历史记录都会导致后续所有哈希都变,篡改痕迹无法隐藏。
```text Record_1: data_1, hash_1 = H(data_1) Record_2: data_2, prev=hash_1, hash_2 = H(prev || data_2) Record_3: data_3, prev=hash_2, hash_3 = H(prev || data_3) ... ```
动手写一个最小哈希链(仍然只用标准库):
```python import hashlib from dataclasses import dataclass
def h(x: str) -> str: return hashlib.sha256(x.encode()).hexdigest()
@dataclass class Record: prev: str data: str hash: str
def append(chain: list[Record], data: str) -> None: prev = chain[-1].hash if chain else "GENESIS" hash_ = h(prev + "|" + data) chain.append(Record(prev=prev, data=data, hash=hash_))
def verify(chain: list[Record]) -> bool: prev = "GENESIS" for r in chain: if r.prev != prev: return False if r.hash != h(r.prev + "|" + r.data): return False prev = r.hash return True
chain: list[Record] = [] append(chain, "Alice->Bob 10") append(chain, "Bob->Carol 5")
print("ok?", verify(chain)) chain[0].data = "Alice->Bob 1000" # 篡改历史 print("ok after tamper?", verify(chain)) ```
这段代码不会阻止 Alice 改文件,但能让任何人检测到改动。工程上,“可检测”是第一步:当你能可靠检测,后续才谈得上惩罚、拒绝同步、分叉选择。
> 边栏|术语表(1) > > - 哈希(Hash):把任意输入映射成固定长度输出的函数,用于完整性校验。 > - 哈希链(Hash Chain):每个条目包含前一条目哈希,从而让篡改可被检测。 > - 创世块(Genesis):链的起点,通常是固定常量或协议定义的初始状态。
---
密码学构件 2:数字签名,让“谁发起的交易”可被验证
哈希链解决“历史是否被改过”,但还没解决:“这笔转账真是 Alice 发起的吗?”如果 Carol 能伪造一条 `Alice->Carol 50` 并广播,其他人怎么判断真假?
答案是数字签名(Digital Signature):
- Alice 有一对密钥:私钥 `sk_A`、公钥 `pk_A`
- 她对交易内容 `m` 生成签名 `sig = Sign(sk_A, m)`
- 任何人都能用 `Verify(pk_A, m, sig)` 验证签名
关键点:签名验证不需要信任签名者,只需要信任密码学假设(算法安全性)与公钥归属。
公钥归属:从“我说这是我的公钥”到“全网认可这是我的公钥”
这里会出现一个常被忽略的坑:即使签名不可伪造,攻击者仍可说:
> “这是 Alice 的公钥。”(实际上是攻击者自己的)
于是攻击者可以用自己的私钥签名,然后宣称“这是 Alice 签的”。这叫公钥绑定问题:公钥必须和身份绑定。
传统互联网用 CA 体系解决:浏览器内置根证书,证书链把域名和公钥绑定起来。区块链世界常见做法不同:
- **地址(Address)= 公钥的哈希**:身份不再是“姓名”,而是“能控制某个私钥的人”。
- 如果你想让“现实身份”与地址绑定,需要额外的认证层(KYC、DID、链下签名声明、Web2 账号绑定等)。
因此,区块链里的“身份”往往是密码学身份(cryptographic identity):谁能证明自己拥有私钥,谁就拥有地址下的资产控制权。
动手实验:用现成工具体验签名
如果你不想一开始就引入编程库,可以用 `ssh-keygen` 生成密钥,用 `openssl` 签名验证(命令会因系统略有差异,这里给出思路):
```bash
生成一对密钥(用于演示,别拿它当生产私钥)
ssh-keygen -t ed25519 -f demo_key -N ""
echo -n "Alice->Bob 10" > tx.txt
用私钥签名(不同系统 openssl/ssh 工具链命令不同,可替换为 gpg)
ssh-keygen -Y sign -f demo_key -n file tx.txt
用公钥验证
ssh-keygen -Y verify -f demo_key.pub -I demo -n file -s tx.txt.sig < tx.txt ```
你会感受到:验证者不需要认识 Alice,也不需要问任何中心机构;只要公钥被认为属于 Alice(或地址),就能验证交易确实由对应私钥发起。
> 边栏|术语表(2) > > - 数字签名:用私钥生成、用公钥验证的不可伪造证据。 > - 地址(Address):常由公钥哈希得到的标识,用于接收资产与验证权限。 > - 密钥管理:私钥丢失=资产丢失;私钥泄露=资产被盗,这是区块链安全的第一现实。
---
只有哈希与签名仍不够:分布式共识决定“哪条链算数”
到目前为止,我们能做到:
- 交易可验证(签名 + 规则)
- 历史篡改可检测(哈希链)
但我们仍然无法解决“分歧”:当网络分区或有人故意制造多个版本时,大家可能各自维护一条看起来都合法的链。
区块链共识要解决的就是:当存在多个候选历史时,如何让全网最终收敛到同一条历史,并让攻击者难以长期维持分裂。
直觉:从“多数投票”到“多数资源”
如果简单按节点数投票,就会被女巫攻击碾压。因此共识通常把“投票权”绑定到某种稀缺资源:
- **工作量证明(PoW)**:投票权 ~ 计算哈希的成本(算力与电力)。
- **权益证明(PoS)**:投票权 ~ 锁定的资产(质押),作恶会被罚没(Slashing)。
- 也存在联盟链常用的 **BFT 类共识**(PBFT/Tendermint 等),依赖明确的成员集合与准入。
对于入门理解,你可以先把它抽象成一句话:
> 共识协议定义了一种“选择规则”,让诚实参与者在观察到不同历史时,能以高概率选择同一个“最可信”的版本。
用一个“分叉”例子理解共识的必要性
假设链上记录的是转账。某一时刻出现两条分叉:
- 链 A:包含 `Alice->Bob 10`
- 链 B:包含 `Alice->Carol 10`
两笔交易都带着正确签名,看起来都合法(因为它们分别在不同历史上下文里余额足够)。系统必须决定最终接受哪一条分叉,否则双花永远无法解决。
PoW 的比特币用“累计工作量最大”来选链(常被口语化为“最长链”,更准确是“最重链”)。PoS 系统会用“累计权益投票”与“最终性(finality)”机制来决定不可逆的检查点。