首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Yul/内联程序集:使用自定义错误消息进行还原

Yul/内联程序集:使用自定义错误消息进行还原
EN

Ethereum用户
提问于 2023-01-11 14:51:11
回答 2查看 425关注 0票数 3

我正在为我的学士论文学习Yul,我目前正忙于理解一个带有require和revert功能的小代码段。

一个简单的要求功能在坚实..。

代码语言:javascript
复制
require(_amountToRaise > 0, "Amount to raise smaller than 0");

..。翻译成Yul/内联程序集.

代码语言:javascript
复制
if iszero(gt(_amountToRaise, 0)) {
    mstore(0x80, shl(229, 4594637))   // <--- what is this?
    mstore(0x84, 32)                  // <--- and this?
    mstore(0x104, 30)
    mstore(0x124, "Amount to raise smaller than 0")
    revert(0x80, 0x64)
}

编辑:请注意,正如答案中提到的,我混淆了.抵消。以下两个答案中的更正。

我了解到,还原消息和字符串也需要通过在前32字节槽中指定字节长度(30)和在下一个32字节槽中指定字符来进行编码。但之后的4字节( 0x80)和32字节槽( 0x84)又如何呢?是我不明白的哈希函数签名吗?

这两行在每个require/revert函数中总是相同的。

提前感谢您的帮助!

EN

回答 2

Ethereum用户

回答已采纳

发布于 2023-01-12 13:07:51

这类代码:

代码语言:javascript
复制
require(_amountToRaise > 0, "Amount to raise smaller than 0");

生成与以下方法的调用数据等效的字节的还原:

代码语言:javascript
复制
function Error(string memory reason) external;

ABI编码的calldata结构如下:

代码语言:javascript
复制
4 bytes selector
32 bytes offset (value 0x20)
32 bytes string length
N bytes string, optionally padded to 32 bytes

因此,生成这个调用数据的正确程序集(理由最多为32个字节):

代码语言:javascript
复制
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 30) // Revert reason length
mstore(add(ptr, 0x44), "Amount to raise smaller than 0")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes

对于自定义错误类型:

代码语言:javascript
复制
contract Foo {
    error MyError();
    error MyErrorWithValue(uint256);
    error MyErrorWithTwoValues(uint256, uint256);

    function f() external {
        revert MyError();
    }

    function g() external {
        revert MyErrorWithValue(555);
    }

    function h() external {
        revert MyErrorWithTwoValues(555, 666);
    }
}

装配当量:

代码语言:javascript
复制
function f() external {
    // revert MyError();
    bytes4 errorSelector = this.MyError.selector;
    assembly {
        mstore(0, errorSelector)
        revert(0, 4)
    }
}

function f() external {
    // revert MyErrorWithValue(555);
    bytes4 errorSelector = this.MyErrorWithValue.selector;
    assembly {
        mstore(0, errorSelector)
        mstore(4, 555)
        revert(0, 0x24)
    }
}

function h() external {
    // revert MyErrorWithValues(555, 666);
    bytes4 errorSelector = this.MyErrorWithTwoValues.selector;
    assembly {
        let ptr := mload(0x40)
        mstore(ptr, errorSelector)
        mstore(add(ptr, 0x04), 555)
        mstore(add(ptr, 0x24), 666)
        revert(ptr, 0x44)
    }
}

在前两种方法中,还原数据适合划痕空间(第一个0x40字节),但第三种方法不适合,应该使用空闲内存指针(存储在0x40的内存中)。正如一些人可能认为,由于revert操作,他们可以使用内存的开头存储超过64个字节-坚实的文档声明,这不能被视为内存安全组装,如果它是重要的情况下。

如果在libraries中发生这种情况,使用带有自定义错误的程序集可能会产生编译器生成的ABI问题。编译器0.8.17不能将自定义错误包含到ABI中,这是由于无法检测到实际使用的错误类型。您可以在这里跟踪此问题:https://github.com/ethereum/solidity/issues/13149

票数 4
EN

Ethereum用户

发布于 2023-01-12 11:04:28

首先,我认为您混合了一些十六进制表示法和一些十进制表示法,正确的分解应该是:

代码语言:javascript
复制
if iszero(gt(_amountToRaise, 0)) {
    mstore(0x80, shl(229, 4594637)) 
    mstore(0x84, 32) 
    mstore(0xA4, 30)
    mstore(0xC4, "Amount to raise smaller than 0")
    revert(0x80, 0x64)
}

在较高级别上,此代码相当于:

代码语言:javascript
复制
require(_amountToRaise > 0, "Amount to raise smaller than 0");

它实际上被翻译成:

代码语言:javascript
复制
if (amount <= 0) {
   revert("Amount to raise smaller than 0");
}

其中,revert返回Error(string)的abi编码的自定义错误“调用”。

编码可以这样分解:

代码语言:javascript
复制
 mstore(0x80, shl(229, 4594637))

Error(string)上为0x80,0x84编写函数选择。

代码语言:javascript
复制
mstore(0x84, 32)

将abi.encoded‘`string’的偏移量写入到0x84,0xA4的编码参数的开头(即,不包括函数选择器),这是abi.encoded动态参数的正常行为,您可以看到这里

代码语言:javascript
复制
mstore(0xA4, 30)
mstore(0xC4, "Amount to raise smaller than 0")

分别写入字符串的长度及其内容。

票数 3
EN
页面原文内容由Ethereum提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://ethereum.stackexchange.com/questions/142752

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档