Ethereum学习笔记 ---- 使用 Remix 调试功能理解 bytes 在 memory 中的布局

编写合约

在浏览器中打开 Remix IDE: https://remix.ethereum.org/,在 contracts 目录下创建如下合约:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.0 <0.9.0;

contract MyTest {
    function bytesInMemory() public pure returns (bytes memory) {
        bytes memory a = "hello";
        bytes memory b = "hi";
        return bytes.concat(a, b);
    }

    function bytesArrayInMemmory(uint a) public pure returns (bytes[] memory) {
        if (a == 0) 
            ++a;
        bytes[] memory result = new bytes[](a);
        for(uint i = 0; i<a; i++){
            result[i] = "hello world";
        }
        return result;
    }
}

编译、部署、调用合约

调试交易

1. 调用函数 bytesInMemory(),分析 bytes 的 Memory Layout

执行 RETURN 前的最后一刻,stack 快照如下

执行完毕时刻的 Memory Layout

{
  "0x0": "0000000000000000000000000000000000000000000000000000000000000000\t????????????????????????????????",
  "0x20": "0000000000000000000000000000000000000000000000000000000000000000\t????????????????????????????????",
  "0x40": "0000000000000000000000000000000000000000000000000000000000000127\t????????????????????????????????",
  "0x60": "0000000000000000000000000000000000000000000000000000000000000000\t????????????????????????????????",
  "0x80": "0000000000000000000000000000000000000000000000000000000000000005\t????????????????????????????????",
  "0xa0": "68656c6c6f000000000000000000000000000000000000000000000000000000\thello???????????????????????????",
  "0xc0": "0000000000000000000000000000000000000000000000000000000000000002\t????????????????????????????????",
  "0xe0": "6869000000000000000000000000000000000000000000000000000000000000\thi??????????????????????????????",
  "0x100": "0000000000000000000000000000000000000000000000000000000000000007\t????????????????????????????????",
  "0x120": "68656c6c6f686900000000000000000000000000000000000000000000000000\thellohi?????????????????????????",
  "0x140": "0000000000002000000000000000000000000000000000000000000000000000\t?????? ?????????????????????????",
  "0x160": "0000000000000768656c6c6f6869000000000000000000000000000000000000\t???????hellohi??????????????????",
  "0x180": "0000000000000000000000000000000000000000000000000000000000000000\t????????????????????????????????"
}

将相关字节染上相同颜色,方便观察;黄色阴影背景部分数据为 return 返回的 ABI 编码后的结果

对 Memory Layout 的分析

mem[0x40:0x60):

  • 空闲地址指针,值为 0x127; 从 0x127 开始存储的内容实际是返回给调用者的 ABI 编码的结果,由于不会再继续执行新命令了,所以没有更新这里的空闲地址指针。

mem[0x80:0xc0):

  • a 在内存中的布局,前32个字节用于表示字节长度,是5;后续的5个字节是 'hello' 的字节码;后续 right-padding 为32字节的倍数。

mem[0xc0:0x100):

  • b 在内存中的布局。

mem[0x100:0x127):

  • bytes.concat(a, b) 拼接后的结果,字节长度为7;在对返回值进行ABI编码之前,也是 right-padding 为32字节的倍数。

mem[0x127:0x187):

  • 将返回值进行 ABI 编码后的结果,共计 0x60 个字节 --- 对应 RETURN 命令执行时 stack 中的 【0x01270x60】;

  • 前 0x20 个字节的值为 0x20,表明数据编码起始地址 offset 为 0x20

  • 第二个 0x20 个字节的值为 0x07,表明 bytes 长度为 7;

  • 第三个 0x20 个字节为 right-padding 后的字节编码。

2. 调用函数 bytesArrayInMemmory(4),分析 bytes[] 的 Memory Layout

执行 RETURN 前的最后一刻,stack 快照如下

执行完毕时刻的内存布局

