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  }