美文网首页
区块链学习打卡-20180706

区块链学习打卡-20180706

作者: macworld67 | 来源:发表于2018-07-06 22:26 被阅读0次

BUG 描述

在调试第五课程序时的遇到了一个奇怪的BUG,我随手添加了一个地址为 0x0000000000000000000000000000000000000001 的雇员,在进行删除操作时,系统提示『资金不足』。然而在salary参数相同的条件下,其他地址(比如:0x2d4f946fc3c540342421f2c80a0f1f1058eb0cdb)的雇员信息却可以正常删除。很显然,我遇到了一个『钉子户』雇员 😂

无法删除地址为 0x0000000000000000000000000000000000000001 的雇员

BUG 假设

遇到这个问题时,脑海里首先闪过以下几种可能

1. 合约账户ETH余额不足

2. 合约中 PAY_DURATION 时间设置过短

3. 合约存在类似于『短地址攻击』的漏洞,导致 0x0000000000000000000000000000000000000001 这样的特殊地址在执行 _partialPaid 函数时,实际发放金额为被计算成了1  * 1000000000000000000000000000000000000000 ether,从而执行失败

前两点假设很容易进行验证,在确认合约余额,延长 PAY_DURATION 时间后,很快就被我排除掉了。而第三个假设,通过检查代码,使用类似 0x0000000000000000000000000000000000000002 地址进行试验,最终也被否定了。

BUG 追踪

由于之前的假设一一被否定,我意识到这个BUG可能并没有想象中那么简单,需要收集更多信息进行调试。在 ganache-cli 的日志中可以发现,交易之前的失败交易触发了revert操作

交易日志

利用remix debug 功能,单步调试,发现问题出在 employeeId.transfer(payment); 这个函数上。

revert 触发前的最后一行代码

然而,光有这些信息仍然无法解释为什么偏偏只有0x0000000000000000000000000000000000000001(后续简称『0x1地址』)这个地址会触发transfer异常,因此,需要继续深入查看实际执行transfer的evm指令。为了方便测试构造一个简化版的函数,分别传入常规地址和0x1地址,同样常规地址执行成功,而0x1地址执行失败。

简化版验证函数

通过对比成功交易的call stack,最终将问题锁定这个CALL指令的调用上

出现问题的CALL指令

官方文档中call函数的函数说明如下

call(g, a, v, in, insize, out, outsize) 

call contract at address a with input mem[in..(in+insize)) providing g gas and v wei and output area mem[out..(out+outsize)) returning 0 on error (eg. out of gas) and 1 on success

可以看出,call返回值为0,也就是调用失败了。由于tansfer传输的金额为v = 0,所以可以排除账户余额不足的情况,最大的可能性是gas不足,也就是目前传入给call的 g = 0x8fc (十进制:2300) gas 不足以支付调用费用。下图是正常的call调用消耗 了700 gas

正常调用 call 费用为 700 gas

而transfer到0x1地址消耗了2300+700 gas,也就是说,传入的gas全部被耗尽后仍然无法完成 call 调用,导致调用失败

异常调用消耗了2300+700 gas

暂且不考虑为什么0x1地址会消耗更多的gas,假设增加传入call操作的gas数量是否能成功调用该函数呢?为了验证这个猜想,构造如下函数

验证gas

该CALL调用参数与之前的函数一致,唯一不同的是call传入的gas数量变为了0xffff 也就是 65535,,传入0x1地址,执行成功!实际call调用消耗gas为700+3000

执行成功

由此可以得知,之前无法删除员工的原因便是:0x1地址调用call操作时消耗的gas超出了预分配的量,导致transfer失败,触发了revert()

GAS消耗之谜

尽管弄明白了调用失败的原因,但目前有一个问题仍旧没能解决,那就是为什么call在调用0x1地址时会消耗更多的gas。为此我查阅了EIP150的中相关gas消耗细节,其中大部分的opcode都规定了明确gas消耗数量,唯独call这一类型需要单独查看黄皮书

EIP150部分细节

在翻阅了黄皮书附录后,我终于找到了call的计费细节

call gas消耗细节-1 call gas消耗细节-2

根据黄皮书中细节描述,call调用的初始消耗是700 gas,触发revert的条件是堆栈深度超过1024或者预分配gas消耗殆尽,而预分配的gas只会在调用(或创建)其他合约时才会被使用,也就是说,对于非合约账户地址(也就是普通钱包地址),在不传入传出额外数据的情况下,不会产生额外的gas消耗,call调用的总gas消耗为700,这也和之前观测到的正常调用消耗一致。黄皮书似乎也没有解答这个问题。

由于之前的一直是在remix的javascript vm环境测试,会不会是这个仿真环境的bug呢?为此我也在ropsten网络进行了测试,结果依然不变,0x1地址会额外消耗3000点gas。主网环境下也依然如此,正常的eth转账需要消耗21000 gas,而如果给0x1地址转账的话,需要消耗24000gas。如果用metamask尝试给0x1地址转账的话,gaslimit 会默认是21000 gas,不出意外的话,这笔转账会fail掉。

给0x1地址转账,需要消耗24000gas

因此,要回答这个问题还是需要去看evm的具体代码实现。这也许是个bug,也许是个feature,也可能是我之前的分析还存在着一些漏洞,如有任何问题,欢迎各位大佬指正,也希望对此问题感兴趣的小伙伴们能做进一步研究。

相关文章

  • 区块链学习打卡-20180706

    BUG 描述 在调试第五课程序时的遇到了一个奇怪的BUG,我随手添加了一个地址为 0x00000000000000...

  • 2018-04-28

    学习区块链我是认真的! 学习区块链我是认真的! 学习区块链我是认真的! 初识区块链从一本账本说起。 早...

  • 区块链学习笔记6-区块链的基本类型

    关于区块链已经学习了六篇,分别是区块链的来源、区块链的定义、区块链的运作原理、区块链软硬分叉,以及区块链的底层架构...

  • 《财富自由之路》“遇到贵人的科学方法”

    区块链学习 今天认真的看了《区块链,新经济蓝图及导读》第二章“区块链2.0 合约” ,本来是想写关于区块链学习笔记...

  • 区块链进阶你要知道这些

    区块链该怎么学?怎么从零基础进阶成技术大神?学习区块链,这些你必须知道。 1、学习区块链技术,首先你要对区块链的基...

  • 区块链入门知识从哪开始?

    学习区块链知识,先学习区块链知识的必要概念。 1,【区块链】 英文名 blockchain,是比特币的底层技术,是...

  • 比特币白皮书详解

    自从习总书记开始带头学习区块链后,很多同学也开始自发学习了解区块链了。但肯定没看懂,因为它太难了。学习区块链的最好...

  • 读书笔记(4)丨区块链买椟不还珠

    在上部分内容的学习中,我们认识到了区块链运行的几大特点:区块链是分布的;区块链是公开的;区块链是加密的;区块链是不...

  • 区块链知识汇总(未完待续)

    区块链学习 深入区块链世界绕不过的系列书籍 强烈推荐 如何学习 下水,买入区块链资产 ,买了你才会有动力深入了解。...

  • 再不去快连(区块链),你就老了!

    本内容欢迎转发。 本片作为区块链研习社学习第二周的周记内容。区块链研习社是目前全国最大的区块链学习...

网友评论

      本文标题:区块链学习打卡-20180706

      本文链接:https://www.haomeiwen.com/subject/jkkbyftx.html