资讯专栏INFORMATION COLUMN

智能合约之 eosio.cdt 我们需要知道的那些事

zhigoo / 1220人阅读

摘要:宏命令被移除,不能直接用去声明一个要用去实例化一个对象,也将一些针对的函数整合进了。重构了,修改成构造函数增加和参数。

eosio.cdt 在 1.2.x 和 1.3.x 的改动比较大, 虽然虚拟机是向后兼容的, 但是为了避免意外情况, 我们都会将陆续将合约代码升级。下面来介绍一下大致的改动。

# 安装 eosio.cdt, 因为 llvm 库比较大, 所以执行 clone 的时候比较慢
$ git clone https://github.com/EOSIO/eosio.cdt.git
$ git submodule update --init --recursive
$ ./build.sh
$ sudo ./install.sh
1.2.x 和 1.3.x 的区别 eoslib C API

uint64_t 别名 account_name, permission_name, scope_name, table_name, action_name 全部移除, 新增 typedef capi_name uint64_t
symbol_name 别名移除,用 symbol_code 代替
移除 time , weight_type typedefs
移除 transaction_id_type, block_id_type typedefs
checksum160 -> capi_checksum160, checksum256 -> capi_checksum256, checksum512 -> capi_checksum512, public_key -> capi_public_key, signature -> capi_signature
移除掉未实现的 api : require_write_lock 和 require_read_lock

eoslib C++ API

移除 bytes typedefs
移除文件 eosiolib/types.hpp
移除文件 eosiolib/optional.hpp, 用 std::optional 代替
移除 eosiolib/core_symbol.hpp 文件, 以后合约需要自行声明 core_symbol
增加文件 eosiolib/name.hpp

eoslib/types.hpp

将 typedef eosio::extensions_types 移到 eosiolib/transaction.hpp
移除掉对 checksum struct 的 == 和 != 的重载
移除掉 API eosio::char_to_symbol, eosio::string_to_name, eosio::name_suffix, 都整合进了 name struct
移除掉宏命令 N(X), 重载运算符 ""_n ,例如 "foo"_n 或者 name("foo") 来转换成 name struct 类型
将 eosio::name struct 的定义 和 ""_n 运算符 移至 eosiolib/name.hpp
ps: 读者可以使用 #define N(X) name(#X) 来减少代码的改动了哈。

eosiolib/name.hpp

移除name 显式 隐式转换成 uint64_t
添加 enum class eosio::name::raw : uint64_t 用于从 name struct 隐式转换成 raw
添加 bool 类型转换,会返回 name struct 转化成 uint64_t 是否为 0
构造函数都用 constexpr, 确保 name struct 实例化时,都会给 value 赋初值
添加新的 constexpr 方法 eosio::name::length, eosio::name::suffix
添加 name struct 的比较函数

eosiolib/symbol.hpp

移除 eosio::symbol_type strcut , 用 eosio::symbol class 代替
添加 eosio::symbol_code struct
移除掉 eosio::string_to_symbol, eosio::is_valid_symbol, eosio::symbol_name_length 方法,都整合进了 symbol_code struct
移除宏命令#define S(P,X) ::eosio::string_to_symbol(P,#X), 直接实例化 symbol class eg: symbol(symbol_code("SYS"), 4) or symbol("SYS", 4)
重构 eosio::extended_symbol struct

eosiolib/asset.hpp

构造器现需要显式传入 quantity 和 symbol, 不再有默认值

eosiolib/contract.hpp

Rename EOSIO_ABO to EOSIO_DISPATCH, 更加明确的表达该宏指令的作用
根据 contract 的改动重构 EOSIO_DISPATCH

eosiolib/multi_index.hpp

索引不能直接用 name struct 需要使用 eosio::name::raw
multi_index code 不再使用 uint64_t, 使用 eosio::name

eosiolib/singleton.hpp

同 multi_index, 用 eosio::name 替代 uint64_t

eosiolib/action.hpp

添加 inline function: eosio::require_auth, eosio::has_auth, eosio::is_account
重构 eosio::permission_level, 用 eosio::name 替换 uint64_t
移除 宏命令 ACTION,整合到了 eosio.hpp
新增 action_wrapper struct, 它的出现,让我们对inline action 的使用更加便利化,相当于把 inline action 封装成一个 struct,直接实例化便可以发送一个 inline action, 下面会写例子。

eosiolib/permission.hpp

修改 eosio::check_transaction_authorization 参数类型 std::set to std::set , 使得能和 eosio 的 public_key 兼容。
eosio::check_permission_authorization 参数 account, permission 类型从 uint64_t 修改成 eosio::name

eosiolib/ignore.hpp

新增 ignore struct, 会让ABI 生成对应的类型, 但datastream 不会去序列化它
新增 ignore_wrapper, 方便其他合约调用声明的 action。

下面我们挑些改动比较大的地方来说下。 1.移除 uint64_t 的多数别名,只留下了一个 capi_name。

其中最大的地方当属 去掉了 uint64_t 的别名,需要用 name struct 来代替, 不应该用新的别名 capi_name。 不说了,笔者改代码改到想哭了。但为什么要做这个改动呢, 目前对于 account_name 等所使用的都是 implicit, 这意味着可能有一些 bad implicit。
Eg:

//@abi action
void hi(){
  name acc = name{10};
  print(acc==10);
}

我本意是要判断 两个 name struct 是否相等, 但是隐式转换使我直接比较整数 10 也能返回 true。
所以重构了 name struct,消除了这种风险。
这次的改动也变得比较偏面向对象思维, 像 eosio::char_to_symbol, eosio::string_to_name, eosio::name_suffix 都被整合进了 name struct 里面。
symbol 和 symbol_code 也被重构了。宏命令 S 被移除,不能直接用 S(4, SYS) 去声明一个 token symbol, 要用 symbol(symbol_code("SYS"), 4) or symbol("SYS", 4)去实例化一个symbol 对象, 也将一些针对 symbol 的函数整合进了 class。

