github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/permission/v2/contract/VoterManager.sol (about) 1 pragma solidity ^0.5.3; 2 3 import "./PermissionsUpgradable.sol"; 4 5 /** @title Voter manager contract 6 * @notice This contract holds implementation logic for all account voter and 7 voting functionality. This can be called only by the implementation 8 contract only. there are few view functions exposed as public and 9 can be called directly. these are invoked by quorum for populating 10 permissions data in cache 11 * @dev each voting record has an attribute operation type (opType) 12 which denotes the activity type which is pending approval. This can 13 have the following values: 14 0 - None - indicates no pending records for the org 15 1 - New org add activity 16 2 - Org suspension activity 17 3 - Revoke of org suspension 18 4 - Assigning admin role for a new account 19 5 - Blacklisted node recovery 20 6 - Blacklisted account recovery 21 */ 22 contract VoterManager { 23 PermissionsUpgradable private permUpgradable; 24 struct PendingOpDetails { 25 string orgId; 26 string enodeId; 27 address account; 28 uint256 opType; 29 } 30 31 struct Voter { 32 address vAccount; 33 bool active; 34 } 35 36 struct OrgVoterDetails { 37 string orgId; 38 uint256 voterCount; 39 uint256 validVoterCount; 40 uint256 voteCount; 41 PendingOpDetails pendingOp; 42 Voter [] voterList; 43 mapping(address => uint256) voterIndex; 44 mapping(uint256 => mapping(address => bool)) votingStatus; 45 } 46 47 OrgVoterDetails [] private orgVoterList; 48 mapping(bytes32 => uint256) private VoterOrgIndex; 49 uint256 private orgNum = 0; 50 51 // events related to managing voting accounts for the org 52 event VoterAdded(string _orgId, address _vAccount); 53 event VoterDeleted(string _orgId, address _vAccount); 54 55 event VotingItemAdded(string _orgId); 56 event VoteProcessed(string _orgId); 57 58 /** @notice confirms that the caller is the address of implementation 59 contract 60 */ 61 modifier onlyImplementation { 62 require(msg.sender == permUpgradable.getPermImpl(), "invalid caller"); 63 _; 64 } 65 66 /** @notice checks if account is a valid voter record and belongs to the org 67 passed 68 * @param _orgId - org id 69 * @param _vAccount - voter account passed 70 */ 71 modifier voterExists(string memory _orgId, address _vAccount) { 72 require(_checkVoterExists(_orgId, _vAccount) == true, "must be a voter"); 73 _; 74 } 75 76 /** @notice constructor. sets the permissions upgradable address 77 */ 78 constructor (address _permUpgradable) public { 79 permUpgradable = PermissionsUpgradable(_permUpgradable); 80 } 81 82 /** @notice function to add a new voter account to the organization 83 * @param _orgId org id 84 * @param _vAccount - voter account 85 * @dev voter capability is currently enabled for network level activities 86 only. voting is not available for org related activities 87 */ 88 function addVoter(string calldata _orgId, address _vAccount) external 89 onlyImplementation { 90 // check if the org exists 91 if (VoterOrgIndex[keccak256(abi.encode(_orgId))] == 0) { 92 orgNum++; 93 VoterOrgIndex[keccak256(abi.encode(_orgId))] = orgNum; 94 uint256 id = orgVoterList.length++; 95 orgVoterList[id].orgId = _orgId; 96 orgVoterList[id].voterCount = 1; 97 orgVoterList[id].validVoterCount = 1; 98 orgVoterList[id].voteCount = 0; 99 orgVoterList[id].pendingOp.orgId = ""; 100 orgVoterList[id].pendingOp.enodeId = ""; 101 orgVoterList[id].pendingOp.account = address(0); 102 orgVoterList[id].pendingOp.opType = 0; 103 orgVoterList[id].voterIndex[_vAccount] = orgVoterList[id].voterCount; 104 orgVoterList[id].voterList.push(Voter(_vAccount, true)); 105 } 106 else { 107 uint256 id = _getVoterOrgIndex(_orgId); 108 // check if the voter is already present in the list 109 if (orgVoterList[id].voterIndex[_vAccount] == 0) { 110 orgVoterList[id].voterCount++; 111 orgVoterList[id].voterIndex[_vAccount] = orgVoterList[id].voterCount; 112 orgVoterList[id].voterList.push(Voter(_vAccount, true)); 113 orgVoterList[id].validVoterCount++; 114 } 115 else { 116 uint256 vid = _getVoterIndex(_orgId, _vAccount); 117 require(orgVoterList[id].voterList[vid].active != true, "already a voter"); 118 orgVoterList[id].voterList[vid].active = true; 119 orgVoterList[id].validVoterCount++; 120 } 121 122 } 123 emit VoterAdded(_orgId, _vAccount); 124 } 125 126 /** @notice function to delete a voter account from the organization 127 * @param _orgId org id 128 * @param _vAccount - voter account 129 * @dev voter capability is currently enabled for network level activities 130 only. voting is not available for org related activities 131 */ 132 function deleteVoter(string calldata _orgId, address _vAccount) external 133 onlyImplementation 134 voterExists(_orgId, _vAccount) { 135 uint256 id = _getVoterOrgIndex(_orgId); 136 uint256 vId = _getVoterIndex(_orgId, _vAccount); 137 orgVoterList[id].validVoterCount --; 138 orgVoterList[id].voterList[vId].active = false; 139 emit VoterDeleted(_orgId, _vAccount); 140 } 141 142 /** @notice function to a voting item for network admin accounts to vote 143 * @param _authOrg org id of the authorizing org. it will be network admin org 144 * @param _orgId - org id for which the voting record is being created 145 * @param _enodeId - enode id for which the voting record is being created 146 * @param _account - account id for which the voting record is being created 147 * @param _pendingOp - operation for which voting is being done 148 */ 149 function addVotingItem(string calldata _authOrg, string calldata _orgId, 150 string calldata _enodeId, address _account, uint256 _pendingOp) 151 external onlyImplementation { 152 // check if anything is pending approval for the org. 153 // If yes another item cannot be added 154 require((_checkPendingOp(_authOrg, 0)), 155 "items pending for approval. new item cannot be added"); 156 uint256 id = _getVoterOrgIndex(_authOrg); 157 orgVoterList[id].pendingOp.orgId = _orgId; 158 orgVoterList[id].pendingOp.enodeId = _enodeId; 159 orgVoterList[id].pendingOp.account = _account; 160 orgVoterList[id].pendingOp.opType = _pendingOp; 161 // initialize vote status for voter accounts 162 for (uint256 i = 0; i < orgVoterList[id].voterList.length; i++) { 163 if (orgVoterList[id].voterList[i].active) { 164 orgVoterList[id].votingStatus[id][orgVoterList[id].voterList[i].vAccount] = false; 165 } 166 } 167 // set vote count to zero 168 orgVoterList[id].voteCount = 0; 169 emit VotingItemAdded(_authOrg); 170 171 } 172 173 /** @notice function processing vote of a voter account 174 * @param _authOrg org id of the authorizing org. it will be network admin org 175 * @param _vAccount - account id of the voter 176 * @param _pendingOp - operation which is being approved 177 * @return success of the voter process. either true or false 178 */ 179 function processVote(string calldata _authOrg, address _vAccount, uint256 _pendingOp) 180 external onlyImplementation voterExists(_authOrg, _vAccount) returns (bool) { 181 // check something if anything is pending approval 182 require(_checkPendingOp(_authOrg, _pendingOp) == true, "nothing to approve"); 183 uint256 id = _getVoterOrgIndex(_authOrg); 184 // check if vote is already processed 185 require(orgVoterList[id].votingStatus[id][_vAccount] != true, "cannot double vote"); 186 orgVoterList[id].voteCount++; 187 orgVoterList[id].votingStatus[id][_vAccount] = true; 188 emit VoteProcessed(_authOrg); 189 if (orgVoterList[id].voteCount > orgVoterList[id].validVoterCount / 2) { 190 // majority achieved, clean up pending op 191 orgVoterList[id].pendingOp.orgId = ""; 192 orgVoterList[id].pendingOp.enodeId = ""; 193 orgVoterList[id].pendingOp.account = address(0); 194 orgVoterList[id].pendingOp.opType = 0; 195 return true; 196 } 197 return false; 198 } 199 200 /** @notice returns the details of any pending operation to be approved 201 * @param _orgId org id. this will be the org id of network admin org 202 */ 203 function getPendingOpDetails(string calldata _orgId) external view 204 onlyImplementation returns (string memory, string memory, address, uint256){ 205 uint256 orgIndex = _getVoterOrgIndex(_orgId); 206 return (orgVoterList[orgIndex].pendingOp.orgId, orgVoterList[orgIndex].pendingOp.enodeId, 207 orgVoterList[orgIndex].pendingOp.account, orgVoterList[orgIndex].pendingOp.opType); 208 } 209 210 /** @notice checks if the voter account exists and is linked to the org 211 * @param _orgId org id 212 * @param _vAccount voter account id 213 * @return true or false 214 */ 215 function _checkVoterExists(string memory _orgId, address _vAccount) 216 internal view returns (bool){ 217 uint256 orgIndex = _getVoterOrgIndex(_orgId); 218 if (orgVoterList[orgIndex].voterIndex[_vAccount] == 0) { 219 return false; 220 } 221 uint256 voterIndex = _getVoterIndex(_orgId, _vAccount); 222 return orgVoterList[orgIndex].voterList[voterIndex].active; 223 } 224 225 /** @notice checks if the pending operation exists or not 226 * @param _orgId org id 227 * @param _pendingOp type of operation 228 * @return true or false 229 */ 230 function _checkPendingOp(string memory _orgId, uint256 _pendingOp) 231 internal view returns (bool){ 232 return (orgVoterList[_getVoterOrgIndex(_orgId)].pendingOp.opType == _pendingOp); 233 } 234 235 /** @notice returns the voter account index 236 */ 237 function _getVoterIndex(string memory _orgId, address _vAccount) 238 internal view returns (uint256) { 239 uint256 orgIndex = _getVoterOrgIndex(_orgId); 240 return orgVoterList[orgIndex].voterIndex[_vAccount] - 1; 241 } 242 243 /** @notice returns the org index for the org from voter list 244 */ 245 function _getVoterOrgIndex(string memory _orgId) 246 internal view returns (uint256) { 247 return VoterOrgIndex[keccak256(abi.encode(_orgId))] - 1; 248 } 249 250 }