EOS 合约基础教程 - 动作 ( action )

动作 ( action ) 是 EOS 合约的灵魂,是 EOS 的基础组成单位。

我们来看看区块链,呃,EOS 链的层次结构:

  1. 首先,一个 EOS 链是由多个块 ( block ) 组成的,目前的 EOS 每秒出两个块

    2.png

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

    3.png

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

    4.png

    这里的每一个动作,可以是延时动作,也可以是内联动作

如果从合约的角度看,合约 ( contract ) 是一个账号下多个可执行动作的载体。比如当前最流行的 EOS 游戏 betdicelucky, 它有以下几个动作

屏幕快照 2018-12-01 上午10.41.42.png

那么,合约里又是如何表示这些动作的呢 ?

合约中的动作 vs C++ 方法

刚刚我们提到,合约是动作的载体,具体到 C++ 上来说,一个动作就是一个公开的 ( pubic ) 的 C++ 类的成员方法

一个动作,在 C++ 合约类中,表示如下

  1. 一个动作必须是 C++ 合约类的成员方法
  2. 成为动作的成员方法,必须使用 [[eosio::action]] C++11 特性修饰,否则就是一个普通的类成员函数
  3. 成为动作的成员方法,访问级别必须是公开的 public
  4. 成为动作的成员方法,必须没有任何返回值,也不能返回任何值,也就是说,必须使用 void 作为返回值
  5. 成为动作的成员方法,可以接受任意数量的参数
  6. 成为动作的成员方法,必须在 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 中导出这个动作。

而剩下的两个动作,higreeting 则可以被正确的执行,因为它们符合动作的 5 个条件

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.