BSV两次算力大战大量细节曝光——公开的算力是安全的基石 - 打点钱包

BSV两次算力大战大量细节曝光——公开的算力是安全的基石
2021.08.11

许多年以后,当矿工哲亮在某个深夜里密切关注其他矿工投票过程时,还会想起那个对抗算力攻击的深夜。

 

那次的攻击,是蓄谋已久的攻击行为。在暗中的观察者,经过了精心策划和筹备之后,发起了第一次攻击。那是在2021年的6月24日,一个匿名的攻击者使用“Zulupool”的名号,采用了一种被学术界称为“区块预扣”的手段,对 Bitcoin SV 网络发起了进攻。这种被称为“区块预扣”的攻击方式,是利用原先 Bitcoin SV 上的商户和收款方依赖“确认数”进行记账的方式,攻击者先自己开始使用极大的算力秘密挖矿(预扣),并在自己挖矿的区块中将 BSV 打包为转给自己(李鬼交易),同时将同样的 BSV 转入交易所(李逵交易)。在秘密挖矿(预扣)一段时间后,交易所因为“李逵交易”的“确认数”达标,就对该交易进行了确认并允许其存款和交易提现。在完成上述动作后,攻击者看到自己秘密挖矿的区块已经高于公开的诚实区块链,就放出了之前秘密挖矿的区块。此时在其他矿工和商户看来,是发现了更多工作量的链,因此放弃了之前的诚实链,切换到了包含了李鬼交易的区块链上,此时“李逵交易”就被删除,交易所发现最初的存款不见了。

 

 

  第一次攻击尝试  

 

