EOS EOS 将多索引表添加到智能合约

pendingauth · 2018年10月30日 · 186 次阅读

本文转载自汇智网官博,原译文链接:http://blog.hubwiz.com/2018/08/12/EOS-multi-index-tables/

在本教程中,我们将在你的智能合约中按步骤创建和使用多索引表。

建多索引表是一种为了在RAM快速访问的方法,主要用来来缓存状态和数据。多索引表支持创建、读取、更新和删除(CRUD) 业务,区块链不行(它只支持创建和读取)。

多索引表提供了快速访问数据存储接口,是一种存储智能合同中使用的数据的实用的方法。在区块链记录交易信息,你应该使用多索引表存储应用程序数据。

使用多索引表,因为他们支持为使用的数据建立多个索引,主索引必须是uint64_t类型和唯一的,但其他的索引,可以有重复的,你可以使用多达16个,类型可以是uint64_t, uint128_t, uint256_t, double or long double

如果你想你需要使用一个字符串做索引,需要转换成一个整数型,将结果存储在随后索引的字段中。

1.创建一个结构

创建一个可以存储在多索引表中的结构,并在要索引的字段上定义getter

请记住,这些getter中必须有一个命名为primary_key(),如果没有这个,编译器eosiocpp将产生一个错误…”it can’t find the field to use as the primary key”即它找不到任何一个字段被作为主键。

如果你想要有一个以上的索引,(最多允许16个),然后为你想要索引的任何字段定义一个getter,这时这个名称就不那么重要了,因为你会把getter名称传递给typedef

struct [[eosio::table]] poll 
{
  uint64_t key; // primary key
  uint64_t pollId; // second key, can be non-unique
  std::string pollName; // name of poll
  uint8_t pollStatus =0; // staus where 0 = closed, 1 = open, 2 = finished
  std::string option; // the item you can vote for
  uint32_t count =0; // the number of votes for each time

  uint64_t primary_key() const { return key; }
  uint64_t by_pollId() const {return pollId; }
};

这里还要注意两件事:

1.注释:[[eosio::table]]编译器需要使用eosiocpp来识别要通过ABI公开该表并使其在智能合约之外可见。

2.结构名称少于12个字符,而且所有的字符都要小写字母。

2.多索引表和定义索引

定义多索引表将使用mystruct,告诉它要索引什么,以及如何获取正在索引的数据。主键将自动创建的,所以使用struct后,如果我想要一个只有一个主键的多索引表,我可以定义它为:

typedef eosio::multi_index<N(mystruct), mystruct> datastore;  

这定义了多个索引通过表名N(mystruct)和结构名mystructN(mystruct)会对结构名编译转换到uint64_t,使用uint64_t来标识属于多索引表的数据。

若要添加附加索引或辅助索引,则使用indexed_by模板作为参数,因此定义变为:

typedef eosio::multi_index<N(mystruct), mystruct, indexed_by<N(secondid), const_mem_fun<mystruct, uint64_t, &mystruct::by_id>>> datastore;  

注意:

indexed_by<N(secondid), const_mem_fun<mystruct, uint64_t, &mystruct::by_id>>  

参数:

  • 字段的名称转换为整数,N(secondid)
  • 一个用户定义的密钥调用接口,const_mem_fun<mystruct, uint64_t, &mystruct::by_id>

来看看有三个索引的情况。

/// @abi table  
      struct mystruct   
      {  
         uint64_t     key;   
         uint64_t     secondid;  
         uint64_t           anotherid;  
         std::string  name;   
         std::string  account;   

         uint64_t primary_key() const { return key; }  
         uint64_t by_id() const {return secondid; }  
         uint64_t by_anotherid() const {return anotherid; }  
      };  

typedef eosio::multi_index<N(mystruct), mystruct, indexed_by<N(secondid), const_mem_fun<mystruct, uint64_t, &mystruct::by_id>>, indexed_by<N(anotherid), const_mem_fun<mystruct, uint64_t, &mystruct::by_anotherid>>> datastore;  

更多的就不列了。

这里要注意的一个重要事项是,结构名与表名的匹配,并且将出现在ABI文件中的名称遵循规则(12个字符,所有都是小写的字母)。如果它们没有遵循这个规则,则表不会通过ABI可见(当然可以通过编辑ABI文件来绕过这一点)。

3.创建定义类型的局部变量

// local instances of the multi indexes  
   pollstable _polls;  
   votes _votes;  

