我最近在重新学Huff,巩固一下细节,也写一个“Huff极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。
所有代码和教程开源在github: github.com/AmazingAng/WTF-Huff
Huff允许你在合约自定义错误Error
,这一讲我们将介绍它。
Error
Solidity中有三种抛出异常的方法error
,require
和assert
,他们都是基于EVM
的revert
指令。在Huff中,我们可以直接使用revert
指令来抛出错误并返回错误信息。
定义错误
你可以在合约接口中定义错误:
使用错误
在方法中,你可以使用内置函数__ERROR()
将错误选择器(error selector)推到堆栈上。
然后我们写一个Main宏作为合约的入口:
分析合约字节码
我们可以使用huffc
命令获取上面合约的runtime code:
打印出的bytecode为:
转换成格式化的表格(后半部分在stack
中省略了一个用不上的selector
):
pc | op | opcode | stack |
---|---|---|---|
[00] | 5f | PUSH0 | 0x00 |
[01] | 35 | CALLDATALOAD | calldata |
[02] | 60 e0 | PUSH1 0xE0 | 0xE0 calldata |
[04] | 1c | SHR | selector |
[05] | 80 | DUP1 | selector selector |
[06] | 63 ee23e358 | PUSH4 0xEE23E358 | 0xEE23E358 selector selector |
[0b] | 14 | EQ | suc selector |
[0c] | 61 0013 | PUSH2 0x0013 | 0x0013 suc selector |
[0f] | 57 | JUMPI | selector |
[10] | 5f | PUSH0 | 0x00 selector |
[11] | 5f | PUSH0 | 0x00 0x00 selector |
[12] | fd | REVERT | selector |
[13] | 5b | JUMPDEST | |
[14] | 60 69 | PUSH1 0x69 | 0x69 |
[16] | 7f 0x110b... | PUSH32 0x110b... | 0x69 0x110b... |
[2d] | 5f | PUSH0 | 0x00 0x69 0x110b... |
[2e] | 52 | MSTORE | 0x110b... |
[2f] | 60 04 | PUSH1 0x04 | 0x04 0x110b... |
[31] | 52 | MSTORE | |
[32] | 60 24 | PUSH1 0x24 | 0x24 |
[34] | 5f | PUSH0 | 0x00 0x24 |
[35] | fd | REVERT |
从[16]
可以看到,目前内置函数__ERROR
并没有被优化的很好,因为它会先计算出4
字节的error selector
(此处为0x110b3655
),然后再将它转换为32
字节的数据(右填充0
,变为110b365500000000000000000000000000000000000000000000000000000000
),最后使用PUSH32
压入堆栈。但实际上,我们需要的只是前4
字节,这样造成了gas浪费。
总结
这一讲,我们介绍了如何在Huff中自定义错误并使用它。Huff提供了内置函数__ERROR
来获取错误选择器,但它没有被很好的优化。