简介

今天我们继续补充智能合约的进阶使用技巧,这次的主题是交易,合约内我们除了可以发起内联action的调用,很多使用还需要直接调用其他的合约action或者以交易的形式调用自身的action。

发起交易/延时交易

在合约内可以非常方便的发起一个交易,无论是调用外部的合约action还是调用自身的,都很容易。

这里可能你会有疑问,为何调用自身的action要通过发起交易的方式呢?一个最主要的原因是需要有交易记录,如果直接作为内联方法调用了,链上是看不到直观的记录的,而我们通过区块链浏览器查看交易时,是需要有交易记录或者交易通知的,才能被查询到。

构建交易

我们首先要引入#include <eosio/transaction.hpp>,然后我们来看下面这个代码示例,这段代码来自系统合约eosio.system中delegate_bandwidth.cpp中changebw方法,这个方法在进行资源带宽的抵押和赎回的时候都会调用。而下面的煮这段代码,是调用合约自身的refund退款方法,即在赎回时需要等待3天的赎回期,3天后退款交易会被执行,然后我们赎回的资源就能转账到我们的账户了。

//构建一个transaction
eosio::transaction out; //配置action,包括了权限、合约、action名称和参数
out.actions.emplace_back( permission_level{from, active_permission}, get_self(), "refund"_n, from); //设置延时调用时间
out.delay_sec = refund_delay_sec; //取消已有延迟交易
eosio::cancel_deferred( from.value ); //发送交易
out.send( from.value, from, true );

  

我们看到这里使用的权限是from的active权限,而from就是发起赎回的账号,因为这个是系统账号,所以有特权,可以使用用户权限进行交易。

接着get_self()参数就是指明了调用的合约就是当前自身合约。refund就是调用的合约action,而后面这个from则是调用refund传递的参数。

如果你希望交易立即执行,下面这句可以忽略out.delay_sec = refund_delay_sec;这一句是设置了延时调用的时间,也就是这个交易不会立即执行,而是要等待refund_delay_sec这么多秒后才会执行,这个时间我们可以在eosio.system.hpp文件中找到static constexpr uint32_t refund_delay_sec = 3 * seconds_per_day;,也就是3天的时间。

eosio::cancel_deferred( from.value );这一句的作用是取消一个延时交易,延时交易在发起的时候需要设置一个id,以便可以取消。为什么需要取消呢?因为会导致重复执行。所以我们会用同一id来标识交易,比如from的值,这就是为什么每个账号只能有一个赎回交易,你发起多笔赎回时,赎回时间是是以你最后一次操作的时间来算的。

out.send( from.value, from, true );发送时,第一个参数就是sender_id,可用于取消交易;第二个参数是payer,也就是这个延时交易所占用的RAM的支付者;最后一个参数是询问是否替换调已存在的交易。

授权

发起交易需要注意一个问题,就是权限的问题,调用任何的合约action我们都需要授权,即使是调用自身合约的action,也是需要授权的。

上面将构建交易的时候我们看到示例代码是用的是发起赎回的用户账户的权限,只因为这是系统合约,它有特权,所以可以直接使用用户权限,否则,如果普通合约想要这样调用,就需要用户授权给该合约,也就是用户需要在自己的active权限中增加合约授权,或者增加一个子权限,授权给合约,然后链接合约特定方法。如图:

图中active有一个account,使用的是eosio.code的权限,这是一个内置的特殊权限,用于合约调用的,所以这里赋予了这个合约账号可以使用该用户active权限的能力。另外active下还有一个新的admin权限,将合约的setstatus方法链接到了这个权限。这两种方法都是用于授权调用合约的。

但是不同的是,active中的授权给合约账号使用该用户的active的权限,而用户的admin权限只有调用合约setstatus的权限,是两个维度的操作,作用完全不同,具体关于权限我们会在后面的文章中进行讲解。

即使是合约调用合约自身的action,也需要给自己授权,如果合约中需要使用到active的权限,就要将eosio.code的权限加到合约的active中。

可支付Action

我们有时会有这样的需求,就是当用户转账到合约的时候我们可以触发一个合约方法,或者希望用户调用合约方法的时候同时要支付一定的费用。

