写在前面

从eos v1.3.0更新开始,eosio提倡使用eosio.cdt工具来编译智能合约,以提高智能合约的执行性能等。其中相较于旧的工具,eosio.cdt更新了不少语法。

eosio

定义的合约类,数据表,对外动作接口格式有所变化。
具体可以参考eosio.cdt/exmaples/abigen_test中的例子。

CONTRACT hello : public eosio::contract { ... };ACTION hi (name user) { ... }TABLE testtable {uint64_t owner;uint64_t third;uint64_t primary_key() const { return owner; }
};

capi_name取代了以前所有uint64_t的别名,如:

  • account_name
  • permission_name
  • scope_name
  • table_name
  • action_name

asset

实现完善了不少,使得代币相关的实现更加严谨。
默认初始化的asset内的symbol为空,值为零。在作asset比较和赋值的时候,一不小新就经常遇到符号错误。在智能合约编写过程中,应该指定所设计的代码的符号与精度。初始值的asset,可以写成这样:

#define XXX_TOKEN_SYMBOL 'SYS'
#define XXX_TOKEN_PRICISION 4
eosio::asset asswecan(0, eosio::symbol(XXX_TOKEN_SYMBOL, XXX_TOKEN_PRICISION));

打印asset时,因为目前仍唯有to_string之类的相关函数,只能使用asset::print来打印。

contract

新的contract构造函数定义变化挺大的。

// action的接受者
// action的所属域code
// action的参数字节流对象,即便有参数,一般情况下也为空.
contract(name receiver,name code,datastream<const char*> ds
): _self(receiver),_code(code),_ds(ds)
{}

datastream

数据流,在EOS中但凡涉及到trx或action的数据存取,都会用到datastream相关的接口。
从实现上来看,datastream也不会很复杂,从某种程度上来说,他就是重载了<<和>>操作符的handle类。
其中,重载了多个<<和>>操作符,主要为不同类型的transaction、action参数的序列化与反序列化提供支持。

template<typename DataStream, typename... Args>
DataStream& operator<<( DataStream& ds, const std::tuple<Args...>& t ) {boost::fusion::for_each( t, [&]( const auto& i ) {ds << i;});return ds;
}template<typename DataStream, typename... Args>
DataStream& operator>>( DataStream& ds, std::tuple<Args...>& t ) {boost::fusion::for_each( t, [&]( auto& i ) {ds >> i;});return ds;
}

为什么对持久化存储的multi_index和singleton的数据结构进行拓展时,一不小心就会出现read错误?这里给出了答案。
也正因如此,EOS系统合约中,有不少用于持久化存储的数据结构预备了多个reserved字段。

inline bool read( char* d, size_t s ) {eosio_assert( size_t(_end - _pos) >= (size_t)s, "read" );memcpy( d, _pos, s );_pos += s;return true;
}

由衷地叹服eosio技术大佬们的c++功力。
从代码实现上禁止用户序列化指针与隔离原语类型模板定义。

template<typename DataStream, typename T, std::enable_if_t<_datastream_detail::is_pointer<T>()>* = nullptr>
DataStream& operator >> ( DataStream& ds, T ) {static_assert(!_datastream_detail::is_pointer<T>(), "Pointers should not be serialized" );return ds;
}template<typename DataStream, typename T, std::size_t N,std::enable_if_t<!_datastream_detail::is_primitive<T>() &&!_datastream_detail::is_pointer<T>()>* = nullptr>
DataStream& operator << ( DataStream& ds, const T (&v)[N] ) {ds << unsigned_int( N );for( uint32_t i = 0; i < N; ++i )ds << v[i];return ds;
}

dispatcher

action分发相关的实现都在这里。
eosio.cdt不再使用宏EOSIO_ABI了,改为使用EOSIO_DISPATCH。

