2.3 了解ABI文件

引言

我们之前已经使用了ABI文件部署了eosio.token合同。 在本教程的这一部分中,我们将概述ABI文件如何与eosio.token合约相关联。
通过eosio.cdt提供的eosio-cpp实用程序可以生成ABI文件。 但是,有些情况可能会导致ABI发生故障。高级C ++模式可以将其提升,自定义类型有时会导致ABI生成问题。 因此,您必须了解ABI文件的工作原理,以便在必要时进行调试和修复。

什么是ABI?

应用程序二进制接口(ABI)是一个基于JSON、介绍如何在JSON和二进制表示之间转换用户操作的接口。 ABI还描述了如何进行数据库状态和JSON的相互转换。 通过对ABI合约进行定义说明后,开发人员和用户将能够通过JSON与您的合约进行无缝交互。
下面是一个空ABI。


{
"version": "eosio::abi/1.0",
"types": [],
"structs": [],
"actions": [],
"tables": [],
"ricardian_clauses": [],
"abi_extensions": [],
"___comment" : ""
}

类型

ABI允许任何客户端或接口解释甚至为您的合约生成GUI。 为了保持其一致性,我们需要描述在我们想要在ABI中描述的任何public action或结构中用作参数的自定义类型。

内置类型

EOSIO中能实现许多自定义内置函数,不需要在ABI文件中描述内置类型。 如果您想熟悉EOSIO的内置插件,此处是相关定义(here)。
以eosio.token为例,唯一需要在ABI文件中进行说明的类型是account_name。 ABI使用“new_type_name”来描述显式类型,在本例中为account_name,而account_name是名称类型的别名。
所以在ABI文件中我们将添加以下内容

现在ABI文件将变为如下内容:

结构体

我们现在需要对数字货币合约的结构进行说明。 通过查看eosio.token.hpp,我们可以快速确定public action使用了哪些结构。 当我们在下一步中描述ABI文件中的操作时,这一点尤为重要。
JSON中结构体定义如下所示:

通过eosio.token合同,我们看到许多需要定义的结构体。 请注意,并非所有结构体都是明确定义的,有些结构体对应于动作的参数。 以下是需要对eosio.token合约进行ABI描述的结构体列表:

隐形结构体

以下结构体是隐含性的,因为其未在合约中明确定义。 查看create操作,您将找到两个参数,account_name类型的issuer和属性类型的maximum_supply。 为简洁起见,本教程不会分开说明每个结构,但应用相同的逻辑,您将得到以下结果:

create
issue
retire
transfer

Copy

{
"name": "transfer",
"base": "",
"fields": [
{
"name":"from",
"type":"account_name"
},
{
"name":"to",
"type":"account_name"
},
{
"name":"quantity",
"type":"asset"
},
{
"name":"memo",
"type":"string"
}
]
}

close

Copy

{
"name": "close",
"base": "",
"fields": [
{
"name":"owner",
"type":"account_name"
},
{
"name":"symbol",
"type":"symbol"
}
]
}

显性结构体

这些结构是明确定义的,因为它们是实例化多索引表的必要条件。 它们的定义与上述所示的隐式结构类似。

account
currency_stats

Actions

其JSON定义如下:

接下来,我们将通过eosio.token合约头文件中描述的所有公共函数来说明eosio.token合约的action。 然后,我们将说明之前描述的结构的每个action的类型。 在大多数情况下,函数名称和结构名称会相同,但不需要一定相同。
下面是JSON实例的链接action到其源代码的操作列表,以了解每个action的定义方式。

create

{
"name": "create",
"type": "create",
"ricardian_contract": ""
}

issue

retire

transfer

close

您会注意到我们之前在ABI定义的“structs”数组中描述了所有这些。

最后我们来说表.下面是表的定义事例:

eosio.token 合约涉及到账户( accounts )和stat两个表。
帐户表是一个基于帐户结构体、i64索引,以一个uint64作为它的主键,密钥被命名为“currency”的表。
下面将说明如何在ABI中定义账户表。

{
"name": "accounts",
"type": "account", // Corresponds to previously defined struct
"index_type": "i64",
"key_names" : ["currency"],
"key_types" : ["uint64"]
}

stat表是一个基于currenct_stats结构、i64索引,以一个uint64作为它的主键,钥匙被命名为“currency”的表。
下面将说明如何在ABI中定义stat表。

{
"name": "stat",
"type": "currency_stats",
"index_type": "i64",
"key_names" : ["currency"],
"key_types" : ["uint64"]
}

您能看到上面的表格均具有相同的“密钥名称”。 为您的密钥命名相似的名称因为它可能传达了一种主观关系,说明任何给定值都可用于查询不同的表。

整合

最后,一旦所有部分均汇总在一起,我们自己就有了一个准确描述eosio.token合同的ABI文件。

数字货币合约中未涉及的内容

向量

在ABI文件中描述向量时,只需使用[]进行添加。如果需要对权限级别的向量进行定义,您可以这样定义:permission_level []

Base 结构体

这是一个很少被提及的特性。 您可以使用基本ABI结构属性来引用另一个继承结构,只要该结构也在同一ABI文件中定义。 如果您的智能合约逻辑不支持继承,Base将不执行任何操作或可能抛出错误。
您可以在系统合同源代码和ABI中看到相关示例。

未提及的ABI特性

为简洁起见,此处省略了ABI规范的一些属性。但是,有一个ABI规范完整地概述ABI的每个属性。

李嘉图合约

条款规定了了特定行为的预期结果, 它也可用于在发件人和合同之间建立条款。

ABI 扩展

ABI中存在一个允许原有客户跳过解析扩展数据的“区块”的广义的“面向未来”的层级。 目前,此属性尚未使用。 将来,每个扩展在该向量中都有自己的“区块”,原有客户及明白如何解读该阶段的新客户能直接跳过此阶段。

维护

每次更改结构体添加表,添加操作或向操作添加参数以及使用新类型时,您都需要记住更新ABI文件。 在许多情况下,更新ABI文件失败不会产生任何错误。

故障排除

表没有返回行数据

检查您的表是否在 GLOSSARY:ABI 文件中准确描述。 例如,如果使用cleos在具有格式错误的GLOSSARY:ABI定义的合约上添加表,然后从该表中获取行,则将收到空结果。 当合约未能在其GLOSSARY:ABI文件中正确描述其表时,cleos在添加行或读取行时不会产生错误。

原文链接:https://developers.eos.io/eosio-home/docs/the-abi