带你学习区块链我爱编程

EOS Dawn 3.0 智能合约 -- blog_view

2018-04-16  本文已影响256人  redbutterfly

1、前提概要

关于如何编译EOS源码、编译docker镜像,搭建节点等等,官方都有相关文档,新的特性也在之前文章中大体介绍了EOS Dawn 3.0整理

2、本次合约介绍

之前在eos dawn 2.0版本,有个示例合约 simpledb,公司需求的智能合约就是实现了类似合约,所以,之前我实现的版本就是根据这个修改的。而且,该合约主要使用的是2.0版中的 db.h头文件中的函数 store_str,以字符串为索引,保存结构体的功能;
但是,在3.0版本中,该功能暂时删除,只能暂时使用其他功能代替,目前,决定使用multi_index容器;

3、multi_index

eos dawn 3.0中的multi_index,使用方法和boost中的multi_index,非常类似,就是多重索引容器,假如清楚其使用方法的话,应该对这个比较熟悉;

声明
typedef eosio::multi_index< tablename,  typename> table( code, scope);

其中,需要的几个参数,如其命名含义:

多级索引

声明时候,可以使用以下方式声明二级索引,或多级索引:

typedef eosio::multi_index< tablename,  typename,
  index_by< scope, const_mem_fun<typename, index_type,   typename::method> >
> table( code, scope);

但是,目前二级索引,只支持uint64_t、uint128_t、 key256(eosio的内建类型) 具体如使用,会在后续程序里面讲解;

方法

声明完成后,可以使用以下功能:

4、更符合面向对象的新合约

Dawn3.0的新合约编写方式见:新格式
该合约要实现以下功能:

  1. 每个用户能够上传自己的blog文章;
  2. 其他用户能够审核该用户的文章是否正确;
  3. 作者可以查询文章状态和数量;

结构定义如下,即abi文件:

{
  "____comment": "This file was generated by eosio-abigen. DO NOT EDIT - 2018-04-16T07:24:15",
  "types": [],
  "structs": [{
      "name": "account",
      "base": "",
      "fields": [{
          "name": "owner",
          "type": "account_name"
        },{
          "name": "blognum",
          "type": "uint32"
        }
      ]
    },{
      "name": "blog",
      "base": "",
      "fields": [{
          "name": "ID",
          "type": "uint64"
        },{
          "name": "status",
          "type": "uint8"
        },{
          "name": "approve_status",
          "type": "string"
        },{
          "name": "producer",
          "type": "account_name"
        },{
          "name": "reviewer",
          "type": "account_name"
        },{
          "name": "content",
          "type": "string"
        }
      ]
    },{
      "name": "upload",
      "base": "",
      "fields": [{
          "name": "producer",
          "type": "account_name"
        },{
          "name": "content",
          "type": "string"
        }
      ]
    },{
      "name": "reviewing",
      "base": "",
      "fields": [{
          "name": "ID",
          "type": "uint64"
        },{
          "name": "reviewer",
          "type": "account_name"
        }
      ]
    },{
      "name": "approved",
      "base": "",
      "fields": [{
          "name": "ID",
          "type": "uint64"
        }
      ]
    },{
      "name": "disapprove",
      "base": "",
      "fields": [{
          "name": "ID",
          "type": "uint64"
        },{
          "name": "reason",
          "type": "string"
        }
      ]
    },{
      "name": "remove",
      "base": "",
      "fields": [{
          "name": "ID",
          "type": "uint64"
        }
      ]
    }
  ],
  "actions": [{
      "name": "upload",
      "type": "upload",
      "ricardian_contract": ""
    },{
      "name": "reviewing",
      "type": "reviewing",
      "ricardian_contract": ""
    },{
      "name": "approved",
      "type": "approved",
      "ricardian_contract": ""
    },{
      "name": "disapprove",
      "type": "disapprove",
      "ricardian_contract": ""
    },{
      "name": "remove",
      "type": "remove",
      "ricardian_contract": ""
    }
  ],
  "tables": [{
      "name": "account",
      "index_type": "i64",
      "key_names": [
        "owner"
      ],
      "key_types": [
        "account_name"
      ],
      "type": "account"
    },{
      "name": "blog",
      "index_type": "i64",
      "key_names": [
        "ID"
      ],
      "key_types": [
        "uint64"
      ],
      "type": "blog"
    }
  ],
  "clauses": []
}

