github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/contracts/chief/src/chief_0.0.6.sol (about)

     1  pragma solidity ^0.4.19;
     2  
     3  contract TribeChief_0_0_6 {
     4  
     5      string vsn = "0.0.6";
     6  
     7      //config >>>>
     8      uint epoch = 6171; // block time 14s, 6171 block = 24H
     9      uint signerLimit = 17;
    10      uint volunteerLimit = 1234;
    11      //config <<<<
    12  
    13      mapping(address => bool) genesisSigner; // genesis signer address
    14  
    15      uint blockNumber;
    16  
    17      //signer info
    18      struct SignerInfo {
    19          uint score;
    20          uint number;
    21      }
    22  
    23      //volunteer object
    24      struct VolunteerInfo {
    25          uint weight; // new volunteer weight = 5
    26          uint number;
    27      }
    28  
    29      address _owner;
    30  
    31      address[] _genesisSignerList;
    32      address[] _signerList;
    33      address[] _volunteerList;
    34      address[] _blackList;
    35  
    36      // the mapping of the signer score and the block number
    37      mapping(address => SignerInfo) signersMap;
    38      // the mapping of the volunteer and block number
    39      //mapping(address => uint) volunteersMap;
    40      mapping(address => VolunteerInfo) volunteersMap;
    41      // the mapping of the blacklist and block number
    42      mapping(address => uint) blMap;
    43  
    44      function TribeChief_0_0_6(address[] genesisSigners, uint _epoch, uint _signerLimit, uint _volunteerLimit) public {
    45          if (_epoch > 0) epoch = _epoch;
    46          if (_signerLimit > 0) signerLimit = _signerLimit;
    47          if (_volunteerLimit > 0) volunteerLimit = _volunteerLimit;
    48          _owner = msg.sender;
    49          uint len = genesisSigners.length;
    50          if (len > 0) {
    51              for (uint i = 0; i < len; i++) {
    52                  address g = genesisSigners[i];
    53                  genesisSigner[g] = true;
    54                  _genesisSignerList.push(g);
    55                  if (i == 0) pushSigner(g, 3);
    56              }
    57          } else {
    58              // normal default for testing
    59              // 0x4110bd1ff0b73fa12c259acf39c950277f266787;
    60              address g1 = uint160(371457687117486736155821182390123011782146942855);
    61              genesisSigner[g1] = true;
    62              _genesisSignerList.push(g1);
    63              pushSigner(g1, 3);
    64          }
    65      }
    66  
    67      // delete a blacklist by index
    68      function deleteBlackList(uint index) private {
    69  
    70          uint blen = _blackList.length;
    71          if (index < blen) {
    72              delete blMap[_blackList[index]];
    73              for (uint i = index; i < blen - 1; i++) {
    74                  _blackList[i] = _blackList[i + 1];
    75              }
    76              _blackList.length -= 1;
    77          }
    78      }
    79  
    80      // delete a volunteer by index
    81      function deleteVolunteer(uint index) private {
    82  
    83          uint vlen = _volunteerList.length;
    84          // _signerList >>>>
    85          if (index < vlen) {
    86              delete volunteersMap[_volunteerList[index]];
    87              for (uint i = index; i < vlen - 1; i++) {
    88                  _volunteerList[i] = _volunteerList[i + 1];
    89              }
    90              _volunteerList.length -= 1;
    91          }
    92      }
    93  
    94      // delete a signer by index
    95      function deleteSigner(uint index) private {
    96          uint slen = _signerList.length;
    97          if (index < slen) {
    98              delete signersMap[_signerList[index]];
    99              for (uint i = index; i < slen - 1; i++) {
   100                  _signerList[i] = _signerList[i + 1];
   101              }
   102              _signerList.length -= 1;
   103          }
   104      }
   105  
   106      // append a sinner to blacklist
   107      function pushBlackList(address sinner) private {
   108          if (sinner != uint160(0) && blMap[sinner] == 0) {
   109              _blackList.push(sinner);
   110              blMap[sinner] = block.number;
   111          }
   112      }
   113  
   114  
   115      // append a volunteer
   116      function pushVolunteer(address volunteer, uint weight) private {
   117          if (weight == 0) {
   118              // weight == 0 表示要删除这个候选人,并放到黑名单里冷静一个 epoch
   119              if (_volunteerList.length > 0) {
   120                  for (uint i = 0; i < _volunteerList.length; i++) {
   121                      if (volunteer == _volunteerList[i]) {
   122                          deleteVolunteer(i);
   123                          break;
   124                      }
   125                  }
   126              }
   127              pushBlackList(volunteer);
   128          } else if (!genesisSigner[volunteer] && weight == 5 && _volunteerList.length < volunteerLimit && volunteersMap[volunteer].number == 0 && blMap[volunteer] == 0 && signersMap[volunteer].number == 0) {
   129              //满员或者已经存在于签名人or候选人则不添加
   130              _volunteerList.push(volunteer);
   131              volunteersMap[volunteer].weight = weight;
   132              volunteersMap[volunteer].number = block.number;
   133          } else if (weight < 5 && volunteersMap[volunteer].number > 0) {
   134              //这种情况只是为了给 weight - 1 ,所以无条件修改
   135              volunteersMap[volunteer].weight = weight;
   136              volunteersMap[volunteer].number = block.number;
   137          }
   138      }
   139  
   140      // generate a random index for remove signers every epoch
   141      function getRandomIdx(address addr, uint max) private view returns (uint256) {
   142          if (max <= 0) {
   143              return 0;
   144          }
   145          uint256 random = uint256(keccak256(addr, block.difficulty, now));
   146          return (random % max);
   147      }
   148  
   149      // append a signer
   150      function pushSigner(address signer, uint score) private {
   151  
   152          if (_signerList.length < signerLimit) {
   153              _signerList.push(signer);
   154              signersMap[signer].score = score;
   155              signersMap[signer].number = block.number;
   156          }
   157      }
   158  
   159      modifier apply(address _addr) {
   160          require(_addr != uint160(0));
   161          require(signersMap[_addr].score > 0);
   162          _;
   163      }
   164      modifier owner(address _addr) {
   165          require(_addr == _owner);
   166          _;
   167      }
   168  
   169      function repeatTi(uint[] tiList, uint ti) private pure returns (bool) {
   170          if (tiList.length > 0) {
   171              for (uint i = 0; i < tiList.length; i++) {
   172                  if (tiList[i] == ti) {
   173                      return true;
   174                  }
   175              }
   176          }
   177          return false;
   178      }
   179  
   180  
   181      uint[] _cleanIdx; //辅助清理
   182      /*
   183          这个方法在每个 epoch 时负责清理志愿者列表
   184      */
   185      function _cleanVolunteerList() private {
   186          _cleanIdx.length = 0;
   187          // 清除低分的志愿者,如果不足 5 分的多于 1/2 则按照分数从低到高只清除到 1/2 ,
   188          // 如果志愿者列表长度不足 limit 的 1/2 则不清除
   189          uint vlen = _volunteerList.length;
   190          if (vlen > volunteerLimit / 2) {
   191              for (uint i1 = 0; i1 < vlen; i1++) {
   192                  if (volunteersMap[_volunteerList[i1]].weight < 5) {
   193                      _cleanIdx.push(i1);
   194                  }
   195              }
   196              if (_cleanIdx.length > volunteerLimit / 2) {
   197                  // 小于 5 的超过 1/2 时,随机清理掉多于 1/2 的部分
   198                  uint total = _cleanIdx.length - (volunteerLimit / 2);
   199                  uint[] memory tiList = new uint[](total);
   200                  // 随机挑选出超过 1/2 的部分的索引,放在 tiList 数组中
   201                  for (uint i2 = 0; i2 < _cleanIdx.length; i2++) {
   202                      uint ti = getRandomIdx(_volunteerList[i2], (_cleanIdx.length - uint(1)));
   203                      //skip out of range
   204                      if (ti >= _cleanIdx.length) continue;
   205                      //skip repeat
   206                      if (repeatTi(tiList, ti)) continue;
   207                      tiList[total] = ti;
   208                      if (total == 0) break;
   209                      total -= 1;
   210                  }
   211                  // 清掉 tiList 数组中记录的索引信息,并将 address 放入 blackList 等到下一个 epoch 解禁
   212                  for (uint i3 = 0; i3 < tiList.length; i3++) {
   213                      uint idx = tiList[i3];
   214                      deleteVolunteer(idx);
   215                      address volunteer = _volunteerList[idx];
   216                      pushBlackList(volunteer);
   217                  }
   218              }
   219          }
   220      }
   221  
   222      // v0.0.4
   223      function _cleanBlacklist() private {
   224          // 1 : clean blacklist
   225          uint blen = _blackList.length;
   226          for (uint i2 = 0; i2 < blen; i2++) {
   227              delete blMap[_blackList[i2]];
   228          }
   229          delete _blackList;
   230      }
   231  
   232      /*
   233          在志愿者列表中随机选出17个节点替换当前列表,
   234          在进入这个方法之前,已经判断过志愿者列表尺寸了,所以这里只管随机拼装即可
   235      */
   236      function generateSignersRule3() private {
   237          address g = _signerList[0];
   238          // 清理旧的列表
   239          address[] memory sl = new address[](_signerList.length);
   240          for (uint j = 0; j < sl.length; j++) {
   241              sl[j] = _signerList[j];
   242          }
   243          for (uint i0 = sl.length; i0 > 0; i0--) {
   244              uint sIndex = i0 - 1;
   245              deleteSigner(sIndex);
   246  
   247              address signerI = sl[sIndex];
   248              if (sIndex > 0 && signerI != uint160(0)) {
   249                  if (volunteersMap[signerI].weight == 0) {
   250                      pushVolunteer(signerI, 5);
   251                  }
   252                  pushVolunteer(signerI, volunteersMap[signerI].weight - 1);
   253              }
   254          }
   255          // 顺序选出一个创世签名人放到首位
   256          if (genesisSigner[g] && _genesisSignerList.length > 1) {
   257              // 这个循环一定会找到一个 genesisSigner 放到 signers 中
   258              for (uint i1 = 0; i1 < _genesisSignerList.length; i1++) {
   259                  if (_genesisSignerList[i1] == g) {
   260                      if (i1 == (_genesisSignerList.length - 1)) {
   261                          pushSigner(_genesisSignerList[0], 3);
   262                      } else {
   263                          pushSigner(_genesisSignerList[i1 + 1], 3);
   264                      }
   265                      break;
   266                  }
   267              }
   268          } else {
   269              pushSigner(_genesisSignerList[0], 3);
   270          }
   271          // 随机填满整个 signerList , 走到这个逻辑时 volunteer 一定比 signers 多,所以一定能填满
   272          // 这个地方循环下来很可能造成 signerList.length < signerLimit 的情况, 后续需要补充
   273          uint[] memory tiList = new uint[](signerLimit);
   274          uint ii = 0;
   275          for (uint i2 = 0; i2 < _volunteerList.length; i2++) {
   276              if (ii >= signerLimit) break;
   277              uint ti = getRandomIdx(_volunteerList[i2], _volunteerList.length - uint(1));
   278              if (repeatTi(tiList, ti)) continue;
   279              pushSigner(_volunteerList[ti], 3);
   280              tiList[ii] = ti;
   281              ii = ii + 1;
   282          }
   283          // 如果不满需要补满
   284          if (ii < signerLimit) {
   285              for (uint i3 = 0; i3 < _volunteerList.length; i3++) {
   286                  //不存在就放进去
   287                  if (signersMap[_volunteerList[i3]].number == 0) pushSigner(_volunteerList[i3], 3);
   288                  //放满了就退出循环
   289                  if (_signerList.length >= signerLimit) break;
   290              }
   291          }
   292      }
   293  
   294  
   295      /* rule 3 : 出块节点列表已满,候选节点列表大于出块节点列表
   296  
   297          在这个规则生效时,签名节点的分数已经没有意义了,
   298          此时的规则是每出一轮块就要替换掉全部的出块节点,
   299          从候选节点列表中按 weight 随机提拔一批新的出块节点到出块节点列表,
   300          将原出块节点列表移入候选节点列表,并将 weight - 1,
   301          当 weight == 0 时则移入黑名单,等待下一个 epoch
   302          假设出块节点列表最大长度 17 ,候选节点列表最大长度与 epoch 相等。每一轮出块,指的就是每 17 个块,每笔交易的确认时间也是 17 块,但是对于交易所来说应该至少经过 34 个块才能确认一笔交易。
   303      */
   304      function updateRule3() private {
   305          uint l = _signerList.length;
   306          uint signerIdx = uint(blockNumber % l);
   307          address si = _signerList[signerIdx];
   308          //1 : 初始签名人不做处理,不是正常签名人 0 分放回志愿者列表,否则 weight - 1
   309          if (signerIdx > uint(0)) {
   310              // 序号对应的不是我,把序号对应的 signer 以 weight=0 扔回志愿者列表 (其实就是删除)
   311              if (msg.sender != si) {
   312                  pushVolunteer(si, 0);
   313                  //此处还不能直接删除,因为不能破坏列表长度,否则对后续取模逻辑有影响,用 0 占位吧
   314                  delete signersMap[si];
   315                  _signerList[signerIdx] = uint160(0);
   316              }
   317          }
   318  
   319          //2 : 如果当前块是签名人列表的最后一块,则生成下一轮的列表
   320          if (signerIdx == uint(l - 1)) {
   321              //if (volunteersMap[msg.sender].weight == 0) {pushVolunteer(msg.sender, 5);}
   322              //pushVolunteer(msg.sender, volunteersMap[msg.sender].weight - 1);
   323              generateSignersRule3();
   324          }
   325      }
   326  
   327  
   328  
   329      /*
   330      rule 1 : 出块节点列表未满
   331          每个节点3分,每错出或漏出一个块扣1分,0分时被放入黑名单
   332          在当前 epoch 不再被选拔
   333  
   334      rule 2 : 出块节点列表已满,候选节点列表小于出块节点列表
   335          此时主要工作是选拔候选节点,为每个被选拔的节点设置 weight = 5,
   336          出块规则与 “出块节点列表未满” 时的规则相同
   337      */
   338      function updateRule1() private {
   339          fixRule1();
   340          // mine
   341          // 如果当前块 不是 signers[ blockNumber % signers.length ] 出的,就给这个 signer 减分
   342          // 否则恢复成 3 分
   343          uint signerIdx = blockNumber % _signerList.length;
   344          //初始签名人不做处理
   345          if (!genesisSigner[_signerList[signerIdx]]) {
   346  
   347              SignerInfo storage signer = signersMap[_signerList[signerIdx]];
   348  
   349              // 序号对应的不是我,则扣它一分
   350              if (msg.sender != _signerList[signerIdx]) {
   351                  if (signer.score > 1) {
   352                      signer.score -= 1;
   353                      signer.number = blockNumber;
   354                  } else {
   355                      // move to blacklist and cannot be selected in this epoch
   356                      pushVolunteer(_signerList[signerIdx], 0);
   357                      // vsn-0.0.3
   358                      // score == 0 , remove on signerList
   359                      deleteSigner(signerIdx);
   360                  }
   361              } else {
   362                  // 恢复分数
   363                  signer.score = 3;
   364              }
   365          }
   366  
   367          //是否提拔一个人到签名人
   368          //后进先出的规则
   369          if (_signerList.length < signerLimit && _volunteerList.length > 0) {
   370              //将候选人列表首个添加到签名人列表
   371              pushSigner(_volunteerList[_volunteerList.length - 1], 3);
   372              //删除该候补志愿者
   373              deleteVolunteer(_volunteerList.length - 1);
   374          }
   375      }
   376  
   377      function fixRule1() private {
   378          // clean signers
   379          for (uint i = 0; i < _signerList.length; i++) {
   380              if (_signerList[i] == uint160(0)) {
   381                  deleteSigner(i);
   382                  i--;
   383              }
   384          }
   385          // clean volunteers
   386          // volunteers.length <= signerLimit now.
   387          for (uint j = 0; j < _signerList.length; j++) {
   388              address v = _signerList[j];
   389              if (volunteersMap[v].number > 0) {
   390                  for (uint k = 0; k < _volunteerList.length; k++) {
   391                      if (v == _volunteerList[k]) {
   392                          deleteVolunteer(k);
   393                          break;
   394                      }
   395                  }
   396              }
   397          }
   398      }
   399  
   400      function update(address volunteer) public apply(msg.sender) {
   401          blockNumber = block.number;
   402          // 每个 epoch 的行为都是固定的,执行完了会决定接下来的行为
   403          if (blockNumber > epoch && blockNumber % epoch == 0) {
   404              // 先清理 blacklist , 因为清理志愿者时还会产生新的 blacklist 成员
   405              _cleanBlacklist();
   406              // 志愿者列表是否需要清理,这是个问题
   407              //_cleanVolunteerList();
   408          }
   409  
   410          // 选拔一个志愿者
   411          if (volunteer != uint160(0)) {
   412              pushVolunteer(volunteer, 5);
   413          }
   414  
   415          if (_signerList.length < signerLimit || _volunteerList.length < _signerList.length) {
   416              // rule 1 和 rule 2 合并为一个函数
   417              updateRule1();
   418          } else {
   419              // rule 3 开始执行新规则
   420              updateRule3();
   421          }
   422      }
   423  
   424      function version() public constant returns (string) {
   425          return vsn;
   426      }
   427  
   428      function getSignerLimit() public constant returns (uint) {
   429          return signerLimit;
   430      }
   431  
   432      function getEpoch() public constant returns (uint) {
   433          return epoch;
   434      }
   435  
   436      function getVolunteerLimit() public constant returns (uint) {
   437          return volunteerLimit;
   438      }
   439  
   440  
   441      function getStatus() public constant returns (
   442      //address[] volunteerList,
   443          address[] signerList,
   444          address[] blackList, // vsn 0.0.3
   445          uint[] memory scoreList,
   446          uint[] memory numberList,
   447          uint totalVolunteer,
   448          uint number
   449      ) {
   450          scoreList = new uint[](_signerList.length);
   451          numberList = new uint[](_signerList.length);
   452          for (uint i = 0; i < _signerList.length; i ++) {
   453              scoreList[i] = signersMap[_signerList[i]].score;
   454              numberList[i] = signersMap[_signerList[i]].number;
   455          }
   456          //这个还是单独获取吧,没有任何意义
   457          //volunteerList = new address[](0);
   458          totalVolunteer = _volunteerList.length;
   459          blackList = _blackList;
   460          signerList = _signerList;
   461          // vsn 0.0.3
   462          number = blockNumber;
   463          return;
   464      }
   465  
   466      // 鉴别一批志愿者是否可用
   467      function filterVolunteer(address[] volunteers) public constant returns (uint[] result) {
   468          result = new uint[](volunteers.length);
   469          if (_volunteerList.length < volunteerLimit) {
   470              for (uint i = 0; i < volunteers.length; i++) {
   471                  address volunteer = volunteers[i];
   472                  if (volunteersMap[volunteer].number == 0 && blMap[volunteer] == 0 && signersMap[volunteer].number == 0) {
   473                      result[i] = 1;
   474                      // true
   475                  } else {
   476                      result[i] = 0;
   477                      // false
   478                  }
   479              }
   480          }
   481          return;
   482      }
   483  
   484      function getVolunteers() public constant returns (
   485          address[] volunteerList,
   486          uint[] weightList,
   487          uint length
   488      ){
   489          weightList = new uint[](_volunteerList.length);
   490          volunteerList = _volunteerList;
   491          for (uint i = 0; i < _volunteerList.length; i++) {
   492              weightList[i] = volunteersMap[_volunteerList[i]].weight;
   493          }
   494          length = volunteerList.length;
   495          return;
   496      }
   497  
   498      /*
   499      //================
   500      // TEST AND DEBUG
   501      //================
   502      function fillSignerForTest() public {
   503          //TODO : for test >>>>
   504          address g2 = uint160(371457687117486736155821182390123011782146942856);
   505          genesisSigner[g2] = true;
   506          _genesisSignerList.push(g2);
   507          address g3 = uint160(371457687117486736155821182390123011782146942857);
   508          genesisSigner[g3] = true;
   509          _genesisSignerList.push(g3);
   510          address g4 = uint160(371457687117486736155821182390123011782146942858);
   511          genesisSigner[g4] = true;
   512          _genesisSignerList.push(g4);
   513          //TODO : for test <<<<
   514  
   515          //0xca35b7d915458ef540ade6068dfe2f44e8fa733c
   516          pushSigner(uint160(1154414090619811796818182302139415280051214250812), 3);
   517          //0xca35b7d915458ef540ade6068dfe2f44e8fa733d
   518          pushSigner(uint160(1154414090619811796818182302139415280051214250813), 3);
   519  
   520          blockNumber = block.number;
   521          fillVolunteerForTest();
   522      }
   523      function fillVolunteerForTest() public {
   524          //0xca35b7d915458ef540ade6068dfe2f44e8fa7330
   525          uint160 b = uint160(1154414090619811796818182302139415280051214250800);
   526          uint n = now;
   527          for (uint i = n; i < n + 10; i++) {
   528              pushVolunteer(uint160(b + i), 5);
   529          }
   530      }
   531      */
   532  
   533  }