EOS基础全家桶(十四)智能合约进阶
简介
通过上一期的学习,大家应该能写一些简单的功能了,但是在实际生产中的功能需求往往要复杂很多,今天我就继续和大家分享下智能合约中的一些高级用法和功能。
使用docker编译
如果你需要使用不同版本的CDT来编译不同的合约,那么这个方法是目前最有效的,使用虚拟机也是类似的原理,但是却没有docker更便利。如果你要通过安装卸载在不同的CDT版本间切换,你这是最低效且难以维护的方式了。
之前我就介绍过了使用使用EOS Studio上传的docker镜像,我们打开docker官方的Hub页面,然后搜索eosio.cdt,找到eostudio下的。这里可以参考文章EOS基础全家桶(十二)智能合约IDE-VSCode 中的 使用eosio.cdt的docker镜像 部分。
我们拉取需要的版本镜像,比如我们现在获取v1.4.1、v1.5.0和v1.6.3这三个版本的镜像。
docker pull eostudio/eosio.cdt:v1.4.1
docker pull eostudio/eosio.cdt:v1.5.0
docker pull eostudio/eosio.cdt:v1.6.3
我们启动时可以通过挂载相应的项目目录到容器上,以便在容器内访问到合约源码,然后使用容器内的CDT程序编译合约。
比如,我的hello合约在~/Projects/hello目录下,那么我可以将~/Projects/hello/挂载到容器的/root/Projects/。当然,你也可以直接挂在项目目录。
docker run --rm -v ~/Projects/hello:/root/Projects/ -w /root/Projects/ eostudio/eosio.cdt:v1.6.3 sh -c "eosio-cpp -abigen hello.cpp -o hello.wasm"
表索引
因为合约表是multiindex类型的,支持多索引,我们在进行表查询的时候,除了可以使用主键,还可以指定其他的索引进行查询。
索引的定义
我们先来把前面的hello合约的my_friend表改改。增加一个第二索引,通过访问时间排序和查询。
private:
struct [[eosio::table]] my_friend
{
name friend_name;
uint64_t visit_time; uint64_t primary_key() const { return friend_name.value; }
double by_secondary() const { return -visit_time; }
}; typedef eosio::multi_index<"friends"_n, my_friend, indexed_by<"time"_n, const_mem_fun<my_friend, double, &my_friend::by_secondary>>> friends;
- 索引类型支持
uint64_t,uint128_t,double,float128,ripemd160,sha256。但是常用的就uint64_t和double,如果需要使用sha256的类型,可以使用fixed_bytes<32>。 
我们增加了一个第二索引的方法,返回的是double。double by_secondary() const { return -visit_time; }
- 索引默认使用正序排序,在合约中查询表时要注意。如需倒序,可以利用负数,即返回double类型,在数值前取负数,则默认排序相当于变成了倒序。
 
这里我们希望使用访问时间倒序,所以在by_secondary方法中,我们返回了-visit_time,这样索引取到的就是访问时间的倒序了。
- 使用multi_index定义表类型的时候,增加indexed_by来定义索引。
 
