Skip to content
Snippets Groups Projects
Commit 223fcc24 authored by Robert Martin-Legene's avatar Robert Martin-Legene
Browse files

Major rewrite of the Sealers contract. Bugs weeded out, and more public functions.

parent 113e0725
No related branches found
No related tags found
No related merge requests found
pragma solidity ^0.4.22; 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 { 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 { struct Proposal {
address victim; // Whom are we voting about. address victim; // Whom are we voting about.
uint votestart; // In which block did this vote begin? uint256 votestart; // In which block did this vote begin?
bool promotion; // true=promotion, false=demotion bool promotion; // true=promotion, false=demotion
address[] voters; // List of voters. address[] voters; // List of voters.
} }
Proposal[] public adminproposals;
address[] public sealers; address[] public sealers;
Proposal[] public sealerproposals;
event vote( address voter, address victim, bool promotionOrDemotion ); event vote( address voter, address victim, bool promotionOrDemotion );
event adminChange( address admin, bool promotionOrDemotion ); event adminChange( address admin, bool promotionOrDemotion );
...@@ -18,107 +24,139 @@ contract Sealers { ...@@ -18,107 +24,139 @@ contract Sealers {
sealers[0] = msg.sender; sealers[0] = msg.sender;
} }
function isSealer( address subj ) public view returns (bool) { // Public utility, works on any list needle and haystack given.
for (uint i=sealers.length; i>0; i--) //
{ // Returns an index to the position of the needle inside haystack.
if (subj == sealers[i-1]) // 0 is first position. -1 means not found.
return true; function findAddressInList( address[] haystack, address needle ) public pure returns (int256) {
} uint256 i = haystack.length;
return false; while ( i-- > 0 )
} if ( needle == haystack[i] )
modifier onlySealersTX { return int256(i & 0x7fffffffffffffffffffffffffffffff);
require( isSealer( tx.origin ), "tx.origin is not a known sealer and can not call this function." ); return -1;
_;
} }
modifier onlySealers { function sealerIdx( address needle ) public view returns (int256) {
require( isSealer( msg.sender ), "msg.sender is not a known sealer and can not call this function." ); 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 (uint) { function sealerLength() public view returns (uint256) {
return sealers.length; return sealers.length;
} }
// This function is to be used to get the address from a specific position in the sealers list.
function sealerPosition( uint idx ) public view returns (address) { // Remember first position is 0.
require( idx <= sealers.length, "There are not that many sealers registered in the list." ); // 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]; 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 { function _trimProposals() private {
uint i = adminproposals.length; for ( uint256 i = sealerproposals.length-1; i>=0; i-- )
while ( i-- > 0 )
{ {
if ( adminproposals[i].votestart < block.number+30000 ) // If a proposal is more than 30K blocks old, then remove it from the list.
{ if ( sealerproposals[i].votestart + 30000 <= block.number )
while ( i < adminproposals.length ) sealerproposals = _remove_entry( i, sealerproposals );
{
adminproposals[i] = adminproposals[i+1];
i--;
}
adminproposals.length--;
}
} }
} }
function admin_promote( address victim, bool promotion ) public onlySealersTX { function promotionIdx( address victim, bool promotion ) public view return (int256) {
// Janitor uint256 idx = sealerproposals.length;
_trimProposals(); while ( idx-- > 0)
bool isS = isSealer(victim); 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? // Is already Sealer and want to promote him?
if ( isS && promotion ) // Can't promote someone who is already a sealer.
revert("You can't promote someone who is already a sealer."); if ( isSealer(victim) && promotion )
return false;
// Is not Sealer and want to demote him? // Is not Sealer and want to demote him?
if ( !isS && !promotion ) // Can't demote someone who is not a sealer.
revert("You can't demote someone who is not a sealer."); if ( !isSealer(victim) && !promotion )
// First see if we can find the index of an already running proposal for this victim,promotion tupple. return false;
uint proppos = adminproposals.length;
while ( proppos>0 && adminproposals[proppos-1].victim != victim && adminproposals[proppos-1].promotion != promotion) // See if the voter is already in the list of voters for this [victim,promotion] tupple
proppos--; int256 proppos = promotionIdx( victim, promotion );
// If we found no matching proposals, we will add it, so we can now vote on it. if ( proppos > -1 && findAddressInList( sealerproposals[proppos].voters, voter ) > -1 )
if (proppos == 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.");
_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 )
{ {
address[] memory sillycompiler; // This is a new proposal, so we will add it, so we can vote on it.
proppos = adminproposals.push( Proposal(victim, block.number, promotion, sillycompiler ) ); address[] memory emptyAddressList;
proppos = sealerproposals.push( Proposal(victim, block.number, promotion, emptyAddressList ) );
} }
// substract one, to make proppos actually reflect the position in the index. else
proppos--; proppos = uint256( signedretval & 0x7fffffffffffffffffffffffffffffff );
// Make things a bit more easy to read by using a shorter variable name // Make things a bit more easy to read by using a shorter variable name
Proposal memory prop = adminproposals[proppos]; Proposal memory prop = sealerproposals[proppos];
// Now look through the proposal and see if this sender already voted.
uint voterpos = prop.voters.length; // Add our vote
while ( voterpos>0 && prop.voters[voterpos-1] != msg.sender ) prop.voters.push( msg.sender );
voterpos--;
// Return if sender already voted // Stop here if we do not have enough votes to perform the actual promotion/demotion
if ( voterpos > 0 )
revert("You can not vote twice.");
// Send notification of the valid vote
emit vote( msg.sender, victim, promotion );
// Do we have enough votes to perform the operation?
if ( prop.voters.length < sealers.length/2+1 ) if ( prop.voters.length < sealers.length/2+1 )
return; return;
//
// Remove the proposal because the voting is complete.
sealerproposals = _remove_entry( proppos, sealerproposals );
// Is it a promotion or a demotion? // Is it a promotion or a demotion?
if ( prop.promotion ) if ( promotion )
{ // Add victim to sealer list
sealers.push( victim ); sealers.push( victim );
}
else else
{ // Remove victim from sealer list
uint i = sealers.length; sealers = _remove_entry( sealerIdx(victim), sealers );
// Delete all occurences of the victim from the sealer list (should be just a single occurence)
while ( i-- > 0 ) {
if (sealers[i] == victim )
{
for (uint j=i; j<sealers.length-1; j--)
sealers[j] = sealers[j+1];
sealers.length--;
}
}
}
// Remove the proposal, as the voting is complete.
for (i=proppos; i<adminproposals.length-1; i--)
adminproposals[i] = adminproposals[i+1];
adminproposals.length--;
// Send notification // Send notification
emit adminChange( victim, promotion ); emit adminChange( victim, promotion );
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment