EOS 实现延时事务(动作)

yufei       5 年, 3 月 前       2696

总所周知的原因,在区块链里实现真随机数是不可能的。只能实现非常假的为随机数。

另一方面,区块链合约执行的时间,并不是实时的,而是创建区块的时间。

比如 EOS 中的 now()current_time() 两个时间都是创块的时间,在同一个块内,所有事务的时间都是一样的。

为了防止随机数被猜中,最好的实现方案,就是产生随机数的动作延时执行,比如 1s 或者 0.5s

那么,本章,我们就来讨论如是实现一个事务或者动作的延时,准确的说,是实现一个动作的延时。

EOS 创建延时动作

什么是延时动作

我们都知道,EOS 中最基本的组成单位是 动作 action ,一个事务,由 0 个或者多个动作组成一个事务/交易,然后矿工会将多个事务打包成一个块。

EOS 的出块时间大概是 0.5s/个。

有了这个前提,我们可以清晰的看到,所谓的延时动作,就是创建一个 动作 ,然后把这个动作放在一个事务/交易里,设定交易的执行时间延时,最后签名并发送这个交易即可。

相关库和函数

EOSIO 的开发者工具 eosio.cdt 提供了两个和事务/交易相关的文件 <eosiolib/transaction.hpp><eosiolib/transaction.h>

其中 <eosiolib/transaction.h> 文件主要包含了读取和解析事务/交易的函数,而 <eosiolib/transaction.hpp> 则包含了如何创建事务以及发送事务的函数。

我们今天的重点是创建和发送延时事务,因此,我们的研究对象就是 <eosiolib/transaction.hpp>

<eosiolib/transaction.hpp> 包含了一个事务的头部 transaction_header 和一个事务类 transaction ,且事务类 transaction 继承自事务头部 transaction_header

   class transaction_header {
   public:
      transaction_header( time_point_sec exp = time_point_sec(now() + 60) )
         :expiration(exp)
      {}

      time_point_sec  expiration;
      uint16_t        ref_block_num;
      uint32_t        ref_block_prefix;
      unsigned_int    max_net_usage_words = 0UL; /// number of 8 byte words this transaction can serialize into after compressions
      uint8_t         max_cpu_usage_ms = 0UL; /// number of CPU usage units to bill transaction for
      unsigned_int    delay_sec = 0UL; /// number of seconds to delay transaction, default: 0

      EOSLIB_SERIALIZE( transaction_header, (expiration)(ref_block_num)(ref_block_prefix)(max_net_usage_words)(max_cpu_usage_ms)(delay_sec) )
   };

   class transaction : public transaction_header {
   public:
      transaction(time_point_sec exp = time_point_sec(now() + 60)) : transaction_header( exp ) {}

      void send(const uint128_t& sender_id, name payer, bool replace_existing = false) const {
         auto serialize = pack(*this);
         send_deferred(sender_id, payer.value, serialize.data(), serialize.size(), replace_existing);
      }

      std::vector<action>  context_free_actions;
      std::vector<action>  actions;
      extensions_type      transaction_extensions;

      EOSLIB_SERIALIZE_DERIVED( transaction, transaction_header, (context_free_actions)(actions)(transaction_extensions) )
   };

从上面的源代码中可以看出

  1. 事务头部有一个参数叫做 delay_sec ,用于表示事务的延时执行时间,默认值为 0L,也就是不延时
  2. 事务类中 transaction 有且只有一个方法 send() ,用于发送该事务。

那么,要实现一个延时事务就很简单了。简单几句代码就解决了

  1. 引入事务头文件

    #include<eosiolib/transaction.hpp>
    
  2. 初始化一个事务

    eosio::transaction t{};
    
  3. 往事务里压入一个动作

    t.actions.emplace_back(
        permission_level(_self,"active"_n),
        _self,
        "lottery"_n,
        std::make_tuple(_self,id)
    );
    
  4. 设置动作的延时,单位为秒

    t.delay_sec = 2; // 延时 2 s
    
  5. 发送事务

    t.send(sender_id,_self);
    

    这里需要注意的是 sender_id 的值,如果发送了相同的 sender_id 值,那么在第三个参数 replace_existing 设定为 true 的时候,那么后面的相同 sender_id 动作会替换原先的动作。 切记切记

范例

下面的范例,创建了一个 hello 的合约,并在合约动作 hi 中创建一个延时事务,延时 2s 调用 world 动作

#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
#include <eosiolib/transaction.hpp>
#include <eosiolib/action.hpp>
#include <eosiolib/system.h>

using namespace eosio;

class hello : public contract {
  public:
      using contract::contract;

      [[eosio::action]]
      void hi() {
        print("Hello ");

        // 创建延时动作
        eosio::transaction t{};
        t.actions.emplace_back(
            permission_level(_self,"active"_n),
            _self,
            "world"_n,
            std::make_tuple(_self,std::string("yufei"))
        );
        t.delay_sec = 2; // 延时 2 s
        t.send(current_time(),_self);
      }

      [[eosio::action]] void world(name from, std::string memo)
      {
        print(" World! ");
        print("Hello ");
        print(std::string(memo));
      }
};
EOSIO_DISPATCH( hello, (hi)(world))
目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

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

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