はじめに
本連載ではEOS Developer Portalで紹介されているeosio.tokenコントラクトを用いて、コントラクトのデプロイや実行をおこない、より実用的なコントラクトの構造や実行方法を学びます。
本記事では、コントラクトのコンパイルに関連するファイルの種類やツールの紹介、ABIと呼ばれるインターフェースの構造について解説します。
本記事は、過去にコンセンサス・ベイスが主宰していたオンラインサロンの記事です。記事は2017年~2018年にかけて執筆されたため、一部は、既に古くなっている可能性があります。あらかじめご了承ください。
関連する記事
EOSIO Developer Portal 解説 最短でHello World!
EOSIO Developer Portal 解説 独自トークンの発行 第1回
EOSIO Developer Portal 解説 独自トークンの発行 第2回
EOSIO Developer Portal 解説 独自トークンの発行 第3回
EOSIO Developer Portal 解説 データの永続化とインラインアクション 第1回
EOSIO Developer Portal 解説 データの永続化とインラインアクション 第2回
EOSIO Developer Portal 解説 データの永続化とインラインアクション 第3回
コントラクト関連ファイルの概要
本連載の第1回で、eosio.cdt(EOSIO Contract Development Toolkit)を用いてコントラクトのコンパイルをおこないました。ここで、コントラクトのコンパイルに関連するファイルの種類とeosio.cdtのツール群について簡単にまとめます。
図1. eosio.cdtのツール群のコントラクト関連ファイル
本連載では、サンプルとして用意されたコントラクトのソースコード(eosio.token.cpp)から、コマンド1に示すeosio-cppコマンドを用いてwasmファイルとabiファイルを生成しました。
wasmファイルは、WebAssemblyのバイナリファイル、abiファイルは、JSON形式で表現されたコントラクトのインターフェースを記述したファイルです。
コマンド1. eosio-cppによるabiファイルの生成
$ eosio-cpp -I include -o eosio.token.wasm src/eosio.token.cpp --abigen
abiファイルは、eosio-cppコマンドで–abigenオプションを指定して生成できる他、コマンド2のとおりeosio-abigenコマンドを用いても生成できます。
コマンド2. eosio-abigenによるabiファイルの生成
$ eosio-abigen -contract=eosio.token -output=eosio.token.abi \
include/eosio.token/eosio.token.hpp src/eosio.token.cpp
通常、これらのツールを用いて自動生成されたABIファイルを用いれば問題ありませんが、より複雑な機能や独自のTypeを定義したコントラクトでは、ABIの自動生成に失敗したり、意図とは異なる定義が生成される可能性があります。
その場合、手動でABIファイルを作成したり修正したりする必要がでてきます。本稿では、手動でABIファイルを編集するために、ABIファイルの基本的な構成について解説します。
なお、今回は使用しませんが、WebAssemblyにはS式ベースで記述されたテキスト形式の表現(wat/wastファイル)もあります。watファイルの生成には、コマンド3に示すとおりeosio-wasm2wastコマンドを用います。
コマンド3. eosio-wasm2wastによるwatファイルの生成
$ eosio-wasm2wast eosio.token.wasm -o eosio.token.wat
ABIファイルの構成
ABIとは、バイナリ形式のコントラクトに対して、人間にとって分かりやすいフォーマットでコミュニケーションするためのインターフェースを提供します。ABIを通じて、JSON形式のユーザーアクションをバイナリ形式に変換したり、バイナリ形式のデータベースの状態をJSON形式に変換したりできます。
本連載で、eosio.tokenコントラクトに対して生成されたABIファイルは、eosio.token.abiとして出力されています。コマンド4を用いて、eosio.token.abiファイルの中身を確認してみましょう。
コマンド4. eosio.token.abiファイルの確認
$ cat eosio.token.abi
{
"____comment": "This file was generated with eosio-abigen...",
"version": "eosio::abi/1.0",
"structs": [
{
"name": "account",
"base": "",
"fields": [
{
"name": "balance",
"type": "asset"
}
]
},
…
}
ABIファイルのトップレベルの構成は、コード1に示す属性で構成されています。表1に、それぞれの属性の概要を示します。
コード1. ABIファイルのテンプレート
{
"___comment" : "",
"version": "eosio::abi/1.0",
"structs": [],
"types": [],
"actions": [],
"tables": [],
"ricardian_clauses": [],
"abi_extensions": []
}
表1. ABIの属性一覧
属性名 | 概要 |
___comment | ABIファイルのコメント |
version | ABIフォーマットのバージョン |
structs | アクションの引数やテーブルのカラムとして定義された構造体の一覧 |
types | Built-inで定義されたデータ型以外のカスタム型一覧 |
actions | アクションの定義一覧 |
tables | テーブルの定義一覧 |
ricardian_clauses | コントラクトの期待する動作や規約などの記述 |
abi_extensions | 将来の拡張性を担保するための領域(現在は未使用) |
コントラクトのインターフェースを表す主要な属性として、Structs, Types, Actions, Tablesのフォーマットについて下記に示します。
Structs
コード2に、Structs定義のテンプレートを示します。Structs定義の要素として、Structの名前、継承する場合の親Struct、フィールドの配列があります。
コード2. Structs定義のテンプレート
{
"name": "issue", //Structの名前
"base": "", //継承する場合の親Struct
"fields": [ // フィールドの配列
{
"name":"", // フィールド名
"type":"" // フィールドの型
}
]
}
Structsには、コントラクトコード内で明示的に記述されたものと、アクションの引数として暗黙的に定義されたものがあります。
例えば、明示的に定義されたStructとして、eosio.token.hppに記述されたcurrency_stats(コード3)に対応するABIはコード4の通りです。
コード3. eosio.token.hppにおけるcurrency_statsの定義抜粋
struct [[eosio::table]] currency_stats {
asset supply;
asset max_supply;
name issuer;
uint64_t primary_key()const { return supply.symbol.code().raw(); }
};
コード4. currency_statsのABI
{
"name": "currency_stats",
"base": "",
"fields": [
{
"name":"supply",
"type":"asset"
},
{
"name":"max_supply",
"type":"asset"
},
{
"name":"issuer",
"type":"account_name"
}
]
}
また、暗黙的に定義されたStructとして、createアクションの引数に対応するStructはコード5の通りです。
コード5. createアクションの引数に対応するstruct
{
"name": "create",
"base": "",
"fields": [
{
"name":"issuer",
"type":"name"
},
{
"name":"maximum_supply",
"type":"asset"
}
]
}
Types
eosio.tokenコントラクトでは使用されていませんが、カスタムのデータ型を定義した場合はコード6に示すフォーマットに従ってTypesの定義を記述します。
コード6. Types定義のテンプレート
{
"new_type_name": "name",
"type": "name"
}
Actions
Actionsには、コントラクトで定義したアクションの名前と、アクションの引数に対応するStructを記述します。
オプションとして、Ricardian Contractと呼ばれる、アクションの期待する動作を記述するクラスを指定することができます。これにより、コントラクトの利用者にアクションの意図を説明したり、もしアクションの機能などにバグがあり意図しない挙動が発生した場合、該当のトランザクションを無効化するなどの対応をすることができます。
アクション定義のテンプレートをコード7に示します。
コード7. Actions定義のテンプレート
{
"name": "transfer", // アクションの名前
"type": "transfer", // アクションの引数となる暗黙的なStruct名
"ricardian_contract": "" // アクションの意図した機能を説明するRicardian Class(オプション)
}
Tables
Tablesには、コントラクトで定義したTableの名前やStruct、インデックス情報などを記載します。コード8に、Tables定義のテンプレートを示します。
コード8. Tables定義のテンプレート
{
"name": "", // テーブルの名前
"type": "", // 対応するStruct
"index_type": "", // 主インデックスのType
"key_names" : [], // キー名の配列
"key_types" : [] // キー名に対応するTypeの配列
}
eosio.tokenコントラクトには、accountsとstatsという2つのテーブルを定義していました。accountsテーブルのABI記述例をコード9に示します。typeには、Structsで定義したStruct名を指定し、主キー(primary_key)としてuint64型のインデックスを指定しています。
コード9. accountsテーブルのABI記述例
{
"name": "accounts",
"type": "account",
"index_type": "i64",
"key_names" : ["primary_key"],
"key_types" : ["uint64"]
}
コントラクトのStructやTableを変更したり、アクションの引数を加えたりといった修正をするたびに、対応するABIファイルの更新も必要です。
また、コントラクトの仕様と異なるABIを使用すると、正しくコントラクトとインタラクションすることができません。
独自のコントラクトを実装する際には、本稿で紹介したeosio.cdtのツール群やABIの知識も活かしてみてください。
参考文献:
- EOSIO Developer Portal – 2.3 Understanding ABI Files
https://developers.eos.io/eosio-home/docs/the-abi - B9lab Academy – Introduction to EOSIO for Developers
https://academy.b9lab.com/courses/course-v1:B9lab+EOSIO-FREE+2018-09/about - Understanding The eosio.token Contract
https://medium.com/coinmonks/understanding-the-eosio-token-contract-87466b9fdca9