2.5 添加内联action

EOS官方文档介绍 2.5 添加内联action (v1.3.0)

简介

之前我们写了一个简单的合约来演示如何使用多索引表。 在本教程的这一部分中,我们将学习如何构建action,并从合约中发送这些操作。

第1步:将eosio.code添加到addressbook的帐户

为了从地址簿发送我们的内联操作,我们需要将eosio.code权限添加到我们的合同帐户的活动权限中。 打开终端并运行以下代码

eosio.code权限是一个用于增强安全性,并使合同能够执行内联操作的伪权限。

第2步:通知

打开我们在上一个教程中编写的addressbook.cpp合约。 我们需要编写一个行为,作为我们的“交易收据”。 为此,我们将在地址簿类中创建一个私有帮助器函数。 既然我们已经了解了ABI声明,我们现在就将其包括在内。

这个函数非常简单,它只接受一个account_name和一个字符串。

第3步:使用require_recipient将操作复制到发件人

现我们已经编写了基本的函数,我们需要在某处复制此消息。 为此,我们需要使用require_recipient方法。

这个方法非常简单,它会将给定的操作复制到提供的用户。 但是,任何人都可以调用此函数,并“伪造”此合约中的收据。 这可能被恶意使用,应该被视为漏洞。 为了解决这个问题,我们要求调用此操作的权限来自合同,为此,我们将使用 get_self()

[[eosio::action]]
void notify(account_name user, std::string msg) {
require_auth(get_self());
require_recipient(user);
}

现在,如果用户bob直接调用此函数,但是传递参数alice,则该操作将引发异常。

第4步:通知帮助程序发送内联交易

由于这个内联action将被多次调用,为简便,我们编写一个快速帮助程序,以最大限度地重用代码。 在合约的私有区域中,定义一个新方法

在这个助手里面,我们将构建一个动作并发送它。

第5步:action构造函数

有几种方法可以在EOSIO合约中构建一个action。 虽然模板和宏可以简化这个过程,但它们无法使你弄清工作原理,你可能感到困惑。 因此,在本教程中,我们将重点关注vanilla implementation。
为了演示一些重要的概念,我们接下来会修改此合约,以便每次对合同采取行动时向用户发送收据。首先,我们将着手“创建记录”这个问题。 当iterator == addresses.end()为true时,在表中找不到记录时会触发这种情况我们将此对象保存到名为notification的操作变量中

action构造函数需要许多参数。

“代码”即“部署合约的帐户”

由于我们调用的操作在此合约中,因此我们将使用get_self()帮助程序。 N(地址簿)也可以在这里工作,但如果这个合约是在不同的帐户名下部署的,那就不行了。 因此,get_self()是更好的选择。

...
private:
void send_summary(account_name user, std::string message){
action notification = action(
permission_level{get_self(),N(active)},
get_self(),
//action
//data
);
}

action

我们之前定义了要在合同中调用的通知操作。 我们在这里使用N()宏.

数据

最后,我们定义了我们想要传递给此操作的数据。 notify函数接受两个参数,一个是account_name和一个字符串。 动作构造函数将数据视为类型字节,因此我们将构建一个元组。 在元组中传递的数据是与顺序有关的的,并且由我们正在调用的动作接受的参数的顺序确定。

发送操作

最后,我们使用action 结构体的发送方法发送操作。

第6步:调用帮助程序并输入相关消息

现在我们已经定义了帮助器,我们应该从相关位置调用它。 我们应该从三个特定的地方调用帮助器:

现在我们已经定义好了所需的一切,下列应是我们的地址簿合同的当前状态:
C++

#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>

using namespace eosio;

class addressbook : public eosio::contract {

public:
using contract::contract;

addressbook(account_name self): contract(self) {}

[[eosio::action]]
void upsert(account_name user, std::string first_name, std::string last_name, std::string street, std::string city, std::string state) {
require_auth( user );
address_index addresses(_self, _self);
auto iterator = addresses.find( user );
if( iterator == addresses.end() )
{
addresses.emplace(user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.street = street;
row.city = city;
row.state = state;
});
send_summary(user, "successfully emplaced record to addressbook");
}
else {
std::string changes;
addresses.modify(iterator, user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.street = street;
row.city = city;
row.state = state;
});
send_summary(user, "successfully modified record in addressbook.");
}
}

[[eosio::action]]
void erase(account_name user){
// require_auth(user);
address_index addresses(_self, _self);
auto iterator = addresses.find( user );
eosio_assert(iterator != addresses.end(), "Record does not exist");
addresses.erase(iterator);
send_summary(user, "successfully erased record from addressbook");
}

[[eosio::action]]
void notify(account_name user, std::string msg) {
require_auth(get_self());
require_recipient(user);
}

private:
struct [[eosio::table]] person {
account_name key;
std::string first_name;
std::string last_name;
std::string street;
std::string city;
std::string state;
uint64_t primary_key() const { return key; }
};
typedef eosio::multi_index<N(people), person> address_index;

void send_summary(account_name user, std::string message){
action(
permission_level{get_self(),N(active)},
get_self(),
N(notify),
std::make_tuple(user, name{user}.to_string() + " " + message)
).send();
}

};

EOSIO_ABI( addressbook, (upsert)(notify)(erase) )

第8 步:重新编译并重新生成ABI文件

现在,因为我们已经对ABI进行了更改,我们需要重新编译合约,包括–abigen符号。 如果您仔细按照说明操作,则不会遇到任何错误。

EOSIO上的智能合约是可升级的,因此我们可以轻松地根据我们的更改重新部署合同。

第9步:测试

现在我们已经修改并部署了合同,我们应该对其进行测试。 在上一个教程中,我们删除了alice的地址簿记录,因此调用upsert将触发我们刚刚在“create”案例中编写的内联操作。
在终端中运行以下命令

cleos将返回一些数据,包括交易中执行的所有操作

executed transaction: 685ecc097edd7b2777f394803edb0a98a99822a4a8a7b2957ebf088483684d5e  152 bytes  1043 us
## addressbook <= addressbook::upsert {"user":"alice","first_name":"alice","last_name":"liddell","street":"123 drink me way","city":"wonde...
## addressbook <= addressbook::notify {"user":"alice","msg":"alice successfully added record to addressbook"}
## alice <= addressbook::notify {"user":"alice","msg":"alice successfully added record to addressbook"}

在底部你会注意到addressbook :: notify复制了alice的有关此交易的一些信息。 所以现在我们可以使用cleos get actions来向我们展示已执行的操作以及与alice相关的操作。

cleos get actions alice

原文链接:https://developers.eos.io/eosio-home/docs/inline-actions