#define EOSIO_DISPATCH_INTERNAL( r, OP, elem ) \case eosio::name( BOOST_PP_STRINGIZE(elem) ).value: \eosio::execute_action( eosio::name(receiver), eosio::name(code), &OP::elem ); \break;// Helper macro for EOSIO_DISPATCH
#define EOSIO_DISPATCH_HELPER( TYPE,  MEMBERS ) \BOOST_PP_SEQ_FOR_EACH( EOSIO_DISPATCH_INTERNAL, TYPE, MEMBERS )#define EOSIO_DISPATCH( TYPE, MEMBERS ) \
extern "C" { \void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \// 只接受自己的action,其他人发来的receipt不会再接收if( code == receiver ) { \switch( action ) { \EOSIO_DISPATCH_HELPER( TYPE, MEMBERS ) \} \/* does not allow destructor of thiscontract to run: eosio_exit(0); */ \} \} \
} \

wabt虚拟机是如何分发action的呢?这里可以略窥其一二。

template<typename Contract, typename FirstAction>
bool dispatch( uint64_t code, uint64_t act ) {if( code == FirstAction::get_account() && FirstAction::get_name() == act ) {Contract().on( unpack_action_data<FirstAction>() );return true;}return false;
}template<typename Contract, typename FirstAction, typename SecondAction, typename... Actions>
bool dispatch( uint64_t code, uint64_t act ) {if( code == FirstAction::get_account() && FirstAction::get_name() == act ) {Contract().on( unpack_action_data<FirstAction>() );return true;}return eosio::dispatch<Contract,SecondAction,Actions...>( code, act );
}template<typename T, typename... Args>
bool execute_action( name self, name code, void (T::*func)(Args...)  ) {size_t size = action_data_size();//using malloc/free here potentially is not exception-safe, although WASM doesn't support exceptionsconstexpr size_t max_stack_buffer_size = 512;void* buffer = nullptr;if( size > 0 ) {buffer = max_stack_buffer_size < size ? malloc(size) : alloca(size);read_action_data( buffer, size );}std::tuple<std::decay_t<Args>...> args;datastream<const char*> ds((char*)buffer, size);ds >> args;T inst(self, code, ds);auto f2 = [&]( auto... a ){((&inst)->*func)( a... );};// 此处会调用f2(args...)boost::mp11::tuple_apply( f2, args );if ( max_stack_buffer_size < size ) {free(buffer);}return true;
}

multi_index

template<name::raw TableName, typename T, typename... Indices>
class multi_index;

因为在定义multi_index时候,TableName需要使用常量表达式,所以现在一般的写法是

// N("testtab") 变为 "testtab"_n
typedef eosio::multi_index< "testtab"_n,  abitest::testtable > testtable_t;

singleton

也是实际上也是一个multi_index,是一个不对外声明的multi_index。在想要用到map, set, tuple等数据结构存储持久化数据,而又不必要对外公开该数据记录,笔者认为采用singleton未尝不是一个好的选择。

template<name::raw SingletonName, typename T>
class singleton
{constexpr static uint64_t pk_value = static_cast<uint64_t>(SingletonName);struct row {T value;uint64_t primary_key() const { return pk_value; }EOSLIB_SERIALIZE( row, (value) )};typedef eosio::multi_index<SingletonName, row> table;public:singleton( name code, uint64_t scope ) : _t( code, scope ) {}// ... private:table _t;
}

transaction

接口相较于旧版本并无太大改动,只是其中的发送延迟交易的接口添加了一个字段。
sender_id 字面上意思是发送者的ID,但结合其他源码的分析,这实际上是需要指定一个唯一的交易ID,只是通常的做法是把这个交易ID指定为发送者的用户ID。
用户ID是uint64_t,sender_id为uint128_t,static_cast类型转换一下就可以了。

void send_deferred(const uint128_t& sender_id, capi_name payer, const char *serialized_transaction, size_t size, uint32_t replace_existing = 0);class transaction : public transaction_header {public:// ...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);}// ...
};