这就会用到合约中的交易通知的功能了,当用户转账到合约时,我们的合约可以收到一个通知,然后进行其他的操作,这个功能是基于交易的通知机制,我们知道,当合约调用时,合约会收到一个调用通知,而转账的时候,我们使用require_recipient(from);会让from账号也收到一个交易通知。所以即使Token的合约是别人的,我们利用这个机制,让用户转账到我们的合约账号,那Token合约会收到通知,用户和接收Token的合约也会接收到通知。

我们在合约中可以针对收到的通知进行处理,在新老CDT中,有不同的写法,早期,都是通过重写EOSIO_DISPATCH方法来实现。如下:

void route()
{
auto transfer_data = eosio::unpack_action_data<st_transfer>(); eosio_assert(transfer_data.quantity.is_valid(), "Invalid token transfer");
eosio_assert(transfer_data.quantity.amount > 0, "Quantity must be positive"); if (transfer_data.to != _self)
{
return;
} require_auth(transfer_data.from); //TODO something
} #undef EOSIO_DISPATCH #define EOSIO_DISPATCH(TYPE, MEMBERS) \
extern "C" \
{ \
void apply(uint64_t receiver, uint64_t code, uint64_t action) \
{ \
if (code == receiver) \
{ \
switch (action) \
{ \
EOSIO_DISPATCH_HELPER(TYPE, MEMBERS) \
} \
/* does not allow destructor of thiscontract to run: eosio_exit(0); */ \
} \
else if (action == "transfer"_n.value) \
{ \
eosio::execute_action(eosio::name(receiver), "transfer"_n, &fisho::lucky::route); \
} \
} \
} EOSIO_DISPATCH(fisho::lucky, (setstatus))

  

其中,我们判断了action是transfer的交易通知会去调用我们自身的route方法,在方法中我们还会进一步验证这笔转账是否是从合法的Token合约而来,以防伪造交易。

现在的CDT版本已经简化了这个功能,我们只需在方法上增加一个监听的标注[[eosio::on_notify("*::transfer")]],其中*代表的是合约,transfer是action。比如:

[[eosio::on_notify("*::transfer")]]
void transfer(name from, name to, asset quantity, string memo)
{
//TODO something
}

  

注意:一定要主要验证交易通知的来源,只验证Token符号而忽略了合约的话,可能导致经济损失,要确保收到的Token是来自于正确的合约,且符号位数是正确的。

总结

这次介绍的合约进阶都是和交易相关的,是非常常用的功能,且也有很多坑,一定要非常小心,以免被黑客攻击,造成经济损失啊。

 

EOS基础全家桶(十五)智能合约进阶2的更多相关文章

  1. EOS基础全家桶(七)合约表操作

    简介 本篇我们开始来为后续合约开发做准备了,先来说说EOS内置的系统合约的功能吧,本篇将侧重于合约表数据的查询,这将有利于我们理解EOS的功能,并可以进行必要的数据查询. EOS基础全家桶(七)合约表 ...

  2. EOS基础全家桶(五)钱包管理

    简介 本篇我们将会学习EOS自带的命令行钱包的使用方法,我们将会使用cleos来控制keosd服务对本地钱包进行管理. 虽然现在市面上已经有很多支持EOS的钱包了,有Web钱包,有app钱包,还有浏览 ...

  3. EOS基础全家桶(十四)智能合约进阶

    简介 通过上一期的学习,大家应该能写一些简单的功能了,但是在实际生产中的功能需求往往要复杂很多,今天我就继续和大家分享下智能合约中的一些高级用法和功能. 使用docker编译 如果你需要使用不同版本的 ...

  4. EOS基础全家桶(十二)智能合约IDE-VSCode

    简介 上一篇我们介绍了EOS的专用IDE工具EOS Studio,该工具的优势是简单,易上手,但是灵活性低,且对系统资源开销大,依赖多,容易出现功能异常.那么我们开发人员最容易使用的,可能还是深度定制 ...

  5. EOS基础全家桶(十)交易Action操作

    简介 区块链上的所有操作都是通过交易(Transaction)上链的,无论你是转账交易还是发起的智能合约的调用,而EOS和传统区块链不同的是EOS在一个交易里可以发起多个行为(Action),这使得E ...

  6. EOS基础全家桶(八)jungle测试网的使用

    简介 前面我们已经学习了一些EOS的基础知识了,但是在EOS主网上的很多操作(比如:抵押.赎回.买卖内存)都是需要EOS链被正式激活后才可使用,而激活EOS链还需要很多的准备操作,我打算在单独的一篇文 ...

  7. EOS基础全家桶(六)账号管理

    简介 本篇我们会学习最基本的账号相关的操作,包括了创建账号和查询,关于账号资源的操作因为必须先部署系统合约,所以我们会留到后面单独写一篇来讲解. 6-EOS基础全家桶(六)账号管理 简介 账号介绍 账 ...

  8. EOS基础全家桶(十三)智能合约基础

    简介 智能合约是现在区块链的一大特色,而不同的链使用的智能合约的虚拟机各不相同,编码语言也有很大差异.而今天我们开始学习EOS的智能合约,我也是从EOS初期一直开发合约至今,期间踩过无数坑,也在Sta ...

  9. EOS基础全家桶(十一)智能合约IDE-EOS_Studio

    简介 我们马上要进入智能合约的开发了,以太坊最初提供了智能合约的功能,并宣告区块链进入2.0时代,而EOS的智能合约更进一步,提供了更多的便利性和可能性.为了进一步了解智能合约,并进行开发,我们需要先 ...