总结如下:

5、代码

/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 *  @auther: redbutterfly
 *  @createtime: 2018-04-16
 */
#include <eosiolib/eosio.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/contract.hpp>

using eosio::indexed_by;
using eosio::const_mem_fun;
using std::string;


class blog_view : public eosio::contract {
    public:
        using contract::contract;
        blog_view(account_name self)
            :eosio::contract(self),
            accounts(_self, _self),
            idlists(_self, _self),
            init_status(std::string(64,'0'))
            {}

        /// @abi action
        void upload(const account_name producer, const std::string content) {
            require_auth(producer);
            
            blog_index upload_blogs(_self, producer);
            //获取ID
            uint32_t nowID = get_ID();

            //TODO: add the dedup
            upload_blogs.emplace(producer, [&]( auto& g ) {
                g.ID = nowID;
                g.status = Status::s_uploaded;
                g.producer = producer;
                g.content = content;
                g.approve_status = init_status;
            });

            //在idlist添加,ID--producer关系,用于之后通过ID查询producer
            idlists.emplace(_self, [&]( auto& g ) {
                g.ID = nowID;
                g.producer = producer;
            });

            //blog数量+1
            blognum_op(producer, '+');
        }

        /// @abi action
        void reviewing(const uint64_t ID, const account_name reviewer) {
            require_auth(reviewer);

            //先通过idlist查询ID,获取用户名,然后才能用mutil_index查询具体用户的blog,下同
            auto itrid = idlists.find(ID);
            eosio_assert(itrid != idlists.end(), "this blog doesn't exists!\n");

            blog_index review_blogs(_self, itrid->producer);

            auto itr = review_blogs.find( ID );
            eosio_assert(itr != review_blogs.end(), "this blog doesn't exists!\n");
            eosio_assert(itr->producer != reviewer, "you can't review youself!\n");
            eosio_assert(itr->status == Status::s_uploaded, "this blog is reviewing or reviewed!\n");
            
            review_blogs.modify(itr, itrid->producer, [&](auto& g){
                g.status = Status::s_reviewing;
                g.reviewer = reviewer;
            });
        }

        /// @abi action
        void approved(const uint64_t ID) {

            auto itrid = idlists.find(ID);
            eosio_assert(itrid != idlists.end(), "this blog doesn't exists!\n");
            blog_index approve_blogs(_self, itrid->producer);

            auto itr = approve_blogs.find( ID );
            eosio_assert(itr != approve_blogs.end(), "this blog doesn't exists!\n");
            eosio_assert(itr->status == Status::s_reviewing, "this blog is reviewed!\n");
            require_auth(itr->reviewer);
            
            approve_blogs.modify(itr, itrid->producer, [&](auto& g){
                g.status = Status::s_approved;
                g.approve_status = std::string("approved");
            });
        }

        /// @abi action
        void disapprove(const uint64_t ID, std::string reason) {

            auto itrid = idlists.find(ID);
            eosio_assert(itrid != idlists.end(), "this blog doesn't exists!\n");
            blog_index disappr_policys(_self, itrid->producer);

            auto itr = disappr_policys.find( ID );
            eosio_assert(itr != disappr_policys.end(), "this blog doesn't exists!\n");
            eosio_assert(itr->status == Status::s_reviewing, "this blog is reviewed!\n");
            require_auth(itr->reviewer);
            
            disappr_policys.modify(itr, itrid->producer, [&](auto& g){
                g.status = Status::s_disapprove;
                g.approve_status = reason;
            });
        }

        /// @abi action
        void remove(const uint64_t ID) {
            auto itrid = idlists.find(ID);
            eosio_assert(itrid != idlists.end(), "this blog doesn't exists!\n");
            blog_index remove_policys(_self, itrid->producer);

            auto itr = remove_policys.find( ID );
            eosio_assert(itr != remove_policys.end(), "this blog doesn't exists!\n");
            require_auth(itr->producer);
            
            //使用erase删除
            remove_policys.erase(itr);
            blognum_op(itr->producer, '-');
        }


    private:
        enum Status {s_uploaded,s_reviewing,s_approved,s_disapprove};

