2018-10-01 03:44:06 -07:00
|
|
|
pragma solidity ^0.4.23;
|
|
|
|
|
|
|
|
contract Clusterkeys {
|
|
|
|
|
2018-10-03 20:03:22 -07:00
|
|
|
// Struct for managing the org details
|
2018-10-02 23:48:20 -07:00
|
|
|
enum Operation {None, Add, Delete}
|
2018-10-01 03:44:06 -07:00
|
|
|
struct OrgDetails {
|
|
|
|
string orgId;
|
2018-11-25 21:38:36 -08:00
|
|
|
string morgId;
|
2018-12-10 20:37:19 -08:00
|
|
|
string [] tmKey;
|
2018-10-02 23:48:20 -07:00
|
|
|
string pendingKey;
|
|
|
|
Operation pendingOp;
|
2018-10-01 03:44:06 -07:00
|
|
|
}
|
|
|
|
OrgDetails [] private orgList;
|
|
|
|
mapping(bytes32 => uint) private OrgIndex;
|
|
|
|
|
2018-12-10 20:37:19 -08:00
|
|
|
// Struct for managing the voter accounst for the org
|
2018-11-25 21:38:36 -08:00
|
|
|
struct MasterOrgDetails {
|
2018-10-01 03:44:06 -07:00
|
|
|
string orgId;
|
2018-12-10 20:37:19 -08:00
|
|
|
address [] voterAccount;
|
|
|
|
string [] tmKey;
|
2018-10-01 03:44:06 -07:00
|
|
|
}
|
2018-12-10 20:37:19 -08:00
|
|
|
MasterOrgDetails [] private masterOrgList;
|
2018-11-25 21:38:36 -08:00
|
|
|
mapping(bytes32 => uint) private MasterOrgIndex;
|
2018-10-01 03:44:06 -07:00
|
|
|
|
2018-10-03 20:03:22 -07:00
|
|
|
// mapping to monitor the voting status for each acount and
|
|
|
|
// overall voting count
|
2018-10-03 03:03:51 -07:00
|
|
|
mapping (uint => mapping (address => bool)) private voteStatus;
|
|
|
|
mapping (uint => uint) private voteCount;
|
|
|
|
|
2018-12-10 20:37:19 -08:00
|
|
|
uint private orgNum = 0;
|
|
|
|
uint private morgNum = 0;
|
|
|
|
|
|
|
|
// events related to Master Org add
|
|
|
|
event MasterOrgAdded(string _orgId);
|
|
|
|
event SubOrgAdded(string _orgId);
|
2018-10-01 03:44:06 -07:00
|
|
|
|
2018-10-03 20:03:22 -07:00
|
|
|
// events related to Org level key management
|
2018-12-10 20:37:19 -08:00
|
|
|
event OrgKeyAdded(string _orgId, string _tmKey);
|
|
|
|
event OrgKeyDeleted(string _orgId, string _tmKey);
|
2018-10-03 20:03:22 -07:00
|
|
|
|
|
|
|
// events related to org level approval process
|
2018-12-10 20:37:19 -08:00
|
|
|
event ItemForApproval(string _orgId, Operation _pendingOp, string _tmKey);
|
2018-10-02 23:48:20 -07:00
|
|
|
|
2018-10-03 20:03:22 -07:00
|
|
|
// events related to managing voting accounts for the org
|
|
|
|
event VoterAdded(string _orgId, address _address);
|
2018-12-10 20:37:19 -08:00
|
|
|
event VoterDeleted(string _orgId, address _address);
|
2018-10-03 20:03:22 -07:00
|
|
|
|
2018-12-11 23:37:13 -08:00
|
|
|
|
|
|
|
// functions to test
|
|
|
|
function getOrgVoteCount(string _orgId) external view returns (uint) {
|
|
|
|
return voteCount[getOrgIndex(_orgId)];
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPendingOp(string _orgId) external view returns (string, Operation) {
|
|
|
|
uint i = getOrgIndex(_orgId);
|
|
|
|
return (orgList[i].pendingKey, orgList[i].pendingOp);
|
|
|
|
}
|
|
|
|
function getVoteStatus(string _orgId) external view returns (bool){
|
|
|
|
return voteStatus[getOrgIndex(_orgId)][msg.sender];
|
|
|
|
}
|
|
|
|
// All internal functions
|
2018-10-02 23:48:20 -07:00
|
|
|
|
2018-10-03 20:03:22 -07:00
|
|
|
// returns the org index for the org list
|
|
|
|
function getOrgIndex(string _orgId) internal view returns (uint)
|
|
|
|
{
|
|
|
|
return OrgIndex[keccak256(abi.encodePacked(_orgId))] - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns the voter index for the org from voter list
|
2018-12-10 20:37:19 -08:00
|
|
|
function getMasterOrgIndex(string _orgId) internal view returns (uint)
|
2018-10-03 20:03:22 -07:00
|
|
|
{
|
2018-11-25 21:38:36 -08:00
|
|
|
return MasterOrgIndex[keccak256(abi.encodePacked(_orgId))] - 1;
|
2018-10-03 20:03:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// initialize the voter account votes to false. This will be called when a
|
|
|
|
// new item is initiated for approval
|
2018-10-03 03:03:51 -07:00
|
|
|
function voterInit(string _orgId) internal {
|
2018-11-21 18:16:34 -08:00
|
|
|
uint orgIndex = getOrgIndex(_orgId);
|
2018-12-11 23:37:13 -08:00
|
|
|
uint morgIndex = getMasterOrgIndex(orgList[orgIndex].morgId);
|
|
|
|
for (uint i = 0; i < masterOrgList[morgIndex].voterAccount.length; i++){
|
|
|
|
voteStatus[orgIndex][masterOrgList[morgIndex].voterAccount[i]] = false;
|
2018-10-03 03:03:51 -07:00
|
|
|
}
|
|
|
|
voteCount[orgIndex] = 0;
|
|
|
|
}
|
|
|
|
|
2018-12-10 20:37:19 -08:00
|
|
|
// processes the vote from the voter account.
|
2018-10-03 03:03:51 -07:00
|
|
|
function processVote (string _orgId) internal {
|
2018-11-21 18:16:34 -08:00
|
|
|
uint orgIndex = getOrgIndex(_orgId);
|
2018-10-03 03:03:51 -07:00
|
|
|
if (voteStatus[orgIndex][msg.sender] == false ){
|
|
|
|
voteStatus[orgIndex][msg.sender] = true;
|
|
|
|
voteCount[orgIndex]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-03 20:03:22 -07:00
|
|
|
// checks if enough votes have been cast for the pending operation. If yes
|
|
|
|
// returns true
|
2018-12-12 16:52:06 -08:00
|
|
|
function checkEnoughVotes (string _orgId, string _morgId) internal view returns (bool) {
|
2018-11-21 18:16:34 -08:00
|
|
|
uint orgIndex = getOrgIndex(_orgId);
|
2018-12-12 16:52:06 -08:00
|
|
|
uint morgIndex = getMasterOrgIndex(_morgId);
|
|
|
|
if (voteCount[orgIndex] > masterOrgList[morgIndex].voterAccount.length / 2 ){
|
2018-10-03 03:03:51 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-11-25 21:38:36 -08:00
|
|
|
function checkKeyInUse(string _orgId, string _morgId, string _key) internal view returns (bool) {
|
2018-11-21 18:16:34 -08:00
|
|
|
bool keyInUse = false;
|
|
|
|
for (uint i = 0; i < orgList.length; i++){
|
|
|
|
if (keccak256(abi.encodePacked(orgList[i].orgId)) == keccak256(abi.encodePacked(_orgId))){
|
|
|
|
continue;
|
|
|
|
}
|
2018-11-25 21:38:36 -08:00
|
|
|
if (keccak256(abi.encodePacked(orgList[i].morgId)) == keccak256(abi.encodePacked(_morgId))){
|
2018-12-10 20:37:19 -08:00
|
|
|
for (uint j = 0; j < orgList[i].tmKey.length; j++){
|
|
|
|
if (keccak256(abi.encodePacked(orgList[i].tmKey[j])) == keccak256(abi.encodePacked(_key))){
|
2018-11-21 18:16:34 -08:00
|
|
|
keyInUse = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (keyInUse == true) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// updates the master keys list with the key being added or deleted
|
2018-11-25 21:38:36 -08:00
|
|
|
function masterKeyUpdate(string _orgId, string _morgId, string _key, Operation _op) internal {
|
2018-12-10 20:37:19 -08:00
|
|
|
uint morgIndex = getMasterOrgIndex(_morgId);
|
2018-11-21 18:16:34 -08:00
|
|
|
if (_op == Operation.Add) {
|
|
|
|
// check if the key is existing. if yes ignore else add to master list
|
|
|
|
bool keyExists = false;
|
2018-12-10 20:37:19 -08:00
|
|
|
for (uint i = 0; i < masterOrgList[morgIndex].tmKey.length; i++){
|
|
|
|
if(keccak256(abi.encodePacked(masterOrgList[morgIndex].tmKey[i])) == keccak256(abi.encodePacked(_key))){
|
2018-11-21 18:16:34 -08:00
|
|
|
keyExists = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (keyExists == false ){
|
2018-12-10 20:37:19 -08:00
|
|
|
masterOrgList[morgIndex].tmKey.push(_key);
|
2018-11-21 18:16:34 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// the key can be deleted from master list only when none of the suborgs have the
|
|
|
|
// key in the private keys
|
2018-11-25 21:38:36 -08:00
|
|
|
if (!(checkKeyInUse(_orgId, _morgId, _key))){
|
2018-11-21 18:16:34 -08:00
|
|
|
uint index;
|
2018-12-10 20:37:19 -08:00
|
|
|
for (index = 0; i < masterOrgList[morgIndex].tmKey.length; index++) {
|
|
|
|
if(keccak256(abi.encodePacked(masterOrgList[morgIndex].tmKey[index])) == keccak256(abi.encodePacked(_key))) {
|
2018-11-21 18:16:34 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-12-10 20:37:19 -08:00
|
|
|
for (uint j = index; j < masterOrgList[morgIndex].tmKey.length -1; j++){
|
|
|
|
masterOrgList[morgIndex].tmKey[j] = masterOrgList[morgIndex].tmKey[j+1];
|
2018-11-21 18:16:34 -08:00
|
|
|
}
|
2018-12-10 20:37:19 -08:00
|
|
|
delete masterOrgList[morgIndex].tmKey[masterOrgList[morgIndex].tmKey.length -1];
|
|
|
|
masterOrgList[morgIndex].tmKey.length --;
|
2018-11-21 18:16:34 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-03 20:03:22 -07:00
|
|
|
// function to process the approavl for add or delete
|
2018-10-03 03:03:51 -07:00
|
|
|
function processApproval(uint _orgIndex) internal {
|
2018-12-12 16:52:06 -08:00
|
|
|
if(checkEnoughVotes(orgList[_orgIndex].orgId, orgList[_orgIndex].morgId)){
|
2018-10-03 03:03:51 -07:00
|
|
|
string storage locKey = orgList[_orgIndex].pendingKey;
|
|
|
|
if (orgList[_orgIndex].pendingOp == Operation.Add){
|
2018-12-10 20:37:19 -08:00
|
|
|
orgList[_orgIndex].tmKey.push(orgList[_orgIndex].pendingKey);
|
2018-11-25 21:38:36 -08:00
|
|
|
masterKeyUpdate(orgList[_orgIndex].orgId, orgList[_orgIndex].morgId, orgList[_orgIndex].pendingKey, Operation.Add);
|
2018-10-03 03:03:51 -07:00
|
|
|
emit OrgKeyAdded(orgList[_orgIndex].orgId, locKey);
|
2018-10-02 23:48:20 -07:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
bool keyExists = false;
|
|
|
|
uint i = 0;
|
2018-10-03 03:03:51 -07:00
|
|
|
(keyExists, i) = checkIfKeyExists (orgList[_orgIndex].orgId, locKey);
|
2018-12-10 20:37:19 -08:00
|
|
|
for (uint j = i; j < orgList[_orgIndex].tmKey.length -1; j++){
|
|
|
|
orgList[_orgIndex].tmKey[j] = orgList[_orgIndex].tmKey[j+1];
|
2018-10-02 18:14:31 -07:00
|
|
|
}
|
2018-12-10 20:37:19 -08:00
|
|
|
delete orgList[_orgIndex].tmKey[orgList[_orgIndex].tmKey.length -1];
|
|
|
|
orgList[_orgIndex].tmKey.length --;
|
2018-11-25 21:38:36 -08:00
|
|
|
masterKeyUpdate(orgList[_orgIndex].orgId, orgList[_orgIndex].morgId, orgList[_orgIndex].pendingKey, Operation.Delete);
|
2018-10-03 03:03:51 -07:00
|
|
|
emit OrgKeyDeleted(orgList[_orgIndex].orgId, locKey);
|
2018-10-01 03:44:06 -07:00
|
|
|
}
|
2018-10-03 03:03:51 -07:00
|
|
|
orgList[_orgIndex].pendingOp = Operation.None;
|
|
|
|
orgList[_orgIndex].pendingKey = "";
|
2018-10-02 23:48:20 -07:00
|
|
|
}
|
2018-10-01 03:44:06 -07:00
|
|
|
}
|
2018-12-11 23:37:13 -08:00
|
|
|
// All public functions
|
|
|
|
|
|
|
|
// checks if the org has any voter accounts set up or not
|
|
|
|
function checkIfVoterExists(string _morgId, address _address) public view returns (bool, uint){
|
|
|
|
bool keyExists = false;
|
|
|
|
uint voterIndex = getMasterOrgIndex(_morgId);
|
|
|
|
for (uint i = 0; i < masterOrgList[voterIndex].voterAccount.length; i++){
|
|
|
|
if(keccak256(abi.encodePacked(masterOrgList[voterIndex].voterAccount[i])) == keccak256(abi.encodePacked(_address))){
|
|
|
|
keyExists = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (keyExists, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
// checks if there the key is already in the list of private keys for the org
|
|
|
|
function checkIfKeyExists(string _orgId, string _tmKey) public view returns (bool, uint){
|
|
|
|
bool keyExists = false;
|
|
|
|
uint orgIndex = getOrgIndex(_orgId);
|
|
|
|
for (uint i = 0; i < orgList[orgIndex].tmKey.length; i++){
|
|
|
|
if(keccak256(abi.encodePacked(orgList[orgIndex].tmKey[i])) == keccak256(abi.encodePacked(_tmKey))){
|
|
|
|
keyExists = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (keyExists, i);
|
|
|
|
}
|
|
|
|
// All extenal view functions
|
|
|
|
|
2018-12-13 07:28:36 -08:00
|
|
|
// returns the number of orgs
|
|
|
|
function getNumberOfOrgs() external view returns (uint){
|
|
|
|
return orgNum;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getOrgInfo(uint _orgIndex) external view returns (string, string){
|
|
|
|
return (orgList[_orgIndex].orgId, orgList[_orgIndex].morgId);
|
|
|
|
}
|
|
|
|
|
2018-12-11 23:37:13 -08:00
|
|
|
// checks if the sender is one of the registered voter account for the org
|
|
|
|
function isVoter (string _orgId, address account) external view returns (bool){
|
|
|
|
bool flag = false;
|
|
|
|
uint orgIndex = getOrgIndex(_orgId);
|
|
|
|
uint vorgIndex = getMasterOrgIndex(orgList[orgIndex].morgId);
|
|
|
|
for (uint i = 0; i < masterOrgList[vorgIndex].voterAccount.length; i++){
|
|
|
|
if ( masterOrgList[vorgIndex].voterAccount[i] == account){
|
|
|
|
flag = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return flag;
|
|
|
|
}
|
|
|
|
|
|
|
|
// checks if the voter account is already in the voter accounts list for the org
|
|
|
|
function checkVotingAccountExists(string _orgId) external view returns (bool)
|
|
|
|
{
|
|
|
|
uint orgIndex = getOrgIndex(_orgId);
|
|
|
|
uint vorgIndex = getMasterOrgIndex(orgList[orgIndex].morgId);
|
|
|
|
if (masterOrgList[vorgIndex].voterAccount.length == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// function to check if morg exists
|
|
|
|
function checkMasterOrgExists (string _morgId) external view returns (bool) {
|
|
|
|
if (MasterOrgIndex[keccak256(abi.encodePacked(_morgId))] == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// function to check if morg exists
|
|
|
|
function checkOrgExists (string _orgId) external view returns (bool) {
|
|
|
|
if (OrgIndex[keccak256(abi.encodePacked(_orgId))] == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// function for checking if org exists and if there are any pending ops
|
|
|
|
function checkOrgPendingOp (string _orgId) external view returns (bool) {
|
|
|
|
uint orgIndex = getOrgIndex(_orgId);
|
|
|
|
if (orgList[orgIndex].pendingOp != Operation.None) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// this function checks of the key proposed is in use in another master org
|
|
|
|
function checkKeyClash (string _orgId, string _key) external view returns (bool) {
|
|
|
|
bool ret = false;
|
|
|
|
uint orgIndex = getOrgIndex(_orgId);
|
|
|
|
// check if the key is already in use with other orgs
|
|
|
|
for (uint i = 0; i < masterOrgList.length; i++){
|
|
|
|
if (keccak256( abi.encodePacked (masterOrgList[i].orgId)) != keccak256( abi.encodePacked(orgList[orgIndex].morgId))) {
|
|
|
|
// check if the key is already present in the key list for the org
|
|
|
|
for (uint j = 0; j < masterOrgList[i].tmKey.length; j++){
|
|
|
|
if (keccak256(abi.encodePacked(masterOrgList[i].tmKey[j])) == keccak256(abi.encodePacked(_key))) {
|
|
|
|
ret = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ret) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ret){
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
// check if the key is pending approval for any of the orgs
|
|
|
|
for (uint k = 0; k < orgList.length; k++){
|
|
|
|
if (keccak256(abi.encodePacked(orgList[k].orgId)) != keccak256(abi.encodePacked(_orgId))){
|
|
|
|
if ((orgList[k].pendingOp == Operation.Add) &&
|
|
|
|
(keccak256(abi.encodePacked(orgList[k].pendingKey)) == keccak256(abi.encodePacked(_key)))){
|
|
|
|
ret = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// All extenal update functions
|
|
|
|
|
|
|
|
// function for adding a new master org
|
|
|
|
function addMasterOrg(string _morgId) external
|
|
|
|
{
|
|
|
|
morgNum++;
|
|
|
|
MasterOrgIndex[keccak256(abi.encodePacked(_morgId))] = morgNum;
|
|
|
|
masterOrgList.push( MasterOrgDetails(_morgId, new address[](0), new string[](0)));
|
|
|
|
emit MasterOrgAdded(_morgId);
|
|
|
|
}
|
|
|
|
|
|
|
|
// function for adding a new master org
|
|
|
|
function addSubOrg(string _orgId, string _morgId) external
|
|
|
|
{
|
|
|
|
orgNum++;
|
|
|
|
OrgIndex[keccak256(abi.encodePacked(_orgId))] = orgNum;
|
|
|
|
orgList.push( OrgDetails(_orgId, _morgId, new string[](0), new string(0), Operation.None ));
|
|
|
|
emit SubOrgAdded(_morgId);
|
|
|
|
}
|
|
|
|
|
|
|
|
// function for adding a voter account to a master org
|
|
|
|
function addVoter(string _morgId, address _address) external
|
|
|
|
{
|
|
|
|
uint morgIndex = getMasterOrgIndex(_morgId);
|
|
|
|
masterOrgList[morgIndex].voterAccount.push(_address);
|
|
|
|
emit VoterAdded(_morgId, _address);
|
|
|
|
}
|
|
|
|
|
|
|
|
// function for deleting a voter account to a master org
|
|
|
|
function deleteVoter(string _morgId, address _address) external
|
|
|
|
{
|
|
|
|
uint morgIndex = getMasterOrgIndex(_morgId);
|
|
|
|
(bool voterExists, uint i) = checkIfVoterExists(_morgId, _address);
|
|
|
|
|
|
|
|
if (voterExists == true) {
|
|
|
|
for (uint j = i; j < masterOrgList[morgIndex].voterAccount.length -1; j++){
|
|
|
|
masterOrgList[morgIndex].voterAccount[j] = masterOrgList[morgIndex].voterAccount[j+1];
|
|
|
|
}
|
|
|
|
delete masterOrgList[morgIndex].voterAccount[masterOrgList[morgIndex].voterAccount.length -1];
|
|
|
|
masterOrgList[morgIndex].voterAccount.length --;
|
|
|
|
emit VoterDeleted(_morgId, _address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// function for adding a private key for the org. Thsi will be added once
|
|
|
|
// approval process is complete
|
|
|
|
function addOrgKey(string _orgId, string _tmKey) external
|
|
|
|
{
|
|
|
|
uint orgIndex = getOrgIndex(_orgId);
|
|
|
|
orgList[orgIndex].pendingKey = _tmKey;
|
|
|
|
orgList[orgIndex].pendingOp = Operation.Add;
|
|
|
|
voterInit(_orgId);
|
|
|
|
emit ItemForApproval(_orgId,Operation.Add, _tmKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
// function for deleting a private key for the org. Thsi will be deleted once
|
|
|
|
// approval process is complete
|
|
|
|
function deleteOrgKey(string _orgId, string _tmKey) external
|
|
|
|
{
|
|
|
|
uint orgIndex = getOrgIndex(_orgId);
|
|
|
|
uint i = 0;
|
|
|
|
bool keyExists = false;
|
|
|
|
(keyExists, i) = checkIfKeyExists (_orgId, _tmKey);
|
|
|
|
if (keyExists == true) {
|
|
|
|
orgList[orgIndex].pendingKey = _tmKey;
|
|
|
|
orgList[orgIndex].pendingOp = Operation.Delete;
|
|
|
|
voterInit(_orgId);
|
|
|
|
emit ItemForApproval(_orgId, Operation.Delete, _tmKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// function for approving key add or delete operations
|
|
|
|
function approvePendingOp(string _orgId) external
|
|
|
|
{
|
|
|
|
uint orgIndex = getOrgIndex(_orgId);
|
|
|
|
processVote(_orgId);
|
|
|
|
processApproval(orgIndex);
|
|
|
|
}
|
|
|
|
|
2018-10-01 03:44:06 -07:00
|
|
|
}
|