投票机制设计
公链
说明:转载请说明出处,无端盗用必追责!
黑名单功能
使用:
1. 限制交易,即在gossipswitch提供blacklist,限制某些账户的交易;
2. 惩罚机制,如果被列入黑名单,不能参与出块;
设计:
1. 发起黑名单请求(必须是超级节点,才能发起设置黑名单的请求),附带惩罚原因,或者是公示的标记;
2. 超级节点对黑名单的提议内容进行审核投票,为了快速达成一致,超过1/3节点投票通过即设置;
3. 支持黑名单的设置和取消功能,建议投票超过2/3才能取消;
4. 投票过程分为两种:投赞成票,弃权(不进行投票);
5. 支持移除黑名单,也采用投票机制;
6. 一旦对某个黑名单进行投票,则不能取消;
代码实现:
1 pragma solidity >=0.4.24 <0.6.0; 2 3 /* 4 * 安全操作函数 5 * SafeMath to avoid data overwrite 6 */ 7 library SafeMath { 8 function mul(uint a, uint b) internal pure returns (uint) { 9 uint c = a * b; 10 require(a == 0 || c / a == b, "overwrite error"); 11 return c; 12 } 13 14 function div(uint a, uint b) internal pure returns (uint) { 15 require(b > 0, "overwrite error"); 16 uint c = a / b; 17 require(a == b * c + a % b, "overwrite error"); 18 return c; 19 } 20 21 function sub(uint a, uint b) internal pure returns (uint) { 22 require(b <= a, "overwrite error"); 23 return a - b; 24 } 25 26 function add(uint a, uint b) internal pure returns (uint) { 27 uint c = a + b; 28 require(c>=a && c>=b, "overwrite error"); 29 return c; 30 } 31 } 32 33 contract JustitiaRight { 34 uint256 public totalSupply; 35 36 function lockCount(address _account, uint _count) public; 37 function unlockCount(address _account, uint _count) public; 38 function residePledge(address _owner) public view returns(uint balance); 39 40 function balanceOf(address _owner) public view returns (uint256 balance); 41 function transfer(address _to, uint256 _value) public returns (bool success); 42 function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); 43 function approve(address _spender, uint256 _value) public returns (bool success); 44 function allowance(address _owner, address _spender) public view returns (uint256 remaining); 45 } 46 47 contract CandidateManage { 48 49 using SafeMath for uint; 50 uint256 public totalPledge; 51 uint constant MINIMUM_PLEDGE_TOKEN = 100; 52 53 JustitiaRight public justitia; 54 55 struct Candidate{ 56 address account; 57 uint pledge; // total support pledge 58 string memo; 59 uint ranking; 60 bool isValid; 61 } 62 address [] public CandidateList; 63 mapping(address => Candidate) public candidateLookup; 64 // mapping(support, totalPledge) 65 mapping(address => uint256) public balanceOfPledge; 66 67 // event define 68 event ApplyToCandidateEvent(address, bool, string); 69 70 // criterias that be a candidate 71 function candidateCriteria(address candidate, uint256 pledge) private view returns(bool){ 72 uint256 balance = justitia.residePledge(candidate); 73 if(MINIMUM_PLEDGE_TOKEN <= balance && balance >= pledge){ 74 return true; 75 } 76 return false; 77 } 78 79 // role classify 80 // normal: account with PR, which has right to vote 81 // participate: normal account which has participate in vote and has peldge currently 82 // candidate: account pledge PR to be a candidate 83 function isNormal(address _account) public view returns(bool){ 84 if(!isParticipate(_account) && !isCandidate(_account)){ 85 return true; 86 } 87 return false; 88 } 89 90 function isParticipate(address _account) public view returns(bool){ 91 if (0 != balanceOfPledge[_account]){ 92 return true; 93 } 94 return false; 95 } 96 97 function isCandidate(address account) public view returns(bool){ 98 return candidateLookup[account].isValid; 99 } 100 101 // get account balance statistic 102 function balanceStatistic(address _owner) public view returns (uint256 balance, uint256 pledge){ 103 require(address(0) != _owner); 104 105 uint256 total; 106 uint256 reside; 107 108 total = justitia.balanceOf(_owner); 109 reside = justitia.residePledge(_owner); 110 111 require(total.sub(reside) == balanceOfPledge[_owner]); 112 113 return (total, balanceOfPledge[_owner]); 114 } 115 116 // get candidate information 117 function candidateState(address candidate) public view returns(uint256, uint256, string){ 118 require(isCandidate(candidate)); 119 uint index; 120 for(index = 0; index < CandidateList.length; index++){ 121 if(CandidateList[index] == candidate){ 122 break; 123 } 124 } 125 return (index, candidateLookup[candidate].pledge, candidateLookup[candidate].memo); 126 } 127 128 // find index to insert the account by specified candidate in CandidateList 129 function findIndexOfCandidate(uint pledge) private view returns(uint){ 130 uint index; 131 for(index = 0; index < CandidateList.length; index++){ 132 if(candidateLookup[CandidateList[index]].pledge <= pledge){ 133 break; 134 } 135 } 136 return index; 137 } 138 139 // add applicant to candidate list 140 function addToCandidateListDescending(address applicant, uint pledge) private returns(uint){ 141 uint index; 142 index = findIndexOfCandidate(pledge); 143 CandidateList.push(applicant); 144 for(uint i = CandidateList.length - 1; i > index; i--){ 145 CandidateList[i] = CandidateList[i - 1]; 146 candidateLookup[CandidateList[i]].ranking = i; 147 } 148 CandidateList[index] = applicant; 149 candidateLookup[CandidateList[index]].ranking = index; 150 return index; 151 } 152 153 // candidate list adjustment 154 function adjustCandidateList(address candidate, uint pledge) public returns(uint){ 155 if(!isCandidate(candidate)){ 156 return addToCandidateListDescending(candidate, pledge); 157 } 158 159 uint currentIndex; 160 uint rightIndex; 161 for(currentIndex = 0; currentIndex < CandidateList.length; currentIndex++){ 162 if(CandidateList[currentIndex] == candidate){ 163 break; 164 } 165 if(candidateLookup[CandidateList[rightIndex]].pledge >= candidateLookup[candidate].pledge){ 166 rightIndex++; 167 } 168 169 } 170 171 // adding 172 if(rightIndex < currentIndex){ 173 for(uint i = currentIndex; i > rightIndex; i--){ 174 CandidateList[i] = CandidateList[i - 1]; 175 candidateLookup[CandidateList[i]].ranking = i; 176 } 177 } else { 178 for(uint j = currentIndex; j < rightIndex; j++){ 179 CandidateList[j] = CandidateList[j + 1]; 180 candidateLookup[CandidateList[j]].ranking = j; 181 } 182 } 183 184 CandidateList[rightIndex] = candidate; 185 candidateLookup[CandidateList[rightIndex]].ranking = rightIndex; 186 return rightIndex; 187 } 188 189 // apply to candidate 190 function ApplyToCandidate(uint pledge, string memo) public returns(bool, string){ 191 require(!isCandidate(msg.sender)); 192 193 string memory errors; 194 if(!candidateCriteria(msg.sender, pledge)){ 195 errors = "errors: some criterias not met."; 196 emit ApplyToCandidateEvent(msg.sender, false, errors); 197 return (false, errors); 198 } 199 200 totalPledge = totalPledge.add(pledge); 201 justitia.lockCount(msg.sender, pledge); 202 balanceOfPledge[msg.sender] = balanceOfPledge[msg.sender].add(pledge); 203 adjustCandidateList(msg.sender, pledge); 204 candidateLookup[msg.sender].memo = memo; 205 candidateLookup[msg.sender].isValid = true; 206 candidateLookup[msg.sender].pledge = candidateLookup[msg.sender].pledge.add(pledge); 207 candidateLookup[msg.sender].account = msg.sender; 208 emit ApplyToCandidateEvent(msg.sender, true, errors); 209 return (true, errors); 210 } 211 212 // get candidates 213 function Candidates() public view returns(address[]){ 214 return CandidateList; 215 } 216 } 217 218 219 contract BlackListElection { 220 using SafeMath for uint; 221 struct BlackListItem{ 222 string reason; 223 address []approveAccounts; 224 address []rejectAccounts; 225 mapping(address => bool) approveRecords; 226 mapping(address => bool) rejectRecoeds; 227 bool isValid; 228 uint index; 229 } 230 mapping(address => BlackListItem) public blackListItemLookup; 231 address [] public blackListProcessing; 232 233 function isRegiste(address _account) public view returns(bool){ 234 return blackListItemLookup[_account].isValid; 235 } 236 237 function toBlackList(address _account, string reason) public returns(uint){ 238 if(!isRegiste(_account)){ 239 blackListItemLookup[_account].reason = reason; 240 blackListItemLookup[_account].approveAccounts.push(_account); 241 blackListItemLookup[_account].approveRecords[msg.sender] = true; 242 blackListItemLookup[_account].isValid = true; 243 blackListItemLookup[_account].index = blackListProcessing.push(_account).sub(1); 244 } else { 245 if(!blackListItemLookup[_account].approveRecords[msg.sender]){ 246 blackListItemLookup[_account].approveAccounts.push(_account); 247 blackListItemLookup[_account].approveRecords[msg.sender] = true; 248 } 249 } 250 return blackListItemLookup[_account].approveAccounts.length; 251 } 252 253 function blackListToProcess() public view returns(address[]){ 254 return blackListProcessing; 255 } 256 257 function removeBlackList(address _account) public { 258 require(isRegiste(_account)); 259 delete blackListProcessing[blackListItemLookup[_account].index]; 260 delete blackListItemLookup[_account]; 261 } 262 } 263 264 265 contract BlackListManage is BlackListElection{ 266 267 uint public thresHoldToAddBlackList; 268 uint public thresHoldToRrmoveBlackList; 269 270 struct BlackList { 271 uint date; 272 bool isValid; 273 } 274 mapping(address => BlackList) public blackListLookup; 275 address [] public blackList; 276 277 event AddToBlackListEvent(address, string); 278 279 function isInBlackList(address _account) public view returns(bool){ 280 return blackListLookup[_account].isValid; 281 } 282 283 function voteForBlacklist(address _account, string comment) public { 284 uint supporters = toBlackList(_account, comment); 285 if (supporters >= thresHoldToAddBlackList){ 286 blackListLookup[_account].date = now; 287 blackListLookup[_account].isValid = true; 288 blackList.push(_account); 289 removeBlackList(_account); 290 emit AddToBlackListEvent(_account, blackListItemLookup[_account].reason); 291 } 292 } 293 294 function getBlackList() public view returns(address[]){ 295 return blackList; 296 } 297 298 } 299 300 /* 301 * 系统合约调用 302 * 管理选举情况,包括:选举,取消选举,选举情况统计等 303 */ 304 contract ElectionManage is CandidateManage, BlackListManage { 305 306 using SafeMath for uint; 307 uint public totalNodes; 308 uint constant ENTRY_HRESHOLD = 100; 309 bool private mainNetSwitch; 310 uint constant MAINNET_ONLINE_THRESHOLD = 1000; 311 312 event MainNetOnlineEvent(uint, uint); 313 event IssueVoteEvent(address, address, uint); 314 event AdjustmentVoteEvent(address, address, uint); 315 316 struct Election{ 317 bool isValid; 318 address[] participates; 319 mapping(address => uint) election; 320 } 321 // record candidate election 322 mapping(address => Election) private candidateElection; 323 324 // constructor 325 constructor (address token, uint nodeNum) public { 326 justitia = JustitiaRight(token); 327 totalNodes = nodeNum; 328 thresHoldToAddBlackList = nodeNum.div(3); 329 thresHoldToRrmoveBlackList = thresHoldToAddBlackList.mul(2) + 1; 330 } 331 332 // try to online main network 333 // we assume that once mainnet onlie, it will onlie forever 334 function tryToOnlineMainNet() private { 335 // only state changed, emit event 336 if(!mainNetSwitch){ 337 if(totalPledge >= MAINNET_ONLINE_THRESHOLD){ 338 mainNetSwitch = true; 339 emit MainNetOnlineEvent(now, totalPledge); 340 } 341 } 342 } 343 344 // to get the pledge of participate for specified candidate 345 function getPledgeFlow(address candidate, address participate) public view returns(uint){ 346 require(isCandidate(candidate)); 347 return candidateElection[candidate].election[participate]; 348 } 349 350 // to get all supporters of the specified candidate 351 function getSupportOfCandidate(address candidate) public view returns(address[]){ 352 require(isCandidate(candidate)); 353 return candidateElection[candidate].participates; 354 } 355 356 // get rank of candidate 357 function ranking(address _candidate) public view returns(uint){ 358 require(isCandidate(_candidate)); 359 require(address(0) != _candidate); 360 uint index; 361 uint rank; 362 for(index = 0; index < CandidateList.length; index++){ 363 if(isCandidate(_candidate)){ 364 rank++; 365 if(_candidate == CandidateList[index]){ 366 return rank; 367 } 368 } 369 } 370 } 371 372 // issue a election for candidate with some pledges 373 function issueVote(address candidate, uint pledge) private { 374 require(isCandidate(candidate)); 375 require(!isCandidate(msg.sender)); 376 require(pledge <= justitia.residePledge(msg.sender)); 377 378 if(!candidateElection[candidate].isValid){ 379 candidateElection[candidate].participates.push(msg.sender); 380 candidateElection[candidate].isValid = true; 381 } 382 candidateLookup[candidate].pledge = candidateLookup[candidate].pledge.add(pledge); 383 candidateElection[candidate].election[msg.sender] = candidateElection[candidate].election[msg.sender].add(pledge); 384 adjustCandidateList(candidate, candidateLookup[candidate].pledge); 385 balanceOfPledge[msg.sender] = balanceOfPledge[msg.sender].add(pledge); 386 totalPledge = totalPledge.add(pledge); 387 justitia.lockCount(msg.sender, pledge); 388 389 emit IssueVoteEvent(msg.sender, candidate, pledge); 390 } 391 392 // to adjustment of voting for specified candidate with pledge 393 function adjustmentVote(address candidate, uint pledge) private { 394 require(isCandidate(candidate)); 395 require(pledge <= candidateElection[candidate].election[msg.sender]); 396 397 candidateLookup[candidate].pledge = candidateLookup[candidate].pledge.sub(pledge); 398 candidateElection[candidate].election[msg.sender] = candidateElection[candidate].election[msg.sender].sub(pledge); 399 adjustCandidateList(candidate, candidateLookup[candidate].pledge); 400 balanceOfPledge[msg.sender] = balanceOfPledge[msg.sender].sub(pledge); 401 totalPledge = totalPledge.sub(pledge); 402 justitia.unlockCount(msg.sender, pledge); 403 404 emit AdjustmentVoteEvent(msg.sender, candidate, pledge); 405 } 406 407 function rightToVoteBlackList(address _account) private view returns(bool){ 408 require(isCandidate(_account)); 409 if(candidateLookup[_account].ranking < totalNodes){ 410 return true; 411 } 412 return false; 413 } 414 415 function GetOnlineSymbol() public view returns(bool){ 416 return mainNetSwitch; 417 } 418 419 function VoteAdjustment(address candidate, uint canceledPledge) public { 420 require(isCandidate(candidate)); 421 adjustmentVote(candidate, canceledPledge); 422 } 423 424 function Votting(address candidate, uint pledge) public{ 425 require(isCandidate(candidate)); 426 issueVote(candidate, pledge); 427 tryToOnlineMainNet(); 428 } 429 430 function SetBlackList(address _account) public{ 431 require(rightToVoteBlackList(msg.sender)); 432 if(!isInBlackList(_account)){ 433 voteForBlacklist(_account, "errors"); 434 } 435 } 436 }