Caloogy 文章
English

别只信一个数据源:交叉验证与独立实现

日期
2025 年 7 月 6 日
系列
方法论
阅读
4 分钟
主题
RedundancyCross validationOracleRpc

一句话版本

一个系统只要依赖单一的数据来源,那个来源出错、被攻破或者撒谎,系统就会"理直气壮地"做出错误决定。 对策有两层:多源交叉验证(问两家以上互相独立的来源,答案不一致就停下)和独立实现(关键计算由两份分别写成的代码各算一遍,防止同一个 bug 两边一起犯)。航空、航天、金融、区块链,都在用这个原则。

全文分三层:

  1. 通俗理解:只看一块手表的人
  2. 实际用处:波音 737 MAX、阿丽亚娜 5 号、DeFi 预言机攻击
  3. 原理与工程要点:怎样才算"独立"、不一致时怎么办、客户端独立复核

第一层:通俗理解

1.1 只看一块手表

航海时代有句老话:"出海要么带一块表,要么带三块,千万别带两块。"

在软件里,"两块表"其实也很有用,前提是把规则定成:不一致就停下来,不做决定。对于安全决策,"停下"往往比"选错"好得多。

1.2 两种不同的冗余

冗余 防什么 例子
多源:同一个问题问不同的来源 某个来源出错、被攻破、撒谎 两家不同公司的区块链节点
多实现:同一个计算由不同的代码算 自己代码里的 bug 前端和后端各自独立计算金额

第二层:实际用处

事件 教训
波音 737 MAX(2018、2019) 自动配平系统 MCAS 只读取一个迎角传感器。传感器故障时,系统反复把机头往下压,两起空难共 346 人遇难。改进后的设计改为比较两个传感器,不一致时停用 MCAS
阿丽亚娜 5 号首飞(1996) 火箭有两套惯性参考系统互为备份,但它们运行同一份软件,遇到同一个数值溢出 bug,几乎同时失效,火箭起飞约 40 秒后解体。硬件冗余,软件不独立,等于没有冗余
DeFi 预言机攻击 2020 年的 bZx 等多起事件中,协议只从一个交易所读取价格,攻击者用闪电贷在那个交易所瞬间把价格砸歪,协议据此做出错误清算或借贷。之后行业普遍转向 Chainlink 等多数据源聚合的预言机
空客电传飞控 主飞控计算机由不同团队、用不同处理器和不同软件分别实现,互相监督,就是为了避免阿丽亚娜 5 号式的共模失效

区块链钱包里的应用:钱包通过 RPC 节点读取余额、预演交易。如果只用一家 RPC,那家节点出故障、被劫持或者撒谎,钱包就会给用户展示错误的信息。用两家互相独立的 RPC 在同一个区块高度上读取并比对,可以发现这类问题。

Caloogy Wallet 也采用了多源交叉验证。


第三层:原理与工程要点

3.1 怎样才算"独立"

看起来独立 实际上不独立
两个不同域名的 API 背后是同一家云服务商、同一个上游节点
两个不同的库 底层调用同一个依赖
两台服务器 跑同一份代码、读同一个配置
两个团队 照抄同一份参考实现

真正的独立意味着:一个出问题的原因,不太可能同时让另一个出同样的问题。

3.2 比对要在"同一时刻"

区块链数据每隔几秒就变。两家节点如果读的是不同区块,结果不一致完全正常。所以要先确定一个区块高度,再让两家都在这个高度上查询。其他系统同理:比较的是同一时刻的快照。

3.3 不一致时怎么办

场景 处理
安全相关(是否允许转账、风控结果) 拒绝并告警(fail-closed,见《Fail-closed》)
展示相关(余额显示) 显示"数据源暂时不一致",而不是任选一个
有三个以上来源 可以多数表决,但仍要记录和告警少数派

3.4 客户端独立复核

一个特别有用的模式:浏览器自己再算一遍。

服务器算出"这笔转账会让你 −100 USDC、对方 +100 USDC",浏览器不直接相信,而是直接连链上节点再算一遍,两者不一致就拒绝。这样即使服务器被攻破、谎报结果,用户看到的关键信息仍然来自浏览器自己的计算。

它的局限也要说清楚:如果页面代码本身被篡改,复核代码会被一起改掉。所以它防的是"服务器被攻破",防不了"前端被攻破"(后者见《Bybit事件与前端完整性》)。

3.5 独立实现的取舍

独立实现(N 版本编程)成本高:两份代码、两套测试、两倍维护。所以只用在最关键、最容易出错的地方,比如:

并且要有专门的测试覆盖两份实现之间的边界情况。


总结

层次 要点
原则 不依赖单一来源;不一致就停下
两种冗余 多源防"来源撒谎",多实现防"自己的 bug"
独立性 原因不同才算独立;阿丽亚娜 5 号是反面教材
实践 同一时刻比对;安全决策 fail-closed;客户端独立复核