【Ethereum】ABI协议
👕【Ethereum】ABI协议
2023-5-25
| 2023-10-15
0  |  0 分钟
type
status
date
slug
summary
tags
category
icon
password
Sub-item
Last edited time
Oct 15, 2023 08:48 AM
Parent item
领域
以太放执行智能合约函数是通过交易的方式实现的,调用合约的参数通过交易中的data字段来传输,通过以太坊浏览器etherscan可以看到合约调用的交易详情中会有<数据输入>的字段,该字段的数据就是编码后的函数及参数,编码规则就是本小姐要介绍的ABI协议,如图所示:
notion image
ABI全称 Application Binary Interface,应用程序二进制接口。定义了调用智能合约函数以及合约之间函数调用的消息编码格式,也可以理解为智能合约函数调用的接口说明。类似Webservice里的 SOAP 协议一样:定义操作函数签名、参数编码、返回结果编码等。
使用ABI协议时必须要求在编译时确定类型,即强类型相关。组装交易数据时,将生成的 ABI 编码数据存储在交易的 data 字段,当以太坊节点执行该交易时,检查 data 字段是否有数据,如果有的话,解析该数据,找到函数入口,再执行该函数调用。
当一个智能合约成功编译出来后,它的ABI接口定义就确定了。比如下面的智能合约:
生成的 ABI 定义如下:
可以看出,生成ABI包含了2 个定义:函数lotus和事件Log_lotus,各个字段含义见上文。根据该 ABI 定义,就可以生成调用该智能合约函数的 ABI 格式的数据了。ABI协议定义的编码格式可以简单表示为:函数选择器 + 参数编码 。
  • 函数选择器
    • 一个函数调用的 ABI 编码数据的前四个字节指定了要调用的函数名字符串的Hash值。函数 Hash 的计算方式是使用 keccak256 函数对函数声明字符串进行哈希,取哈希结果的前 4 个字节。如下:
      函数名如果有多个参数使用“ , ”隔开,注意:要去掉表达式中的所有空格。在Geth客户端,通过命令可以得到hash:
      截取前 4 个字节,即 0xcc822237。
  • 参数编码
    • 由于前面的函数哈希使用了四个字节,真正参数数据将从第五个字节开始编码。ABI 编码规则根据参数类型的不同有所区别,分为固定长度编码和动态长度编码。
      固定长度的类型有如下几种。
    • uint<M>:M 为integer 类型,代表 M 个字节,0 < M<= 256,M % 8 == 0,如 uint32、uint8、uint256。
    • int<M>:同上。同为从 8 到 256 位的无符号整数。
    • uint 和int:整型,分别是 uint256 和int256 的别名。注意:如果定义函数参数类型是uint,那么计算哈希时要转换成 uint256。
    • address:地址,20 个字节,160 位长度。
    • bool:布尔类型,1 个字节,true:1,false:0。
    • bytes<M>:固定大小的字节数组,0<M<=32,其中byte 是 bytes1 的别名。
    • 固定类型的数据编码固定长度为32 字节,从左往右按照大端字节序存储数据,不足 32字节长度的需要加0 补足 32 字节。
      动态长度编码类型有如下几种:
    • bytes:动态分配大小的字节数组。该类型是一个引用类型,不是一个值类型。
    • string:动态分配大小的UTF8 编码字符串,该类型是一个引用类型,不是一个值类型。
    • 动态长度的编码稍微有点复杂,我们在例子中详细介绍。
下面是abi 编码一个例子。该例子包含动态长度和固定长度混合编码的参数。
函数定义:f(uint,uint32[],bytes10,bytes)
调用方法:f(0x123, [0x456, 0x789], "1234567890", "Hello, world!")
函数选择器:bytes4(sha3("f(uint256,uint32[],bytes10,bytes)"))
生成的 ABI 编码数据如下:
0x8be65246是函数选择器的值,数编码数据依次如下。
  • 0x0000000000000000000000000000000000000000000000000000000000000123,该数据表示第一个参数数值,32字节长度的值0x123。
  • 0x0000000000000000000000000000000000000000000000000000000000000080,该段数据表示第二个参数的偏移值,偏移值为0x80=4×32 字节。注意,第二个参数的具体的数值编码是在固定长度数据编码之后,因此,这个偏移值的计算方法为:第一个参数数据长度(32字节)+本偏移值长度(32字节)+第三个参数的偏移值长度(32字节)+第四个参数值长度(32字节)。
  • 0x3132333435363738393000000000000000000000000000000000000000000000,该数值表示第三个参数的编码,在右侧补 0 到 32 字节大小。
  • 0x00000000000000000000000000000000000000000000000000000000000000e0,该数值表示第四个参数的偏移,计算方法为:第一个动态参数的偏移值(432字节) + 第一个动态参数的大小(332字节)。
接下来的数据首先就是上面第二个参数[0x456,0x789] 编码。拆解如下:
  • 0x0000000000000000000000000000000000000000000000000000000000000002,该数值表示整个数组的长度,为2。
  • 0x0000000000000000000000000000000000000000000000000000000000000456,该数值表示第一个元素的数值,为0x456。
  • 0x0000000000000000000000000000000000000000000000000000000000000789,该数值表示第二个元素的数值,为0x789。
最后是第三个参数“Hello, world! ”的编码。拆解如下:
  • 0x000000000000000000000000000000000000000000000000000000000000000d,该数值表示元素的字节长度,为13。
  • 0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000, 该数值表示字符串“Hello, world!”。按照 ascii 编码,不足32字节补位到 32 字节。
区块链
  • 区块链
  • 公链
  • 【Ethereum】RLP协议【认证与授权】HTTPS协议介绍
    目录