在第一次(#692928-#692931)的攻击尝试中,攻击者在区块头填写“Zulupool”来仿冒原本就在跳链的“Zulupool”(Zulupool 是一个使用了 Hathor 协议进行联合挖矿的矿池,在 BSV 挖矿收益高的时候,会切换到 BSV 上挖矿)。因为在短时间内涌入大量算力会导致出块时间缩短,此时矿工坚持在自己的孤块上进行算力竞争是一种理性的行为,因此当时网络上的其他参与方并没有认定这是一次攻击。事后回想起来,这可能是一次演练。

 

 

  第二次大规模攻击  

 

在汲取了第一次攻击经验之后,攻击者于7月1日故伎重演,发动了第二次攻击。第二次攻击开始于区块高度 #693995A。

07:16:38 Taal 挖出了 #693999A (2de6ca),

07:39:48 仿冒“ZULUPooL”的攻击者在连续放出 #693996B(282a39) - #693999B(b3fb8a)四个区块,因为 #693999B 的工作量证明更大导致其他矿工开始以 #693999B 工作。

07:44:39 攻击者挖出了  #694000A(eeb47c),让其他矿工重新切换到 #694000A 上,攻击者自己丢弃了 #693995B - #693999B 三个区块。

07:57:59 的时候矿工crypto/CODEBLACK/ 挖出了 #694001C(46d995)。

08:14:52 #694001C 被攻击者放出的 #694001D (01fcf0)- #694003D(25813b) 给孤立了。

08:24:38 攻击者放出了#694003A(5c2a59),非常无厘头地孤立了自己的 #694003D(25813b)

08:25:23 攻击者的#694004A(789f48)成为了主链的一部分。其余矿工跟随。

08:30:44 Taal 挖到了 #694005A(f69f4c)

08:56:36 Taal 挖到了#694008A(b79739)。

09:05:17 攻击者放出了#694006E(5082f6) - #694008E (79969d)三个区块。此时的区块工作量超过了 #694008A,BSV全网切换到了攻击者的 E 链上。BSV 网络的其他矿工,包括 TAAL、ViaBTC、Bcdda,还开心的在之前的 #694008E(79969d) 上继续出块。与此同时,攻击者在 TAAL 的 #694007A 之后连续打包了六个区块,#694008F(797698) - #694012F(4f76c1),并没有对外广播。

09:15:25 此时的区块高度达到了 #694011E(102fac)。矿工们没有想到危险已经迫近。

09:16:07 攻击者广播了 #694008F(797698) - #694012F(4f76c1),一共六个区块,并超过了当前的区块高度,让所有矿工切换到分叉点 #694005(f69f4c),包含 #694012F 的分支上开始计算。在外界来看,这里是发生了一个6区块的重组。

09:17:05 攻击者广播了下一个区块 #694013F(c9e938),获得了网络的其他矿工的认可。最终  F 链成为了最长链的一部分。

11:39:08 攻击者广播了 #694028G(2b635f),重组了 #694025F(c9d50f),可是好景不长。

11:40:00 又被 Hath 在 #694029F(6448fb)进行了重组。

18:43:58 攻击者又卷土重来。在 #694077F(9dbf18)高度使用 #694074H(79848a)至 #694077H(ba8b77)进行了重组。

 

有趣的是,#694008B - #694013B 在区块内使用的时间戳,是每隔两秒一个。这也迷惑了其他参与者。根据区块的时间戳来计算,矿工们会得出攻击者的算力非常大的假象。而实际上,区块头中的时间允许在一定范围内调整并也会被认为是符合共识的网络协调时,攻击者利用了这个特性,人工的写入了时间戳,来让矿工们以为这些区块是在短时间内发生的,并因为 DAA 的特性,让下个区块的工作量因为时间戳的差异略微的提升了一点,导致在同高度相比时的工作量竞争上具备优势。而实际上攻击者是在 09:05:15 的时候广播了#694008E,并于 09:16:41 的时候放出了 #694012F,并于 09:17:05 的时候更新了 #694013F。有理由认为 ##694008B - #694012B 这五个区块,是在 09:05:15 至 09:16:41 的时候发现的。此时 BSV 网络的难度较低,全网的其余矿工也在 10 分钟的时间里出了三个区块。因此可以大约估计出,此时攻击者调动的算力,是原本全网总算力的一倍以上。

在晚些时候,攻击者也尝试多次一次性放出三个以上的区块来重组其他的矿工。不同的矿工们虽然觉得是被其他矿工攻击,但也没有实锤的证据。看到的更多是自己的区块被重组,攻击者获取到了收益。但实际上这部分收益并不是明面上看到的攻击者 ZULUPooL 拿走的,真正的攻击者仿冒了 ZULUPooL。现在回顾起来,我们看到攻击者并不是单纯的仅在一条链上挖,其甚至孤立了自己的链。这部分的攻击是在尝试利用不明真相的矿工,去制造超过6个区块高度以上的重组。

 

 

  多次尝试攻击  

 

随后在的几天,攻击者使用类似的方法持续开展进攻,重组的区块也越来越多。 #694628 到 #694633的五个区块的重组 、 #694663 到 #694673 的十个区块的重组、#694776 到 #694785 的十个区块的重组。在几次试探没有得到矿工的反馈之后,一次对矿工的真正考验开始了!

 

  100 个区块高度重组和算力大战  

在8月3日 15:42:34 首先我们挖到了 #698740A(d838a5)。五分钟之后的 15:48:32,矿工们正在链高度 #698740A工作,此时所有矿工都收到了来自“Taal.com”发布的区块,但其实在这里,是攻击者仿冒了“Taal.com”,使用了 Taal 的真实收款地址,这种伪装让 Taal 并没有任何“损失”,但却让其他的矿工感到迷惑,并不知道是否应该信任这个区块。但是攻击者犯了一个错误,就是 Taal 实际上部署了 MinerID,这是一种可以在区块中签名来表明矿工真实身份的协议。因此攻击者仿冒的 Taal.com 被轻松识别了出来。只是我们还不知道,这将拉开 BSV 区块链上 100 个区块高度重组和算力大战的序幕——攻击者发布的区块高度从 #698641B(e43f25) 开始,持续到 #698725B(ed21ddf)。虽然,这里 #698725B(ed21ddf) 的区块高度并没有 #698740A(d838a5) 高,但是攻击者的算力大于当时的全网算力并伪造了时间戳,其累计的工作量超过了当时矿工的累积工作量,因此被视为此时的最长链。与此同时,攻击者又悄悄回到了 #698736A(69f2db2),开始挖 #698737C(3d27d7)。

 

所有矿工在经历了从 #698740A(d838a5) 到 #698641A(e43f25) 这个 100 个区块的回退,开始在新的区块高度 #698725B 上工作,并因时间戳修改带来的区块工作量的假象蒙蔽,和攻击者一起,迅速的把攻击链 B 的区块高度推高到了 #698757B(7923fa),此时攻击链上已经有了 112 个确认,而同时,一场事关整个战场走势的矿工反击会议也紧急召开,矿工们紧张地商量着对策。在 17:49 前后,Taal 决定和这么长的链重组说“不”!拒绝在攻击者的链上进行投票,恢复到在最初的诚实链上进行挖矿。于是矿工们回到了 #698740A(d838a5) 并成功的于 18:15:21 挖出了 #698741A(81321e),并一直挖下去,直到 03:47:47 #698814A(a969d3)。并且在19:58 的时候,BA 发布了声明,号召大家拒绝不诚实的链,在诚实的链上进行挖矿。

 

在 03:48:10 的时候,攻击者突然放出来 #698737C(3d27d7) - #698818C(30fe7e),并在之上公开持续挖了几个块到 #698820C(2950c8)。之后攻击者回到了 #698814A (a969d3)开始继续挖,但没有公开。 

03:57:34 矿工们因工作量证明更大,切换到了攻击者的 #698820C(2950c8)

04:07:50 矿工们在攻击者的链上挖出了 #698823C(2d3d0a)。

04:14:56 矿工们第二次主动的人工拒绝了 #698737C(3d27d7) 而回退到了诚实的 #698736A(69f2db2) 上。

04:54:50 SVpool 挖到了 #698818A(d07d42)。

06:27:31 攻击者放出了 #698815C(c246501) 到 #698831C(89f565),导致矿工又切换到了 #698831C(89f565)。

06:34:49 矿工们第三次人工拒绝了 #698815C(c246501),回到了 #698818A(d07d42) 的诚实链上开始继续挖矿。在此之后暂时没有检测到攻击,最终,在众多矿工的努力下,诚实链的工作量最终超过了攻击链,成为了最长链的一部分。


诚实的节点真的赢了么?可以看到,在 #698749 和 #698750,攻击者竟然使用算力帮助诚实链。有理由相信,在两条分叉链的 #698642A 和 #698642 中,存在着不同的双花交易。在攻击链得到了 100 个区块以上的确认后,攻击链就有可能得到商户的认可,在100个确认后即可到账。随后这个攻击链又被诚实链覆盖,攻击链上的交易在诚实链上也成为了无效交易。这样原先认可了攻击链上交易的商户就会受到损失。

 

 

  如何抵抗算力攻击  

 

诞生了十一年的比特币协议,是否可以抵抗这种算力压倒性的攻击?又怎么对抗超大算力的攻击呢?

 

  诚实的矿工和 MinerID  

 

同时我们看到攻击者可以轻易的仿冒其他矿工,混淆了视听,攻击者并不在意算力所产生的收益,甚至直接使用了矿工的收益地址。让我们以区块中的收益为安全因素的考量受到了挑战。虽然我们更愿意去接收熟识的长期维护诚实账本的诚实矿工的信息,却不能够依靠区块中的 Coinbase 和收益地址,来确定哪个行为是该诚实矿工的行为从而做出正确的响应。也就无法快速达成共识。因此我们需要催促矿工们尽快部署使用 MinerID,来构筑矿工的信用,并通过诚实矿工对外发布自己的投票的方式来影响和达成共识。

 

  比特币的安全性来自于“公开”  

 

我所关注的,是如何保护安全即时交易。如果要求每个人因为重组发生的可能性,就去放弃安全即时交易而要求100个以上的确认,那比特币系统就输了。这里背后有一些博弈的思考。主要是我如何才可以既保护矿工不会被人为的陷害(分区攻击,主动发双花给矿工),又要降低成本(为了防止攻击提升所有人的成本代价是不值得的),还得保护用户。因此一个正确的安全模型,是很重要的。

 

我依然认为小额的即时交易是安全的。可以看到这次的攻击,攻击者调动了超过当时 BSV 全网的算力,但是同时也承担了被诚实链覆盖后的成本。付出了这么大的成本,如果仅对小额零确认进行攻击,收益有限的话,是经济上不可行的。如果是大额的交易呢?我们还可以依赖确认数么?

 

比特币的安全性来自于“公开”。因此每个商户都应该关注区块链上的所有区块头数据,可以及时的通过检测区块头,来识别正常的孤块重组和攻击行为,并针对不同的情况开展不同的对策。因为一个区块内的孤块和重组,是会经常发生的。只要区块重组是公开发生,而不是发生了区块预扣的情况,就对交易的安全没有影响。在公开发生的重组中,商户可以清楚的看到两条竞争链的情况,他只需要保证自己收到的交易在两条链上都同时存在即可。但是如果发生了区块预扣,商户只能看到一条链,因此无从判断自己的交易是否被所有矿工接收,这是一种对网络的攻击行为。对于诚实记账的矿工来说,是需要确保自己打包的区块会成为最长链的一部分。因此对于这次攻击,大家都坚持在大算力矿工的领导下,跟随了算力领导者的选择,迅速的达成了共识让最长链超过了攻击链。这种依据区块头信息来判断和选择诚实的区块链的能力,是矿工需要尽力去说服商户去做的。因此矿工才被激励而广播区块头。因为矿工需要尽快广播自己工作的区块头数据来获得商户关于诚实的认同。隐匿自己的区块头是不诚实的,其原因是隐藏了信息让用户无从判断是否存在危险。用户不应该去相信一个虽然工作量更大,但是却隐藏了自己的链。所以单纯出于利益驱动,所以只要区块头是藏起来的,收款方就完全可以不认可隐藏的区块链上的记账结果要求等待分叉结束。或者发起退款。这是一个基于商户自己的数据视角的,标准确认流程。

 

  区块链的分叉检测相应措施  

 

整个生态需要严格的遵守最长链的原则。但这并不是简单根据算力最长链这一个唯一的标准来进行判断。矿工需要判断怎样是诚实的链,并且使用手中的算力进行表达。用户需要根据公开的算力信息,去判断区块链账本上发生了什么,然后通过明显的公开迹象来判断应该做出什么响应。商户在发现存在超长的分叉的时候,就要敏锐的认为不仅仅是需要满足区块确认数,还应考虑其他安全措施。        

我建议商户采取的对策是这样的,在对用户进行入账确认的时候,查看区块链的区块头和工作量状态,1. 在没有发现分叉链的情况下跟从区块确认数;2. 如果发现了区块分叉,那么就需要让该笔交易在两条或者多条区块链上同时存在,如果多条区块链均包含了该交易,则可以确认不管哪个分叉胜出,交易可以被包含在账本中;3. 检测该笔交易是否存在双花,如果发现了双花,则应拒绝该笔交易,商户可以选择对该笔交易进行退款;4. 如果该交易暂时没有发现双花,但只存在一个区块链上而不是在所有检测到的链上同时存在,则需要等待,直到获得多条链同时的确认,或者其中有些链成为孤块为止。这种判断策略,仅仅需要关注区块头数据,以及交易的默克尔证明,是不管矿工决定挖哪条链,商户都可以保护自己利益而采取的最佳策略。

 

  什么是诚实的区块链和博弈  

 

因为矿工并没有因双花达成先后的共识,所以矿工不应去鉴别双花交易中哪个交易诚实哪个交易属于双花。矿工要做到的,是如实披露双花,并保证用户具备正确检测到双花的能力。除此之外,矿工不太有可能去守护每一笔交易来保证交易没有双花。这样做会推高整个系统的成本,但矿工可以用正确的策略来让每一笔双花都可以被及时检测到。只要用户可以迅速检测到双花,就可以迅速退款。要求付款方重新付款。问题其实已经解决了。

这种机制,就鼓励了节点对于自己正在挖的和接收到的区块头进行全网广播。因为只有自己挖的区块头被全网公开看到,才可以洗脱私自挖矿的嫌疑而被矿工防御所孤立,被生态指责为破坏网络安全性而受到排挤。比特币协议最重要的部分是博弈和经济激励,节点并不是带着利他的心去传输任何一个字节的数据,更是要通过自己的付出来获得全生态的认同,获得其他矿工的接受。

 

通过上述针对区块头数据的甄别采取不同应对策略,不管矿工怎么选,理性的用户可能选择的最佳策略是确定的,所以这个博弈结果是一个纳什均衡,并被所有的矿工所认知而形成共识。所以矿工必然要选择一个在纳什均衡下的最佳应对策略,来防止自己的区块被生态拒绝,这个时候达到整个生态的纳什均衡。矿工就知道什么账本才是诚实的账本而被生态所接受。这个诚实,就是公开无隐瞒。之后也许出于成本考虑,用户就可能会基于风险和收益的考量而节约一些成本。比如只问少数的矿工而不是所有矿工。

 

从这个角度来看,所谓的“诚实”并不只是无法被检验的“先到先得”,而是恰当的承诺和履行承诺,以及正确的公开。“先到先得”只是矿工可以采取的一个合理的策略。任何隐藏的行为都会降低系统的安全性,破坏安全即时交易的安全性,应当被视为对网络的攻击行为。双花的检测,并不应当是由矿工必须提供的服务,但是在被询问的时候,矿工应该要给出正确的结果,自己是否接受交易并给出承诺。

 

 

引入了 MinerID 和矿工对查询结果的承诺,以及商户验证公开区块头数据的能力,我们可以构筑一个更加安全的区块链系统,让矿工、商户和用户都在激励下付出安全成本来提升自己的安全性,从而无惧隐藏的算力的攻击。

 

 

 

 

■ ■■■■

 

商务合作

(WeChat ID:yqw4322)

(Telegram: https://t.me/DotWalletMiemie

 

 

客户服务

(WeChat ID:DotWallet-D)

Official English Telegram Group: t.me/dotwallet