免责声明:本文旨在传递更多市场信息,不构成任何投资建议。文章仅代表作者观点,不代表火星财经官方立场。
边肖:记得要集中注意力。
来源:慢雾科技
撰文:Thinking @ 慢雾安全团队
事件背景
分析来自一笔转账金额为10w USDT的交易,但手续费高达7,676 ETH。
https://ethers can . io/tx/0x2c 9931793876 db 33 B1 a9 aad 123 ad 4921 DFB 9 CD 5 e 59 dbb 78 ce 78 f 277759587115
技术层面的核心问题是ethjs-util的intToBuffer不支持传入的浮点数据,ethereumjs使用ethjs-util的intToBuffer。
简而言之:DApp在使用ethereumjs构造交易时,如果传入的手续费有小数,会因为类型转换的bug而返回一个大数作为浏览器中的手续费。并且硬件钱包也没有明确展示,导致用户在这里直接授权和签署天价费的交易。
根据这个问题
https://github . com/ethereumjs/ethereumjs-mono repo/issues/1497的描述,开始分析。
我们用倒叙的方式解释问题,这样更容易理解。核心问题是ethjs-util的intToBuffer不支持浮点数据。
首先看键码。ethereumjs的问题提的比较多。主要关注两个参数的值,maxPriorityFeePerGas和maxFeePerGas。由于传入的浮点型,导致计算错误,得出错误的程序,造成“天价手续事件”。
经过分析,这两个参数被toBuffer处理,所以对toBuffer进行分析。
https://github . com/ethereumjs/ethereumjs-monorepo/blob/cf 95 e 04 c 6a/packages/tx/hide/EIP 1559 transaction . ts # L200-L201
this . maxFeePerGas=new BN(to buffer(maxFeePerGas===' '?0x ' : maxFeePerGas))this . maxPriorityFeePerGas=new BN(to buffer(maxPriorityFeePerGas===' '?0x' : maxPriorityFeePerGas))
ToBuffer会调用ethjs-util的intToBuffer函数,主要处理两件事。
https://github . com/ethjs/ethjs-util/blob/e9 aede 6681/dist/ethjs-util . js # l 1950
函数intto buffer(I){ var hex=intto hex(I);返回新的缓冲区(padToEven(hex.slice(2)),' hex ');}
将int变成hex https://github . com/ethjs/ethjs-util/blob/e9 aede 6681/dist/ethjs-util . js # l 1939
函数intToHex(I){ var hex=I . tostring(16);//eslint-disable-line返回“0x”十六进制;}
确定它是否能被2整除。如果没有,就需要在字符的开头加一个0。这里,主要目的是成功地将2组1数据写入缓冲区。https://github . com/ethjs/ethjs-util/blob/e9 aede 6681/dist/ethjs-util . js # l 1920
函数padToEven(value){ var a=value;//eslint-disable-line if(type of a!==' string '){ throw new Error(' while padding to even,value必须是string,当前是' typeof a ',while padToEven ');} if(a . length % 2){ a=' 0 ' a;}返回a;}
错误地
示例数据:33974229950.550003 进行分析,经过 intToBuffer 函数中的 intToHex 和 padToEven 处理后得到 7e9059bbe.8ccd,这部分浏览器 js 和 nodejs 的结果都是一致的。不一致的地方是在 new Buffer 的操作:
new Buffer(padToEven(hex.slice(2)), 'hex');
首先输入的示例字符 33974229950.550003 会进入到 intToBuffer 的函数中进行处理。
同步分析 intToBuffer 的处理过程,这部分和」关键代码分析「部分的代码逻辑是一样的,处理转换部分得到的结果是 7e9059bbe.8ccd。
接下来分析如何将转换后的字符填充进入的 buffer 中,通过这步可以得到 buffer 的内容是 126, 144, 89, 187, 14, 140, 205 对应的是 7e, 90, 59, bb, e, 8c, cd。
> 0x7e -> 126> 0x90 -> 144> 0x59 -> 89> 0xbb -> 187> 0xe -> 14> 0x8c -> 140> 0xcd -> 205
这里发现 e. 这部分的小数点消失了,于是开始解小数点消失之迷,追踪到 hexWrite 这个函数,这个函数会将得到的数据 2 个一组进行切分。然后用了 parseInt 对切分后的数据进行解析。
然而 parseInt('e.',16) -> 14===parseInt('e',16) -> 14 消失的小数点被 parseInt 吃掉了,导致最终写入到 buffer 中的数据发生了错误,写入 buffer 的值是 7e9059bbe8ccd。

首先我们先看下如下的示例:
node 三组不同的数据填充到 buffer 得到的结果居然是一样的,经过分析 node 的 buffer 有个小特性,就是 2 个一组切分后的数据,如果没法正常通过 hex 解析的,就会把那一组数据以及之后的数据都不处理了,直接返回前面可以被正常处理的那部分数据。可以理解为被截断了。这部分可以参考 node 底层的 buffer 中 node_buffer.cc 中的代码逻辑。
> new Buffer('7e9059bbe', 'hex') > new Buffer('7e9059bbe.8ccd', 'hex') > new Buffer('7e9059bb', 'hex')
node 的执行结果:
浏览器由于会将原始数据 7e9059bbe.8ccd 中的 . 吃掉,所以最终错误的值是 7e9059bbe8ccd,相比正确的值 07e9059bbe 大很多。
浏览器的执行结果:

从库的使用者的角度来看,使用者应该要自行阅读第三方库的开发文档和对接文档,并且也要对代码中接入第三方库的逻辑进行测试,通过构造大量的数据进行测试,确保业务上能够正常按照期望执行,保证高标准的测试用例的覆盖率。
参考资料 :https://github.com/ethereumjs/ethereumjs-monorepo/issues/1497https://blog.deversifi.com/23-7-million-dollar-ethereum-transaction-fee-post-mortem/https://www.chainnews.com/news/611706276133.htm
以上就是帮企客小编为大家带来的全部内容,希望可以帮助到大家。免责声明:本文为转载,非本网原创内容,不代表本网观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
如有疑问请发送邮件至:bangqikeconnect@gmail.com