资讯专栏INFORMATION COLUMN

以太坊ABI介绍(二)

xiaoxiaozi / 3424人阅读

摘要:上一篇我们讲了中的参数含义以太坊介绍一,但是只有函数定义也是不行的,我们还需要调用,当调用一个函数时也需要对该函数进行编码,这样才能执行,那么以太坊是如何生成可供调用的字节码的。

上一篇我们讲了ABI中的参数含义 以太坊ABI介绍(一),但是只有函数定义也是不行的,我们还需要调用,当调用一个函数时也需要对该函数进行编码,这样EVM才能执行,那么以太坊是如何生成可供EVM调用的字节码的。

生成的字节码主要分两部分:函数选择器和参数编码

函数选择器

即函数编码,对函数名称+参数类型进行sha3(keccak256)哈希运算之后,取前4个字节

1、方法一:
安装pyethereum [https://github.com/ethereum/p...]

> from ethereum.utils import sha3
> sha3("set(uint256").hex()
"0x60fe47b16ed402aae66ca03d2bfc51478ee897c26a1158669c7058d5f24898f4"

> sha3("setA(uint256)")[0:4].hex()
"60fe47b1"

2、方法二:
打开Ganache,默认端口7545,然后再命令行执行以下命令

curl -X POST -i http://localhost:7545 --data "{
  "jsonrpc":"2.0",
  "method":"web3_sha3",
  "params":["set(uint256)"]
}"

返回结果:
{
  "jsonrpc": "2.0",
  "result": "0x60fe47b16ed402aae66ca03d2bfc51478ee897c26a1158669c7058d5f24898f4"
}

3、方法三:

const Web3 = require("web3")
const web3 = new Web3()
console.log(web3.sha3("set(uint256)"))
# 0x60fe47b16ed402aae66ca03d2bfc51478ee897c26a1158669c7058d5f24898f4

取前四个字节(一个字节=2个16进制字符)即:0x60fe47b1

参数编码

由于函数编码占用了4个字节,所以参数编码从第五位开始

参数的编码根据类型的不同,编码方式也有所区别。主要分为固定类型和动态类型

1、固定类型

uint:M为integer类型代表M bits,0 < M <= 256 , M % 8 == 0,如uint32,uint8,uint256。

int:同上。同为从8到256位的无符号整数。

uint和int:整型,分别是uint256和int256的别名。注意: 函数参数类型是uint,转sha3码时要变成uint256。

address:地址,20个字节,160bits。

bool:布尔类型,1个字节,true:1,false:0。高位补0

bytes:固定大小的字节数组,0

2、动态类型

bytes:动态分配大小字节数组。不是一个值类型!

string:动态大小UTF8编码的字符串,不是一个值类型!

T[] 某个类型的不定长数组

T[k] 某个类型的定长数组

编码规则

固定类型的编码就很简单,直接将参数值转成32字节长度的16进制即可。但是有区别的是:不足32bytes时,数字类型,如果是正数高位补0,如果是负数高位补1,布尔类型高位补0,字节类型、字符串类型在低位补全
动态类型的编码稍微复杂点,如果是固定长度就不需要计算偏移量,如果是不定长度就需要先计算偏移量,并在最后加上长度和具体值的编码,详细步骤下面会介绍

具体编码过程

介绍的例子和官方文档一样,如果理解有偏差可以查看源文档

pragma solidity ^0.4.16;

contract Foo {
  function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; }
  function bar(bytes3[2] memory) public pure {}
  function f(uint, uint32[], bytes10, bytes) public pure {}
}
案例

案例一:
函数:baz(bytes3[2] memory)
调用:baz(69, true)

0xcdcd77c0,在node中使用new Web3().sha3("baz(uint32,bool)")生成

const Web3 = require("web3")

const web3 = new Web3()
console.log(web3.sha3("f(uint256,uint32[],bytes10,bytes)"))

0x0000000000000000000000000000000000000000000000000000000000000045,十进制69,转成16进制为45,因为是正数,高位补0至32bytes

0x0000000000000000000000000000000000000000000000000000000000000001,bool类型,true=1false=0,高位补0

所以最终字符串为:(换行显示是为了方便查看,实际是没有换行)

0xcdcd77c0
0x0000000000000000000000000000000000000000000000000000000000000045
0x0000000000000000000000000000000000000000000000000000000000000001

返回:该函数返回的是true,output将会是

0x0000000000000000000000000000000000000000000000000000000000000000

案例二:
函数:bar(bytes3[2] memory)
调用:bar(["abc", "def"])

0xfce353f6,在node中使用new Web3().sha3("bar(bytes3[2])")生成

固定长度不需要计算偏移量

0x6162630000000000000000000000000000000000000000000000000000000000,字符串abc转成16进制后为616263,低位补0,字符串转16进制可以使用这个工具

0x6465660000000000000000000000000000000000000000000000000000000000,同上

所以最终字符串为:(换行显示是为了方便查看,实际是没有换行)

0xfce353f6
0x6162630000000000000000000000000000000000000000000000000000000000
0x6465660000000000000000000000000000000000000000000000000000000000

案例三:
函数:f(uint,uint32[],bytes10,bytes)
调用:f(0x123, [0x456, 0x789], "1234567890", "Hello, world!")

