Solidity中的EVM内存布局
Solidity中的EVM内存布局
内存可以的长度可以达到2 ** 256,其中每个元素存储可以存储1字节数据:
index 0 1 2 … 0xfff…fff = 2**256 - 1
memory | 00 | 00 | 00 | … | 00 |
预留空间
0x00 - 0x3f (64 bytes)
: scratch space for hashing methods0x40 - 0x5f (32 bytes)
: free memory pointer - pointer to next available location in memory tostore data0x60 - 0x7f (32 bytes)
: zero slot - used as initial value for dynamic memory arrays and shouldnever be written to
自由内存指针 (0x40)
0x80
:自由内存指针初始化指向的位置
mstore(p, v)
:向内存地址p存储32字节
mload(p)
:从内存地址p取出32字节
一些结论
- 在内存中存储,最小的单位为
32
字节,即便是uint8在内存中也占用32字节。 - 结构体在内存中是直接存储的,没有前缀和后缀。
- 固定长度的数组在内存中按顺序存储,没有前缀和后缀。
- 动态数组,首先存储数组有几个这样的元素,然后就是数组元素的存储,如果使用汇编的话,需要手动修改0x40内存区域指向的下一块未使用的内存地址,不使用汇编则不需要。
test_val
→internal_func_return_val
:函数返回uint256存储在栈顶。
contract MemInternalFuncReturn {
function internal_func_return_val() private pure returns (uint256) {
return uint256(0xababab);
}
function test_val() public pure {
// 0xababab will be stored in top of the stack
internal_func_return_val();
}
}
test_mem
→internal_func_return_me
:返回存储在内存中:长度、3个元素。
contract MemInternalFuncReturn {
function internal_func_return_mem()
private
pure
returns (bytes32[] memory)
{
bytes32[] memory arr = new bytes32[](3);
arr[0] = bytes32(uint256(0xaaa));
arr[1] = bytes32(uint256(0xbbb));
arr[2] = bytes32(uint256(0xccc));
return arr;
}
function test_mem()
public
pure
returns (uint256 len, bytes32 a0, bytes32 a1, bytes32 a2)
{
// Stores 0x80 to top of the stack
// 0x80 = memory pointer to beginning of arr
internal_func_return_mem();
// Read data from arr, initialized in internal_func_return_mem, using assembly
assembly {
len := mload(0x80)
a0 := mload(0xa0)
a1 := mload(0xc0)
a2 := mload(0xe0)
}
}
}
- 值小于32字节:在左边补0
// 0x000000000000000000000000abababababababababababababababababababab
function encode_addr() public pure returns (bytes memory) {
address addr = 0xABaBaBaBABabABabAbAbABAbABabababaBaBABaB;
return abi.encode(addr);
}
- 固定大小的数据:在右边补0
// 0xaabbccdd00000000000000000000000000000000000000000000000000000000
function encode_bytes4() public pure returns (bytes memory) {
bytes4 b4 = 0xaabbccdd;
return abi.encode(b4);
}
- 动态数组被abi.encode之后在内存中的存储:偏移量(往后偏移多少,是真实的数据)、元素个数、元素数据
// Dynamic size types
// offset | length | data
// offset = 32 bytes index where data starts
// length = 32 bytes data length
// 0x0000000000000000000000000000000000000000000000000000000000000020
// 0000000000000000000000000000000000000000000000000000000000000003
// ababab0000000000000000000000000000000000000000000000000000000000
function encode_bytes() public pure returns (bytes memory) {
bytes memory b = new bytes(3);
b[0] = 0xab;
b[1] = 0xab;
b[2] = 0xab;
return abi.encode(b);
}
// 0x0000000000000000000000000000000000000000000000000000000000000020
// 0000000000000000000000000000000000000000000000000000000000000003
// 0000000000000000000000000000000000000000000000000000000000000001
// 0000000000000000000000000000000000000000000000000000000000000002
// 0000000000000000000000000000000000000000000000000000000000000003
function encode_uint8_arr() public pure returns (bytes memory) {
uint8[] memory a = new uint8[](3);
a[0] = 1;
a[1] = 2;
a[2] = 3;
return abi.encode(a);
}
- 固定数组被abi.encode之后在内存中的存储:直接是数据
// Fixed size arrays
// 0x0000000000000000000000000000000000000000000000000000000000000001
// 0000000000000000000000000000000000000000000000000000000000000002
// 0000000000000000000000000000000000000000000000000000000000000003
function encode_uint256_fixed_size_arr()
public
pure
returns (bytes memory)
{
uint8[3] memory a;
a[0] = 1;
a[1] = 2;
a[2] = 3;
return abi.encode(a);
}
- 结构体被abi.encode之后在内存中的存储,直接存储元素
struct Point {
uint256 x;
uint128 y;
uint128 z;
}
// 0x0000000000000000000000000000000000000000000000000000000000000001
// 0000000000000000000000000000000000000000000000000000000000000002
// 0000000000000000000000000000000000000000000000000000000000000003
function encode_struct() public pure returns (bytes memory) {
Point memory p = Point(1, 2, 3);
return abi.encode(p);
}
- 结构体动态数组被abi.encode之后在内存中的存储
// Dynamic sized array of structs
// offset | length | struct data
// 0x0000000000000000000000000000000000000000000000000000000000000020
// 0000000000000000000000000000000000000000000000000000000000000003
// 0000000000000000000000000000000000000000000000000000000000000001
// 0000000000000000000000000000000000000000000000000000000000000002
// 0000000000000000000000000000000000000000000000000000000000000003
// 0000000000000000000000000000000000000000000000000000000000000004
// 0000000000000000000000000000000000000000000000000000000000000005
// 0000000000000000000000000000000000000000000000000000000000000006
// 0000000000000000000000000000000000000000000000000000000000000007
// 0000000000000000000000000000000000000000000000000000000000000008
// 0000000000000000000000000000000000000000000000000000000000000009
function encode_struct_array() public pure returns (bytes memory) {
Point[] memory arr = new Point[](3);
arr[0] = Point(1, 2, 3);
arr[1] = Point(4, 5, 6);
arr[2] = Point(7, 8, 9);
return abi.encode(arr);
}
- 返回内存中的数据,两个uint256,return(数据起始地址,数据的长度)
function test_return_vals() public pure returns (uint256, uint256) {
// return(start, len) - Halt execution and return data stored in memory from start to start + len
assembly {
mstore(0x80, 11)
mstore(0xa0, 22)
return(0x80, 0x40)
}
}
- 构造动态数组,进行返回,return(数据起始地址,数据的长度)
function test_return_dyn_arr() public pure returns (uint256[] memory) {
// ABI encode uint256[] array with 3 elements 11, 22 and 33
assembly {
// offset
mstore(0x80, 0x20)
// length
mstore(add(0x80, 0x20), 3)
// array elements
mstore(add(0x80, 0x40), 11)
mstore(add(0x80, 0x60), 22)
mstore(add(0x80, 0x80), 33)
// No need to update free memory pointer - function execution ends here
return(0x80, mul(5, 0x20))
}
}
function test_return() public pure returns (uint256, uint256) {
// Returns (11, 22)
test_return_vals();
// This code will never execute
return (333, 444);
}
- 汇编调用revert,就是revert(start, len)
function test_revert() public pure {
// revert(start, len) - Revert execution and return data store in memory from start to start + len
assembly {
mstore(0x80, "ERROR HERE")
revert(0x80, 0x20)
}
}
- keccak256(start, len)
function test_keccak() public pure returns (bytes32) {
// keccak256(start, len) - Keccak256 from data in memory from start to start + len
assembly {
mstore(0x80, 1)
mstore(0xa0, 2)
let h := keccak256(0x80, 0x40)
mstore(0xc0, h)
return(0xc0, 0x20)
}
}
function keccak() public pure returns (bytes32) {
return keccak256(abi.encodePacked(uint256(1), uint256(2)));
}
- 计算gas的消耗
- 计算gas的消耗
// Memory expansion gas cost
// Gas cost is quadratic to memory allocation.
contract MemExp {
function alloc_mem(uint256 n) external view returns (uint256) {
uint256 gas_start = gasleft();
uint256[] memory arr = new uint256[](n);
uint256 gas_end = gasleft();
return gas_start - gas_end;
}
}
// arr size | gas
// 0 | 120
// 1 | 178
// 10 | 232
// 20 | 293
// 30 | 354
// 40 | 415
// 50 | 477
// 60 | 540
// 70 | 602
// 80 | 666
// 90 | 729
// 100 | 793
// 110 | 857
// 120 | 922
// 130 | 987
// 140 | 1053
// 150 | 1118
// 160 | 1185
// 170 | 1251
// 180 | 1318
// 190 | 1386
// 200 | 1454
// 1000 | 8144
// 2000 | 20023
// 3000 | 35808
// 4000 | 55500
// 5000 | 79097
// 6000 | 106601
// 7000 | 138011
// 8000 | 173328
// 9000 | 212550
// 10000 | 255679
// 11000 | 302715
// 12000 | 353656
// 13000 | 408504
// 14000 | 467257
// 15000 | 529918
// 16000 | 596484
// 17000 | 666957
// 18000 | 741336
// 19000 | 819621
// 20000 | 901812