2.重构了contract.hpp , EOSIO_ABI 修改成 EOSIO_DISPATCH
contract( name receiver, name code, datastream ds ):_self(receiver),_code(code),_ds(ds) {}

构造函数增加 code 和 ds 参数。增加 ds 参数是为了方便我们手动解析数据。 这跟后面要说到的 ignore struct 有比较大的关系。
这种改动也意味着我们重写 apply 的方式要改动.
Eg:

extern "C" {
  void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
    auto self = receiver;
    // 拦截 失败的 deferred_trx
    if( code == "eosio"_n.value && action == "onerror"_n.value ) {
      const auto act_data = unpack_action_data();
      auto sender = uint64_t( act_data.sender_id >> 64);
      if( sender == self){
        test bos(eosio::name(receiver), eosio::name(code),datastream(nullptr, 0));
        bos.resend( act_data.unpack_sent_trx(), uint64_t( act_data.sender_id) );
      }
    // 拦截 eosio.token 的 EOS 转账操作
    } else if ( code == "eosio.token"_n.value ){
      test bos(eosio::name(receiver), eosio::name(code),datastream(nullptr, 0));
      const auto t = unpack_action_data();
      if(t.from.value != self && t.to.value == self){
        bos._transfer(t.from, t.to, t.quantity, t.memo);   
      }
    }else if ( code == self || action == "onerror"_n.value ) {
      switch( action ) {
        EOSIO_DISPATCH_HELPER( test, (hi))
      }
    }
  }
}
3. ignore struct , ignore_wrapper 和 action_wrapper 的使用

在 action 的参数加上 ignore struct, 会告诉虚拟机,不要解析此数据, 让自己手动解析。
使用 action_wrapper 把 hello:hi action 包装起来。
使用inline action 时,用 ignore_wrapper 表明该参数是一个 ignore 类型。
Eg:

#include 
#include
using namespace eosio;

CONTRACT hello : public eosio::contract {
 public:
   using contract::contract;

   ACTION hi( name user, ignore, ignore) {
     print_f( "Hello % from hello", user );

     // 读取 ignore 数据。
     uint64_t test;
     _ds >> test;
     printui(test);
     std::string str;
     _ds >> str;
     prints_l(str.c_str(),str.size());
   }

   // 用 action_wrapper , 把 hello::hi action 包装起来
   using hi_action = action_wrapper<"hi"_n, &hello::hi>;

   ACTION inlineaction( name user, name inlinecode ){
     print_f( "Hello % from send_inline", user );
     // constructor takes two arguments (the code the contract is deployed on and the set of permissions)
     // 实例化 hi_action, 并进行调用。
     // inlinecode 参数及对应的 hi action 的合约账号。
     hello::hi_action hi(inlinecode, {_self, "active"_n});
     hi.send(user,ignore_wrapper(22),ignore_wrapper("asdfa"));
   }

};

EOSIO_DISPATCH( hello, (hi)(inlineaction) )

结论:两个版本的主要改动是消除隐式转换的风险。 也重构了一些模块的代码, 变得更加直观。 新增了 action_wrapper struct, 使得 inline action 的使用更加方便快捷。

转载请注明来源:https://eos.live/detail/17811

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/24449.html

相关文章

  • 如何安装EOS智能合约开发工具包CDT

    摘要:本文简单的介绍一下如何安装智能合约开发工具包,简称,是与智能合约编制相关的工具集合。对于初学者来说,可以通过使用来编译智能合约和生成。 本文简单的介绍一下如何安装EOS智能合约开发工具包(Contract Development Toolkit),简称CDT,是与智能合约编制相关的工具集合。对于EOSIO初学者来说,可以通过使用CDT来编译智能合约和生成ABI。 从1.3.x开始,CD...

    lx1036 评论0 收藏0
  • EOS DApp开发入门(二): 智能合约探究

    摘要:在看启动脚本输出的时候,发现了这两样输出设置和智能合约,以及安装合约开发工具。合约开发工具是的工具链和一组工具,用于促进平台的合同编写。系统智能合约,可以进行很多系统级别的操作,比如用户投票将用户注册成为生产者。 Previously 在EOS DApp开发入门(一)中,通过docker image的方式架起了本地的eos区块链,使Note chain DApp与本地区块链进行交互,成...

    Honwhy 评论0 收藏0
  • 使用docker compose在EOS本地Testnet上开发

    摘要:为本地配置的钱包。以太坊,主要是针对工程师使用进行区块链以太坊开发的详解。以太坊,主要是介绍使用进行智能合约开发交互,进行账号创建交易转账代币开发以及过滤器和交易等内容。这里是如何使用和在本地上开发 EOS区块链的开发并不是立竿见影的,因为需要一些非显而易见的组件,需要对它们进行配置和协同工作。 nodeos:块生成器守护程序。 keosd:钱包守护进程,存储私钥。 eosio-cp...

    cod7ce 评论0 收藏0
  • EOS开发中数据持久性问题(上)

    摘要:需要对构造函数进行命名和配置,以使用我们之前定义的结构。我们的构造函数传递的范围参数是正在部署合约的区块链上的帐户。此方法接受两个参数,即此记录的范围和回调函数。回调函数用于处理表的修改。但是如果用户想要完全删除记录呢请看的数据持久性下。 本教程假定你已经完成了EOS开发从智能合约开始。 要了解数据持久性,请编写一个简单的智能合约,作为地址记录。虽然这个用例由于各种原因而不太适合作为生...

    cikenerd 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<