大家好,我是币须达摩团队的smith,今天给大家展示公信链上的智能合约如何编写,在这节课中已做一个任务列表为案例(todolist)
一 智能合约
首先介绍一下智能合约,智能合约是尼克萨博提出的,那么长一段的定义。对开发者的定义是,安全环境下的能持续运行的持续性脚本。
它有三个属性
1 合约被编写为区块链中的代码,在区块链中,但合约账本是公开账本。
2 当到达触发事件,合同根据条件自行执行。
3 监管人员可以使用区块链来了解市场中的活动,同时保持各个参与者的隐私
自动售货机就是智能合约的鼻祖,拥有硬币的人都可用购买物品,相对安全的执行环境。
二 需求
好了合约的概念讲完了。我们就通过公信链的智能合约,做个todolist,需求就是新增,删除,完成任务条目。
三 环境准备
@@@@发送 我把相关链接发到下面
1 下载智能合约IDE
https://github.com/gxchain/gxchain-alpha/releases/latest
2 注册测试账户
https://testnet.wallet.gxchain.org/#/
3 认领测试网代币
https://testnet.gxchain.org/gxc/get_token?init0
4 测试网区块浏览器
https://testnet.explorer.gxchain.org/#/account/damo-test
5 我们使用ide是用测试环境,看这篇文章 《智能合约IDE的用法》
https://docs.gxchain.org/zh/contract/deploy.html#%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6ide%E6%96%B9%E5%BC%8F
四 智能合约基本结构-解析helloworld
大家下载的功夫,我们看看智能合约的基本结构
1
1 #include <graphenelib/contract.hpp> =》智能合约的父类
2 #include <graphenelib/dispatcher.hpp> =》事务分发相关,GRAPHENE_ABI就在这定义
@@@展示下GRAPHENE_ABI位置
3 #include <graphenelib/print.hpp> =》打印日志相关
4 #include <graphenelib/types.h> =》合约用到的基本数据类型
5 #include <graphenelib/multi_index.hpp> =》多索引容器,
@@@@展示下迭代器和增删改的接口位置
using namespace graphene;
6 class todolist : public contract=>然后接下来是定义合约类,需要继承合约contract,这就是contract的定义.仅定义了_self
@@@@@展示下contract.hpp的定义
7 ///@abi action /// 给action加注释,方便导出abi, ABI(Application Binary Interface): 应用程序二进制接口 .描述了合约的接口,数据类型等
void hi(std::string user)然后是action名称要符合规范小于等于12位,a~z 1~5构成,即这里的hi函数名
8 GRAPHENE_ABI(helloworld, (hi)) // 实现apply函数接口,公信链把事务分发给对应合约处理
五 好了,大家对一个基本合约的构造已经了解了,
然后看多索引表的基本结构
//@abi table todo i64 // 给加入abi注释,方便导出abi
struct todo{
uint64_t id; //定义主键成员
std::string description; //我们写todolist的内容
uint64_t completed; //
uint64_t primary_key() const {return id;} //定义一级索引函数,这个函数的函数名和类型都是固定的,不能改动(大家可以查查公信链给的样例,每个合约都有这个函数定义),用于指定唯一主键,这里把id作为主键
GRAPHENE_SERIALIZE(todo, (id)(description)(completed)) //序列化和反序列号table成员。
};
typedef multi_index<N(todo), todo> todo_index;//定义多索引容器类型,一个是table名称,一个是多索引表的类型
todo_index todos; //定义成员多索引容器对象,需要在构造函数中用todos(_self, _self)初始化=》code,scope=>code默认指合约用户id,scope即范围,可以和code一样,或调用者的用户id
@@@@我们这就新建一个合约,以hello为模板,工程名叫todoadv吧
<1 删去他的hi函数
<2 删去他的GRAPHENE_ABI宏的action定义
<3 引用multi_index.hpp 头文件
#include <graphenelib/multi_index.hpp>
#include <graphenelib/system.h> //断言相关
#include <graphenelib/global.h>//获取区块链信息相关
总的
#include <graphenelib/contract.hpp>
#include <graphenelib/dispatcher.hpp>
#include <graphenelib/print.hpp>
#include <graphenelib/types.h>
#include <graphenelib/multi_index.hpp>
#include <graphenelib/system.h>
#include <graphenelib/global.h>
<4 替换helloworld为todolist
把我们的table定义拷进去,在构造函数下面初始化todos(_self, _self)
private:
//@abi table todo i64
struct todo{
uint64_t id;
std::string description;
uint64_t completed;
uint64_t primary_key() const {return id;}
GRAPHENE_SERIALIZE(todo, (id)(description)(completed))
};
typedef multi_index<N(todo), todo> todo_index;
todo_index todos;
在构造函数下面初始化todos(_self, _self)
六 多索引表增删改的接口原型,都在multi_index.hpp
1 增加操作
这里还有三个知识点
《1 执行emplace要指定资源付费人,payer是合约资源付费人,
写self就是合约用户本身付费,
交易发起交易人付费,可以取 get_trx_sender();
《2 后面是lamda表达式,这个即引用传值[&]和引用调用auto&todo
《3 available_primary_key,即multi_index提供的,获取当前表的下一个可用主键值
@@@@@@然后编写在ide编写增加操作接口,复制进去,编译下没问题
/// @abi action
void create(const std::string& description){
uint64_t sender = get_trx_sender();
todos.emplace(sender, [&](auto& todo){
todo.id = todos.available_primary_key();
todo.description = description;
todo.completed = 0;
});
}
2 查询删除操作
删除操作比较简洁,erase即可。需要先判断索引是否存在
find函数,即返回满足查询条件的索引
@@@@@@然后再ide编写查询删除接口,复制进去,编译下没问题
/// @abi action
void destory(const uint32_t id){
auto it = todos.find(id);
graphene_assert(it != todos.end(), "todo does not exist!");
todos.erase(it);
print("todo#", id, " destory");
}
3 查询修改操作
修改操作的化,即modify传要修改的索引,付费用户id,和lambda表达式。
给变量赋值即可,也是需要先查询索引是否存在
@@@@@@然后再ide编写查询修改接口,复制进去,编译下没问题
/// @abi action
void complete(const uint32_t id){
auto it = todos.find(id);
graphene_assert(it != todos.end(), "todo does not exist!");
if ( 0 == it->completed) {
uint64_t sender = get_trx_sender();
todos.modify(it,sender, [&](auto& todo){
todo.completed = 1;
});
print("todo#", id, " complete");
}
}
查询有四个函数,大家根据情况调用即可。begin和end大家使用容器也接触比较多
有find
lower_bound
upper_bound
get
合约编写完了,我就就编译部署上区块链吧
@@@
编译部署好了,我们看看区块链浏览器的数据
现在是部署合约,如果合约有更新,就重新编译下,按一下这里的刷新按钮。
比如我们新增一个t action
/// @abi action
void t(){
print("hello#", id, " complete");
}
再这个api宏后面加action名称
GRAPHENE_ABI(todolist, (create)(complete)(t))
好了,合约部分讲完,我们讲下合约和前端交互
七 前端和智能合约交互
需要下载前端的代码,这里用到react和gscatter
@@@@@@发送链接
https://github.com/dharmachain/gxc-todolist
gscatter-js使用的demo可以参考这里。gscatter-js主要负责账户相关功能(保存私钥和授权等)
https://github.com/gxchain/gscatter-js/tree/master/mock-sites
api的使用,,文档在这里(我们用到了两个api,分别是获取table的数据getTableObjects,以及调用合约的参数callContract)
https://gxchain.github.io/gxclient-node/api/
原型就是如下所示
主要用到了 https://gxchain.github.io/gxclient-node/api/#contract-api
《1 getTableObjects 获取合约table数据
getTableObjects(contract_name, table_name, start, limit) ⇒ Promise.<any>
《2 callContract 调用合约
callContract(contract_name, method_name, params, amount_asset, broadcast) ⇒ Promise.<any>
前端我们写好了,安装好了,我们就跑一下
npm run start运行
看跑起来了, 点击完成,然后刷新区块浏览器
删除个试试, 点击完成,然后刷新区块浏览器
完成个任务试试, 点击完成,然后刷新区块浏览器
最后
ok,课程讲完了,更详细的学习资源,大家可用看一下链接,谢谢大家
最后,可用加入公信链开发者群, 夜神舞可以给入群指引
https://docs.gxchain.org/zh/contract/ 官方文档
https://mp.weixin.qq.com/s/HZ-_pJ5gvjGjPpQtBEI98A 官方视频教程《简介与运行原理》
https://mp.weixin.qq.com/s/xtmxOPI9YAUrJkgsBcPR5A 官方视频教程 《内置数据类型与持久化存储》
https://mp.weixin.qq.com/s/2WE2D0orGQWi3XxQVTtobQ 官方视频教程《火箭入门GXChain开发系列第三篇 | 智能合约内置API介绍、演示》
https://mp.weixin.qq.com/s/3diKqSiCAuYsy7kpX6nfqw 官方视频教程《火箭入门GXChain开发系列第四篇 | 实战:口令红包合约讲解》
https://ethfans.org/topics/125 Vitalik 智能合约演讲PPT
网友评论