有没有一种好的方法可以索引智能合约中的大量数据以便能够高效地读取它们?

Is there a good way to index a lot of data in a smart contract to be able to read it efficiently?

我正在编写一个 solidity 智能合约,用户可以在其中添加一个字符串(例如一句笑话),其他用户应该可以对这些笑话进行投票。例如,如果合同中有一百万个笑话(所有的票数都不同),是否可以按票数对这些笑话进行排名?例如。显示百万个笑话中投票最高的 10 个?我知道这在小范围内是可能的,但在更大范围内我不太确定。

下面附上了实现您需要的主要功能的合约变体:

  • “笑话”记录登记
  • 无重复投票登记
  • 前10名的定义(希望我没记错排序算法)

文本中的哈希值用作“笑话”的标识符。

但是,为了向用户提供高质量的功能,您需要一个外部应用程序来监视 JokeCreatedJokeVoted 事件本合同并维护一个“笑话”记录的外部数据库。 这是因为以太坊合约对于大型数组的迭代操作无效,你需要一个外部服务来:

  • 为用户提供一个通用的“笑话”列表
  • 为用户提供“笑话”的一般评级列表 使用外部应用程序时,也可以将判断前10名的功能排除在合约之外(在函数VotesForJoke中),这将大大降低投票交易的成本。 另外需要注意的是识别用户的问题,避免重新投票,因为一般情况下无法在以太坊本身层面解决。
pragma solidity ^0.5.8;

contract Jokes
{

    struct Joke 
    {
         string                 text ;
        address                 sender ;
        bytes32                 author ;
        uint256                 votes ;
        mapping (bytes32=>bool) voters ;
    }

    mapping (bytes32 => Joke)  JokesList ;
                  bytes32[10]  Top10 ;

    event JokeCreated(bytes32 hash, string text, address sender, bytes32  author) ; 
    event JokeVoted  (bytes32 hash, bytes32 voter) ; 

    constructor() public
    {
    }

   function NewJoke(string  memory text_, bytes32  author_) public 
   {
       bytes32  hash ;
    
            hash=keccak256(abi.encodePacked(text_)) ;

       if(JokesList[hash].author!=0x00)  require(false) ;

         JokesList[hash] =Joke({   text: text_, 
                                 sender: tx.origin,
                                 author: author_,
                                  votes: 0         }) ;

      emit JokeCreated(hash, text_, tx.origin, author_) ; 
   }


   function VotesForJoke(bytes32  hash_, bytes32  voter_) public 
   {
      uint256  votes ;
      uint256  i ;

// Check for existance
       if(JokesList[hash_].author==0x00)  require(false) ;
// Check for re-voting
       if(JokesList[hash_].voters[voter_]==true)  require(false) ;

          JokesList[hash_].voters[voter_]=true ;
          JokesList[hash_].votes++ ;

// Ordering top 10
            votes=JokesList[hash_].votes ;

        for(i=9 ; i>=0 ; i--)
          if(votes>=JokesList[Top10[i]].votes)
          {
              if(i!=9 && Top10[i]!=hash_)  Top10[i+1]=Top10[i] ;
          }
          else
          {
               break ;  
          }

          if(i!=9)  Top10[i+1]=hash_ ;

// Emit event of voting
      emit JokeVoted(hash_, voter_) ; 
   }

   function GetJoke(bytes32  hash_) public view returns (string memory, address, bytes32,uint256 retVal)
   {
        return(JokesList[hash_].text, JokesList[hash_].sender, JokesList[hash_].author, JokesList[hash_].votes) ;
   }

   function GetTop10() public view returns (bytes32[10] memory retVal)
   {
       return(Top10) ;
   }
    
}