我们是这样定义索引的indexed_by<"time"_n, const_mem_fun<my_friend, double, &my_friend::by_secondary>>,time是定义了索引的名称,查询时需要使用;指定my_friend类型的by_secondary方法为索引获取的方法,类型是double。
索引的使用
我们在hello合约中增加一个action,我们通过time索引查询最近一个访问的朋友是谁,然后再次拜访他。
[[eosio::action]] void meetagain()
{
uint32_t now = current_time_point().sec_since_epoch(); auto time_idx = friend_table.get_index<"time"_n>();
auto last_meet_itr = time_idx.begin();
check(last_meet_itr != time_idx.end(), "I don't have a friend."); time_idx.modify(last_meet_itr, get_self(), [&](auto &f) {
f.visit_time = now;
});
}
这个方法中核心有三个,首先是通过表的get_index<>方法来获取索引的对象。这里泛型类型指定的就是name类型的索引名friend_table.get_index<"time"_n>()。
然后是使用该索引对象来获取所以数据,可以使用find方法来查询,也可直接获取begin和end位置的数据。因为我们是要获取最近访问的一条数据,该索引又是时间倒序的,所以我们取begin就是了。
最后要注意,对表进行增删改的时候,使用的也是索引对象的emplace、modify和erase方法,而非表对象的。
权限验证
这里要说说权限验证了,这个可以说是非常重要的了。我们写的合约,不一定是任何人都可以调用的,比如你转账,肯定只有用户自己才能操作自己的资产吧,所以在合约中验证调用者的权限就很重要了。
但是,权限验证是很大的一块功能,细说的话,我留到后面讲解权限管理的时候再说,今天只说说最简单的验证。
在合约中使用内置方法require_auth(user),这是一个强制验证方法,如果不通过,就会直接报错返回。参数user是name类型的账号用户名。假设我们要验证是否是合约本身账号调用的,只需require_auth(get_self())即可。
还有一种方式是验证方法,返回true或者false的,has_auth(user),使用方式是一样的,只是返回了验证结果,而不会直接报错。
内联Action
我们通常调用程序内的方法都是直接引用,然后调用即可,但是在区块链上,有时我们需要把这种调用关系向外暴露,那就需要使用到内联交易了,这相当于在Action中又发起了对另一个Action的公开调用,在链上会有这两次调用的记录,并在同一个Transaction的记录中,这就是内联调用。
在链上我们看到过A将EOS转给了B,B在收到EOS后立即转给了C,而这部分EOS从A到B到C的操作都是一个trxid,这就是一个内联Action调用的典型应用。
我们以官方的eosio.token合约的方法为例。我们看官方合约可以看到,一般都定义了头文件,而Action和Table的定义都在头文件中,头文件里还会有使用action_wrapper定义的各个Action的别名,比如:
using create_action = eosio::action_wrapper<"create"_n, &token::create>;
using issue_action = eosio::action_wrapper<"issue"_n, &token::issue>;
using retire_action = eosio::action_wrapper<"retire"_n, &token::retire>;
using transfer_action = eosio::action_wrapper<"transfer"_n, &token::transfer>;
using open_action = eosio::action_wrapper<"open"_n, &token::open>;
using close_action = eosio::action_wrapper<"close"_n, &token::close>;
这样写的好处就是为了方便合约内进行action调用。
同合约内,只需实例化一个对应action_wrapper类型的action,然后调用send方法将action参数传入即可。
跨合约的调用,也只需引入包含该action_wrapper定义的头文件即可。
以下是在eosio.system合约中进行内存购买时调用eosio.token的合约将手续费转到eosio.ramfee账号的操作。
token::transfer_action transfer_act{ token_account, { {payer, active_permission} } };
transfer_act.send( payer, ramfee_account, fee, "ram fee" );
token_account是token合约所在的账号,就是eosio.token。{ {payer, active_permission} }指明了该action调用需要使用的权限是payer账号的active权限。(这里能使用payer的权限是因为该合约账号是特权账号)
send传递的参数是action的参数。我们知道transfer方法的参数有from、to、quantity和memo。payer就是购买内存的用户,是from。ramfee_account是eosio.ramfee账号,是to。fee就是手续费,是quantity。ram fee就是memo。
总结
今天是我们分享的第一篇合约进阶,后续还会有很多分享,合约中还有很多功能和技巧,将更好的帮助你解决实际生产中的问题。
EOS基础全家桶(十四)智能合约进阶的更多相关文章
- EOS基础全家桶(七)合约表操作
		
简介 本篇我们开始来为后续合约开发做准备了,先来说说EOS内置的系统合约的功能吧,本篇将侧重于合约表数据的查询,这将有利于我们理解EOS的功能,并可以进行必要的数据查询. EOS基础全家桶(七)合约表 ...
 - EOS基础全家桶(五)钱包管理
		
简介 本篇我们将会学习EOS自带的命令行钱包的使用方法,我们将会使用cleos来控制keosd服务对本地钱包进行管理. 虽然现在市面上已经有很多支持EOS的钱包了,有Web钱包,有app钱包,还有浏览 ...
 - EOS基础全家桶(十二)智能合约IDE-VSCode
		
简介 上一篇我们介绍了EOS的专用IDE工具EOS Studio,该工具的优势是简单,易上手,但是灵活性低,且对系统资源开销大,依赖多,容易出现功能异常.那么我们开发人员最容易使用的,可能还是深度定制 ...
 - EOS基础全家桶(十)交易Action操作
		
简介 区块链上的所有操作都是通过交易(Transaction)上链的,无论你是转账交易还是发起的智能合约的调用,而EOS和传统区块链不同的是EOS在一个交易里可以发起多个行为(Action),这使得E ...
 - EOS基础全家桶(八)jungle测试网的使用
		