现在我已经定义了一个带有两个索引的多索引表,我可以在我的智能合约中使用它。

如下是一个智能合约使用两个索引的多索引表的例子。在这里你可以看到如何遍历表,如何在同一合约中使用两个表,我们未来将增加额外的教程,利用多索引表。

\#include <eosiolib/eosio.hpp>  

using namespace eosio;  

class youvote : public contract {  
  public:  
      youvote(account_name s):contract(s), _polls(s, s), _votes(s, s)  
      {}  

      // public methods exposed via the ABI  
      // on pollsTable  

      /// @abi action  
      void version()  
      {  
          print("YouVote version  0.01");   

      };  

      /// @abi action  
      void addpoll(account_name s, std::string pollName)  
      {  
          //require_auth(s);  

          print("Add poll ", pollName);   

          // update the table to include a new poll  
          _polls.emplace(get_self(), [&](auto& p)  
                                      {  
                                        p.key = _polls.available_primary_key();  
                                        p.pollId = _polls.available_primary_key();  
                                        p.pollName = pollName;  
                                        p.pollStatus = 0;  
                                        p.option = "";  
                                        p.count = 0;  
                                      });  
      };  


      /// @abi action  
      void rmpoll(account_name s, std::string pollName)  
      {  
          //require_auth(s);  

          print("Remove poll ", pollName);   

          std::vector<uint64_t> keysForDeletion;  
          // find items which are for the named poll  
          for(auto& item : _polls)  
          {  
              if (item.pollName == pollName)  
              {  
                  keysForDeletion.push_back(item.key);     
              }  
          }  

          // now delete each item for that poll  
          for (uint64_t key : keysForDeletion)  
          {  
              print("remove from _polls ", key);  
              auto itr = _polls.find(key);  
              if (itr != _polls.end())  
              {  
                _polls.erase(itr);  
              }  
          }  


          // add remove votes ... don't need it the actions are permanently stored on the block chain  

          std::vector<uint64_t> keysForDeletionFromVotes;  
          // find items which are for the named poll  
          for(auto& item : _votes)  
          {  
              if (item.pollName == pollName)  
              {  
                  keysForDeletionFromVotes.push_back(item.key);     
              }  
          }  

          // now delete each item for that poll  
          for (uint64_t key : keysForDeletionFromVotes)  
          {  
              print("remove from _votes ", key);  
              auto itr = _votes.find(key);  
              if (itr != _votes.end())  
              {  
                _votes.erase(itr);  
              }  
          }  


      };  

      /// @abi action  
      void status(std::string pollName)  
      {  
          print("Change poll status ", pollName);  

          std::vector<uint64_t> keysForModify;  
          // find items which are for the named poll  
          for(auto& item : _polls)  
          {  
              if (item.pollName == pollName)  
              {  
                  keysForModify.push_back(item.key);     
              }  
          }  

          // now get each item and modify the status  
          for (uint64_t key : keysForModify)  
          {  

            print("modify _polls status", key);  
            auto itr = _polls.find(key);  
            if (itr != _polls.end())  
            {  
              _polls.modify(itr, get_self(), [&](auto& p)  
                                              {  
                                                p.pollStatus = p.pollStatus + 1;  
                                              });  
            }  
          }  
      };  

      /// @abi action  
      void statusreset(std::string pollName)  
      {  
          print("Reset poll status ", pollName);   

          std::vector<uint64_t> keysForModify;  
          // find all poll items  
          for(auto& item : _polls)  
          {  
              if (item.pollName == pollName)  
              {  
                  keysForModify.push_back(item.key);     
              }  
          }  

          // update the status in each poll item  
          for (uint64_t key : keysForModify)  
          {  
              print("modify _polls status", key);  
              auto itr = _polls.find(key);  
              if (itr != _polls.end())  
              {  
                _polls.modify(itr, get_self(), [&](auto& p)  
                                                {  
                                                  p.pollStatus = 0;  
                                                });  
              }  
          }  
      };  


