Function Selector and Argument Encoding

详细可查看 官方文档

参考自己博客 Function Selector and Argument Encoding

在 Ethereum 生态系统中,ABI (Application Binary Interface,应用二进制接口) 是从区块链外部与合约进行交互以及合约与合约间进行交互的一种标准方式。数据会根据其类型按照这份手册中说明的方法进行编码。

Function Selector

原理

某个函数签名的 Keccak (SHA-3) 哈希的前 4 字节,指定了要调用的函数,形如 bytes4(keccak256('balanceOf(address)')) == 0x70a08231 这种形式,0x70a08231 便是 balanceOf(address) 的 Function Selector

  • 基础原型即是函数名称加上由括号括起来的参数类型列表,参数类型间由一个逗号分隔开,且没有空格

  • 对于 uint 类型,要转成 uint256 进行计算,比如 ownerOf(uint256) 其 Function Selector = bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e

  • 函数参数包含结构体,相当于把结构体拆分成单个参数,只不过这些参数用 () 扩起来,详细可看下面的例子

例子

pragma solidity >=0.4.16 <0.9.0;
pragma experimental ABIEncoderV2;

contract Demo {
    struct Test {
        string name;
        string policies;
        uint num;
    }
    
    uint public x;
    function test1(bytes3) public {x = 1;}
    function test2(bytes3[2] memory) public  { x = 1; }
    function test3(uint32 x, bool y) public  { x = 1; }
    function test4(uint, uint32[] memory, bytes10, bytes memory) public { x = 1; }
    function test5(uint, Test memory test) public { x = 1; }
    function test6(uint, Test[] memory tests) public { x = 1; }
    function test7(uint[][] memory,string[] memory) public { x = 1; }
}

/* 函数选择器
{
    "0d2032f1": "test1(bytes3)",
    "2b231dad": "test2(bytes3[2])",
    "92e92919": "test3(uint32,bool)",
    "4d189ce2": "test4(uint256,uint32[],bytes10,bytes)",
    "4ca373dc": "test5(uint256,(string,string,uint256))",
    "ccc5bdd2": "test6(uint256,(string,string,uint256)[])",
    "cc80bc65": "test7(uint256[][],string[])",
    "0c55699c": "x()"
}
*/

Function Selector and Argument Encoding

原理

  • 动态类型的数据,比如动态数组,结构体,变长字节,其编码后存储其 offsetlengthdata

    • 先把参数顺序存储:如果是定长数据类型,直接存储其 data,如果是变长数据类型,先存储其 offset

    • 顺序遍历变长数据:先存储 offset,对于第一个变长数据,先存储其 offset = 0x20 * number ( number 是函数参数的个数 );对于下一个变长数据,其 offset = offset_of_prev + 0x20 + 0x20 * number (第一个 0x20 是存储前一个变长数据的长度占用的大小,number 是前一个变长数据的元素个数)

    • 顺序遍历变长数据:存储完 offset ,接着就是遍历每个变长数据,分别存储其 lengthdata

    • ( ps: 对于结构体这样的类型,存储的时候可把结构体内元素看成是一个新函数的参数,这样的话,对于结构体中的第一个变长数据,其 offset = 0x20 * numnum 是结构体元素的个数 )

例子

针对上述的合约例子的 7 个函数,其函数调用最终编码如下

  • test1("0x112233")

  • test2(["0x112233","0x445566"])

  • test3(0x123,1)

  • test4(0x123,["0x11221122","0x33443344"],"0x31323334353637383930","0x3132333435")

  • test5(0x123,["cxy","pika",123])

  • test6(0x123,[["cxy1","pika1",123], ["cxy2","pika2",456]])

  • test7([[1,2],[3]],["one","two","three"])

例题

balsn 2020

  • 题目名称 Election

!!! note 注:题目附件相关内容可至 ctf-challenges/blockchain 仓库寻找。

Last updated