EOS開發教程(2.6) 理解 ABI文件

in eos •  4 months ago 

簡介
在上個教程中,妳使用我們提供的ABI文件部署了eosio.token合約。這個教程我們將整體介紹ABI文件是如何與eosio.token合約進行關聯的。
可以使用eosio.cdt提供的eosio-cpp工具來生成ABI文件。但是,在有些情況下,這種做法會導致ABI生成失敗。高級c++模型可能會跳過它,自定義類型有時可能會導致ABI的生成出現問題。出於這些原因,妳迫切需要知道ABI文件是如何工作的,這樣的話,如果遇到了問題妳就可以debug或是修復那些問題。
什麽是ABI?
應用二進制接口(ABI)是壹個基於json的描述文件,它描述了如何把用戶的行為在json和二進制文件間進行轉換。ABI還描述了如何把數據庫狀態轉換成json,或把json轉換成數據庫狀態。壹旦妳通過ABI描述了妳的合約,開發者和用戶就能通過json與妳的合約進行無縫互動。
創建壹個 ABI 文件
讓我們從壹個空的ABI文件開始,我們把它命名為eosio.token.abi
{ "version": "eosio::abi/1.0", "types": [], "structs": [], "actions": [], "tables": [], "ricardian_clauses": [], "abi_extensions": [], "___comment" : "" }
類型
ABI能讓任何客戶端或接口為妳的合約生成壹套用戶界面。為了能持續運行,就需要在ABI中描述這些自定義類型 — 它們要在任意公共行為或結構中用作參數,需要被描述。
內建類型
EOSIO實現了許多自定義的內建類型。內建類型不需要在ABI文件中描述。如果妳想了解這些內建類型,請查看這裏。
{ "new_type_name": "name", "type": "name" }
現在這個ABI文件看起來像是這樣:
{ "version": "eosio::abi/1.0", "types": [{ "new_type_name": "name", "type": "name" }], "structs": [], "actions": [], "tables": [], "ricardian_clauses": [], "abi_extensions": [] }
結構
暴露到ABI的結構也需要描述。我們看壹下eosio.token.hpp文件,很快就能發現哪些結構是用於公共行為的。對於下壹步來說,這步尤為重要。
壹個結構對象在json中的定義看起來像是這樣的:
{ "name": "issue", //The name "base": "", //Inheritance, parent struct "fields": [] //Array of field objects describing the struct's fields. }
字段
{ "name":"", // The field's name "type":"" // The field's type }
在 eosio.token合約中,很多結構都需要定義。請註意,不是所有的結構都是顯式定義的,有些是作為行為的參數。下面是需要在eosio.token合約中提供ABI描述的結構列表::
隱式結構
下面的結構都是隱式的,這些結構在合約中都不是顯示定義的。比如create行為,妳會發現兩個參數,壹個name類型的issuer參數和壹個asset類型的maxium_supply參數。為了簡化,本教程不會 列出每壹個結構,不過邏輯是相似的,妳最終會看到下面的內容:
create
{ "name": "create", "base": "", "fields": [ { "name":"issuer", "type":"name" }, { "name":"maximum_supply", "type":"asset" } ] }
issue
{ "name": "issue", "base": "", "fields": [ { "name":"to", "type":"name" }, { "name":"quantity", "type":"asset" }, { "name":"memo", "type":"string" } ] }
retire
{ "name": "retire", "base": "", "fields": [ { "name":"quantity", "type":"asset" }, { "name":"memo", "type":"string" } ] }
transfer
{ "name": "transfer", "base": "", "fields": [ { "name":"from", "type":"name" }, { "name":"to", "type":"name" }, { "name":"quantity", "type":"asset" }, { "name":"memo", "type":"string" } ] }
close
{ "name": "close", "base": "", "fields": [ { "name":"owner", "type":"name" }, { "name":"symbol", "type":"symbol" } ] }
顯式結構
這些結構是顯式定義的,它們是初始化壹個多下標表的必要部分。對它們的描述和上面對隱式結構的描述毫無二致。
account
{ "name": "account", "base": "", "fields": [ { "name":"balance", "type":"asset" } ] }
currency_stats
{ "name": "currency_stats", "base": "", "fields": [ { "name":"supply", "type":"asset" }, { "name":"max_supply", "type":"asset" }, { "name":"issuer", "type":"account_name" } ] }
行為
壹個行為的json對象定義看起來像是下面這樣的:
{ "name": "transfer", //The name of the action as defined in the contract "type": "transfer", //The name of the implicit struct as described in the ABI "ricardian_contract": "" //An optional ricardian clause to associate to this action describing its intended functionality. }
通過集合所有在eosio.token合約頭文件中描述的公共函數來描述eosio.token合約的行為。
然後根據它前面描述的結構來描述每個行為的類型。多數情況下,函數名稱和結構名稱是相同的,不過這不是必須的。
下面是壹個鏈接到源代碼的列表,提供了示例json,用於描述每個行為。
create
{ "name": "create", "type": "create", "ricardian_contract": "" }
issue
{ "name": "issue", "type": "issue", "ricardian_contract": "" }
retire
{ "name": "retire", "type": "retire", "ricardian_contract": "" }
transfer
· JSON
{ "name": "transfer", "type": "transfer", "ricardian_contract": "" }
close
{ "name": "close", "type": "close", "ricardian_contract": "" }

描述表。下面是壹個表的json對象定義:
{ "name": "", //The name of the table, determined during instantiation. "type": "", //The table's corresponding struct "index_type": "", //The type of primary index of this table "key_names" : [], //An array of key names, length must equal length of key_types member "key_types" : [] //An array of key types that correspond to key names array member, length of array must equal length of key names array. }
eosio.token合約初始化了兩個表:accounts和stats。
accounts表有壹個i64索引,它是基於account結構的,它有壹個uint64作為它的primary key。
下面是accounts表在ABI文件中的描述:
{ "name": "accounts", "type": "account", // Corresponds to previously defined struct "index_type": "i64", "key_names" : ["primary_key"], "key_types" : ["uint64"] }
stat表有壹個i64索引,它是基於currency_stats結構的,它有壹個uint64作為它的primary key。
下面是stat表在ABI文件中的描述:
{ "name": "stat", "type": "currency_stats", "index_type": "i64", "key_names" : ["primary_key"], "key_types" : ["uint64"] }
妳會看到上面的表擁有相同的“key name”。把妳的key用相似的名字來命名,這樣可以提示它們間的關系。在這個實現中壹樣,意味著任意給定值都能用來查詢不同的表。
綜合
最後,就是壹個準確描述了eosio.token合約的ABI文件。
{ "version": "eosio::abi/1.0", "types": [ { "new_type_name": "name", "type": "name" } ], "structs": [ { "name": "create", "base": "", "fields": [ { "name":"issuer", "type":"name" }, { "name":"maximum_supply", "type":"asset" } ] }, { "name": "issue", "base": "", "fields": [ { "name":"to", "type":"name" }, { "name":"quantity", "type":"asset" }, { "name":"memo", "type":"string" } ] }, { "name": "retire", "base": "", "fields": [ { "name":"quantity", "type":"asset" }, { "name":"memo", "type":"string" } ] }, { "name": "close", "base": "", "fields": [ { "name":"owner", "type":"name" }, { "name":"symbol", "type":"symbol" } ] }, { "name": "transfer", "base": "", "fields": [ { "name":"from", "type":"name" }, { "name":"to", "type":"name" }, { "name":"quantity", "type":"asset" }, { "name":"memo", "type":"string" } ] }, { "name": "account", "base": "", "fields": [ { "name":"balance", "type":"asset" } ] }, { "name": "currency_stats", "base": "", "fields": [ { "name":"supply", "type":"asset" }, { "name":"max_supply", "type":"asset" }, { "name":"issuer", "type":"name" } ] } ], "actions": [ { "name": "transfer", "type": "transfer", "ricardian_contract": "" }, { "name": "issue", "type": "issue", "ricardian_contract": "" }, { "name": "retire", "type": "retire", "ricardian_contract": "" }, { "name": "create", "type": "create", "ricardian_contract": "" }, { "name": "close", "type": "close", "ricardian_contract": "" } ], "tables": [ { "name": "accounts", "type": "account", "index_type": "i64", "key_names" : ["currency"], "key_types" : ["uint64"] }, { "name": "stat", "type": "currency_stats", "index_type": "i64", "key_names" : ["currency"], "key_types" : ["uint64"] } ], "ricardian_clauses": [], "abi_extensions": [] }
Token合約沒有提到的案例
向量
在妳的ABI文件中描述壹個向量,只需要在類型後面加上[],如果妳像描述壹個授權級別的向量,妳可以像這樣描述:permission_level[]
本教程不包括的額外的ABI屬性
為了簡化,本教程跳過了壹些ABI屬性。不過,有壹個ABI規範,它將概述整個ABI的每個屬性
Ricardian Clauses
Ricardian clauses描述了特定行為的預期結果。它也可以用來在發送者和合約之間建立條款。
ABI 拓展
壹個通用的“未來驗證”層,允許老客戶端跳過對擴展數據“塊”的解析。目前,此屬性未使用。將來,每個擴展在這個向量中都有自己的“塊”,以便老客戶端跳過它,而新客戶端知道如何解釋它。

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!