      /// @abi action  
      void addpollopt(std::string pollName, std::string option)  
      {  
          print("Add poll option ", pollName, "option ", option);   

          // find the pollId, from _polls, use this to update the _polls with a new option  
          for(auto& item : _polls)  
          {  
              if (item.pollName == pollName)  
              {  
                    // can only add if the poll is not started or finished  
                    if(item.pollStatus == 0)  
                    {  
                        _polls.emplace(get_self(), [&](auto& p)  
                                          {  
                                            p.key = _polls.available_primary_key();  
                                            p.pollId = item.pollId;  
                                            p.pollName = item.pollName;  
                                            p.pollStatus = 0;  
                                            p.option = option;  
                                            p.count = 0;  
                                          });  
                    }  
                    else  
                    {  
                        print("Can not add poll option ", pollName, "option ", option, " Poll has started or is finished.");  
                    }  

                    break; // so you only add it once  
              }  
          }  
      };  

      /// @abi action  
      void rmpollopt(std::string pollName, std::string option)  
      {  
          print("Remove poll option ", pollName, "option ", option);   

          std::vector<uint64_t> keysForDeletion;  
          // find and remove the named poll  
          for(auto& item : _polls)  
          {  
              if (item.pollName == pollName)  
              {  
                  keysForDeletion.push_back(item.key);     
              }  
          }  


          for (uint64_t key : keysForDeletion)  
          {  
              print("remove from _polls ", key);  
              auto itr = _polls.find(key);  
              if (itr != _polls.end())  
              {  
                  if (itr->option == option)  
                  {  
                      _polls.erase(itr);  
                  }  
              }  
          }  
      };  


      /// @abi action  
      void vote(std::string pollName, std::string option, std::string accountName)  
      {  
          print("vote for ", option, " in poll ", pollName, " by ", accountName);   

          // is the poll open  
          for(auto& item : _polls)  
          {  
              if (item.pollName == pollName)  
              {  
                  if (item.pollStatus != 1)  
                  {  
                      print("Poll ",pollName,  " is not open");  
                      return;  
                  }  

                  break; // only need to check status once  
              }  
          }  

          // has account name already voted?    
          for(auto& vote : _votes)  
          {  
              if (vote.pollName == pollName && vote.account == accountName)  
              {  
                  print(accountName, " has already voted in poll ", pollName);  
                  //eosio_assert(true, "Already Voted");  
                  return;  
              }  
          }  

          uint64_t pollId =99999; // get the pollId for the _votes table  

          // find the poll and the option and increment the count  
          for(auto& item : _polls)  
          {  
              if (item.pollName == pollName && item.option == option)  
              {  
                  pollId = item.pollId; // for recording vote in this poll  

                  _polls.modify(item, get_self(), [&](auto& p)  
                                                {  
                                                    p.count = p.count + 1;  
                                                });  
              }  
          }  

          // record that accountName has voted  
          _votes.emplace(get_self(), [&](auto& pv)  
                                      {  
                                        pv.key = _votes.available_primary_key();  
                                        pv.pollId = pollId;  
                                        pv.pollName = pollName;  
                                        pv.account = accountName;  
                                      });          
      };  

  private:      

    // create the multi index tables to store the data  

      /// @abi table  
      struct poll   
      {  
        uint64_t      key; // primary key  
        uint64_t      pollId; // second key, non-unique, this table will have dup rows for each poll because of option  
        std::string   pollName; // name of poll  
        uint8_t      pollStatus =0; // staus where 0 = closed, 1 = open, 2 = finished  
        std::string  option; // the item you can vote for  
        uint32_t    count =0; // the number of votes for each itme -- this to be pulled out to separte table.  

        uint64_t primary_key() const { return key; }  
        uint64_t by_pollId() const {return pollId; }  
      };  
      typedef eosio::multi_index<N(poll), poll, indexed_by<N(pollId), const_mem_fun<poll, uint64_t, &poll::by_pollId>>> pollstable;  


      /// @abi table  
      struct pollvotes   
      {  
         uint64_t     key;   
         uint64_t     pollId;  
         std::string  pollName; // name of poll  
         std::string  account; //this account has voted, use this to make sure noone votes > 1  

         uint64_t primary_key() const { return key; }  
         uint64_t by_pollId() const {return pollId; }  
      };  
      typedef eosio::multi_index<N(pollvotes), pollvotes, indexed_by<N(pollId), const_mem_fun<pollvotes, uint64_t, &pollvotes::by_pollId>>> votes;  

      // local instances of the multi indexes  
      pollstable _polls;  
      votes _votes;  
};  

EOSIO_ABI( youvote, (version)(addpoll)(rmpoll)(status)(statusreset)(addpollopt)(rmpollopt)(vote))  

注意EOSIO_ABI调用,它通过ABI公开函数,重要的是函数名ABI函数名规则一定要匹配。

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册