随机推荐

  1. (四)Parameters,简单参数就用这个

    Parameters注解在测试方法上指定参数列表,然后在测试方法中声明对应的形参,形参与参数列表一一对应,但名字可以不同,如下所示: public class Test1 { @Parameters( ...

  2. Android学习笔记点击事件和触摸事件的区别

    当我们点击手机屏幕的时候Android系统不仅会触发单击事件,还会触发触摸事件.在Android中它会先触发触摸事件,如果这个触摸事件没有被消费掉再去触发单击事件 代码示例: MainActivty. ...

  3. S7-1200视频教程: S7-1200的功能与特点-跟我学 - 1/112

    S7-1200视频教程: S7-1200的功能与特点-跟我学 - 1/112 观看连接: http://www.elearning.siemens.com.cn/video/Course/201012 ...

  4. 多语言工作者の十日冲刺<10/10>

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 团队作业第五次--Alpha冲刺 这个作业的目标 团队进行Alpha冲刺--第十天(05.09) 作业正文 ...

  5. js Date format(日期格式化:yyyy-MM-dd HH:mm:ss.S)

    今天在做日期显示的时候,那个显示格式困扰了很久,各种组件都尝试了,总是不如意,最后自己网上找了一个,然后稍微修改一下,感觉这个Util挺常用的,这里mark一下 Date.prototype.form ...

  6. MQ消息队列(1)—— 概念和使用场景

    一.什么是消息队列  消息即是信息的载体.为了让消息发送者和消息接收者都能够明白消息所承载的信息(消息发送者需要知道如何构造消息:消息接收者需要知道如何解析消息),它们就需要按照一种统一的格式描述消息 ...

  7. phpstorm设置xdebug调试

    phpstorm设置xdebug调试# wamp开发环境安装完成以后,打开网页,输入 :localhost 检测xdebug是否开启 3.若xdebug已开启,请找到你wamp或者phpstudy的安 ...

  8. 动态追踪技术之SystemTap

    SystemTap SystemTap是一个深入检查Linux系统活动的工具,使用该工具编写一些简单的代码就可以轻松的提取应用或内核的运行数据,以诊断复杂的性能或者功能问题.有了它,开发者不再需要重编 ...

  9. python基础扩展(二)

    python基础扩展(二) 常用操作 1.startswith(以什么开始) endswith(y)什么结束 s='taiWanw39dd' print(s.startswith('t')) #意思是 ...

  10. python自学之基础知识

    python学习笔记 列表的添加拓展 列表的批量添加 用for循环可以批量添加 list=['李雷雷','韩梅梅',180,110] for i in rang(3): list.append(i) ...