ABIEncoderV2
求推荐ABIEncoderV2的相关材料,谢谢
# ABIEncoderV2 最近在写一些合约,在合约的编译过程中会遇到如下的错误提示,最开始都没有想到是哪里出了问题,故写下这篇文章以作记录。 ![image20210819222747300.png](https://img.learnblockchain.cn/attachments/2021/08/aVNUyUhE611e8140ea9c8.png) ## 解决方案: 在合约头部,添加如下一行,指明ABI编码方式: ```js pragma experimental ABIEncoderV2; ``` ## 问题分析: 该种类型的报错是指:嵌套的动态数组在目前的Solidity中暂未被支持。 然而在已有的合约:如`BaseBoringBatchable.sol`中的`batch`函数: ```js function batch(bytes[] calldata calls, bool revertOnFail) external payable returns (bool[] memory successes, bytes[] memory results) { uint n = calls.length; successes = new bool[](n); results = new bytes[](n); for (uint i = 0; i < n; i++) { bytes memory data = calls[i]; (bool success, bytes memory result) = address(this).delegatecall(data); require(success || !revertOnFail, _getRevertMsg(result)); successes[i] = success; results[i] = result; } } function _getRevertMsg(bytes memory data) internal returns(string) { uint len; uint pointer; assembly{ len := mload(data) pointer := add(data, 0x04) } if (len < 68) { return("Transaction reverted silently"); } return abi.decode(pointer, (string)); } ``` 在上述的`batch`函数中,其函数参数中接受一个类型为`bytes[]`的参数,如果简单的把这个函数放入到合约中进行编译,就会产生: > UnimplementedFeatureError: Nested dynamic arrays not implemented here 那么,为什么bytes[]是一个嵌套的动态数组呢? 原因是bytes类型本身就是一个动态数组,string也是,uint[]也是。 ![image20210819231253988.png](https://img.learnblockchain.cn/attachments/2021/08/h34EtjGR611e81582e614.png) 所以bytes[]就是一个嵌套的动态数组。 ## bytes[]在内存中的排布 针对上述的batch函数,其bytes[]需要存储每一个需要调用的方法的calldata。例如我们要调用如下方法: ```js function commitEth( address payable _beneficiary, bool readAndAgreedToMarketParticipationAgreement ) ``` 则我们的`calldata`应该为: ```js abi.encodePacked(address(this).commitEth.selector, uint256(uint160(address(this))), uint256(0x01)); => 0x73973fcb 000000000000000000000000c351628eb244ec633d5f21fbd6621e1a683b1181 0000000000000000000000000000000000000000000000000000000000000001 ``` 则在内存中使用bytes[]为: ```js function hack() public payable{ bytes[] memory data = new bytes[](3); bytes momory call_data = abi.encodePacked(address(this).commitEth.selector, uint256(uint160(address(this))), uint256(0x01)); data[0] = call_data; data[1] = call_data; data[2] = call_data; } ``` 当把三个call_data放置在内存中的bytes[]时,其内存排布应该如下: ```js 0x00 0000000000000000000000000000000000000000000000000000000000000003 // len 0x20 0000000000000000000000000000000000000000000000000000000000000080 // loc1 0x40 00000000000000000000000000000000000000000000000000000000000000e4 // loc2 0x60 0000000000000000000000000000000000000000000000000000000000000148 // loc3 0x80 0000000000000000000000000000000000000000000000000000000000000044 // len(part1) 0xa0 73973fcb // part1 0xa4 000000000000000000000000c351628eb244ec633d5f21fbd6621e1a683b1181 // part1 0xc4 0000000000000000000000000000000000000000000000000000000000000001 // part1 0xe4 0000000000000000000000000000000000000000000000000000000000000044 // len(part2) 0x104 73973fcb // part2 0x108 000000000000000000000000c351628eb244ec633d5f21fbd6621e1a683b1181 // part2 0x128 0000000000000000000000000000000000000000000000000000000000000001 // part2 0x148 0000000000000000000000000000000000000000000000000000000000000044 // len(part3) 0x14c 73973fcb // part3 0x16c 000000000000000000000000c351628eb244ec633d5f21fbd6621e1a683b1181 // part3 0x18c 0000000000000000000000000000000000000000000000000000000000000001 // part3 ``` ![image20210820000113755.png](https://img.learnblockchain.cn/attachments/2021/08/5l5xNMPE611e816775891.png) 可以发现并不是完全一致,它利用了data[0]=data[1]=data[2]的条件,故重复指向同一个内存位点。 ## bytes[]在插槽中的排布 同样针对上述`batch`函数,让其在EVM的插槽中进行存储,则合约应该如下设计: ```js contract Exploit { bytes[] public data; function hack() public payable{ bytes momory call_data = abi.encodePacked(bytes4(0x73973fcb), uint256(uint160(address(this))), uint256(0x01)); data.push(call_data); data.push(call_data); data.push(call_data); } } ``` 同样添加三个call_data到data中,则data在EVM存储时的排布应该为: ```js base_key = 0x0000000000000000000000000000000000000000000000000000000000000000 base_value = 3 part1_key = keccak256(base_key) + 1 part1_value = 0x89 part2_key = keccak256(base_key) + 2 part2_value = 0x89 part3_key = keccak256(base_key) + 3 part3_value = 0x89 sub_part1_key1 = keccak256(part1_key) sub_part1_value1 = 73973fcb000000000000000000000000c351628eb244ec633d5f21fbd6621e1a sub_part1_key2 = sub_part1_key1 + 1 sub_part1_value2 = 683b118100000000000000000000000000000000000000000000000000000000 sub_part1_key3 = sub_part1_key1 + 2 sub_part1_value3 = 0000000100000000000000000000000000000000000000000000000000000000 ``` ![image20210819235635522.png](https://img.learnblockchain.cn/attachments/2021/08/IvPNQJwj611e817567881.png) 求推荐ABIEncoderV2的相关材料,谢谢
ABIEncoderV2
最近在写一些合约,在合约的编译过程中会遇到如下的错误提示,最开始都没有想到是哪里出了问题,故写下这篇文章以作记录。
解决方案:
在合约头部,添加如下一行,指明ABI编码方式:
pragma experimental ABIEncoderV2;
问题分析:
该种类型的报错是指:嵌套的动态数组在目前的Solidity中暂未被支持。
然而在已有的合约:如BaseBoringBatchable.sol
中的batch
函数:
function batch(bytes[] calldata calls, bool revertOnFail) external payable returns (bool[] memory successes, bytes[] memory results) {
uint n = calls.length;
successes = new bool[](n);
results = new bytes[](n);
for (uint i = 0; i < n; i++) {
bytes memory data = calls[i];
(bool success, bytes memory result) = address(this).delegatecall(data);
require(success || !revertOnFail, _getRevertMsg(result));
successes[i] = success;
results[i] = result;
}
}
function _getRevertMsg(bytes memory data) internal returns(string) {
uint len;
uint pointer;
assembly{
len := mload(data)
pointer := add(data, 0x04)
}
if (len < 68) {
return("Transaction reverted silently");
}
return abi.decode(pointer, (string));
}
在上述的batch
函数中,其函数参数中接受一个类型为bytes[]
的参数,如果简单的把这个函数放入到合约中进行编译,就会产生:
UnimplementedFeatureError: Nested dynamic arrays not implemented here
那么,为什么bytes[]是一个嵌套的动态数组呢?
原因是bytes类型本身就是一个动态数组,string也是,uint[]也是。
所以bytes[]就是一个嵌套的动态数组。
bytes[]在内存中的排布
针对上述的batch函数,其bytes[]需要存储每一个需要调用的方法的calldata。例如我们要调用如下方法:
function commitEth(
address payable _beneficiary,
bool readAndAgreedToMarketParticipationAgreement
)
则我们的calldata
应该为:
abi.encodePacked(address(this).commitEth.selector,
uint256(uint160(address(this))),
uint256(0x01));
=>
0x73973fcb
000000000000000000000000c351628eb244ec633d5f21fbd6621e1a683b1181
0000000000000000000000000000000000000000000000000000000000000001
则在内存中使用bytes[]为:
function hack() public payable{
bytes[] memory data = new bytes[](3);
bytes momory call_data = abi.encodePacked(address(this).commitEth.selector,
uint256(uint160(address(this))),
uint256(0x01));
data[0] = call_data;
data[1] = call_data;
data[2] = call_data;
}
当把三个call_data放置在内存中的bytes[]时,其内存排布应该如下:
0x00 0000000000000000000000000000000000000000000000000000000000000003 // len
0x20 0000000000000000000000000000000000000000000000000000000000000080 // loc1
0x40 00000000000000000000000000000000000000000000000000000000000000e4 // loc2
0x60 0000000000000000000000000000000000000000000000000000000000000148 // loc3
0x80 0000000000000000000000000000000000000000000000000000000000000044 // len(part1)
0xa0 73973fcb // part1
0xa4 000000000000000000000000c351628eb244ec633d5f21fbd6621e1a683b1181 // part1
0xc4 0000000000000000000000000000000000000000000000000000000000000001 // part1
0xe4 0000000000000000000000000000000000000000000000000000000000000044 // len(part2)
0x104 73973fcb // part2
0x108 000000000000000000000000c351628eb244ec633d5f21fbd6621e1a683b1181 // part2
0x128 0000000000000000000000000000000000000000000000000000000000000001 // part2
0x148 0000000000000000000000000000000000000000000000000000000000000044 // len(part3)
0x14c 73973fcb // part3
0x16c 000000000000000000000000c351628eb244ec633d5f21fbd6621e1a683b1181 // part3
0x18c 0000000000000000000000000000000000000000000000000000000000000001 // part3
可以发现并不是完全一致,它利用了data[0]=data[1]=data[2]的条件,故重复指向同一个内存位点。
bytes[]在插槽中的排布
同样针对上述batch
函数,让其在EVM的插槽中进行存储,则合约应该如下设计:
contract Exploit {
bytes[] public data;
function hack() public payable{
bytes momory call_data = abi.encodePacked(bytes4(0x73973fcb),
uint256(uint160(address(this))),
uint256(0x01));
data.push(call_data);
data.push(call_data);
data.push(call_data);
}
}
同样添加三个call_data到data中,则data在EVM存储时的排布应该为:
base_key = 0x0000000000000000000000000000000000000000000000000000000000000000
base_value = 3
part1_key = keccak256(base_key) + 1
part1_value = 0x89
part2_key = keccak256(base_key) + 2
part2_value = 0x89
part3_key = keccak256(base_key) + 3
part3_value = 0x89
sub_part1_key1 = keccak256(part1_key)
sub_part1_value1 = 73973fcb000000000000000000000000c351628eb244ec633d5f21fbd6621e1a
sub_part1_key2 = sub_part1_key1 + 1
sub_part1_value2 = 683b118100000000000000000000000000000000000000000000000000000000
sub_part1_key3 = sub_part1_key1 + 2
sub_part1_value3 = 0000000100000000000000000000000000000000000000000000000000000000
求推荐ABIEncoderV2的相关材料,谢谢
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
- 发表于 2021-08-20 00:06
- 阅读 ( 440 )
- 学分 ( 11 )
- 分类:智能合约
评论