quorum/controls/permission/VoterManager.sol

207 lines
8.1 KiB
Solidity

pragma solidity ^0.5.3;
import "./PermissionsUpgradable.sol";
contract VoterManager {
PermissionsUpgradable private permUpgradable;
// enum PendingOpType {0-None, 1-OrgAdd, 2-OrgSuspension, 3-OrgRevokeSuspension, 4-AddOrgAdmin}
struct PendingOpDetails {
string orgId;
string enodeId;
address account;
uint opType;
}
struct VoterDetails {
address vAccount;
bool active;
}
struct OrgVoterDetails {
string orgId;
uint voterCount;
uint validVoterCount;
uint voteCount;
PendingOpDetails pendingOp;
VoterDetails [] voterList;
mapping(address => uint) voterIndex;
mapping(uint => mapping(address => bool)) votingStatus;
}
OrgVoterDetails [] private orgVoterList;
mapping(bytes32 => uint) private VoterOrgIndex;
uint private orgNum = 0;
// events related to managing voting accounts for the org
event VoterAdded(string _orgId, address _address);
event VoterDeleted(string _orgId, address _address);
event VotingItemAdded(string _orgId);
event VoteProcessed(string _orgId);
modifier onlyImpl
{
require(msg.sender == permUpgradable.getPermImpl());
_;
}
modifier voterExists(string memory _orgId, address _address) {
require(checkIfVoterExists(_orgId, _address) == true, "must be a voter");
_;
}
constructor (address _permUpgradable) public {
permUpgradable = PermissionsUpgradable(_permUpgradable);
}
// returns the voter index
function getVoterIndex(string memory _orgId, address _vAccount) internal view returns (uint)
{
uint orgIndex = getVoterOrgIndex(_orgId);
return orgVoterList[orgIndex].voterIndex[_vAccount] - 1;
}
// returns the master org index for the org from voter list
function getVoterOrgIndex(string memory _orgId) internal view returns (uint)
{
return VoterOrgIndex[keccak256(abi.encodePacked(_orgId))] - 1;
}
// checks if the org has any voter accounts set up or not
function checkIfVoterExists(string memory _orgId, address _address) public view returns (bool){
uint orgIndex = getVoterOrgIndex(_orgId);
if (orgVoterList[orgIndex].voterIndex[_address] == 0) {
return false;
}
uint voterIndex = getVoterIndex(_orgId, _address);
return orgVoterList[orgIndex].voterList[voterIndex].active;
}
// Get number of total voters
function getNumberOfVoters(string calldata _orgId) external view returns (uint)
{
return orgVoterList[getVoterOrgIndex(_orgId)].voterCount;
}
// Get number of valid voters
function getNumberOfValidVoters(string calldata _orgId) external view returns (uint)
{
return orgVoterList[getVoterOrgIndex(_orgId)].validVoterCount;
}
// checks if the voting accounts exists for the org
function checkVotingAccountExists(string calldata _orgId) external view returns (bool)
{
uint orgIndex = getVoterOrgIndex(_orgId);
return (orgVoterList[orgIndex].validVoterCount > 0);
}
// function for adding a voter account to a master org
function addVoter(string calldata _orgId, address _address) external
{
// check if the org exists
if (VoterOrgIndex[keccak256(abi.encodePacked(_orgId))] == 0) {
orgNum++;
VoterOrgIndex[keccak256(abi.encodePacked(_orgId))] = orgNum;
uint 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[_address] = orgVoterList[id].voterCount;
orgVoterList[id].voterList.push(VoterDetails(_address, true));
}
else {
uint id = getVoterOrgIndex(_orgId);
// check of the voter already present in the list
if (orgVoterList[id].voterIndex[_address] == 0) {
orgVoterList[id].voterCount++;
orgVoterList[id].voterIndex[_address] = orgVoterList[id].voterCount;
orgVoterList[id].voterList.push(VoterDetails(_address, true));
orgVoterList[id].validVoterCount++;
}
else {
uint vid = getVoterIndex(_orgId, _address);
require(orgVoterList[id].voterList[vid].active != true, "already a voter");
orgVoterList[id].voterList[vid].active = true;
orgVoterList[id].validVoterCount++;
}
}
emit VoterAdded(_orgId, _address);
}
// function for deleting a voter account to a master org
function deleteVoter(string calldata _orgId, address _address) external voterExists(_orgId, _address)
{
uint id = getVoterOrgIndex(_orgId);
uint vId = getVoterIndex(_orgId, _address);
orgVoterList[id].validVoterCount --;
orgVoterList[id].voterList[vId].active = false;
emit VoterDeleted(_orgId, _address);
}
// function for adding an item into voting queue of the org
function addVotingItem(string calldata _authOrg, string calldata _orgId, string calldata _enodeId, address _account, uint _pendingOp) external
{
// check if anything is pending approval for the org. If yes another item cannot be added
require((checkPendingOp(_authOrg, 0)), "Items pending approval. New item cannot be added");
uint id = getVoterOrgIndex(_authOrg);
orgVoterList[id].pendingOp.orgId = _orgId;
orgVoterList[id].pendingOp.enodeId = _enodeId;
orgVoterList[id].pendingOp.account = _account;
orgVoterList[id].pendingOp.opType = _pendingOp;
// init vote status
for (uint 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);
}
// process vote and update status
function processVote(string calldata _authOrg, address _vAccount, uint _pendingOp) external voterExists(_authOrg, _vAccount) returns (bool) {
// check something is pending approval
require(checkPendingOp(_authOrg, _pendingOp) == true, "nothing to approve");
uint id = getVoterOrgIndex(_authOrg);
// check if vote 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;
}
function checkPendingOp(string memory _orgId, uint _pendingOp) internal view returns (bool){
return (orgVoterList[getVoterOrgIndex(_orgId)].pendingOp.opType == _pendingOp);
}
function getVoteCount(string calldata _orgId) external view returns (uint, uint) {
uint orgIndex = getVoterOrgIndex(_orgId);
return (orgVoterList[orgIndex].voteCount, orgVoterList[orgIndex].validVoterCount);
}
function getPendingOpDetails(string calldata _orgId) external view returns (string memory, string memory, address, uint){
uint orgIndex = getVoterOrgIndex(_orgId);
return (orgVoterList[orgIndex].pendingOp.orgId, orgVoterList[orgIndex].pendingOp.enodeId, orgVoterList[orgIndex].pendingOp.account, orgVoterList[orgIndex].pendingOp.opType);
}
}