0x8be65246,在node中使用f(uint256,uint32[],bytes10,bytes)生成

0x00000000000000000000000000000000000000000000000000000000000001230x123对应的16进制,正数补全

0x0000000000000000000000000000000000000000000000000000000000000080,动态类型,计算偏移量。这个的偏移量是指实际存储值的位置,由于这个函数有4个变量,那么实际存储值的位置就是第五个32bytes位置,也就是说偏移量等于4*32bytes=128,转成16进制后就是对应的值

0x3132333435363738393000000000000000000000000000000000000000000000,字符串1234567890转成16进制后为31323334353637383930,bytes类型,低位补全

0x00000000000000000000000000000000000000000000000000000000000000e0,动态类型,计算偏移量,这个偏移量就等于参数长度4*32bytes+前面的动态参数参数占有的长度(因为前面只有一个动态参数,所以这个长度就是1*32bytes+2*32bytes,1*32bytes是第一个动态参数长度所占的bytes数,2*32bytes是因为该函数中的第一个动态参数有2个值),那么具体的值就是 4*32bytes+(1*32bytes+2*32bytes)=7*32bytes=224,转成16进制就是e0,高位补全就是对应的值

0x0000000000000000000000000000000000000000000000000000000000000002,第一个动态参数的长度,长度为2

0x0000000000000000000000000000000000000000000000000000000000000456,第一个动态参数中的第一个元素

0x0000000000000000000000000000000000000000000000000000000000000789,第一个动态参数中的第二个元素

0x000000000000000000000000000000000000000000000000000000000000000d,第二个动态参数的长度,长度为13

0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000,第二个动态参数的值编码

所以最终字符串为:(换行显示是为了方便查看,实际是没有换行)

0x8be65246
0000000000000000000000000000000000000000000000000000000000000123
0000000000000000000000000000000000000000000000000000000000000080
3132333435363738393000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000456
0000000000000000000000000000000000000000000000000000000000000789
000000000000000000000000000000000000000000000000000000000000000d
48656c6c6f2c20776f726c642100000000000000000000000000000000000000

以上就是以太坊调用函数时生成字节码的完整过程了!



欢迎订阅「K叔区块链」 - 专注于区块链技术学习

博客地址:http://www.jouypub.com
简书主页:https://www.jianshu.com/u/756c9c8ae984
segmentfault主页:https://segmentfault.com/blog/jouypub
腾讯云主页:https://cloud.tencent.com/developer/column/72548

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/24343.html

相关文章

  • 以太ABI介绍(一)

    摘要:但是对于调用者来说,就需要知道合约有哪些方法,方法的参数是什么,返回值是什么,而这些信息就记录在智能合约的中。 ABI是什么 ABI全称 Application Binary Interface,字面意思是应用程序二进制接口,可以通俗的理解为合约的接口说明,当合约被编译后,它对应的abi也就确定了。abi有点类似于程序中的接口文档,描述了字段名称、字段类型、方法名称、参数名称、参数类型...

    Cruise_Chan 评论0 收藏0
  • python使用web3py与以太智能合约交互

    摘要:合约规定了公共职能部门,以创造新的投资和撤资,这将成为对冲基金的主要职能。对冲基金的用户通过其以太坊地址来识别,该地址相当于公钥。我发现与其他程序如程序的以太坊智能合约进行交互的最简单方法是使用他们的。 在以太坊和其他区块链中,仍有很多被证明的概念正在实施,开发人员在尝试如何应对这些新概念。作为dInvest 系列文章一部分,我也在研究以太坊并尝试在区块链中实施对冲基金。在上一篇文章中...

    Joyven 评论0 收藏0
  • 以太钱包开发系列4 - 发送Token(代币)

    摘要:一系列的类似的对象,如果无返回值时,可以省略。表示函数声明自己不会改变状态变量的值。表示函数可以接收,否则表示不能。哈哈,恭喜大家,到这里这里就完整的实现了一个基于以太坊去中心化网页钱包。 本文首发于深入浅出区块链社区原文链接:[使用 ethers.js 开发以太坊Web钱包4 - 发送Token (代币)](https://learnblockchain.cn/20...,请读者前往...

    Honwhy 评论0 收藏0
  • 以太钱包开发系列4 - 发送Token(代币)

    摘要:一系列的类似的对象,如果无返回值时,可以省略。表示函数声明自己不会改变状态变量的值。表示函数可以接收,否则表示不能。哈哈,恭喜大家,到这里这里就完整的实现了一个基于以太坊去中心化网页钱包。 本文首发于深入浅出区块链社区原文链接:[使用 ethers.js 开发以太坊Web钱包4 - 发送Token (代币)](https://learnblockchain.cn/20...,请读者前往...

    HmyBmny 评论0 收藏0
  • python和flask框架开发以太智能合约

    摘要:是以太坊开发的个人区块链,可用于部署合约,开发应用程序和运行测试。安装是一个用于与以太坊交互的库。启动以太坊测试区块链服务器要部署智能合约,我们应该启动测试以太坊服务器。最后,你将在以太坊合约中设置调用用户对象时获得的值。 将数据存储在数据库中是任何软件应用程序不可或缺的一部分。无论如何控制该数据库都有一个该数据的主控。区块链技术将数据存储到区块链网络内的区块中。因此,只要某个节点与网...

    enrecul101 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<