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 }