EOS Dawn 3.0 智能合約 -- blog_view

1绊率、前提概要

關(guān)于如何編譯EOS源碼垂睬、編譯docker鏡像媳荒,搭建節(jié)點(diǎn)等等,官方都有相關(guān)文檔羔飞,新的特性也在之前文章中大體介紹了EOS Dawn 3.0整理

2肺樟、本次合約介紹

之前在eos dawn 2.0版本,有個示例合約 simpledb逻淌,公司需求的智能合約就是實現(xiàn)了類似合約么伯,所以,之前我實現(xiàn)的版本就是根據(jù)這個修改的卡儒。而且田柔,該合約主要使用的是2.0版中的 db.h頭文件中的函數(shù) store_str俐巴,以字符串為索引,保存結(jié)構(gòu)體的功能硬爆;
但是欣舵,在3.0版本中,該功能暫時刪除缀磕,只能暫時使用其他功能代替缘圈,目前,決定使用multi_index容器袜蚕;

3糟把、multi_index

eos dawn 3.0中的multi_index,使用方法和boost中的multi_index牲剃,非常類似遣疯,就是多重索引容器,假如清楚其使用方法的話凿傅,應(yīng)該對這個比較熟悉缠犀;

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

其中,需要的幾個參數(shù)聪舒,如其命名含義:

  • tablename:該table的名稱辨液;
  • typename: 該容器存儲的結(jié)構(gòu)體;
  • code:本合約的名稱过椎,例如 N(tests)室梅;
  • scope:數(shù)據(jù)存儲的賬戶名;
多級索引

聲明時候疚宇,可以使用以下方式聲明二級索引亡鼠,或多級索引:

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的內(nèi)建類型) 具體如使用榜揖,會在后續(xù)程序里面講解勾哩;

方法

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

  • table.emplace(scope, [&]( auto& g ) { ... }) 添加數(shù)據(jù)举哟;
  • table.find(primary_key) 用關(guān)鍵字查找思劳;
  • table.modify(itr, scope, [&]( auto& g ) { ... }) 修改數(shù)據(jù);
  • table.erase(itr) 刪除
  • table.begin() 數(shù)據(jù)起始
  • table.end() 數(shù)據(jù)末尾
  • ...

4妨猩、更符合面向?qū)ο蟮男潞霞s

Dawn3.0的新合約編寫方式見:新格式
該合約要實現(xiàn)以下功能:

  1. 每個用戶能夠上傳自己的blog文章潜叛;
  2. 其他用戶能夠?qū)徍嗽撚脩舻奈恼率欠裾_;
  3. 作者可以查詢文章狀態(tài)和數(shù)量;

結(jié)構(gòu)定義如下威兜,即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": []
}

總結(jié)如下:

  • table: account(存儲用戶blog數(shù)量)销斟、blog(保存用戶blog);
  • action:
    • upload 上傳blog信息;
    • reviewing 審核員開始審核椒舵;
    • approved 通過審核蚂踊;
    • disapprove 未通過審核;
    • remove 用戶刪除自己blog笔宿;

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關(guān)系措伐,用于之后通過ID查詢producer
            idlists.emplace(_self, [&]( auto& g ) {
                g.ID = nowID;
                g.producer = producer;
            });

            //blog數(shù)量+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};

        /**
         * 用于保存用戶信息,保存用戶文章數(shù)量
         * @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和用戶關(guān)系粪躬,假如不保存担败,則審核員每次都要提交文章作者;
         */
        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) )

要點(diǎn)簡單總結(jié):

  1. 智能合約的開發(fā)镰官,主要是要摒棄之前開發(fā)習(xí)慣提前,因為要在一定的限制下開發(fā)需要的功能;比如泳唠,在審查員審查blog的時候狈网,在blog_index結(jié)構(gòu)中,用scope作為主分類笨腥,然后使用ID作為primary_key拓哺,這樣就要求每次要查詢primary_key的時候,首先要知道scope脖母,但是士鸥,雖然審查員可以每次都傳入producer,但是太過麻煩谆级,這種時候烤礁,就要在生成一個結(jié)構(gòu)體idlist,用于存儲這種關(guān)系肥照,因為idlist的scope是合約本身脚仔,這就可以避免不知道scope的情況;
  2. 在進(jìn)行upload時候舆绎,blog的approve_status屬性是string鲤脏,我將其初始化為一個64字符的字符串。此處是因為亿蒸,假如我先設(shè)置空值凑兰,或短字符串的時候掌桩,當(dāng)審核員要修改此string,并超過原先值的時候姑食,就需要producer的權(quán)限波岛,正常來說,審核員要提交了音半,還需要上傳者的權(quán)限则拷,這就不對了。所以曹鸠,此處設(shè)置一個長字符串煌茬,并要求審核員設(shè)置不要超過64;

其他彻桃,就沒有什么難點(diǎn)或者問題了坛善。

6、執(zhí)行

使用eosiocpp編譯完后邻眷,執(zhí)行上傳:
image.png

使用tester作為上傳者眠屎,先上傳兩個blog:
image.png

使用get table查看上傳結(jié)果:
image.png

審核員yanyan先開始審核第二個,發(fā)送開始審核action:
image.png

審核通過后肆饶,發(fā)送通過的action:
image.png

審核第一個(略過reviewing改衩,同上),但是驯镊,發(fā)現(xiàn)問題葫督,執(zhí)行審核不通過:

image.png

用戶看到審核不通,則刪除不通過的合約:
image.png

以上板惑,就是該智能合約的執(zhí)行過程橄镜。

7、其他

當(dāng)然洒放,該合約還有很多可以改進(jìn)的地方蛉鹿,比如blog增加第二個key,更加方便的排重往湿,或者添加統(tǒng)計未審核blog的table妖异,方便審核員查找等等。

代碼見:我的GitHub

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末领追,一起剝皮案震驚了整個濱河市他膳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绒窑,老刑警劉巖棕孙,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡蟀俊,警方通過查閱死者的電腦和手機(jī)钦铺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肢预,“玉大人矛洞,你說我怎么就攤上這事√逃常” “怎么了沼本?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長锭沟。 經(jīng)常有香客問我抽兆,道長,這世上最難降的妖魔是什么族淮? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任辫红,我火速辦了婚禮,結(jié)果婚禮上祝辣,老公的妹妹穿的比我還像新娘厉熟。我一直安慰自己,他們只是感情好较幌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著白翻,像睡著了一般乍炉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上滤馍,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天岛琼,我揣著相機(jī)與錄音,去河邊找鬼巢株。 笑死槐瑞,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阁苞。 我是一名探鬼主播困檩,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼那槽!你這毒婦竟也來了悼沿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤骚灸,失蹤者是張志新(化名)和其女友劉穎糟趾,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡义郑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年蝶柿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片非驮。...
    茶點(diǎn)故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡交汤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出院尔,到底是詐尸還是另有隱情蜻展,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布邀摆,位于F島的核電站纵顾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏栋盹。R本人自食惡果不足惜施逾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望例获。 院中可真熱鬧汉额,春花似錦、人聲如沸榨汤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽收壕。三九已至妓灌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜜宪,已是汗流浹背虫埂。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留圃验,地道東北人掉伏。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像澳窑,于是被迫代替她去往敵國和親斧散。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內(nèi)容