公信宝 公信宝智能合约编写讲义

pendingauth · 2019年03月05日 · 92 次阅读

本文转载自简书,作者剑有偏锋,原文链接:https://www.jianshu.com/p/445eb33513f0

大家好,我是币须达摩团队的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.

《2 callContract 调用合约
callContract(contract_name, method_name, params, amount_asset, broadcast) ⇒ Promise.

前端我们写好了,安装好了,我们就跑一下
npm run start运行
看跑起来了, 点击完成,然后刷新区块浏览器
删除个试试, 点击完成,然后刷新区块浏览器
完成个任务试试, 点击完成,然后刷新区块浏览器

最后

ok,课程讲完了,更详细的学习资源,大家可以看一下链接,谢谢大家
最后,可用加入公信链开发者群, 夜神舞可以给入群指引

共收到 0 条回复
1楼 已删除
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册