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 个条件