简介 前面我们已经学习了一些EOS的基础知识了,但是在EOS主网上的很多操作(比如:抵押.赎回.买卖内存)都是需要EOS链被正式激活后才可使用,而激活EOS链还需要很多的准备操作,我打算在单独的一篇文 ...
 - EOS基础全家桶(六)账号管理
		
简介 本篇我们会学习最基本的账号相关的操作,包括了创建账号和查询,关于账号资源的操作因为必须先部署系统合约,所以我们会留到后面单独写一篇来讲解. 6-EOS基础全家桶(六)账号管理 简介 账号介绍 账 ...
 - EOS基础全家桶(十五)智能合约进阶2
		
简介 今天我们继续补充智能合约的进阶使用技巧,这次的主题是交易,合约内我们除了可以发起内联action的调用,很多使用还需要直接调用其他的合约action或者以交易的形式调用自身的action. 发起 ...
 - EOS基础全家桶(十三)智能合约基础
		
简介 智能合约是现在区块链的一大特色,而不同的链使用的智能合约的虚拟机各不相同,编码语言也有很大差异.而今天我们开始学习EOS的智能合约,我也是从EOS初期一直开发合约至今,期间踩过无数坑,也在Sta ...
 - EOS基础全家桶(十一)智能合约IDE-EOS_Studio
		
简介 我们马上要进入智能合约的开发了,以太坊最初提供了智能合约的功能,并宣告区块链进入2.0时代,而EOS的智能合约更进一步,提供了更多的便利性和可能性.为了进一步了解智能合约,并进行开发,我们需要先 ...
 
随机推荐
- Java实现 LeetCode 7整数反转
			
7. 整数反转 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 1: 输入: 123 输出: 321 示例 2: 输入: -123 输出: -321 示例 3: 输入: ...
 - Java实现 蓝桥杯 算法提高 矩阵翻转
			
问题描述 Ciel有一个N*N的矩阵,每个格子里都有一个整数. N是一个奇数,设X = (N+1)/2.Ciel每次都可以做这样的一次操作:他从矩阵选出一个X*X的子矩阵,并将这个子矩阵中的所有整数都 ...
 - java实现第八届蓝桥杯树型显示
			
树型显示 题目描述 对于分类结构可以用树形来形象地表示.比如:文件系统就是典型的例子. 树中的结点具有父子关系.我们在显示的时候,把子项向右缩进(用空格,不是tab),并添加必要的连接线,以使其层次关 ...
 - Linux 用户和用户组管理-用户信息文件
			
用户信息文件存在在/etc/passwd中,vi /etc/passwd 其中,有七列以:分隔的信息 第一列表示用户(account),第二列表示密码标志(真正的密码存在在/etc/shadow中), ...
 - PAT 人口普查
			
某城镇进行人口普查,得到了全体居民的生日.现请你写个程序,找出镇上最年长和最年轻的人. 这里确保每个输入的日期都是合法的,但不一定是合理的,假设已知镇上没有超过 200 岁的老人,而今天是 2014 ...
 - 如何安装vue脚手架?
			
前提(已经安装好node,可以正常使用npm) 一.cmd输入 npm install vue-cli -g ---- 全局安装vue-cli工具 安装好过后,再输入指令 vue --version ...
 - 为什么我觉得 Java 的 IO 很复杂?
			
初学者觉得复杂是很正常的,归根结底是因为没有理解JavaIO框架的设计思想: 可以沿着这条路想一想: 1,学IO流之前,我们写的程序,都是在内存里自己跟自己玩.比如,你声明个变量,创建个数组,创建个集 ...
 - css3图片防止变形
			
1.object-fit 由于图片原始大小都不一样,强行设定大小值会导致拉伸,如果不设定大小则参差不齐. 之前我们大多数用的 大多数都是利用background-size: cover 来避免对图片造 ...
 - 自己动手实现深度学习框架-7 RNN层--GRU, LSTM
			
目标 这个阶段会给cute-dl添加循环层,使之能够支持RNN--循环神经网络. 具体目标包括: 添加激活函数sigmoid, tanh. 添加GRU(Gate Recurrent U ...
 - [computer graphics]简单光照模型(Phong和Blinn-Phong)和明暗处理
			
简单光照模型(Phong和Blinn-Phong)和明暗处理 支持点光源和平行光,是一种简单光照模型,它将光照分解成了三个部分,分别为 漫反射 镜面反射 环境光 如图所示,是一个简单的几何模型. \( ...