之前我们介绍了比特币的写入流程 比特币技术入门3(写入流程) 为了简化模型,加了一些限制,使得系统基本和一个单机系统运行轨迹一样。但是比特币是一个分布式系统啊,还有那么多副本,而且没有用 Quorum 机制,那么系统就必然会导致某些节点的链是不一致的,那么比特币是如何解决这个冲突呢?
本文预计阅读时间 5 分钟。
双重支付概念
比特币作为电子货币,且不利用第三方机构。首先需要解决的问题就是:如何防止双重支付(double-spending)? 双重支付简单来说就是一份货币被使用了多次。
这个约束在比特币结构的基础上就可以表示成:一个交易的输出最多接一个交易的输入,即最多被消费一次。
双重支付是比特币系统要解决问题。对应有两道防火墙,第一道是本地检查,第二道是分布式协议。
理想情况
我们之前介绍过一种理想的工作模式:严格按照每10分钟有一个节点挖到矿,且一个区块在10分钟之内可以到达所有节点。
在这种理想的工作模式下,系统会出现双重支付吗?不会。简单看一下:现在有两笔账单 A 和 B,不分先后,都要消费同一笔比特币。
(1)第一种情况:假如 A 在 B 之前被写入了区块链。在某个节点挖到矿准备写入 B 时,根据我们之前的假设,这个节点已经同步了最新的区块链。因此,A 已经在这个节点的本地链上了,通过本地检查会发现 B 不合法,直接把 B 扔掉。
(2)第二种情况:A 和 B 都还未写入区块链。并且都被一个节点接收,这时这个节点直接在本地检查候选区块中的所有账单中是否有冲突,检查到 A 和 B了,扔掉后来的一个。
可以看到,在理想情况下,本地检查就可以避免双重支付。但是现实情况呢?
现实情况
现实情况是不一定严格10分钟有一个节点挖到矿,可能每隔任意时间就有节点挖到矿了,也可能两个节点同时挖到矿了。而且可能出现网络分区导致一个区块不确定多久会广播到所有节点。
由于节点挖到矿之后,并不需要请求其他节点同意就直接写入本地区块链了,因此很容易出现各个节点维护不同的链的情况。但是,毕竟是个分布式系统,不同的节点总会进行通信的,当他们通信时,就会检测出冲突,类似 git 的分支合并。git 的分支合并冲突是交给用户手动解决的,但是比特币的冲突可以靠系统解决。中本聪给出了一个简单的解决方法:
以最长链作为有效链,新的区块需要加到最长链后,短链在一定时间后会被抛弃。
这个类似排队上公交车,这时候有两个队,管理员说以最长队为准,这时候后来的人为了保证自己排到前面,都会去排最长的队。这时,短的链就会被丢弃,我们看个例子:
上边是节点 A 的链,下边是节点 B 的链,当节点 A 接收到 B 的链,A 会抛弃绿色块,把 B 的链同步过来,在黄色块继续写。
可能的双重支付
在这种机制下,有没有漏洞让我把一块钱花多遍呢?有的。我可以先把交易写入区块链,然后想办法把这笔交易废弃掉,也就是让这笔交易的链分支成为短的分支(再写一个长分支),这样这笔分支就不会被认可了。我就可以继续创建一笔新的交易来花这笔钱了。
好了,准备就绪,我要开始干坏事了,看示例图:
我控制着节点 A。交易A 是第一笔交易,并且已经被广播到其他节点了。为了让交易 A 失效,我需要重新开一个黑链包含交易 B。其实就是和其他节点赛跑,我需要先追上绿链,并且超过绿链一个块,这样我就可以同化其他的节点了。
而这就需要我连着挖到两个矿。这是个什么概念?从概率的期望来说,这就相当于我有比特币系统中一半以上的计算资源。我都这么牛逼了,还需要黑系统吗?我只需要努力挖矿就能得到很高的回报。
然而更多情况是下面这种:
我和别人的计算能力差不多,当我挖到一个矿写入一个黑块时,另外两个节点已经挖到两个矿了,他们互相同步数据根本不会鸟我,而这个差距还会越来越大,最终我只能选择放弃抵抗。
这里还有一个细节:块是一个一个广播出去的,不是整个链广播出去的,因此,当某个节点接收到一个和自己分支冲突的块时,不会马上丢掉,会先缓存在本地,当缓存的所有块构成的某个分支超过当前工作的分支,就会切换到最长分支上去。
一般来说,当链上的一个块后边接了 6 个块了,就可以确认这个块的所有交易不会被丢弃了,所以比特币账单的确认时间是 1 个小时。
总结
善良的矿工本身具有一个块的先手优势,而且占据系统主要的计算能力,在这两个条件下,黑客基本没得玩。以游戏来打比喻,高级玩家会想方设法维护这个规则来获取利益,只有那些底层玩家才想破坏规则,但是还干不过高级玩家。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)