一分钟理解:比特币第一次严重漏洞,生产1844亿枚比特币!是如何发生的

一分钟理解:比特币第一次严重漏洞,生产1844亿枚比特币!是如何发生的

1 事件

2010年8月15日(也就是比特币诞生的第二年,创世区块于2009年1月3日诞生),有人发现,在比特币区块链的第74638块上,一笔让人惊愕的交易。这笔交易出现了184 467 440 737.09551616个比特币,其中有922亿个比特币被发送到两个地址,交易如下:

比特币总量是2100万——《比特币知识(3)——比特币上限为什么是2100万》,为什么这两笔交易,会大于总比特币总量?

这恐怕是比特币史上最严重一次漏洞事件了,如果不及时修复这个问题,比特币系统或许直接被毁掉。

2 溢出原理

这个问题是怎么发生的?原理其实也简单:大数溢出引起,算是编码不严谨引起的了。

在小时候,我们学数数时候,用的方法——掰手指。

从1数到10,十个手指最多可以表示到10(不考虑用脚或者半截手指情况),如果数到11,10个手指不够用,手指表示就回到1了,这算是最原始的一个大数溢出的认识吧。

计算机对数的表示也是类似,也有最大限度。

为了方便表达,我们用Go语言的uint8做例子,一个uint8 表示,uint8一个字节空间,表达的范围是:bin(00000000) 到bin(11111111),也就是0-255,如果再大,就会发生溢出,高位被抛弃掉,又会变成0开始了。

代码演示:

理解了这个,比特币的漏洞的理解就方便了。

3 故事例比

用个简朴的例子,父亲去给两个孩子交学费:父亲把钱递给学校财务,搞定,回家睡觉。

嗯~貌似还漏了些什么,得看看两个孩子学费一共要多少,自己带的钱够不够,如果够,就缴费;如果不够还能啥,筹钱去啊!

父亲这次兜里带着10块钱去给两个孩子缴129块钱的学费。

还是用Go的uint8,封装一个函数做演示,:

import (
    "fmt"
)

func submit(account, cost1, cost2 uint8) (bool, uint8) {
    var total = cost1 + cost2
    if total <= account {
        fmt.Printf("账户金额:%d, 待缴金额:(%d+%d=)%d\n", account, cost1, cost2, total)
        return true, account - total
    }

    return false, account
}

func main() {
    account := uint8(10)
    cost1 := uint8(129)
    cost2 := uint8(129)

    success, account := submit(account, cost1, cost2)
    if success {
        fmt.Println("缴费成功,余额:", account)
    } else {
        fmt.Println("金额不够,凑钱去!")
    }
}

代码看着没有问题,如果预期正常,应该输出:金额不够,凑钱去!

但实际上却缴费成功了。

(对了,哪的学校兜里10块钱就敢去交学费了?告诉我,我也去霸个学位)

学费为129+129=258(也就是256+2),在一个字节的 uint8存储,就发生了溢出。

回到比特币的那个漏洞,就是和“父亲交学费”的逻辑一样,发生错误的原因:当时的比特币系统仅检测转出的总额是否小于未花费输出,而没有检测每笔转出是否小于未花费输出,以及也没有考虑总额是否有溢出的情况。

当时转出两个922亿个比特币的账户,仅有0.5btc,为什么会转账成功?

因为两个转出额之和发生了溢出(和uint8溢出原理一样,不同是其他的小数类型的溢出):


打包区块奖励
= 输入金额 - 总输出金额
= 0.50btc - (92233720368.54275808 + 92233720368.54275808))
= 0.50 btc- (-0.010btc))
= 0.51btc

这个打包奖励被矿机收囊,更大的影响是无中生有的发行了1844亿个比特币。

4 问题修复

在漏洞被披露后的三小时内,中本聪及其他比特币开发者发布了0.3.1版本的修复程序,此更新引入了逻辑,拒绝具有溢出输出值的交易。

在第74691区块时候,带补丁版本的块链终于追赶上出现漏洞的块链,也就是大约20多小时后,比特币以硬分叉情况有惊无险地解决了这次最为重大的危机事件。

对于大数溢出逻辑代码如何修复,就不用讲述了,应该会coding 的伙伴都懂,可能实际编码中不太严谨而已。

(全文完)

(欢迎转载本站文章,但请注明作者和出处 云域 – Yuccn

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注