github.com/XinFinOrg/xdcchain@v1.1.0/contracts/validator/contract/XDCValidator.sol (about) 1 2 pragma solidity ^0.4.21; 3 4 import "./libs/SafeMath.sol"; 5 6 7 contract XDCValidator { 8 using SafeMath for uint256; 9 10 event Vote(address _voter, address _candidate, uint256 _cap); 11 event Unvote(address _voter, address _candidate, uint256 _cap); 12 event Propose(address _owner, address _candidate, uint256 _cap); 13 event Resign(address _owner, address _candidate); 14 event Withdraw(address _owner, uint256 _blockNumber, uint256 _cap); 15 event UploadedKYC(address _owner,string kycHash); 16 event InvalidatedNode(address _masternodeOwner, address[] _masternodes); 17 18 struct ValidatorState { 19 address owner; 20 bool isCandidate; 21 uint256 cap; 22 mapping(address => uint256) voters; 23 } 24 25 struct WithdrawState { 26 mapping(uint256 => uint256) caps; 27 uint256[] blockNumbers; 28 } 29 30 mapping(address => WithdrawState) withdrawsState; 31 32 mapping(address => ValidatorState) validatorsState; 33 mapping(address => address[]) voters; 34 35 // Mapping structures added for KYC feature. 36 mapping(address => string[]) public KYCString; 37 mapping(address => uint) public invalidKYCCount; 38 mapping(address => mapping(address => bool)) public hasVotedInvalid; 39 mapping(address => address[]) public ownerToCandidate; 40 address[] public owners; 41 42 address[] public candidates; 43 44 uint256 public candidateCount = 0; 45 uint256 public ownerCount =0; 46 uint256 public minCandidateCap; 47 uint256 public minVoterCap; 48 uint256 public maxValidatorNumber; 49 uint256 public candidateWithdrawDelay; 50 uint256 public voterWithdrawDelay; 51 52 modifier onlyValidCandidateCap { 53 // anyone can deposit X XDC to become a candidate 54 require(msg.value >= minCandidateCap); 55 _; 56 } 57 58 modifier onlyValidVoterCap { 59 60 require(msg.value >= minVoterCap); 61 _; 62 } 63 64 modifier onlyKYCWhitelisted { 65 require(KYCString[msg.sender].length!=0 || ownerToCandidate[msg.sender].length>0); 66 _; 67 } 68 69 modifier onlyOwner(address _candidate) { 70 require(validatorsState[_candidate].owner == msg.sender); 71 _; 72 } 73 74 modifier onlyCandidate(address _candidate) { 75 require(validatorsState[_candidate].isCandidate); 76 _; 77 } 78 79 modifier onlyValidCandidate (address _candidate) { 80 require(validatorsState[_candidate].isCandidate); 81 _; 82 } 83 84 modifier onlyNotCandidate (address _candidate) { 85 require(!validatorsState[_candidate].isCandidate); 86 _; 87 } 88 89 modifier onlyValidVote (address _candidate, uint256 _cap) { 90 require(validatorsState[_candidate].voters[msg.sender] >= _cap); 91 if (validatorsState[_candidate].owner == msg.sender) { 92 require(validatorsState[_candidate].voters[msg.sender].sub(_cap) >= minCandidateCap); 93 } 94 _; 95 } 96 97 modifier onlyValidWithdraw (uint256 _blockNumber, uint _index) { 98 require(_blockNumber > 0); 99 require(block.number >= _blockNumber); 100 require(withdrawsState[msg.sender].caps[_blockNumber] > 0); 101 require(withdrawsState[msg.sender].blockNumbers[_index] == _blockNumber); 102 _; 103 } 104 105 function XDCValidator ( 106 address[] _candidates, 107 uint256[] _caps, 108 address _firstOwner, 109 uint256 _minCandidateCap, 110 uint256 _minVoterCap, 111 uint256 _maxValidatorNumber, 112 uint256 _candidateWithdrawDelay, 113 uint256 _voterWithdrawDelay 114 ) public { 115 minCandidateCap = _minCandidateCap; 116 minVoterCap = _minVoterCap; 117 maxValidatorNumber = _maxValidatorNumber; 118 candidateWithdrawDelay = _candidateWithdrawDelay; 119 voterWithdrawDelay = _voterWithdrawDelay; 120 candidateCount = _candidates.length; 121 owners.push(_firstOwner); 122 ownerCount++; 123 for (uint256 i = 0; i < _candidates.length; i++) { 124 candidates.push(_candidates[i]); 125 validatorsState[_candidates[i]] = ValidatorState({ 126 owner: _firstOwner, 127 isCandidate: true, 128 cap: _caps[i] 129 }); 130 voters[_candidates[i]].push(_firstOwner); 131 ownerToCandidate[_firstOwner].push(_candidates[i]); 132 validatorsState[_candidates[i]].voters[_firstOwner] = minCandidateCap; 133 } 134 } 135 136 137 // uploadKYC : anyone can upload a KYC; its not equivalent to becoming an owner. 138 function uploadKYC(string kychash) external { 139 KYCString[msg.sender].push(kychash); 140 emit UploadedKYC(msg.sender,kychash); 141 } 142 143 // propose : any non-candidate who has uploaded its KYC can become an owner by proposing a candidate. 144 function propose(address _candidate) external payable onlyValidCandidateCap onlyKYCWhitelisted onlyNotCandidate(_candidate) { 145 uint256 cap = validatorsState[_candidate].cap.add(msg.value); 146 candidates.push(_candidate); 147 validatorsState[_candidate] = ValidatorState({ 148 owner: msg.sender, 149 isCandidate: true, 150 cap: cap 151 }); 152 validatorsState[_candidate].voters[msg.sender] = validatorsState[_candidate].voters[msg.sender].add(msg.value); 153 candidateCount = candidateCount.add(1); 154 if (ownerToCandidate[msg.sender].length ==0){ 155 owners.push(msg.sender); 156 ownerCount++; 157 } 158 ownerToCandidate[msg.sender].push(_candidate); 159 voters[_candidate].push(msg.sender); 160 emit Propose(msg.sender, _candidate, msg.value); 161 } 162 163 function vote(address _candidate) external payable onlyValidVoterCap onlyValidCandidate(_candidate) { 164 validatorsState[_candidate].cap = validatorsState[_candidate].cap.add(msg.value); 165 if (validatorsState[_candidate].voters[msg.sender] == 0) { 166 voters[_candidate].push(msg.sender); 167 } 168 validatorsState[_candidate].voters[msg.sender] = validatorsState[_candidate].voters[msg.sender].add(msg.value); 169 emit Vote(msg.sender, _candidate, msg.value); 170 } 171 172 function getCandidates() public view returns(address[]) { 173 return candidates; 174 } 175 176 function getCandidateCap(address _candidate) public view returns(uint256) { 177 return validatorsState[_candidate].cap; 178 } 179 180 function getCandidateOwner(address _candidate) public view returns(address) { 181 return validatorsState[_candidate].owner; 182 } 183 184 function getVoterCap(address _candidate, address _voter) public view returns(uint256) { 185 return validatorsState[_candidate].voters[_voter]; 186 } 187 188 function getVoters(address _candidate) public view returns(address[]) { 189 return voters[_candidate]; 190 } 191 192 function isCandidate(address _candidate) public view returns(bool) { 193 return validatorsState[_candidate].isCandidate; 194 } 195 196 function getWithdrawBlockNumbers() public view returns(uint256[]) { 197 return withdrawsState[msg.sender].blockNumbers; 198 } 199 200 function getWithdrawCap(uint256 _blockNumber) public view returns(uint256) { 201 return withdrawsState[msg.sender].caps[_blockNumber]; 202 } 203 204 function unvote(address _candidate, uint256 _cap) public onlyValidVote(_candidate, _cap) { 205 validatorsState[_candidate].cap = validatorsState[_candidate].cap.sub(_cap); 206 validatorsState[_candidate].voters[msg.sender] = validatorsState[_candidate].voters[msg.sender].sub(_cap); 207 208 // refund after delay X blocks 209 uint256 withdrawBlockNumber = voterWithdrawDelay.add(block.number); 210 withdrawsState[msg.sender].caps[withdrawBlockNumber] = withdrawsState[msg.sender].caps[withdrawBlockNumber].add(_cap); 211 withdrawsState[msg.sender].blockNumbers.push(withdrawBlockNumber); 212 213 emit Unvote(msg.sender, _candidate, _cap); 214 } 215 216 function resign(address _candidate) public onlyOwner(_candidate) onlyCandidate(_candidate) { 217 validatorsState[_candidate].isCandidate = false; 218 candidateCount = candidateCount.sub(1); 219 for (uint256 i = 0; i < candidates.length; i++) { 220 if (candidates[i] == _candidate) { 221 delete candidates[i]; 222 break; 223 } 224 } 225 uint256 cap = validatorsState[_candidate].voters[msg.sender]; 226 validatorsState[_candidate].cap = validatorsState[_candidate].cap.sub(cap); 227 validatorsState[_candidate].voters[msg.sender] = 0; 228 // refunding after resigning X blocks 229 uint256 withdrawBlockNumber = candidateWithdrawDelay.add(block.number); 230 withdrawsState[msg.sender].caps[withdrawBlockNumber] = withdrawsState[msg.sender].caps[withdrawBlockNumber].add(cap); 231 withdrawsState[msg.sender].blockNumbers.push(withdrawBlockNumber); 232 emit Resign(msg.sender, _candidate); 233 } 234 235 // voteInvalidKYC : any candidate can vote for invalid KYC i.e. a particular candidate's owner has uploaded a bad KYC. 236 // On securing 75% votes against an owner ( not candidate ), owner & all its candidates will lose their funds. 237 function voteInvalidKYC(address _invalidCandidate) onlyValidCandidate(msg.sender) onlyValidCandidate(_invalidCandidate) public { 238 address candidateOwner = getCandidateOwner(msg.sender); 239 address _invalidMasternode = getCandidateOwner(_invalidCandidate); 240 require(!hasVotedInvalid[candidateOwner][_invalidMasternode]); 241 hasVotedInvalid[candidateOwner][_invalidMasternode] = true; 242 invalidKYCCount[_invalidMasternode] += 1; 243 if( invalidKYCCount[_invalidMasternode]*100/getOwnerCount() >= 75 ){ 244 // 75% owners say that the KYC is invalid 245 address[] memory allMasternodes = new address[](candidates.length-1) ; 246 uint count=0; 247 for (uint i=0;i<candidates.length;i++){ 248 if (getCandidateOwner(candidates[i])==_invalidMasternode){ 249 // logic to remove cap. 250 candidateCount = candidateCount.sub(1); 251 allMasternodes[count++] = candidates[i]; 252 delete candidates[i]; 253 delete validatorsState[candidates[i]]; 254 delete KYCString[_invalidMasternode]; 255 delete ownerToCandidate[_invalidMasternode]; 256 delete invalidKYCCount[_invalidMasternode]; 257 } 258 } 259 for(uint k=0;k<owners.length;k++){ 260 if (owners[k]==_invalidMasternode){ 261 delete owners[k]; 262 ownerCount--; 263 break; 264 } 265 } 266 emit InvalidatedNode(_invalidMasternode,allMasternodes); 267 } 268 } 269 270 // invalidPercent : get votes against an owner in percentage. 271 function invalidPercent(address _invalidCandidate) onlyValidCandidate(_invalidCandidate) view public returns(uint){ 272 address _invalidMasternode = getCandidateOwner(_invalidCandidate); 273 return (invalidKYCCount[_invalidMasternode]*100/getOwnerCount()); 274 } 275 276 277 // getOwnerCount : get count of total owners; accounts who own atleast one masternode. 278 function getOwnerCount() view public returns (uint){ 279 return ownerCount; 280 } 281 282 // getKYC : get KYC uploaded of the owner of the given masternode or the owner themselves 283 function getLatestKYC(address _address) view public returns (string) { 284 if(isCandidate(_address)){ 285 return KYCString[getCandidateOwner(_address)][KYCString[getCandidateOwner(_address)].length-1]; 286 } 287 else{ 288 return KYCString[_address][KYCString[_address].length-1]; 289 } 290 } 291 292 function getHashCount(address _address) view public returns(uint){ 293 return KYCString[_address].length; 294 } 295 296 function withdraw(uint256 _blockNumber, uint _index) public onlyValidWithdraw(_blockNumber, _index) { 297 uint256 cap = withdrawsState[msg.sender].caps[_blockNumber]; 298 delete withdrawsState[msg.sender].caps[_blockNumber]; 299 delete withdrawsState[msg.sender].blockNumbers[_index]; 300 msg.sender.transfer(cap); 301 emit Withdraw(msg.sender, _blockNumber, cap); 302 } 303 }