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 methods
  • 0x40 - 0x5f (32 bytes): free memory pointer - pointer to next available location in memory tostore data
  • 0x60 - 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字节

一些结论

  1. 在内存中存储,最小的单位为32字节,即便是uint8在内存中也占用32字节。
  2. 结构体在内存中是直接存储的,没有前缀和后缀。
  3. 固定长度的数组在内存中按顺序存储,没有前缀和后缀。
  4. 动态数组,首先存储数组有几个这样的元素,然后就是数组元素的存储,如果使用汇编的话,需要手动修改0x40内存区域指向的下一块未使用的内存地址,不使用汇编则不需要。
  5. test_valinternal_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();
    }
}
  1. test_meminternal_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)
        }
    }
}
  1. 值小于32字节:在左边补0
// 0x000000000000000000000000abababababababababababababababababababab

function encode_addr() public pure returns (bytes memory) {
    address addr = 0xABaBaBaBABabABabAbAbABAbABabababaBaBABaB;
    return abi.encode(addr);
}
  1. 固定大小的数据:在右边补0
// 0xaabbccdd00000000000000000000000000000000000000000000000000000000
function encode_bytes4() public pure returns (bytes memory) {
    bytes4 b4 = 0xaabbccdd;
    return abi.encode(b4);
}
  1. 动态数组被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);
}
  1. 固定数组被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);
}
  1. 结构体被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);
}
  1. 结构体动态数组被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);
}
  1. 返回内存中的数据,两个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)
    }
}
  1. 构造动态数组,进行返回,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);
}
  1. 汇编调用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)
    }
}
  1. 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)));
}
  1. 计算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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值