EOS 合约基础教程 - 动作 ( action )
动作 ( action ) 是 EOS 合约的灵魂,是 EOS 的基础组成单位。
我们来看看区块链,呃,EOS 链的层次结构:
-
首先,一个 EOS 链是由多个块 ( block ) 组成的,目前的 EOS 每秒出两个块

-
其次, 每个块又由多个事务/交易 ( transcation ) 组成

-
最后,每个事务/交易,由一个或多个动作 ( action ) 组成

这里的每一个动作,可以是延时动作,也可以是内联动作
如果从合约的角度看,合约 ( contract ) 是一个账号下多个可执行动作的载体。比如当前最流行的 EOS 游戏 betdicelucky, 它有以下几个动作
那么,合约里又是如何表示这些动作的呢 ?
合约中的动作 vs C++ 方法
刚刚我们提到,合约是动作的载体,具体到 C++ 上来说,一个动作就是一个公开的 ( pubic ) 的 C++ 类的成员方法
一个动作,在 C++ 合约类中,表示如下
- 一个动作必须是 C++ 合约类的成员方法
- 成为动作的成员方法,必须使用
[[eosio::action]]C++11 特性修饰,否则就是一个普通的类成员函数 - 成为动作的成员方法,访问级别必须是公开的
public - 成为动作的成员方法,必须没有任何返回值,也不能返回任何值,也就是说,必须使用
void作为返回值 - 成为动作的成员方法,可以接受任意数量的参数
- 成为动作的成员方法,必须在
EOSIO_DISPATCH中导出
只有满足了以上条件,一个 C++ 类成员函数才能成为一个动作
我们来看一个范例
#include <eosiolib/eosio.hpp> using namespace eosio; using namespace std; class hello:public eosio::contract { public: using eosio::contract::contract; // 一个名为 hi 的动作,没有任何参数 [[eosio::action]] void hi(){ } // 一个名为 greeting 的动作,接受一个参数 [[eosio::action]] void greeting(name to){} // 这不是一个动作,因为动作没有返回值,也不能返回任何值 [[eosio::action]] int age() { return 28; } // 这不是一个动作,因为没有使用 [[eosio::action]] 修饰 void born() { print(1990); } // 这虽然是一个动作,因为没有被导出,所以根本不会被执行到 [[eosio::action]] void province() { print("北京"); } }; EOSIO_DISPATCH(hello,(hi)(greeting)(age)(born))
对于上面这段代码,编译是通过不了的,提示的错误如下
hello.cpp:37:1: error: no matching function for call to 'execute_action' EOSIO_DISPATCH(hello,(hi)(greeting)(age)(province))
原因就是我们的 age() 方法有返回值,改成不返回任何值就好了
[[eosio::action]] void age() {}
修改后的代码如下
#include <eosiolib/eosio.hpp> using namespace eosio; using namespace std; class hello:public eosio::contract { public: using eosio::contract::contract; // 一个名为 hi 的动作,没有任何参数 [[eosio::action]] void hi(){ } // 一个名为 greeting 的动作,接受一个参数 [[eosio::action]] void greeting(name to){} // 一个名为 age 的动作,虽然有返回值,但返回值会被合约忽略 [[eosio::action]] void age() { } // 这不是一个动作,因为没有使用 [[eosio::action]] 修饰 void born() { print(1990); } // 这虽然是一个动作,因为没有被导出,所以根本不会被执行到 [[eosio::action]] void province() { print("北京"); } }; EOSIO_DISPATCH(hello,(hi)(greeting)(age)(born))
编译和部署合约,然后我们看看 born 这个方法是否可以执行不?
cleos push action hello born '[]' -p hi error 2018-12-01T03:09:29.848 thread-0 main.cpp:3151 main ] Failed with error: Assert Exception (10) !action_type.empty(): Unknown action born in contract hello
哇,执行报错了,提示动作不存在。
究其原因,是因为我们没有使用 [[eosio::action]] 修饰,导致生成的 hello.abi 文件里没有这个动作
cat hello.abi
{
"____comment": "This file was generated with eosio-abigen. DO NOT EDIT Sat Dec 1 11:10:44 2018",
"version": "eosio::abi/1.1",
"structs": [
{
"name": "age",
"base": "",
"fields": []
},
{
"name": "greeting",
"base": "",
"fields": [
{
"name": "to",
"type": "name"
}
]
},
{
"name": "hi",
"base": "",
"fields": []
},
{
"name": "province",
"base": "",
"fields": []
}
],
"types": [],
"actions": [
{
"name": "age",
"type": "age",
"ricardian_contract": ""
},
{
"name": "greeting",
"type": "greeting",
"ricardian_contract": ""
},
{
"name": "hi",
"type": "hi",
"ricardian_contract": ""
},
{
"name": "province",
"type": "province",
"ricardian_contract": ""
}
],
"tables": [],
"ricardian_clauses": [],
"variants": [],
"abi_extensions": []
}
同样的,如果我们执行 province 是可以成功的,但是并没有我们预想的那样输出 北京
cleos push action hello province '[]' -p hi executed transaction: f3ebef27345fdc4f53022ecb3f5447b24b51625b95ee79047446498c9889ab2c 96 bytes 245 us # hello <= hello::province ""
究其原因,是因为我们没有在 EOSIO_DISPATCH 中导出这个动作。
而剩下的两个动作,hi 和 greeting 则可以被正确的执行,因为它们符合动作的 5 个条件