        /**
         * 用于保存用户信息,保存用户文章数量
         * @abi table account i64
         */
        struct account {
            account( account_name o = account_name() ):owner(o){}

            account_name owner;
            uint32_t     blognum = 0;

            bool is_empty()const { return !blognum; }

            uint64_t primary_key()const { return owner; }

            EOSLIB_SERIALIZE( account, (owner)(blognum) )
        };

        typedef eosio::multi_index< N(account), account> account_index;

        /**
         * 保存文章ID和用户关系,假如不保存,则审核员每次都要提交文章作者;
         */
        struct idlist {
            uint64_t ID;
            account_name producer;

            uint64_t primary_key()const { return ID; }

            EOSLIB_SERIALIZE( idlist, (ID)(producer) )
        };
        typedef eosio::multi_index< N(idlist), idlist> idlist_index;

        //@abi table blog i64
        struct blog {
            uint64_t ID;
            uint8_t status;
            std::string approve_status;
            account_name producer;
            account_name reviewer;
            std::string content;

            auto primary_key() const { return ID; }

            EOSLIB_SERIALIZE( blog, (ID)(status)(approve_status)(producer)(reviewer)(content) )
        }; 

        typedef eosio::multi_index< N(blog), blog> blog_index;

        account_index accounts;
        idlist_index idlists;
        std::string init_status;

        // get the code's policynum
        uint32_t get_ID() {
            auto itr = accounts.find( _self );
            if ( itr == accounts.end() ) {
                return 0;
            } else {
                return itr->blognum;
            }
        }

        /**
         * to operate the account's policynum
         * op : '+','-'
         */
        void blognum_op(account_name name, char op) {
            auto itr = accounts.find( name );
            if ( itr == accounts.end() ) {
                accounts.emplace(name, [&]( auto& g ) {
                    g.owner = name;
                    g.blognum = 1;
                });
            } else {
                if( op == '+' ) {
                    accounts.modify(itr, itr->owner, []( auto& g ) {g.blognum += 1;});
                } else if( op == '-' ) {
                    accounts.modify(itr, itr->owner, []( auto& g ) {g.blognum -= 1;});
                }
            }

            itr = accounts.find( _self );
            if ( itr == accounts.end() ) {
                accounts.emplace(_self, [&]( auto& g ) {
                    g.owner = _self;
                    g.blognum = 1;
                });
            } else {
                if( op == '+' ) {
                    accounts.modify(itr, itr->owner, []( auto& g ) {g.blognum += 1;});
                }
            }
        }
};

EOSIO_ABI( blog_view, (upload)(reviewing)(approved)(disapprove)(remove) )

要点简单总结:

  1. 智能合约的开发,主要是要摒弃之前开发习惯,因为要在一定的限制下开发需要的功能;比如,在审查员审查blog的时候,在blog_index结构中,用scope作为主分类,然后使用ID作为primary_key,这样就要求每次要查询primary_key的时候,首先要知道scope,但是,虽然审查员可以每次都传入producer,但是太过麻烦,这种时候,就要在生成一个结构体idlist,用于存储这种关系,因为idlist的scope是合约本身,这就可以避免不知道scope的情况;
  2. 在进行upload时候,blog的approve_status属性是string,我将其初始化为一个64字符的字符串。此处是因为,假如我先设置空值,或短字符串的时候,当审核员要修改此string,并超过原先值的时候,就需要producer的权限,正常来说,审核员要提交了,还需要上传者的权限,这就不对了。所以,此处设置一个长字符串,并要求审核员设置不要超过64;

其他,就没有什么难点或者问题了。

6、执行

使用eosiocpp编译完后,执行上传: image.png
使用tester作为上传者,先上传两个blog: image.png
使用get table查看上传结果: image.png
审核员yanyan先开始审核第二个,发送开始审核action: image.png
审核通过后,发送通过的action: image.png

审核第一个(略过reviewing,同上),但是,发现问题,执行审核不通过:

image.png
用户看到审核不通,则删除不通过的合约: image.png
以上,就是该智能合约的执行过程。

7、其他

当然,该合约还有很多可以改进的地方,比如blog增加第二个key,更加方便的排重,或者添加统计未审核blog的table,方便审核员查找等等。

代码见:我的GitHub

上一篇下一篇

猜你喜欢

热点阅读