diff --git a/src/contract.Sealers.sol b/src/contract.Sealers.sol new file mode 100644 index 0000000000000000000000000000000000000000..2f1b8118811f7b97bf9e4ea10a0b85b79b44e88c --- /dev/null +++ b/src/contract.Sealers.sol @@ -0,0 +1,224 @@ +// vim:syntax:filetype=javascript:ai:sm + +pragma solidity ^0.4.24; + +// This contract is supposed to maintain the list of authorized sealers. +// For this to work, a sealer must deploy the contract and then all sealers +// must, say, every 5 minutes, run the related SealerSync.js + +contract Sealers { + + // This struct contains a list of votes and who has voted for each victim/beneficiary. + // Votes taking longer than 30K blocks, will have to restart. + struct Proposal { + address victim; // Whom are we voting about. + uint votestart; // In which block did this vote begin? + bool promotion; // true=promotion, false=demotion + address[] voters; // List of voters. + } + address[] public sealers; + Proposal[] public sealerproposals; + + event vote( address voter, address victim, bool promotionOrDemotion ); + event adminChange( address admin, bool promotionOrDemotion ); + + constructor() public + { + sealers.push( msg.sender ); + } + + // This function is used to know how many sealers are in the sealers list. + function sealerLength() public view returns (uint) + { + return sealers.length; + } + + // This function is to be used to get the address from a specific position in the sealers list. + // Remember first position is 0. + // Last position is sealers.length-1. + function sealerPosition( uint idx ) public view returns (address) + { + require( idx < sealers.length, "There are not that many sealers registered in the list." ); + return sealers[idx]; + } + + function isSealer( address subject ) public view returns (bool) + { + return ( _findAddressInList( sealers, subject ) > 0 ); + } + + function requireSealer( address subject ) public view + { + require( isSealer(subject), "Not sealer" ); + } + + // Returns an index to the position of the needle inside haystack. + // Beware that: + // 0 = not found. + // 1 = first position. + function _findAddressInList( address[] haystack, address needle ) private view returns (uint) + { + uint i = haystack.length; + while ( i-- > 0 ) + if ( needle == haystack[i] ) + return i+1; + return 0; + } + + function _remove_proposal( uint idx ) private + { + // Move all items in the list (after idx) one step closer to the + // front of the list. + uint max = sealerproposals.length; + while ( ++idx < max ) + sealerproposals[idx-1] = sealerproposals[idx]; + // "pop" the end of the list, making the list shorter. + sealerproposals.length--; + } + + function _del_votes( address victim ) private + { + uint i_max = sealerproposals.length; + uint i = i_max; + // check all proposals + while ( i-- > 0 ) + { + // check all voters for every proposal + uint j_max = sealerproposals[i].voters.length; + uint j = j_max; + while ( j-- > 0 ) + if ( sealerproposals[i].voters[j] == victim ) + { + // Found the victim as voter, but since he is about + // to be deleted, we will remove him from the list. + uint k = j; + while ( ++k < j_max ) + sealerproposals[i].voters[k-1] = sealerproposals[i].voters[k]; + sealerproposals[i].voters.length--; + j_max--; + if ( sealerproposals[i].voters.length == 0 ) + { + _remove_proposal( i ); + i_max--; + } + } + } + } + + function _remove_sealer( address victim ) private + { + // Remove votes that the victim has already cast. + _del_votes( victim ); + // Move all items in the list (after match) one step closer to the + // front of the list. + uint max = sealers.length; + uint i = max; + while ( i-- > 0 ) + if ( sealers[i] == victim ) + { + // We could have recycled 'i' here, but for clarity, we don't. + uint j = i; + while ( ++j < max ) + sealers[j-1] = sealers[j]; + // "pop" the end of the list, making the list shorter. + sealers.length--; + return; + } + } + + // This function sees if any proposals have overstayed their welcome and thus + // needs to be removed. + function _trimProposals() private + { + for ( uint i = sealerproposals.length-1; i>=0; i-- ) + { + // If a proposal is more than 30K blocks old, then remove it from the list. + if ( sealerproposals[i].votestart + 30000 <= block.number ) + _remove_proposal( i ); + } + } + + // Returns an index to the position of the proposal inside matching the [victim,promotion] tuple + // Beware that: + // 0 = not found. + // 1 = first position. + function promotionIdx( address victim, bool promotion ) public view returns (uint) + { + uint i = sealerproposals.length; + while ( i-- > 0) + if ( sealerproposals[i].victim == victim && sealerproposals[i].promotion == promotion ) + return i+1; + return 0; + } + + // You can call this for free and know if your promote call + // will get accepted. Save the network, call this first. + function mayVote( address voter, address victim, bool promotion ) public view returns (bool) + { + // Is caller a sealer? + if ( !isSealer(voter) ) + return false; + + // Is already Sealer and want to promote him? + // Can't promote someone who is already a sealer. + if ( isSealer(victim) && promotion ) + return false; + + // Is not Sealer and want to demote him? + // Can't demote someone who is not a sealer. + if ( !isSealer(victim) && !promotion ) + return false; + + // See if the voter is already in the list of voters for this [victim,promotion] tuple + uint proppos = promotionIdx( victim, promotion ); + if ( proppos > 0 && _findAddressInList( sealerproposals[proppos-1].voters, voter ) > 0 ) + return false; + return true; + } + + // Calling this function will vote for adding an additional or + // removing an existing member of "the inner circle". + // As per usual, this requires n/2+1 votes. + // The boolean must be true if you want to add a sealer + // and false if you want to remove one. + function promote( address victim, bool promotion ) public + { + if ( ! mayVote(msg.sender, victim, promotion)) + revert("That seems redundant or is otherwise not allowed."); + _trimProposals(); + + // Send notification of the vote + emit vote( msg.sender, victim, promotion ); + + uint proppos = promotionIdx( victim, promotion ); + if ( proppos == 0 ) + { + // This is a new proposal, so we will add it, so we can vote on it. + address[] memory emptyAddressList; + proppos = sealerproposals.push( Proposal(victim, block.number, promotion, emptyAddressList ) ); + } + proppos--; + + // Add our vote + sealerproposals[proppos].voters.push( msg.sender ); + + // Stop here if we do not have enough votes to perform the actual promotion/demotion + if ( sealerproposals[proppos].voters.length < sealers.length/2+1 ) + return; + + // Remove the proposal because the voting is complete. + _remove_proposal( proppos ); + + // Is it a promotion or a demotion? + if ( promotion ) + // Add victim to sealer list + sealers.push( victim ); + else + // Remove victim from sealer list + _remove_sealer( victim ); + + // Send notification + emit adminChange( victim, promotion ); + } +} + diff --git a/src/contract.Sealers.solc b/src/contract.Sealers.solc deleted file mode 100644 index 47ae489fe24a48ddaea4b76ad6316d1e142cac45..0000000000000000000000000000000000000000 --- a/src/contract.Sealers.solc +++ /dev/null @@ -1,163 +0,0 @@ -pragma solidity ^0.4.22; - -// This contract is supposed to maintain the list of authorized sealers. -// For this to work, a sealer must deploy the contract and then all sealers -// must occasionally (every 5 minutes) run the related SealerSync.js - -contract Sealers { - - // This struct contains a list of votes and who has voted for each victim/beneficiary. - // Votes taking longer than 30K blocks, will have to restart. - struct Proposal { - address victim; // Whom are we voting about. - uint256 votestart; // In which block did this vote begin? - bool promotion; // true=promotion, false=demotion - address[] voters; // List of voters. - } - address[] public sealers; - Proposal[] public sealerproposals; - - event vote( address voter, address victim, bool promotionOrDemotion ); - event adminChange( address admin, bool promotionOrDemotion ); - - constructor() public { - sealers[0] = msg.sender; - } - - // Public utility, works on any list needle and haystack given. - // - // Returns an index to the position of the needle inside haystack. - // 0 is first position. -1 means not found. - function findAddressInList( address[] haystack, address needle ) public pure returns (int256) { - uint256 i = haystack.length; - while ( i-- > 0 ) - if ( needle == haystack[i] ) - return int256(i & 0x7fffffffffffffffffffffffffffffff); - return -1; - } - function sealerIdx( address needle ) public view returns (int256) { - return findAddressInList( sealers, needle ); - } - // This function is to be used to know how many sealers are in the sealers list. - function sealerLength() public view returns (uint256) { - return sealers.length; - } - // This function is to be used to get the address from a specific position in the sealers list. - // Remember first position is 0. - // Last position is sealers.length-1. - function sealerPosition( uint256 idx ) public view returns (address) { - require( idx < sealers.length, "There are not that many sealers registered in the list." ); - return sealers[idx]; - } - function isSealer( address subject ) public view returns (bool) { - return ( sealerIdx(subject) > -1 ); - } - function requireSealer( address subject ) public view { - require( isSealer(subject), "Not sealer" ); - } - - function _remove_entry( uint256 idx, list alist ) private returns (list) { - uint256 pos = idx; - // Move all items in the list (after idx) one step closer to the - // front of the list. - while ( ++pos < list.length ) - alist[pos-1] = alist[pos]; - // "pop" the end of the list, making the list shorter. - alist.length--; - return alist; - } - - // This function sees if any proposals have overstayed their welcome and thus - // needs to be removed. - function _trimProposals() private { - for ( uint256 i = sealerproposals.length-1; i>=0; i-- ) - { - // If a proposal is more than 30K blocks old, then remove it from the list. - if ( sealerproposals[i].votestart + 30000 <= block.number ) - sealerproposals = _remove_entry( i, sealerproposals ); - } - } - - function promotionIdx( address victim, bool promotion ) public view return (int256) { - uint256 idx = sealerproposals.length; - while ( idx-- > 0) - if ( sealerproposals[idx].victim == victim && sealerproposals[idx].promotion == promotion ) - return idx; - return -1; - } - - // You can call this for free and know if your promote call - // will get accepted. Save the network, call this first. - function mayVote( address voter, address victim, bool promotion ) public view returns (bool) - { - // Is caller a sealer? - if ( !isSealer(voter) ) - return false; - - // Is already Sealer and want to promote him? - // Can't promote someone who is already a sealer. - if ( isSealer(victim) && promotion ) - return false; - - // Is not Sealer and want to demote him? - // Can't demote someone who is not a sealer. - if ( !isSealer(victim) && !promotion ) - return false; - - // See if the voter is already in the list of voters for this [victim,promotion] tupple - int256 proppos = promotionIdx( victim, promotion ); - if ( proppos > -1 && findAddressInList( sealerproposals[proppos].voters, voter ) > -1 ) - return false; - return true; - } - - // Calling this function will vote for adding an additional or - // removing an existing member of "the inner circle". - // As per usual, this requires n/2+1 votes. - // The boolean must be true if you want to add a sealer - // and false if you want to remove one. - function promote( address victim, bool promotion ) public { - if ( ! mayVote(msg.sender, victim, promotion)) - revert("That seems redundant."); - _trimProposals(); - - // Send notification of the vote - emit vote( msg.sender, victim, promotion ); - - // signed as opposed to unsigned (int) - int256 signedretval = promotionIdx( victim, promotion ); - uint proppos; - if ( signedretval == -1 ) - { - // This is a new proposal, so we will add it, so we can vote on it. - address[] memory emptyAddressList; - proppos = sealerproposals.push( Proposal(victim, block.number, promotion, emptyAddressList ) ); - } - else - proppos = uint256( signedretval & 0x7fffffffffffffffffffffffffffffff ); - - // Make things a bit more easy to read by using a shorter variable name - Proposal memory prop = sealerproposals[proppos]; - - // Add our vote - prop.voters.push( msg.sender ); - - // Stop here if we do not have enough votes to perform the actual promotion/demotion - if ( prop.voters.length < sealers.length/2+1 ) - return; - - // Remove the proposal because the voting is complete. - sealerproposals = _remove_entry( proppos, sealerproposals ); - - // Is it a promotion or a demotion? - if ( promotion ) - // Add victim to sealer list - sealers.push( victim ); - else - // Remove victim from sealer list - sealers = _remove_entry( sealerIdx(victim), sealers ); - - // Send notification - emit adminChange( victim, promotion ); - } -}