{"content":{"title":"calldata 编码规则","body":"## solidity 官方文档中的描述：\r\n\r\n\r\n\r\n>**定义：** `len(a)`是一个二进制字符串`a`的字节长度。`len(a)`的类型被呈现为`uint256`\r\n>\r\n>我们把实际的编码`enc`定义为一个由 ABI 类型到二进制字符串的值的映射，因此，当且仅当`X`的类型是动态的，`len(enc(X))`才会依赖于`X`的值\r\n>\r\n>\r\n>**定义**：对于任意的ABI值`X`，根据`X`的实际类型递归的定义`enc(X)`\r\n\r\n>- `(T1,...,Tk)` 对于 `k >= 0` 且任意类型 `T1`， ...， `Tk`\r\n>\r\n>`enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))`\r\n>\r\n>这里， `X = (X(1), ..., X(k))` 并且 `head` 和 `tail` 被定义为如下 `Ti` ：\r\n>\r\n>如果 `Ti` 是静态类型：\r\n>\r\n>​\t`head(X(i)) = enc(X(i))` 和 `tail(X(i)) = \"\"` （空字符串）\r\n>\r\n>否则，即 `Ti` 是动态类型时，它们被定义为：\r\n>\r\n>​\t`head(X(i)) = enc(len( head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1)) ))` `tail(X(i)) = enc(X(i))`\r\n>\r\n>注意，在动态类型的情况下，由于 head 部分的长度仅取决于类型而非值，所以 `head(X(i))` 是定义明确的。 **它的值是从 `enc(X)` 的开始算起的， `tail(X(i))` 的起始位在 `head(X(i))` 中的偏移量**。\r\n>\r\n>- `T[k]` 对于任意 `T` 和 `k`：\r\n>\r\n>`enc(X) = enc((X[0], ..., X[k-1]))`\r\n>\r\n>即，它就像是个由相同类型的 `k` 个元素组成的元组那样被编码的。\r\n>\r\n>- `T[]` 当 `X` 有 `k` 个元素 （ `k` 的类型为 `uint256`）：\r\n>\r\n>`enc(X) = enc(k) enc((X[0], ..., X[k-1]))`\r\n>\r\n>也就是说，它被编码为具有相同类型的 `k` 元素的元组（即静态大小为 `k` 的数组），前缀为元素的数量。\r\n>\r\n>- 具有 `k` 字节长度的 `bytes`， （假设其类型为 `uint256`）：\r\n>\r\n>`enc(X) = enc(k) pad_right(X)`，即，字节数被编码为 `uint256`，紧跟着实际的 `X` 的字节码序列， 再在前边（左边）补上可以使 `len(enc(X))` 成为 32 的倍数的最少数量的 0 值字节数据。\r\n>\r\n>- `string`：\r\n>\r\n>`enc(X) = enc(enc_utf8(X))`， 即 `X` 被 UTF-8 编码，且在后续编码中将这个值解释为 `bytes` 类型。 注意，在随后的编码中使用的长度是其 UTF-8 编码的字符串的字节数，而不是其字符数。\r\n>\r\n>- `uint<M>`： `enc(X)` 是在 `X` 的大端序编码的高位（左侧）补充若干 0 值字节以使其长度成为 32 字节。\r\n>\r\n>- `address`： 与 `uint160` 的情况相同。\r\n>\r\n>- `int<M>`： `enc(X)` 是在 `X` 的大端序的 2 的补码编码的高位（左侧）添加若干字节数据以使其长度成为 32 字节； 对于负数，添加值为 `0xff` 的字节数据，对于正数，添加 0 值字节数据。\r\n>\r\n>- `bool`： 与 `uint8` 的情况相同， `1` 用来表示 `true`， `0` 表示 `false`。\r\n>\r\n>- `fixed<M>x<N>`： `enc(X)` 就是 `enc(X * 10**N)`，其中 `X * 10**N` 可以理解为 `int256`。\r\n>\r\n>- `fixed`： 与 `fixed128x18` 的情况相同。\r\n>\r\n>- `ufixed<M>x<N>`： `enc(X)` 就是 `enc(X * 10**N)` ，其中 `X * 10**N` 可以理解为 `uint256`。\r\n>\r\n>- `ufixed`： 与 `ufixed128x18` 的情况相同。\r\n>\r\n>- `bytes<M>`： `enc(X)` 就是 `X` 的字节序列加上为使长度成为 32 字节而添加的若干 0 值字节。\r\n>\r\n>注意，对于任意的 `X`， `len(enc(X))` 都是 32 的倍数。\r\n\r\n\r\n\r\n## 展开理解（说人话）\r\n\r\n#### 基本概念\r\n\r\n1. **len(a)**：表示二进制字符串 `a` 的字节长度，其类型为 `uint256`。\r\n2. **enc**：表示 ABI 类型到二进制字符串的值的映射，即编码函数。\r\n\r\n\r\n\r\n#### 递归定义`enc(X)`\r\n\r\n\r\n\r\n`exc(X)`是根据`X`的实际类型来递归定义的：\r\n\r\n\r\n\r\n1. 对于元组（`T1, ..., Tk`）（结构体）\r\n\r\n   - `enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))`\r\n\r\n   - 对于元组（结构体）中的每个元素`Xi`\r\n\r\n     - 当对应的$X_i$是静态类型，`head(X(i))`就等于`enc(X(i))`（**实际对应的参数的内容**），`tail(X(i))`为空（**不存在**）。即，**静态类型会在他的`head`位置直接存储其实际的值**\r\n\r\n     - 当对应的$X_i$是动态类型，`head(X(i))`是`enc`数据的偏移量（**距离`enc`开始存储的位置的偏移量**），`tail(X(i))=enc(X(i))`（即 **`tail`存储动态数据中的实际数据**）即，**动态类型会在它的`head`存储它的长度，在他的`tail`存储它实际的参数内容**\r\n\r\n2. 固定大小数组`T[K]`\r\n\r\n   - `enc(X) = enc((X[0], ..., X[k-1]))`\r\n   - 他被编码为`k`个相同类型元素的元组（又回到了第一条，根据元素的类型再进行不同的区分）\r\n\r\n3. 动态大小数组`T[]`\r\n\r\n   - `enc(X) = enc(k) enc((X[0], ..., X[k-1]))` \r\n   - 编码为元素数量`k`和`k`个相同类型元素的数组（首位存储动态数组的长度，后续存储动态数组每一个元素的值，回到第一条根据元素的类型再进行区分）\r\n\r\n4. 字节数组`bytes`\r\n\r\n   - `enc(X) = enc(len(X)) pad_right(X)`\r\n   - 编码为长度和实际字节数据，数据右填充为32字节倍数。（首位存储字节数组的长度（独自占用一个字（32 bytes），后续存储`bytes`字节数组的实际内容。当最后的内容没占满一个字（32 bytes）时补码 0 占满整个字）\r\n\r\n5. 字符串`string`：\r\n\r\n   - `enc(X) = enc(enc_utf8(X))`\r\n   - 编码为 UTF-8 字符串的字节表示，右填充为32字节倍数。\r\n\r\n#### 基本类型编码\r\n\r\n1. `uint<M>`：\r\n   - `enc(X)`是`X`的大端序编码，**高位（左侧）补零**到 32 字节。\r\n2. `address`：\r\n   - 类似`uint160`，**补零（左补零）** 到 32 字节\r\n3. `int<M>`：\r\n   - `enc(X)`是`X`的大端序2的补码编码，**高位（左）补**`0xff`或`0x00`到 32 字节\r\n4. `bool`：\r\n   - 类似`uint8`，`1`代表`true`，`0`表示`false`\r\n5. `fixed<M>x<N>`和`ufixed<M>x<N>`：\r\n   - `enc(X)`是`X * 10**N`表示为`int256`或`uint256`\r\n6. `bytes<M>`：\r\n   - `enc(X)`是`X`的字节序列，**右填充**为 32 字节倍数\r\n\r\n#### 动态类型的 `head` 和 `tail`\r\n\r\n- 对于动态类型，`head`部分存储了`enc(X)`数据的偏移量，`tail` 部分存储实际数据。\r\n- 对于动态数组 `X`，`head(X(i))` 是从 `enc(X)` 开始到 `tail(X(i))` 的字节数\r\n\r\n#### 举例：\r\n\r\n假设函数`createOrder`，参数的 Signature 如下：\r\n\r\n```solidity\r\nfunction createOrder((address, address[], string)) public\r\n```\r\n\r\n调用时传递参数：\r\n\r\n```solidity\r\n(address1, [address2, address3], ”hello\")\r\n```\r\n\r\n**编码过程：**\r\n\r\n1. **计算`head`部分**\r\n\r\n   - `head(address1)`=`enc(address1)`\r\n   - `head(address[])`= 偏移量\r\n   - `head(string)` = 偏移量\r\n\r\n2. 计算`tail`部分：\r\n\r\n   - `tail(address[])` = `enc(address2) enc(address3)`\r\n   - `tail(string)` = `enc(\"hello\")`\r\n\r\n   \r\n\r\n整个`calldata`依次连接`head`和`tail`部分\r\n\r\n\r\n\r\n## 复杂 calldata 解码举例\r\n\r\n#### 第一步 decode\r\n\r\n接下来对这个复杂的 calldata 解码：\r\n\r\n`0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000447d39aaf100000000000000000000000021d44c73b6b341f98be57eaf8008bcbf6e2d58110000000000000000000000000000000000000000000000000de27d72f9c740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a44a393a41000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000b1a02fac5914898f0c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cdc1e5f9d480000000000000000000000000000000000000000000000000001c6bf526340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cb085d52bff5a0e380b3be7906ae76369d8babe300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7c34340b8805ec5a767050658cfa389c49304ca0000000000000000000000001d308089a2d1ced3f1ce36b1fcaf815b07217be300000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000`\r\n\r\n\r\n\r\n首先 Foundry pretty-calldata decode：\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/05/H69SLHxj66546fb2ded18.png)\r\n\r\n```assembly\r\n Possible methods:\r\n - multicall(bytes[])\r\n ------------\r\n [000]: 0000000000000000000000000000000000000000000000000000000000000020  # 第一个动态结构的 head 指向它的 tail 偏移 0x20\r\n [020]: 0000000000000000000000000000000000000000000000000000000000000002  # 进入动态结构，因为这个动态结构是 bytes[] 动态数组，所以第一个存储的是 长度\r\n [040]: 0000000000000000000000000000000000000000000000000000000000000040  \r\n # 子结构中第一个元素的 head ，存储第一个元素 tail 的偏移（距离 `enc(X[]i)` （0x40） 的偏移）得到第一个元素 tail 的位置 0x40 + 0x40 = 0x80\r\n # 是 0x40 而不是 0x20 的原因：动态数组的 enc(X) 为 ``enc(X) = enc(k) enc((X[0], ..., X[k-1]))` \r\n # 0x20 对应的是 enc(k) ,0x40 对应的是 enc((X[0], ..., X[k-1])) 也就是实际上 0x40 对应的 enc\r\n [060]: 00000000000000000000000000000000000000000000000000000000000000c0  # 子结构中第一个元素的 head ，计算得到 tail 的偏移为 0x40 + 0xc0 = 0x100\r\n [080]: 0000000000000000000000000000000000000000000000000000000000000044  \r\n # 子结构中第一个元素的 tail。这个元素是一个 bytes 类型，第一个位置存储的是 bytes 字节数组的长度 0x44, 最后一个元素的位置为 0xa0 + 0x60(0x44 补满 bytes32) = 0x100\r\n [0a0]: 7d39aaf100000000000000000000000021d44c73b6b341f98be57eaf8008bcbf  # 子结构中第一个元素的实际内容\r\n [0c0]: 6e2d58110000000000000000000000000000000000000000000000000de27d72  # 子结构中第一个元素的实际内容\r\n [0e0]: f9c7400000000000000000000000000000000000000000000000000000000000  # 子结构中第一个元素的实际内容 + 补码到占用一整个 bytes32 长度\r\n [100]: 00000000000000000000000000000000000000000000000000000000000002a4  \r\n # 子结构中第二个元素的 tail。这个元素是一个 bytes 类型，第一个位置存储的是 bytes 字节数组的长度 0x2a4, 最后一个元素的位置为 0x120 + 0x2c0(0x2a4 补满 bytes32) = 0x3e0 \r\n [120]: 4a393a4100000000000000000000000000000000000000000000000000000000 # 子结构中第二个元素的实际内容\r\n ...\r\n [3c0]: 0000000000000000000000000000000000000000000000000000000000000000 # 子结构中第二个元素的实际内容 + 补码到占用一整个 bytes32 长度\r\n```\r\n\r\n分解后发现，子结构中的两个元素又是两个 calldata。对这两个 calldata 再 decode\r\n\r\n\r\n\r\n#### decode 出的第一个 calldata decode\r\n\r\n这个 calldata 是一个静态 calldata，直接就能 decode：\r\n\r\n\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/05/Cts3gW6M66546fc9efe5c.png)\r\n\r\n#### decode 出的第二个 calldata decode\r\n\r\n\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/05/BauJHeTz66546fe1c1bca.png)\r\n\r\n\r\ndecode 发现这个 calldata 是一个动态 calldata，对其进行分析：\r\n\r\n```assembly\r\n Possible methods:\r\n - createOrder(((address,address,address,address,address,address[]),(uint256,uint256,uint256,uint256,uint256,uint256,uint256),uint8,uint8,bool,bool,bytes32))\r\n ------------\r\n [000]: 0000000000000000000000000000000000000000000000000000000000000020 # 第一个动态结构的 head 指向它的 tail 偏移 0x20\r\n [020]: 00000000000000000000000000000000000000000000000000000000000001a0 \r\n # 第一个动态结构的子结构(以下简称子结构)的 head（因为这个 struct 是动态的），指向第一个元素的 tail 的偏移（距离0x20） 0x20 + 0x1a0 = 0x1c0\r\n [040]: 00000000000000000000000000000000000000b1a02fac5914898f0c80000000 # 子结构第二个元素（一个静态的结构体，所以 head 直接存储了内容）的第一个元素 uint256 1\r\n [060]: 0000000000000000000000000000000000000000000000000000000000000000 # 子结构第二个元素的第二个元素 uint256 2\r\n [080]: 0000000000000000000000000000000000000000000000000000000000000000 # 子结构第二个元素的第三个元素 uint256 3\r\n [0a0]: 00000000000000000000000000000000000000000000000000000cdc1e5f9d48 # 子结构第二个元素的第四个元素 uint256 4\r\n [0c0]: 0000000000000000000000000000000000000000000000000001c6bf52634000 # 子结构第二个元素的第五个元素 uint256 5\r\n [0e0]: 0000000000000000000000000000000000000000000000000000000000000000 # 子结构第二个元素的第六个元素 uint256 6\r\n [100]: 0000000000000000000000000000000000000000000000000000000000000000 # 子结构第二个元素的第七个元素 uint256 7\r\n [120]: 0000000000000000000000000000000000000000000000000000000000000002 # 子结构的第三个元素 uint8\r\n [140]: 0000000000000000000000000000000000000000000000000000000000000000 # 子结构的第四个元素 uint8\r\n [160]: 0000000000000000000000000000000000000000000000000000000000000001 # 子结构的第五个元素 bool\r\n [180]: 0000000000000000000000000000000000000000000000000000000000000001 # 子结构的第六个元素 bool\r\n [1a0]: 0000000000000000000000000000000000000000000000000000000000000000 # 子结构的第七个元素 bytes32\r\n [1c0]: 000000000000000000000000cb085d52bff5a0e380b3be7906ae76369d8babe3 # 子元素第一个元素的 tail， address 1\r\n [1e0]: 0000000000000000000000000000000000000000000000000000000000000000 # 子元素第一个元素的 tail， address 2\r\n [200]: 0000000000000000000000000000000000000000000000000000000000000000 # 子元素第一个元素的 tail， address 3\r\n [220]: 000000000000000000000000b7c34340b8805ec5a767050658cfa389c49304ca # 子元素第一个元素的 tail， address 4\r\n [240]: 0000000000000000000000001d308089a2d1ced3f1ce36b1fcaf815b07217be3 # 子元素第一个元素的 tail， address 5\r\n [260]: 00000000000000000000000000000000000000000000000000000000000000c0 \r\n # 子元素第一个元素的 tail， address[] 这是 address[]  元素的 head，存储了 address[] tail 的偏移（距离 0x1c0) 0x1c0 + 0xc0 = 0x280\r\n [280]: 0000000000000000000000000000000000000000000000000000000000000000 # 子元素第一个元素中的 address[] 的 tail\r\n```\r\n\r\n所以对应调用该函数时实际上的结构是这样的：\r\n\r\n\r\n\r\n```solidity\r\ncreateOrder(((0xcb085d52bff5a0e380b3be7906ae76369d8babe3,0x0000000000000000000000000000000000000000,0x0000000000000000000000000000000000000000,0xb7c34340b8805ec5a767050658cfa389c49304ca,0x1d308089a2d1ced3f1ce36b1fcaf815b07217be3,[]),(14072960000000000000000000000000,0,0,500000000000000,500000000000000,0,\r\n0),2,0,true,true,0x0000000000000000000000000000000000000000000000000000000000000000))\r\n```\r\n\r\nFoundry 验证一下：\r\n\r\n\r\n\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/05/w5ukDdzu66546ff7171ae.png)\r\n\r\n\r\n\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/05/vliSzT2X665470033717b.png)"},"author":{"user":"https://learnblockchain.cn/people/16222","address":"0xa8ffd8058570ccab8475b4a2c429bdb9038662b9"},"history":"bafkreifb7cuw2oayjt6hgxn22jjv4xmnqsm5g332iv7dvvu5kg2cn3wddu","timestamp":1716810223,"version":1}