一个惊天 bug,2.2 亿没了!
因为一个程序 bug,直接损失了 2.2 亿人民币。。。
这是近几天发生在 NFT 圈子里的一件事,某项目方因为自己的程序代码出错,导致锁定的 11539.5 个 ETH(以太坊代币)无法取出。
按照目前的价格,这些 ETH 总价值 2.2 亿人民币,妥妥肉疼。
NFT 是非同质化代币,属于区块链领域的一种应用,可以理解为一种虚拟的数字资产。
比如,你购买了一张 NFT 头像,那这张图片在网络世界里就唯一属于你。即便大家可以复制拷贝,但所有权归属于你。
这次出事的是一个叫 Akutar 的 NTF 项目方,他们发起了一个 NFT 产品售卖,采用的是荷兰式拍卖法。
所谓荷兰式拍卖法,就是由高到低出价的拍卖方式,这和传统的递增叫价拍卖方式有所不同。
举个例子,假设一件产品的定价是 10000 元,想买入的就直接按照这个价格下单。如果最终成交价是 8000 元,那项目方会退你 2000 元。
设计这种规则的目的其实也是激励用户早点下单,因为早买晚买都不亏,加上 NFT 限量,就更提升了购买者的动机。
很快,这个项目就完成了 3400 万美元的预定销售额。
接下来,就是项目方履行后续环节了,退款和提现。
按照规则,项目方需要先退回最终成交价和定价之间的差额给用户,然后才能自己提走项目售卖收益。
为了确保公平,也表态自己不会跑路的决心,项目方在智能合约里规定了需要先完成差价退款才能提现。
智能合约,可以理解成锁定在区块链中的一个事先定好的规则,任何人都无法篡改,只会被自动执行。
于是,在智能合约代码里就有这么一个判断条件。
懂点技术的读者知道,这是一段函数代码,这个函数的作用是项目方用来提现的。
其中被红框标记起来的是一个判断条件,refundProgress >= totalBids,问题也恰恰出在这里。
按照定义,变量 refundProgress 表示已经完成的用户退款数,而变量 totalBids 表示所有用户下单的 NFT 总数。
因为一个用户可以购买多个 NFT,所以 totalBids 的数值一定是大于或等于 refundProgress 的。
也就是说,不可能出现像智能合约代码中 refundProgress >= totalBids 的情况。
因此,当代码执行到这里的时候,这个判断条件始终不会成立,后面的合约代码就无法执行。
看懂的读者应该知道了,这个 bug 的关键点就是这两个变量之间的判断符号写反了。
程序员写的是 >=,而正确的应该是写成 <=。
用户无法获得退款,项目方也无法提现。
对用户来说,虽然不能获得退款,但至少还是买到了 NFT。但对于项目方来说,这价值 2.2 亿人民币的收益就直接被永久锁定了,无法提走。
在区块链的世界里,这些代币就永久不会被其他人取出,和销毁没什么区别。
项目方相当于手持了一个永远不会被打开的钱包,纵使腰缠万贯,但也仅仅是一个账面数字,没有使用价值。
一个符号,价值 2.2 亿,可以说是一个惊天 bug 了。
可能有人会说了,上线前难道不经过测试么,这么大的项目竟然犯这种低级错误。
那么,这是谁的锅?
首先,项目方在设计智能合约时一定会提前定义好规则,然后交给程序员去实施。上线前,也会经过测试和验收。
其次,程序员在代码中写入这个规则时,也一定是建立在理解这个规则的前提下。可能是手抖,可能是粗心,恰好就把判断符号写反了。
另外,测试在上线验收环节,可能没有进行极端情况测试,只是按照一个人买一个 NFT 的场景完成了测试。
最后,项目方验收时,以为能跑通全流程就没问题了。
项目上线后,在生产环境出现了一个人买多个 NFT 的情况,bug 被触发。
在我看来,问题的核心起点在程序员,验收不严谨的责任在测试,而最终对外背锅的一定是项目方。
这不是某一个人的锅,而是一口大锅罩在了整个项目组头上。
这样的问题在平时产品开发中其实并不少见,因为一个判断条件的失误,轻则引起功能异常,重则导致巨大的经济损失。
如果你们感兴趣,可以自己去查一下过去这些年因为程序 bug 导致产品重大损失的案例,其实有不少。
那些厉害的程序员之所以贵,也是有原因的。
如果为了省钱招了一些三流程序员填坑,或许只会给你挖出更大的坑。
这个世纪什么最贵?
人才!
················· 唐韧出品 ·················
想起当年我还在写代码的时候,对于这种大小判断逻辑特别敏感,因为真的非常容易写错。
当时我用的办法就是用几种情况的具体数值代替变量写到程序里,然后分别运行几次。
办法虽然笨,但有效。