Update XDCValidator.sol

This commit is contained in:
Galaxy 2024-02-26 11:44:43 +04:00
parent 1720858916
commit dae2413c4c

View file

@ -1,9 +1,7 @@
pragma solidity ^0.4.21; pragma solidity ^0.4.21;
import "./libs/SafeMath.sol"; import "./libs/SafeMath.sol";
contract XDCValidator { contract XDCValidator {
using SafeMath for uint256; using SafeMath for uint256;
@ -12,7 +10,7 @@ contract XDCValidator {
event Propose(address _owner, address _candidate, uint256 _cap); event Propose(address _owner, address _candidate, uint256 _cap);
event Resign(address _owner, address _candidate); event Resign(address _owner, address _candidate);
event Withdraw(address _owner, uint256 _blockNumber, uint256 _cap); event Withdraw(address _owner, uint256 _blockNumber, uint256 _cap);
event UploadedKYC(address _owner,string kycHash); event UploadedKYC(address _owner, string kycHash);
event InvalidatedNode(address _masternodeOwner, address[] _masternodes); event InvalidatedNode(address _masternodeOwner, address[] _masternodes);
struct ValidatorState { struct ValidatorState {
@ -23,8 +21,8 @@ contract XDCValidator {
} }
struct WithdrawState { struct WithdrawState {
mapping(uint256 => uint256) caps; mapping(uint256 => uint256) caps;
uint256[] blockNumbers; uint256[] blockNumbers;
} }
mapping(address => WithdrawState) withdrawsState; mapping(address => WithdrawState) withdrawsState;
@ -42,28 +40,30 @@ contract XDCValidator {
address[] public candidates; address[] public candidates;
uint256 public candidateCount = 0; uint256 public candidateCount = 0;
uint256 public ownerCount =0; uint256 public ownerCount = 0;
uint256 public minCandidateCap; uint256 public minCandidateCap;
uint256 public minVoterCap; uint256 public minVoterCap;
uint256 public maxValidatorNumber; uint256 public maxValidatorNumber;
uint256 public candidateWithdrawDelay; uint256 public candidateWithdrawDelay;
uint256 public voterWithdrawDelay; uint256 public voterWithdrawDelay;
modifier onlyValidCandidateCap { modifier onlyValidCandidateCap() {
// anyone can deposit X XDC to become a candidate // anyone can deposit X XDC to become a candidate
require(msg.value >= minCandidateCap); require(msg.value >= minCandidateCap);
_; _;
} }
modifier onlyValidVoterCap { modifier onlyValidVoterCap() {
require(msg.value >= minVoterCap); require(msg.value >= minVoterCap);
_; _;
} }
modifier onlyKYCWhitelisted { modifier onlyKYCWhitelisted() {
require(KYCString[msg.sender].length!=0 || ownerToCandidate[msg.sender].length>0); require(
_; KYCString[msg.sender].length != 0 ||
ownerToCandidate[msg.sender].length > 0
);
_;
} }
modifier onlyOwner(address _candidate) { modifier onlyOwner(address _candidate) {
@ -76,33 +76,38 @@ contract XDCValidator {
_; _;
} }
modifier onlyValidCandidate (address _candidate) { modifier onlyValidCandidate(address _candidate) {
require(validatorsState[_candidate].isCandidate); require(validatorsState[_candidate].isCandidate);
_; _;
} }
modifier onlyNotCandidate (address _candidate) { modifier onlyNotCandidate(address _candidate) {
require(!validatorsState[_candidate].isCandidate); require(!validatorsState[_candidate].isCandidate);
_; _;
} }
modifier onlyValidVote (address _candidate, uint256 _cap) { modifier onlyValidVote(address _candidate, uint256 _cap) {
require(validatorsState[_candidate].voters[msg.sender] >= _cap); require(validatorsState[_candidate].voters[msg.sender] >= _cap);
if (validatorsState[_candidate].owner == msg.sender) { if (validatorsState[_candidate].owner == msg.sender) {
require(validatorsState[_candidate].voters[msg.sender].sub(_cap) >= minCandidateCap); require(
validatorsState[_candidate].voters[msg.sender].sub(_cap) >=
minCandidateCap
);
} }
_; _;
} }
modifier onlyValidWithdraw (uint256 _blockNumber, uint _index) { modifier onlyValidWithdraw(uint256 _blockNumber, uint _index) {
require(_blockNumber > 0); require(_blockNumber > 0);
require(block.number >= _blockNumber); require(block.number >= _blockNumber);
require(withdrawsState[msg.sender].caps[_blockNumber] > 0); require(withdrawsState[msg.sender].caps[_blockNumber] > 0);
require(withdrawsState[msg.sender].blockNumbers[_index] == _blockNumber); require(
withdrawsState[msg.sender].blockNumbers[_index] == _blockNumber
);
_; _;
} }
function XDCValidator ( function XDCValidator(
address[] _candidates, address[] _candidates,
uint256[] _caps, uint256[] _caps,
address _firstOwner, address _firstOwner,
@ -129,19 +134,28 @@ contract XDCValidator {
}); });
voters[_candidates[i]].push(_firstOwner); voters[_candidates[i]].push(_firstOwner);
ownerToCandidate[_firstOwner].push(_candidates[i]); ownerToCandidate[_firstOwner].push(_candidates[i]);
validatorsState[_candidates[i]].voters[_firstOwner] = minCandidateCap; validatorsState[_candidates[i]].voters[
_firstOwner
] = minCandidateCap;
} }
} }
// uploadKYC : anyone can upload a KYC; its not equivalent to becoming an owner. // uploadKYC : anyone can upload a KYC; its not equivalent to becoming an owner.
function uploadKYC(string kychash) external { function uploadKYC(string kychash) external {
KYCString[msg.sender].push(kychash); KYCString[msg.sender].push(kychash);
emit UploadedKYC(msg.sender,kychash); emit UploadedKYC(msg.sender, kychash);
} }
// propose : any non-candidate who has uploaded its KYC can become an owner by proposing a candidate. // propose : any non-candidate who has uploaded its KYC can become an owner by proposing a candidate.
function propose(address _candidate) external payable onlyValidCandidateCap onlyKYCWhitelisted onlyNotCandidate(_candidate) { function propose(
address _candidate
)
external
payable
onlyValidCandidateCap
onlyKYCWhitelisted
onlyNotCandidate(_candidate)
{
uint256 cap = validatorsState[_candidate].cap.add(msg.value); uint256 cap = validatorsState[_candidate].cap.add(msg.value);
candidates.push(_candidate); candidates.push(_candidate);
validatorsState[_candidate] = ValidatorState({ validatorsState[_candidate] = ValidatorState({
@ -149,9 +163,11 @@ contract XDCValidator {
isCandidate: true, isCandidate: true,
cap: cap cap: cap
}); });
validatorsState[_candidate].voters[msg.sender] = validatorsState[_candidate].voters[msg.sender].add(msg.value); validatorsState[_candidate].voters[msg.sender] = validatorsState[
_candidate
].voters[msg.sender].add(msg.value);
candidateCount = candidateCount.add(1); candidateCount = candidateCount.add(1);
if (ownerToCandidate[msg.sender].length ==0){ if (ownerToCandidate[msg.sender].length == 0) {
owners.push(msg.sender); owners.push(msg.sender);
ownerCount++; ownerCount++;
} }
@ -160,144 +176,220 @@ contract XDCValidator {
emit Propose(msg.sender, _candidate, msg.value); emit Propose(msg.sender, _candidate, msg.value);
} }
function vote(address _candidate) external payable onlyValidVoterCap onlyValidCandidate(_candidate) { function vote(
validatorsState[_candidate].cap = validatorsState[_candidate].cap.add(msg.value); address _candidate
) external payable onlyValidVoterCap onlyValidCandidate(_candidate) {
validatorsState[_candidate].cap = validatorsState[_candidate].cap.add(
msg.value
);
if (validatorsState[_candidate].voters[msg.sender] == 0) { if (validatorsState[_candidate].voters[msg.sender] == 0) {
voters[_candidate].push(msg.sender); voters[_candidate].push(msg.sender);
} }
validatorsState[_candidate].voters[msg.sender] = validatorsState[_candidate].voters[msg.sender].add(msg.value); validatorsState[_candidate].voters[msg.sender] = validatorsState[
_candidate
].voters[msg.sender].add(msg.value);
emit Vote(msg.sender, _candidate, msg.value); emit Vote(msg.sender, _candidate, msg.value);
} }
function getCandidates() public view returns(address[]) { function getCandidates() public view returns (address[]) {
return candidates; return candidates;
} }
function getCandidateCap(address _candidate) public view returns(uint256) { function getCandidateCap(address _candidate) public view returns (uint256) {
return validatorsState[_candidate].cap; return validatorsState[_candidate].cap;
} }
function getCandidateOwner(address _candidate) public view returns(address) { function getCandidateOwner(
address _candidate
) public view returns (address) {
return validatorsState[_candidate].owner; return validatorsState[_candidate].owner;
} }
function getVoterCap(address _candidate, address _voter) public view returns(uint256) { function getVoterCap(
address _candidate,
address _voter
) public view returns (uint256) {
return validatorsState[_candidate].voters[_voter]; return validatorsState[_candidate].voters[_voter];
} }
function getVoters(address _candidate) public view returns(address[]) { function getVoters(address _candidate) public view returns (address[]) {
return voters[_candidate]; return voters[_candidate];
} }
function isCandidate(address _candidate) public view returns(bool) { function isCandidate(address _candidate) public view returns (bool) {
return validatorsState[_candidate].isCandidate; return validatorsState[_candidate].isCandidate;
} }
function getWithdrawBlockNumbers() public view returns(uint256[]) { function getWithdrawBlockNumbers() public view returns (uint256[]) {
return withdrawsState[msg.sender].blockNumbers; return withdrawsState[msg.sender].blockNumbers;
} }
function getWithdrawCap(uint256 _blockNumber) public view returns(uint256) { function getWithdrawCap(
uint256 _blockNumber
) public view returns (uint256) {
return withdrawsState[msg.sender].caps[_blockNumber]; return withdrawsState[msg.sender].caps[_blockNumber];
} }
function unvote(address _candidate, uint256 _cap) public onlyValidVote(_candidate, _cap) { function unvote(
validatorsState[_candidate].cap = validatorsState[_candidate].cap.sub(_cap); address _candidate,
validatorsState[_candidate].voters[msg.sender] = validatorsState[_candidate].voters[msg.sender].sub(_cap); uint256 _cap
) public onlyValidVote(_candidate, _cap) {
validatorsState[_candidate].cap = validatorsState[_candidate].cap.sub(
_cap
);
validatorsState[_candidate].voters[msg.sender] = validatorsState[
_candidate
].voters[msg.sender].sub(_cap);
// refund after delay X blocks // refund after delay X blocks
uint256 withdrawBlockNumber = voterWithdrawDelay.add(block.number); uint256 withdrawBlockNumber = voterWithdrawDelay.add(block.number);
withdrawsState[msg.sender].caps[withdrawBlockNumber] = withdrawsState[msg.sender].caps[withdrawBlockNumber].add(_cap); withdrawsState[msg.sender].caps[withdrawBlockNumber] = withdrawsState[
msg.sender
].caps[withdrawBlockNumber].add(_cap);
withdrawsState[msg.sender].blockNumbers.push(withdrawBlockNumber); withdrawsState[msg.sender].blockNumbers.push(withdrawBlockNumber);
emit Unvote(msg.sender, _candidate, _cap); emit Unvote(msg.sender, _candidate, _cap);
} }
function resign(address _candidate) public onlyOwner(_candidate) onlyCandidate(_candidate) { function resign(
address _candidate
) public onlyOwner(_candidate) onlyCandidate(_candidate) {
validatorsState[_candidate].isCandidate = false; validatorsState[_candidate].isCandidate = false;
candidateCount = candidateCount.sub(1); candidateCount = candidateCount.sub(1);
for (uint256 i = 0; i < candidates.length; i++) { for (uint256 i = 0; i < candidates.length; i++) {
if (candidates[i] == _candidate) { if (candidates[i] == _candidate) {
address[] memory cacheArray = ownerToCandidate[msg.sender];
uint256 cacheArrayLength = cacheArray.length;
for (uint256 j = 0; j < cacheArrayLength; j++) {
if (cacheArray[j] == _candidate) {
delete cacheArray[j];
}
}
ownerToCandidate[msg.sender] = removeZeroAddresses(cacheArray);
if (ownerToCandidate[msg.sender].length == 0) {
ownerCount--;
}
delete candidates[i]; delete candidates[i];
break; break;
} }
} }
candidates = removeZeroAddresses(candidates);
uint256 cap = validatorsState[_candidate].voters[msg.sender]; uint256 cap = validatorsState[_candidate].voters[msg.sender];
validatorsState[_candidate].cap = validatorsState[_candidate].cap.sub(cap); validatorsState[_candidate].cap = validatorsState[_candidate].cap.sub(
cap
);
validatorsState[_candidate].voters[msg.sender] = 0; validatorsState[_candidate].voters[msg.sender] = 0;
// refunding after resigning X blocks // refunding after resigning X blocks
uint256 withdrawBlockNumber = candidateWithdrawDelay.add(block.number); uint256 withdrawBlockNumber = candidateWithdrawDelay.add(block.number);
withdrawsState[msg.sender].caps[withdrawBlockNumber] = withdrawsState[msg.sender].caps[withdrawBlockNumber].add(cap); withdrawsState[msg.sender].caps[withdrawBlockNumber] = withdrawsState[
msg.sender
].caps[withdrawBlockNumber].add(cap);
withdrawsState[msg.sender].blockNumbers.push(withdrawBlockNumber); withdrawsState[msg.sender].blockNumbers.push(withdrawBlockNumber);
emit Resign(msg.sender, _candidate); emit Resign(msg.sender, _candidate);
} }
// voteInvalidKYC : any candidate can vote for invalid KYC i.e. a particular candidate's owner has uploaded a bad KYC. // voteInvalidKYC : any candidate can vote for invalid KYC i.e. a particular candidate's owner has uploaded a bad KYC.
// On securing 75% votes against an owner ( not candidate ), owner & all its candidates will lose their funds. // On securing 75% votes against an owner ( not candidate ), owner & all its candidates will lose their funds.
function voteInvalidKYC(address _invalidCandidate) onlyValidCandidate(msg.sender) onlyValidCandidate(_invalidCandidate) public { function voteInvalidKYC(
address _invalidCandidate
)
public
onlyValidCandidate(msg.sender)
onlyValidCandidate(_invalidCandidate)
{
address candidateOwner = getCandidateOwner(msg.sender); address candidateOwner = getCandidateOwner(msg.sender);
address _invalidMasternode = getCandidateOwner(_invalidCandidate); address _invalidMasternode = getCandidateOwner(_invalidCandidate);
require(!hasVotedInvalid[candidateOwner][_invalidMasternode]); require(!hasVotedInvalid[candidateOwner][_invalidMasternode]);
hasVotedInvalid[candidateOwner][_invalidMasternode] = true; hasVotedInvalid[candidateOwner][_invalidMasternode] = true;
invalidKYCCount[_invalidMasternode] += 1; invalidKYCCount[_invalidMasternode] += 1;
if( invalidKYCCount[_invalidMasternode]*100/getOwnerCount() >= 75 ){ if (
(invalidKYCCount[_invalidMasternode] * 100) / getOwnerCount() >= 75
) {
// 75% owners say that the KYC is invalid // 75% owners say that the KYC is invalid
address[] memory allMasternodes = new address[](candidates.length-1) ; address[] memory allMasternodes = new address[](
uint count=0; candidates.length - 1
for (uint i=0;i<candidates.length;i++){ );
if (getCandidateOwner(candidates[i])==_invalidMasternode){ uint count = 0;
for (uint i = 0; i < candidates.length; i++) {
if (getCandidateOwner(candidates[i]) == _invalidMasternode) {
// logic to remove cap. // logic to remove cap.
candidateCount = candidateCount.sub(1); candidateCount = candidateCount.sub(1);
allMasternodes[count++] = candidates[i]; allMasternodes[count++] = candidates[i];
delete candidates[i];
delete validatorsState[candidates[i]]; delete validatorsState[candidates[i]];
delete candidates[i];
delete KYCString[_invalidMasternode]; delete KYCString[_invalidMasternode];
delete ownerToCandidate[_invalidMasternode]; delete ownerToCandidate[_invalidMasternode];
delete invalidKYCCount[_invalidMasternode]; delete invalidKYCCount[_invalidMasternode];
} }
} }
for(uint k=0;k<owners.length;k++){ candidates = removeZeroAddresses(candidates);
if (owners[k]==_invalidMasternode){ for (uint k = 0; k < owners.length; k++) {
delete owners[k]; if (owners[k] == _invalidMasternode) {
ownerCount--; delete owners[k];
break; ownerCount--;
break;
} }
} }
emit InvalidatedNode(_invalidMasternode,allMasternodes); owners = removeZeroAddresses(owners);
emit InvalidatedNode(_invalidMasternode, allMasternodes);
} }
} }
// invalidPercent : get votes against an owner in percentage. // invalidPercent : get votes against an owner in percentage.
function invalidPercent(address _invalidCandidate) onlyValidCandidate(_invalidCandidate) view public returns(uint){ function invalidPercent(
address _invalidCandidate
) public view onlyValidCandidate(_invalidCandidate) returns (uint) {
address _invalidMasternode = getCandidateOwner(_invalidCandidate); address _invalidMasternode = getCandidateOwner(_invalidCandidate);
return (invalidKYCCount[_invalidMasternode]*100/getOwnerCount()); return ((invalidKYCCount[_invalidMasternode] * 100) / getOwnerCount());
} }
// getOwnerCount : get count of total owners; accounts who own atleast one masternode. // getOwnerCount : get count of total owners; accounts who own atleast one masternode.
function getOwnerCount() view public returns (uint){ function getOwnerCount() public view returns (uint) {
return ownerCount; return ownerCount;
} }
// getKYC : get KYC uploaded of the owner of the given masternode or the owner themselves // getKYC : get KYC uploaded of the owner of the given masternode or the owner themselves
function getLatestKYC(address _address) view public returns (string) { function getLatestKYC(address _address) public view returns (string) {
if(isCandidate(_address)){ if (isCandidate(_address)) {
return KYCString[getCandidateOwner(_address)][KYCString[getCandidateOwner(_address)].length-1]; return
} KYCString[getCandidateOwner(_address)][
else{ KYCString[getCandidateOwner(_address)].length - 1
return KYCString[_address][KYCString[_address].length-1]; ];
} else {
return KYCString[_address][KYCString[_address].length - 1];
} }
} }
function getHashCount(address _address) view public returns(uint){ function getHashCount(address _address) public view returns (uint) {
return KYCString[_address].length; return KYCString[_address].length;
} }
function withdraw(uint256 _blockNumber, uint _index) public onlyValidWithdraw(_blockNumber, _index) { function withdraw(
uint256 _blockNumber,
uint _index
) public onlyValidWithdraw(_blockNumber, _index) {
uint256 cap = withdrawsState[msg.sender].caps[_blockNumber]; uint256 cap = withdrawsState[msg.sender].caps[_blockNumber];
delete withdrawsState[msg.sender].caps[_blockNumber]; delete withdrawsState[msg.sender].caps[_blockNumber];
delete withdrawsState[msg.sender].blockNumbers[_index]; delete withdrawsState[msg.sender].blockNumbers[_index];
msg.sender.transfer(cap); msg.sender.transfer(cap);
emit Withdraw(msg.sender, _blockNumber, cap); emit Withdraw(msg.sender, _blockNumber, cap);
} }
function removeZeroAddresses(
address[] memory addresses
) private pure returns (address[] memory) {
address[] memory newAddresses = new address[](addresses.length);
uint256 j = 0;
for (uint256 i = 0; i < addresses.length; i++) {
if (addresses[i] != address(0)) {
newAddresses[j] = addresses[i];
j++;
}
}
// Resize the array.
assembly {
mstore(newAddresses, j)
}
return newAddresses;
}
} }