美文网首页区块链研习社
EOS初探(3) - 智能合约投票

EOS初探(3) - 智能合约投票

作者: 呆呆的爸 | 来源:发表于2018-06-22 16:57 被阅读163次

    本章节利用EOS的智能合约实现了简单的投票,功能包括

    1. 创建投票
    2. 发起投票
    3. 投票/委托投票
    4. 投票的winner提案

    与以太坊的
    https://solidity.readthedocs.io/en/latest/solidity-by-example.html#voting
    功能类似,大家可以比较下Ethereum的Solidity版本与EOS的C++版本语法上的差别。

    本文参考了https://www.cnblogs.com/Jogging/p/ballot.html,针对最新的EOS版本(1.0.5)修改了部分合约代码以及步骤。

    创建合约

    eos@mybc:~$ eosiocpp -n ballot
    created ballot from skeleton
    eos@mybc:~$ cd ballot/
    eos@mybc:~/ballot$ ll
    total 16
    drwxr-xr-x 2 eos eos 4096 Jun 20 14:43 ./
    drwxr-xr-x 8 eos eos 4096 Jun 20 14:43 ../
    -rw-rw-r-- 1 eos eos  275 Jun 20 14:43 ballot.cpp
    -rw-rw-r-- 1 eos eos   92 Jun 20 14:43 ballot.hpp
    eos@mybc:~/ballot$
    

    修改ballot.hpp

    /**
     *  @file
     *  @copyright defined in eos/LICENSE.txt
     */
    #include <eosiolib/eosio.hpp>
    #include <string>
    
    namespace eosio {
    
    using std::string;
    
    class ballot : public eosio::contract
    {
    public:
      using contract::contract;
      ballot(account_name self) : contract(self) {}
    
      // 投票发起人接口
      void addposposal(string posposal_name);                      // 初始化投票提案
      void addvoter(account_name voter_name, uint64_t weight = 1); // 初始化投票人
      // 普通选民接口
      void delegate(account_name voter, account_name delegate_to); // 委托投票(跟随投票)
      void vote(account_name voter, string proposal);              // 投票给某个提议
      void winproposal();                                          // 返回投票数最多的提议
      void allproposal();                                          // 返回全部提案投票状态
    
    private:
      // 定义投票人
      struct voter
      {
        account_name account;      // 投票人
        uint64_t weight;           // 权重
        bool voted;                // 如果值为true,代表这个投票人已经投过票
        uint64_t delegate_account; // 委托投票人地址
        string pos_name;           // 投票提案名
    
        uint64_t primary_key() const { return account; }
    
        EOSLIB_SERIALIZE(voter, (account)(weight)(voted)(delegate_account)(pos_name))
      };
      // 提案的数据结构
      struct posposal
      {
        string name;        // 提案的名称
        uint64_t voteCount; // 提议接受的投票数
    
        uint64_t primary_key() const { return eosio::string_to_name(name.c_str()); }
    
        EOSLIB_SERIALIZE(posposal, (name)(voteCount))
      };
    
      typedef eosio::multi_index<N(voter), voter> voter_index;
      typedef eosio::multi_index<N(posposal), posposal> posposal_index;
    };
    
    }
    
    

    修改ballot.cpp

    #include <eosiolib/eosio.hpp>
    #include "ballot.hpp"
    
    namespace eosio {
    
    // 投票发起人接口
    void ballot::addposposal(string posposal_name) // 初始化投票提案
    {
      require_auth(_self);
    
      posposal_index posposals(_self, _self);
      auto it = posposals.find(eosio::string_to_name(posposal_name.c_str()));
      eosio_assert(it == posposals.end(), "posposal exist");
    
      posposals.emplace(_self, [&](auto &a) {
        a.name = posposal_name;
        a.voteCount = 0;
      });
    }
    void ballot::addvoter(account_name voter_name, uint64_t weight /* =1 */) // 初始化投票人
    {
      require_auth(_self);
      eosio_assert(weight > 0, "must positive weight");
    
      voter_index voters(_self, _self);
      auto it = voters.find(voter_name);
    
      eosio_assert(it == voters.end(), "voter exist");
      voters.emplace(_self, [&](auto &a) {
        a.account = voter_name;
        a.weight = weight;
        a.voted = false;
        a.delegate_account = 0;
        a.pos_name = "";
      });
    }
    // 普通选民接口
    void ballot::delegate(account_name voter, account_name delegate_to) // 委托投票(跟随投票)
    {
      require_auth(voter);
    
      voter_index voters(_self, _self);
      auto vit = voters.find(voter);
      eosio_assert(vit->voted == false, "is voted");
      eosio_assert(vit != voters.end(), "voter not exist");
      auto it = voters.find(delegate_to);
      eosio_assert(it != voters.end(), "delegate_to not exist");
    
      while (it != voters.end() && it->delegate_account != 0 && it->delegate_account != voter)
        it = voters.find(it->delegate_account);
    
      eosio_assert(it != voters.end(), "delegate obj not exist");
      eosio_assert(it->account != voter, "not delegate self");
    
      if (it->voted)
      {
        vote(voter, it->pos_name);
      }
      else
      {
        voters.modify(it, _self, [&](auto &a) {
          a.weight += vit->weight;
        });
        voters.modify(vit, _self, [&](auto &a) {
          a.voted = true;
        });
      }
    }
    void ballot::vote(account_name voter, string proposal) // 投票给某个提议
    {
      require_auth(voter);
      posposal_index posposals(_self, _self);
      auto it = posposals.find(eosio::string_to_name(proposal.c_str()));
      eosio_assert(it != posposals.end(), "posposal not exist");
    
      voter_index voters(_self, _self);
      auto vit = voters.find(voter);
      eosio_assert(vit->voted == false, "is voted");
      eosio_assert(vit != voters.end(), "voter not exist");
    
      voters.modify(vit, _self, [&](auto &a) {
        a.voted = true;
        a.pos_name = proposal;
      });
    
      posposals.modify(it, _self, [&](auto &a) {
        a.voteCount += vit->weight;
      });
    }
    void ballot::winproposal() // 返回投票数最多的提议
    {
      posposal_index posposals(_self, _self);
      auto win = posposal();
      uint64_t max = 0;
      for (auto it : posposals)
      {
        if (it.voteCount > max)
        {
          max = it.voteCount;
          win = it;
        }
      }
      if (max > 0)
      {
        eosio::print("win posposal is: ", win.name.c_str(), "vote count", win.voteCount, "\n");
      }
      else
      {
        eosio::print("not vote", "\n");
      }
    }
    void ballot::allproposal() // 返回全部提案投票状态
    {
      posposal_index posposals(_self, _self);
      uint64_t idx = 0;
      for (auto it : posposals)
      {
        eosio::print(" posposal ", idx, ":", it.name.c_str(), ", vote count:", it.voteCount, "\n");
      }
    }
    
    } /// namespace eosio
    
    EOSIO_ABI(eosio::ballot, (addposposal)(addvoter)(delegate)(vote)(winproposal)(allproposal))
    
    

    编译合约

    EOS智能合约部署的时候只认wast格式,可以通过eosiocpp命令(需要编译eos)编译合约

    eos@mybc:~/ballot$ eosiocpp -o ballot.wast ballot.cpp
    eos@mybc:~/ballot$ eosiocpp -g ballot.abi ballot.cpp
    3083077ms thread-0   abi_generator.hpp:68          ricardian_contracts  ] Warning, no ricardian clauses found for Ballot
    
    3083077ms thread-0   abi_generator.hpp:75          ricardian_contracts  ] Warning, no ricardian contract found for addposposal
    
    3083077ms thread-0   abi_generator.hpp:75          ricardian_contracts  ] Warning, no ricardian contract found for addvoter
    
    3083077ms thread-0   abi_generator.hpp:75          ricardian_contracts  ] Warning, no ricardian contract found for delegate
    
    3083077ms thread-0   abi_generator.hpp:75          ricardian_contracts  ] Warning, no ricardian contract found for vote
    
    3083077ms thread-0   abi_generator.hpp:75          ricardian_contracts  ] Warning, no ricardian contract found for winproposal
    
    3083077ms thread-0   abi_generator.hpp:75          ricardian_contracts  ] Warning, no ricardian contract found for allproposal
    
    Generated ballot.abi ...
    eos@mybc:~/ballot$ ll
    total 244
    drwxr-xr-x 2 eos eos   4096 Jun 20 14:51 ./
    drwxr-xr-x 8 eos eos   4096 Jun 20 14:48 ../
    -rw-rw-r-- 1 eos eos   1760 Jun 20 14:51 ballot.abi
    -rw-rw-r-- 1 eos eos   3266 Jun 20 14:48 ballot.cpp
    -rw-rw-r-- 1 eos eos   1769 Jun 20 14:47 ballot.hpp
    -rw-rw-r-- 1 eos eos  18200 Jun 20 14:51 ballot.wasm
    -rw-rw-r-- 1 eos eos 206171 Jun 20 14:51 ballot.wast
    eos@mybc:~/ballot$
    

    环境准备

    创建合约账号以及投票账号

    注意!默认账号eosio需要导入私钥,否则会报错Error 3090003: provided keys, permissions, and delays do not satisfy declared authorizations

    eos@mybc:~/contracts/ballot$ cleos create key
    Private key: 5KPysXNbzLq7T7pumusTkqHcVancsJmo8dLMWDuAVDyfkhVA5HZ
    Public key: EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg
    eos@mybc:~/contracts/ballot$ cleos wallet create -n gxk
    Creating wallet: gxk
    Save password to use in the future to unlock this wallet.
    Without password imported keys will not be retrievable.
    "PW5JKsVdSDJ9f3vnwNpcEev9JPnqpTyDDAnwcMcnJ4W1W1Y2dx112"
    eos@mybc:~/contracts/ballot$ cleos wallet import 5KPysXNbzLq7T7pumusTkqHcVancsJmo8dLMWDuAVDyfkhVA5HZ -n gxk
    imported private key for: EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg
    eos@mybc:~/contracts/ballot$ 
    eos@mybc:~/contracts/ballot$ cleos create account eosio acc1 EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg
    Error 3090003: provided keys, permissions, and delays do not satisfy declared authorizations
    Ensure that you have the related private keys inside your wallet and your wallet is unlocked.
    eos@mybc:~/contracts/ballot$ cleos wallet import 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 -n gxk
    imported private key for: EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
    eos@mybc:~/contracts/ballot$ cleos create account eosio acc1 EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg
    executed transaction: 06abb464a28cf7d81598896f7b46504314ccb0defbdee052e7382f3b4ef3c6c9  200 bytes  222 us
    #         eosio <= eosio::newaccount            {"creator":"eosio","name":"acc1","owner":{"threshold":1,"keys":[{"key":"EOS51R5G76bu9rK7g7WqAjH3j9Wr...
    warning: transaction executed locally, but may not be confirmed by the network yet
    eos@mybc:~/contracts/ballot$ cleos create account eosio acc1 EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg
    eos@mybc:~/contracts/ballot$ cleos create account eosio acc2 EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg
    eos@mybc:~/contracts/ballot$ cleos create account eosio acc3 EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg
    eos@mybc:~/contracts/ballot$ cleos create account eosio acc4 EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg
    eos@mybc:~/contracts/ballot$ cleos create account eosio acc5 EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg
    eos@mybc:~/contracts/ballot$ cleos create account eosio accballot EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg
    eos@mybc:~/contracts/ballot$ cleos get accounts EOS51R5G76bu9rK7g7WqAjH3j9WrBfEmiz5NPaV13eCVnHc9V2NXg
    {
      "account_names": [
        "acc1",
        "acc2",
        "acc3",
        "acc4",
        "acc5",
        "accballot"
      ]
    }
    

    部署智能合约

    首先需要把合约目录拷贝到docker的挂载点, 设置成本地路径对于docker来说是无效的,会报no wast file found,需要拷贝文件到docker能访问的目录

    Reading WAST/WASM from contracts/ballot/ballot.wast...
    1995557ms thread-0 main.cpp:2712 main ] Failed with error: Assert Exception (10)
    !wast.empty(): no wast file found contracts/ballot/ballot.wast

    cp -r ballot /opt/eosio/data/contracts/
    

    开始正式部署

    eos@mybc:~/contracts/ballot$ 
    eos@mybc:~$ cleos set contract accballot /opt/eosio/bin/data-dir/contracts/ballot
    Reading WAST/WASM from /opt/eosio/bin/data-dir/contracts/ballot/ballot.wasm...
    Using already assembled WASM...
    Publishing contract...
    executed transaction: d76b4c9ff65b9c49c7412a2ccd2fa152241d65d92f33b8e6230df0959de98648  7872 bytes  1086 us
    #         eosio <= eosio::setcode               {"account":"accballot","vmtype":0,"vmversion":0,"code":"0061736d01000000017a1460027f7f0060037f7e7e00...
    #         eosio <= eosio::setabi                {"account":"accballot","abi":"0e656f73696f3a3a6162692f312e3000060b616464706f73706f73616c00010d706f73...
    warning: transaction executed locally, but may not be confirmed by the network yet
    eos@mybc:~$ 
    
    

    增加投票选项

    eos@mybc:~$ cleos push action accballot addposposal '["baidu"]' -p accballot
    executed transaction: 913a8b376bc4ac49e320819bd9a1ec35e2d7a9ca280b5fe11d8ddd9b0d93be75  104 bytes  442 us
    #     accballot <= accballot::addposposal       {"posposal_name":"baidu"}
    warning: transaction executed locally, but may not be confirmed by the network yet
    eos@mybc:~$ cleos push action accballot addposposal '["alibaba"]' -p accballot
    eos@mybc:~$ cleos push action accballot addposposal '["163"]' -p accballot
    eos@mybc:~$ cleos push action accballot addposposal '["360"]' -p accballot
    eos@mybc:~$ cleos push action accballot addposposal '["qq"]' -p accballot
    eos@mybc:~$ cleos push action accballot addposposal '["yy"]' -p accballot
    
    

    添加投票人

    eos@mybc:~$ cleos push action accballot addvoter '["acc1",1]' -p accballot
    executed transaction: 172e42446d8379fecc430ae5a2fe76a691e5f1020be4fba12fdb49803b57f517  112 bytes  330 us
    #     accballot <= accballot::addvoter          {"voter_name":"acc1","weight":1}
    warning: transaction executed locally, but may not be confirmed by the network yet
    eos@mybc:~$ cleos push action accballot addvoter '["acc2",1]' -p accballot
    eos@mybc:~$ cleos push action accballot addvoter '["acc3",1]' -p accballot
    eos@mybc:~$ cleos push action accballot addvoter '["acc4",1]' -p accballot
    eos@mybc:~$ cleos push action accballot addvoter '["acc5",1]' -p accballot
    
    

    开始投票

    eos@mybc:~$ cleos push action accballot allproposal '[]'  -p acc1
    executed transaction: c80fc27e5d73183473c802e7485b2bbccadecf320d574755b8ddd338bc63db74  96 bytes  780 us
    #     accballot <= accballot::allproposal       {}
    >>  posposal 0:163vote count0
    warning: transaction executed locally, but may not be confirmed by the network yet
    eos@mybc:~$ cleos push action accballot vote '["acc1","qq"]' -p acc1
    executed transaction: c00f730ed13457e2b353cc64b8c9a4945521bbdf6b8f55290d6d2cfc9a516374  104 bytes  545 us
    #     accballot <= accballot::vote              {"voter":"acc1","proposal":"qq"}
    warning: transaction executed locally, but may not be confirmed by the network yet
    eos@mybc:~$ cleos push action accballot vote '["acc2","qq"]' -p acc2
    eos@mybc:~$ cleos push action accballot vote '["acc3","163"]' -p acc3
    eos@mybc:~$ cleos push action accballot vote '["acc4","qq"]' -p acc4
    eos@mybc:~$ cleos push action accballot delegate '["acc5","acc3"]' -p acc5
    
    

    投票结果

    eos@mybc:~$ cleos push action accballot winproposal '[]' -p acc1
    executed transaction: ceadf4c99152ba1ce66c6a41d4d0249a34f97c2d23ad4ee8a3ec2ba0b125053c  96 bytes  705 us
    #     accballot <= accballot::winproposal       {}
    >> win posposal is: qqvote count3
    warning: transaction executed locally, but may not be confirmed by the network yet
    eos@mybc:~$ 
    
    

    nodeosd节点会打印如下日志(需要在config.ini中开启contrac-console = true)
    这里合约中的idx变量没有递增,所以posposal都是0,大家勿怪

    1168829ms thread-0   apply_context.cpp:28          print_debug          ] 
    [(accballot,allproposal)->accballot]: CONSOLE OUTPUT BEGIN =====================
     posposal 0:163, vote count:2
     posposal 0:360, vote count:0
     posposal 0:alibaba, vote count:0
     posposal 0:baidu, vote count:0
     posposal 0:qq, vote count:3
     posposal 0:yy, vote count:0
    
    [(accballot,allproposal)->accballot]: CONSOLE OUTPUT END   =====================
    
    

    相关文章

      网友评论

      • 逆天西瓜:已经导入私钥了,create account eosio的时候还是报那个错
        逆天西瓜:@bifrostx 嗯,config.ini里面有个默认的私钥,导入就好了
        bifrostx:我也遇到你这个问题,导入了作者的这个私钥,发现不报这个错误了。

      本文标题:EOS初探(3) - 智能合约投票

      本文链接:https://www.haomeiwen.com/subject/roakyftx.html