EOS智能合约添加多索引表table

作者: 编程狂魔 | 来源:发表于2018-08-15 12:11 被阅读4次

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

    建多索引表是一种为了在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

         /// @abi table
          struct mystruct 
          {
             uint64_t     key; 
             uint64_t     secondid;
             std::string  name; 
             std::string  account; 
    
             uint64_t primary_key() const { return key; } // getter for primary key
             uint64_t by_id() const {return secondid; } // getter for additional key
          };
    

    这里还要注意两件事:

    1.注释:

    /// @abi 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函数名规则一定要匹配。

    ======================================================================

    分享一个交互式的在线编程实战,EOS智能合约与DApp开发入门

    EOS教程

    本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。

    • web3j教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
    • 以太坊教程,主要介绍智能合约与dapp应用开发,适合入门。
    • 以太坊开发,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
    • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
    • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和事件等内容。
    • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和事件等。

    汇智网原创翻译,转载请标明出处。这里是原文

    相关文章

      网友评论

        本文标题:EOS智能合约添加多索引表table

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