quorum/permission/contract/VoterManager.sol

230 lines
9.8 KiB
Solidity

pragma solidity ^0.5.3;
import "./PermissionsUpgradable.sol";
/// @title Voter manager contract
/// @notice This contract holds implementation logic for all account voter and
/// @notice voting functionality. This can be called only by the implementation
/// @notice contract only. there are few view functions exposed as public and
/// @notice can be called directly. these are invoked by quorum for populating
/// @notice permissions data in cache
contract VoterManager {
PermissionsUpgradable private permUpgradable;
// PendingOpType {0-None, 1-OrgAdd, 2-OrgSuspension,
// 3-OrgRevokeSuspension, 4-AssignAdminRole}
struct PendingOpDetails {
string orgId;
string enodeId;
address account;
uint256 opType;
}
struct Voter {
address vAccount;
bool active;
}
struct OrgVoterDetails {
string orgId;
uint256 voterCount;
uint256 validVoterCount;
uint256 voteCount;
PendingOpDetails pendingOp;
Voter [] voterList;
mapping(address => uint256) voterIndex;
mapping(uint256 => mapping(address => bool)) votingStatus;
}
OrgVoterDetails [] private orgVoterList;
mapping(bytes32 => uint256) private VoterOrgIndex;
uint256 private orgNum = 0;
// events related to managing voting accounts for the org
event VoterAdded(string _orgId, address _vAccount);
event VoterDeleted(string _orgId, address _vAccount);
event VotingItemAdded(string _orgId);
event VoteProcessed(string _orgId);
/// @notice confirms that the caller is the address of implementation
/// @notice contract
modifier onlyImplementation {
require(msg.sender == permUpgradable.getPermImpl(), "invalid caller");
_;
}
/// @notice checks if account is a valid voter record and belongs to the org
/// @notice passed
/// @param _orgId - org id
/// @param _vAccount - voter account passed
modifier voterExists(string memory _orgId, address _vAccount) {
require(_checkVoterExists(_orgId, _vAccount) == true, "must be a voter");
_;
}
/// @notice constructor. sets the permissions upgradable address
constructor (address _permUpgradable) public {
permUpgradable = PermissionsUpgradable(_permUpgradable);
}
/// @notice function to add a new voter account to the organization
/// @param _orgId org id
/// @param _vAccount - voter account
/// @dev voter capability is currently enabled for network level activities
/// @dev only. voting is not available for org related activities
function addVoter(string calldata _orgId, address _vAccount) external
onlyImplementation {
// check if the org exists
if (VoterOrgIndex[keccak256(abi.encode(_orgId))] == 0) {
orgNum++;
VoterOrgIndex[keccak256(abi.encode(_orgId))] = orgNum;
uint256 id = orgVoterList.length++;
orgVoterList[id].orgId = _orgId;
orgVoterList[id].voterCount = 1;
orgVoterList[id].validVoterCount = 1;
orgVoterList[id].voteCount = 0;
orgVoterList[id].pendingOp.orgId = "";
orgVoterList[id].pendingOp.enodeId = "";
orgVoterList[id].pendingOp.account = address(0);
orgVoterList[id].pendingOp.opType = 0;
orgVoterList[id].voterIndex[_vAccount] = orgVoterList[id].voterCount;
orgVoterList[id].voterList.push(Voter(_vAccount, true));
}
else {
uint256 id = _getVoterOrgIndex(_orgId);
// check if the voter is already present in the list
if (orgVoterList[id].voterIndex[_vAccount] == 0) {
orgVoterList[id].voterCount++;
orgVoterList[id].voterIndex[_vAccount] = orgVoterList[id].voterCount;
orgVoterList[id].voterList.push(Voter(_vAccount, true));
orgVoterList[id].validVoterCount++;
}
else {
uint256 vid = _getVoterIndex(_orgId, _vAccount);
require(orgVoterList[id].voterList[vid].active != true, "already a voter");
orgVoterList[id].voterList[vid].active = true;
orgVoterList[id].validVoterCount++;
}
}
emit VoterAdded(_orgId, _vAccount);
}
/// @notice function to delete a voter account from the organization
/// @param _orgId org id
/// @param _vAccount - voter account
/// @dev voter capability is currently enabled for network level activities
/// @dev only. voting is not available for org related activities
function deleteVoter(string calldata _orgId, address _vAccount) external
onlyImplementation
voterExists(_orgId, _vAccount) {
uint256 id = _getVoterOrgIndex(_orgId);
uint256 vId = _getVoterIndex(_orgId, _vAccount);
orgVoterList[id].validVoterCount --;
orgVoterList[id].voterList[vId].active = false;
emit VoterDeleted(_orgId, _vAccount);
}
/// @notice function to a voting item for network admin accounts to vote
/// @param _authOrg org id of the authorizing org. it will be network admin org
/// @param _orgId - org id for which the voting record is being created
/// @param _enodeId - enode id for which the voting record is being created
/// @param _account - account id for which the voting record is being created
/// @param _pendingOp - operation for which voting is being done
function addVotingItem(string calldata _authOrg, string calldata _orgId,
string calldata _enodeId, address _account, uint256 _pendingOp)
external onlyImplementation {
// check if anything is pending approval for the org.
// If yes another item cannot be added
require((_checkPendingOp(_authOrg, 0)),
"items pending for approval. new item cannot be added");
uint256 id = _getVoterOrgIndex(_authOrg);
orgVoterList[id].pendingOp.orgId = _orgId;
orgVoterList[id].pendingOp.enodeId = _enodeId;
orgVoterList[id].pendingOp.account = _account;
orgVoterList[id].pendingOp.opType = _pendingOp;
// initialize vote status for voter accounts
for (uint256 i = 0; i < orgVoterList[id].voterList.length; i++) {
if (orgVoterList[id].voterList[i].active) {
orgVoterList[id].votingStatus[id][orgVoterList[id].voterList[i].vAccount] = false;
}
}
// set vote count to zero
orgVoterList[id].voteCount = 0;
emit VotingItemAdded(_authOrg);
}
/// @notice function processing vote of a voter account
/// @param _authOrg org id of the authorizing org. it will be network admin org
/// @param _vAccount - account id of the voter
/// @param _pendingOp - operation which is being approved
/// @return success of the voter process. either true or false
function processVote(string calldata _authOrg, address _vAccount, uint256 _pendingOp)
external onlyImplementation voterExists(_authOrg, _vAccount) returns (bool) {
// check something if anything is pending approval
require(_checkPendingOp(_authOrg, _pendingOp) == true, "nothing to approve");
uint256 id = _getVoterOrgIndex(_authOrg);
// check if vote is already processed
require(orgVoterList[id].votingStatus[id][_vAccount] != true, "cannot double vote");
orgVoterList[id].voteCount++;
orgVoterList[id].votingStatus[id][_vAccount] = true;
emit VoteProcessed(_authOrg);
if (orgVoterList[id].voteCount > orgVoterList[id].validVoterCount / 2) {
// majority achieved, clean up pending op
orgVoterList[id].pendingOp.orgId = "";
orgVoterList[id].pendingOp.enodeId = "";
orgVoterList[id].pendingOp.account = address(0);
orgVoterList[id].pendingOp.opType = 0;
return true;
}
return false;
}
/// @notice returns the details of any pending operation to be approved
/// @param _orgId org id. this will be the org id of network admin org
function getPendingOpDetails(string calldata _orgId) external view
onlyImplementation returns (string memory, string memory, address, uint256){
uint256 orgIndex = _getVoterOrgIndex(_orgId);
return (orgVoterList[orgIndex].pendingOp.orgId, orgVoterList[orgIndex].pendingOp.enodeId,
orgVoterList[orgIndex].pendingOp.account, orgVoterList[orgIndex].pendingOp.opType);
}
/// @notice checks if the voter account exists and is linked to the org
/// @param _orgId org id
/// @param _vAccount voter account id
/// @return true or false
function _checkVoterExists(string memory _orgId, address _vAccount)
internal view returns (bool){
uint256 orgIndex = _getVoterOrgIndex(_orgId);
if (orgVoterList[orgIndex].voterIndex[_vAccount] == 0) {
return false;
}
uint256 voterIndex = _getVoterIndex(_orgId, _vAccount);
return orgVoterList[orgIndex].voterList[voterIndex].active;
}
/// @notice checks if the pending operation exists or not
/// @param _orgId org id
/// @param _pendingOp type of operation
/// @return true or false
function _checkPendingOp(string memory _orgId, uint256 _pendingOp)
internal view returns (bool){
return (orgVoterList[_getVoterOrgIndex(_orgId)].pendingOp.opType == _pendingOp);
}
/// @notice returns the voter account index
function _getVoterIndex(string memory _orgId, address _vAccount)
internal view returns (uint256) {
uint256 orgIndex = _getVoterOrgIndex(_orgId);
return orgVoterList[orgIndex].voterIndex[_vAccount] - 1;
}
/// @notice returns the org index for the org from voter list
function _getVoterOrgIndex(string memory _orgId)
internal view returns (uint256) {
return VoterOrgIndex[keccak256(abi.encode(_orgId))] - 1;
}
}