我正在为我的学士论文学习Yul,我目前正忙于理解一个带有require和revert功能的小代码段。
一个简单的要求功能在坚实..。
require(_amountToRaise > 0, "Amount to raise smaller than 0");..。翻译成Yul/内联程序集.
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函数中总是相同的。
提前感谢您的帮助!
发布于 2023-01-12 13:07:51
这类代码:
require(_amountToRaise > 0, "Amount to raise smaller than 0");生成与以下方法的调用数据等效的字节的还原:
function Error(string memory reason) external;ABI编码的calldata结构如下:
4 bytes selector
32 bytes offset (value 0x20)
32 bytes string length
N bytes string, optionally padded to 32 bytes因此,生成这个调用数据的正确程序集(理由最多为32个字节):
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对于自定义错误类型:
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);
}
}装配当量:
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
发布于 2023-01-12 11:04:28
首先,我认为您混合了一些十六进制表示法和一些十进制表示法,正确的分解应该是:
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)
}在较高级别上,此代码相当于:
require(_amountToRaise > 0, "Amount to raise smaller than 0");它实际上被翻译成:
if (amount <= 0) {
revert("Amount to raise smaller than 0");
}其中,revert返回Error(string)的abi编码的自定义错误“调用”。
编码可以这样分解:
mstore(0x80, shl(229, 4594637))在Error(string)上为0x80,0x84编写函数选择。
mstore(0x84, 32)将abi.encoded‘`string’的偏移量写入到0x84,0xA4的编码参数的开头(即,不包括函数选择器),这是abi.encoded动态参数的正常行为,您可以看到这里。
mstore(0xA4, 30)
mstore(0xC4, "Amount to raise smaller than 0")分别写入字符串的长度及其内容。
https://ethereum.stackexchange.com/questions/142752
复制相似问题