{
  "0x0": "0000000000000000000000000000000000000000000000000000000000000000\t????????????????????????????????",
  "0x20": "0000000000000000000000000000000000000000000000000000000000000000\t????????????????????????????????",
  "0x40": "0000000000000000000000000000000000000000000000000000000000000220\t??????????????????????????????? ",
  "0x60": "0000000000000000000000000000000000000000000000000000000000000000\t????????????????????????????????",
  "0x80": "0000000000000000000000000000000000000000000000000000000000000004\t????????????????????????????????",
  "0xa0": "0000000000000000000000000000000000000000000000000000000000000120\t??????????????????????????????? ",
  "0xc0": "0000000000000000000000000000000000000000000000000000000000000160\t????????????????????????????????",
  "0xe0": "00000000000000000000000000000000000000000000000000000000000001a0\t??????????????????????????????? ",
  "0x100": "00000000000000000000000000000000000000000000000000000000000001e0\t????????????????????????????????",
  "0x120": "000000000000000000000000000000000000000000000000000000000000000b\t???????????????????????????????\u000b",
  "0x140": "68656c6c6f20776f726c64000000000000000000000000000000000000000000\thello world?????????????????????",
  "0x160": "000000000000000000000000000000000000000000000000000000000000000b\t???????????????????????????????\u000b",
  "0x180": "68656c6c6f20776f726c64000000000000000000000000000000000000000000\thello world?????????????????????",
  "0x1a0": "000000000000000000000000000000000000000000000000000000000000000b\t???????????????????????????????\u000b",
  "0x1c0": "68656c6c6f20776f726c64000000000000000000000000000000000000000000\thello world?????????????????????",
  "0x1e0": "000000000000000000000000000000000000000000000000000000000000000b\t???????????????????????????????\u000b",
  "0x200": "68656c6c6f20776f726c64000000000000000000000000000000000000000000\thello world?????????????????????",
  "0x220": "0000000000000000000000000000000000000000000000000000000000000020\t??????????????????????????????? ",
  "0x240": "0000000000000000000000000000000000000000000000000000000000000004\t????????????????????????????????",
  "0x260": "0000000000000000000000000000000000000000000000000000000000000080\t????????????????????????????????",
  "0x280": "00000000000000000000000000000000000000000000000000000000000000c0\t????????????????????????????????",
  "0x2a0": "0000000000000000000000000000000000000000000000000000000000000100\t????????????????????????????????",
  "0x2c0": "0000000000000000000000000000000000000000000000000000000000000140\t????????????????????????????????",
  "0x2e0": "000000000000000000000000000000000000000000000000000000000000000b\t???????????????????????????????\u000b",
  "0x300": "68656c6c6f20776f726c64000000000000000000000000000000000000000000\thello world?????????????????????",
  "0x320": "000000000000000000000000000000000000000000000000000000000000000b\t???????????????????????????????\u000b",
  "0x340": "68656c6c6f20776f726c64000000000000000000000000000000000000000000\thello world?????????????????????",
  "0x360": "000000000000000000000000000000000000000000000000000000000000000b\t???????????????????????????????\u000b",
  "0x380": "68656c6c6f20776f726c64000000000000000000000000000000000000000000\thello world?????????????????????",
  "0x3a0": "000000000000000000000000000000000000000000000000000000000000000b\t???????????????????????????????\u000b",
  "0x3c0": "68656c6c6f20776f726c64000000000000000000000000000000000000000000\thello world?????????????????????",
  "0x3e0": "0000000000000000000000000000000000000000000000000000000000000000\t????????????????????????????????"
}

将相关字节染上相同颜色,方便观察;黄色阴影背景部分数据为 return 返回的 ABI 编码后的结果

对 Memory Layout 的分析

mem[0x40:0x60):

  • 空闲地址指针,值为 0x220;从 0x220 开始存储的内容实际是返回给调用者的 ABI 编码的结果,由于不会再继续执行新命令了,所以没有更新这里的空闲地址指针。

mem[0x80:0xa0):

  • 动态数组 result 的元素数量,为4,因为我们是以 4 为参数调用的 bytesArrayInMemmory().

mem[0xa0:0xc0):

  • result[0] 对应的 bytes 所在的起始地址,值为 0x120,表明 result[0] 对应的字符串起始地址在 0x120; 后续 mem[0xc0:0xe0)mem[0xe0:0x100)mem[0x100:0x120) 分别是 result[1] ~ result[3] 中存储的 bytes 起始地址的指针。

mem[0x120:0x160):

  • result[0] 对应的 bytes 的实际编码;其中 mem[0x120:140) 是字符串的长度,mem[0x140:160) 是 right-padding 后的字符串。
    后续的 mem[0x160:0x1a0)mem[0x1a0:0x1e0)mem[0x1e0:0x220) 分别是 result[1] ~ result[3] 对应的 bytes 的实际编码。

mem[0x220:0x3e0):

  • 对 result 进行 ABI 编码后的结果,共计 0x1c0 个字节 --- 对应 RETURN 命令执行时 stack 中的 【0x0220,0x1c0】。这一块数据编码是遵照 合约ABI规范 独立进行的编码,其中的 offset 与实际的 memory 地址无关,而是相对于这块数据自身计算出的 offset。

  • 由于返回值类型 bytes[] 是动态类型,所以起始位置的 mem[0x220:0x240) 是 head(X(1)),为 0x20,表明 0x20 个byte 后是 bytes[] 的实际编码;

  • mem[0x240:0x260) = 4,表明 bytes[] 有4个元素;

  • mem[0x260:0x3e0) 是对 bytes[] 4个元素的编码;

    • mem[0x260:0x280) = 0x80,是 result[0] 编码结果的起始位置相对于 mem[0x260:0x3e0) 这块数据起始位置的 offset;

    • mem[0x260:0x280) = 0xc0,是 result[1] 编码结果的起始位置相对于 mem[0x260:0x3e0) 这块数据起始位置的 offset;

    • mem[0x260:0x280) = 0x100,是 result[2] 编码结果的起始位置相对于 mem[0x260:0x3e0) 这块数据起始位置的 offset;

    • mem[0x260:0x280) = 0x140,是 result[3] 编码结果的起始位置相对于 mem[0x260:0x3e0) 这块数据起始位置的 offset;

    • mem[0x2e0:0x320)mem[0x320:0x360)mem[0x360:0x3a0)mem[0x3a0:0x3e0) 分别是 result[0] ~ result[3] 的实际数据编码结果。

posted on 2024-09-11 17:04  HorseShoe2016  阅读(18)  评论(0编辑  收藏  举报