由 Whitepaper - Coinbugs: Enumerating Common Blockchain Implementation-Level Vulnerabilities 翻译总结而来。
网络分割有可能导致 双花。因为当网络重新统一时,其中一个网络的交易会被回滚,如果黑客能设法不让交易转发到原链就有可能实现双花。所以网络分割有可能是 最重要 的一种攻击向量。
不同客户端实现有可能对区块验证的判断逻辑不同,进而导致网络分割。
本质上是客户端的 equivalence 同等性 问题。
事实上,对一个协议的实现,实际上是对一个协议的一门方言 dialect 的实现
而且 Pieter Wuille 解释过,实际的 consensus rules 事实上是不可知的 (尤其是对于区块验证这种 context/state-dependent 的事情)。比如:
所以实现 同等性 很难。
SIGHASH_SINGLE
交易时, bitcoin-ruby 的实现是对的,但关键是 bitcoin-core 本身的实现是错的。(一些 edge case 可能一开始考虑不到)个人觉得,就算是软分叉也会有这些问题。
文档作为 spec 其实是不够的,实际如何 implement 才最有可信力。
比如说 bitcoin 的代码才是最准确的文档。
如何尽量避免这类问题? 只能手把手比对源码, 设计 input tests, 甚至 cross-implementation fuzzing。
就算只有一种客户端实现, 运行环境不同也可能导致执行结果不同。(架构 32-bit vs 64-bit, 操作系统, 时间/地区设置, 配置...)。另外依赖的系统库版本也有可能不同(甚至没有固定住版本、开启了自动升级)。
比如说 bitcoin OpenSSL's ECDSA signature handling 就导致过两次共识问题:
ecdsa_verify
本来既接受 BER 又接受 DER,但从 这个 commit 以后就只接受 DER 编码签名。(这是为了防止 CVE-2014-8275,即防止 利用 BER 编码绕过黑名单。)编写一个区块链客户端应该尽量避免受执行环境影响。
如何尽量避免这类问题?
客户端处理区块时会在存储中查找之前见过的区块哈希,看这个区块是否也在之前见过。如果已经见过就认为这个区块之前被处理过,并丢弃这个区块。这是用来防止 p2p 网络(广播)造成的一个区块被处理多次的一种优化。
很重要的一点是,如果一个区块被校验认为无效并拒绝,它的 hash 也会被标记为已处理。那么如果一个恶意节点成功地在不改变区块 hash 的情况下使一个有效区块变得无效,那么这个区块 hash 到底对应的是那个有效的区块还是那个无效的区块就不可知了。
一个节点,不需要挖矿,可以监听网络中新挖出的区块并尝试快速地广播它的相同 hash 的但无效的变体来利用这个漏洞。这会导致其他先接收到这个无效区块的节点会拒绝掉后面真正有效的区块,而其他先接收到有效区块的节点则会正确地接受这个区块,导致网络分割。
上述分析讲述的是针对区块 hash 而言,对于交易 hash 也是同样的道理。
新 PR 有可能改变 validation rule,或者改变了但自己不知道。
需要检查 client 做了什么可能会影响 consensus rules,以及底层调用库的升级有没有可能会影响 consensus rules。
如果真的升级,升级方式包括:
也要保证
如何尽量避免这类问题?
仔细对比 旧结构、旧函数 有没有在 新代码 中被使用,或 新代码 有没有影响 旧的规则。
处理临时分叉链时数据混了
略
难以区分叶子节点和中间节点
如何尽量避免这类问题?
WIP