eosio.cdt心得相关推荐

  1. eosio.cdt:EOS智能合约工具集

    目前EOS已经迎来了1.5.x时代,很多内容都有了较大的改变.其中智能合约的工作流程发生了改变,EOSIO为智能合约提供了独立且功能完整的工具集eosio.cdt.该工具集基于WASM平台,可解耦于e ...

  2. EOSIO.DCT 1.3以上版本工具编写EOS智能合约重大更新

    1, 注意使用eosio.cdt 1.3及以上版本工具, c11语法 定义table, "bankerlist" 是表名, 生成abi文件使用 struct [[eosio::ta ...

  3. EOS系列 - EOSIO WEB IDE

    这是一个面向开发者的开发容器, 具有IDE和私链测试环境 介绍功能 在Gitpod.io和Docker的支持下, 这个工具在云服务中运行,使新开发人员能够建立智能合约和Web应用程序开发环境以及完全集 ...

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

    本文简单的介绍一下如何安装EOS智能合约开发工具包(Contract Development Toolkit),简称CDT,是与智能合约编制相关的工具集合.对于EOSIO初学者来说,可以通过使用CDT ...

  5. EOS系列 - EOSIO 2.0

    EOSIO 2.0 - 增强性能,提高安全性和新的开发人员工具 版本历史 block.one在2018年6月正式发布EOSIO 1.0版本 block.one在2019年6月1日发布会上正式宣布推出E ...

  6. EOSIO 指南(安装EOSIO)

    安装EOSIO 第1步:安装二进制文件 本教程将使用预构建二进制文件,为了让你尽快开始,这是最好的选择,从源代码构建是一个选项,但会让你延迟一个小时或更长时间,并且可能会遇到构建错误. 以下命令将下载 ...

  7. 从部署EOSIO到部署智能合约

    提示:如果你对EOSIO已经有了一定的了解,那么此文章能帮助你快速安装部署一个智能合约 文章目录 EOSIO简单介绍 一.部署EOSIO 1.安装 下载EOSIO源码 构建EOSIO二进制文件 安装E ...

  8. 多索引表 (4)multi_index.hpp源代码

    1. 在线hpp源代码 https://developers.eos.io/manuals/eosio.cdt/v1.7/group__multiindex 1.1 get_code和get_scop ...

  9. eos操作系统_EOS基础全家桶(十一)智能合约IDE-EOS_Studio

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

最新文章

  1. Windows下编译tensorflow-gpu教程
  2. 【洛谷】马的遍历--广度优先搜索(BFS)
  3. Uncaught TypeError: $(…).orgcharts is not a function
  4. 关于angularjs dom渲染结束再执行的问题
  5. mysql php遍历数据6_PHP 循环遍历数据里中的内容
  6. html5新增标签/删除标签
  7. BOOST库介绍(六)——容器
  8. 服务器3D场景建模(三):体素场景(一)
  9. html5 自动扣图,html5 canvas+js实现ps钢笔抠图
  10. 计算机考研复习资料推荐
  11. qt QTableWidgetItem设置文字样式
  12. 虚拟人在虚拟现实中应用的技术思考与研究
  13. 微信公众号文章留言功能开通方法
  14. vue+elementUI中使用Echarts (懒人无脑版)
  15. 十分钟教你用 svg 做出精美的动画!
  16. Sprite 从PS切图到具体实现完整过程
  17. ROS机器人技术课程大作业,机器人基础实例
  18. 高手背后的操作系统----心思知体系
  19. Docker - 配置DaoCloud的Docker加速器2
  20. Educational Codeforces Round #136 (Rated for Div. 2) A~C

热门文章

  1. 调用Xmlrpc接口
  2. loadrunner12 + ie11 无internet, 代码中文乱码
  3. Linux常用到的指令汇总
  4. python split()函数
  5. WPF 浏览文件夹,获取其路径
  6. 万源之源之drupal7
  7. selenium+unittest自动化测试(二)---浏览器控制及元素定位的方法
  8. 03-02 capabilities 设置
  9. android怎样判断插入数据是否成功_Android 端 V1/V2/V3 签名的原理
  10. the enigma x64 6.7_颜值不输宝马,零百加速6.7秒,国产最强轿跑SUV就是它了