这篇文章早早就写好了,但是最近工作特别忙,一直没有润色,所以就没更新。而且,EOS Dawn 4.0 也发布了,感觉研究不过来了。。。。
合约开发一些小思考
熟悉合约的开发之后,其实可以发现,合约的编写本身比较“简单”;因为合约开发讲究实现功能:逻辑实现简单、复杂合约尽量拆分、抱着安全。
习惯各种复杂业务逻辑的开发者,肯定感觉合约写起来很简单。但是,这也是另一种“复杂”;因为你要在一个有限功能、有限存储、节约资源的框架下,实现你想实现的功能,想想其实挺有挑战的O(∩_∩)O
稍微说了点想法,还是快点开始正题吧!
智能合约之间的调用
这个之前在以太坊一些小问题中说过,为了让智能合约能够更新,最好是逻辑、数据业务分开在不懂合约中,然后用逻辑合约调用数据合约;虽然EOS的智能合约没有这方面问题,但是,合约间调用还是少不了的。EOS中合约的调用,主要的实现就是靠函数:
//file: contracts/eosiolib/action.h
template
void dispatch_inline( account_name code, action_name act,
vector perms,
std::tuple args ) {
action( perms, code, act, std::move(args) ).send();
}
从入参,就能大体看出如何使用了:
- code:合约名称;
- act:action的名称;
- perms:授权信息,格式如下:
permission_level( account_name a, permission_name p )
比如level{tester, N(active)}
; - args:action的入参,结构使用的C++的tuple;
这是,比如麻烦的调用,程序也提供了比较方面的使用方式,如下的宏定义:
#define INLINE_ACTION_SENDER3( CONTRACT_CLASS, FUNCTION_NAME, ACTION_NAME )\
::eosio::inline_dispatcher::call
#define INLINE_ACTION_SENDER2( CONTRACT_CLASS, NAME )\
INLINE_ACTION_SENDER3( CONTRACT_CLASS, NAME, ::eosio::string_to_name(#NAME) )
#define INLINE_ACTION_SENDER(...) BOOST_PP_OVERLOAD(INLINE_ACTION_SENDER,__VA_ARGS__)(__VA_ARGS__)
#define SEND_INLINE_ACTION( CONTRACT, NAME, ... )\
INLINE_ACTION_SENDER(std::decay_t, NAME)( (CONTRACT).get_self(),\
BOOST_PP_TUPLE_ENUM(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__)) );
至于,如何使用呢,我们接下来用个例子。
调用例子
我们就用前面一章中的例子来展示如何调用,blog_view,这章中,我们实现了一个简单的用户上传博客的功能,然后审核员能够审核用户的文章是否有敏感信息;
比较符合区块链开发思路的方式,既然用户上传知识、审核员审核文章,这都是工作量,那就要给用户和审核员一些利益,好让用户多过来上传文章,壮大我们的区块链合约;我们就可以在审核员审核通过后,自动的使用合约给用户和审核员发送token奖励;
也就是在approved函数中,添加奖励功能;
首先假设,使用eosio.token合约,发布了token “BLOG币”,并使用issue发行给合约blog.view一定量的币;
///注意要include eosio.token的头文件
/// @abi action
void approved(const uint64_t ID) {
auto itrid = idlists.find(ID);
eosio_assert(itrid != idlists.end(), "this blog doesn't exists!\n");
blog_index approve_blogs(_self, itrid->producer);
auto itr = approve_blogs.find( ID );
eosio_assert(itr != approve_blogs.end(), "this blog doesn't exists!\n");
eosio_assert(itr->status == Status::s_reviewing, "this blog is reviewed!\n");
require_auth(itr->reviewer);
approve_blogs.modify(itr, itrid->producer, [&](auto& g){
g.status = Status::s_approved;
g.approve_status = std::string("approved");
});
//审核通过后,给予奖励
SEND_INLINE_ACTION( eosio::token(N(eosio.token)), transfer, {_self,N(active)}, { _self, producer, itrid->producer, "reward"} );
}
可以看到,最后一个语句就是调用的其他合约 eosio.token,这语句是合约中最最简单的调用方式,参数详解:
- eosio::token(N(eosio.token)) 前面是eosio.token头文件中定义的结构,然后传入的参数,是该合约上传的账户;
- transfer 即需要调用的方法;
- {_self,N(active)} 本次交易,需要的权限;
- 后面的参数,就是本次action需要的所有参数;
当然,实现的内部调用的方法,还是很多种形式,比如action().send()、dispatch_inline()、INLINE_ACTION_SENDER等等,其实最根本的,还是action.send(),其他所有方法都是对该方法的封装。
PS:合约间调用,还有一个比较严重的问题,就是目前合约调用的权限签名,只支持调用合约的签名。比如合约A,调用合约B的action,假设为transfer,transfer需要验证发送者权限,则只能验证合约A的权限。假如,用户h,调用A合约,A合约调用B合约的transfer,则只能有合约B,本身发出转账,用h用户的账户发出转账是不行的。。。挺绕的。
比如上面的例子,只能是_self本身发出转账,任何其他账户都不行。
期望在4.0改进吧,不然限制太大。