diff --git a/bin/sealeradd.sh b/bin/sealeradd.sh new file mode 100755 index 0000000000000000000000000000000000000000..53025d20b04bc6634e9575c7370175fb8ac9af37 --- /dev/null +++ b/bin/sealeradd.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Robert Martin-Legene <robert@nic.ar> + +# Proposes to promote a new sealer or to demote an existing sealer. +# Also sends this vote to the new Sealers contract, so we have a place +# a contract can look up which addresses are sealers. + +if [ -z "${BFAHOME}" ]; then echo "\$BFAHOME not set. Did you source bfa/bin/env ?" >&2; exit 1; fi +source ${BFAHOME}/bin/libbfa.sh || exit 1 + +operation="false" +if [ $( basename $0 ) == "sealeradd.sh" ] +then + operation="true" +fi +victim="$1" +( + echo "clique.propose(\"0x${victim#0x}\", ${operation});" + contractCall Sealers admin_promote \"0x${victim#0x}\" ${operation} +) | geth_attach diff --git a/bin/sealerrem.sh b/bin/sealerrem.sh new file mode 120000 index 0000000000000000000000000000000000000000..9f238ec6fc1160bcb11d5db94c4a25a38c68c150 --- /dev/null +++ b/bin/sealerrem.sh @@ -0,0 +1 @@ +sealeradd.sh \ No newline at end of file diff --git a/src/contract.Sealers.solc b/src/contract.Sealers.solc new file mode 100644 index 0000000000000000000000000000000000000000..03804744d7b7758a192b1e2c1f34a38271955344 --- /dev/null +++ b/src/contract.Sealers.solc @@ -0,0 +1,125 @@ +pragma solidity ^0.4.22; + +contract Sealers { + + 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. + } + Proposal[] public adminproposals; + address[] public sealers; + + event vote( address voter, address victim, bool promotionOrDemotion ); + event adminChange( address admin, bool promotionOrDemotion ); + + constructor() public { + sealers[0] = msg.sender; + } + + function isSealer( address subj ) public view returns (bool) { + for (uint i=sealers.length; i>0; i--) + { + if (subj == sealers[i-1]) + return true; + } + return false; + } + modifier onlySealersTX { + require( isSealer( tx.origin ), "tx.origin is not a known sealer and can not call this function." ); + _; + } + modifier onlySealers { + require( isSealer( msg.sender ), "msg.sender is not a known sealer and can not call this function." ); + _; + } + + function sealerLength() public view returns (uint) { + return sealers.length; + } + + 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 _trimProposals() private { + uint i = adminproposals.length; + while ( i-- > 0 ) + { + if ( adminproposals[i].votestart < block.number+30000 ) + { + while ( i < adminproposals.length ) + { + adminproposals[i] = adminproposals[i+1]; + i--; + } + adminproposals.length--; + } + } + } + + function admin_promote( address victim, bool promotion ) public onlySealersTX { + // Janitor + _trimProposals(); + bool isS = isSealer(victim); + // Is already Sealer and want to promote him? + if ( isS && promotion ) + revert("You can't promote someone who is already a sealer."); + // Is not Sealer and want to demote him? + if ( !isS && !promotion ) + revert("You can't demote someone who is not a sealer."); + // First see if we can find the index of an already running proposal for this victim,promotion tupple. + uint proppos = adminproposals.length; + while ( proppos>0 && adminproposals[proppos-1].victim != victim && adminproposals[proppos-1].promotion != promotion) + proppos--; + // If we found no matching proposals, we will add it, so we can now vote on it. + if (proppos == 0) + { + address[] memory sillycompiler; + proppos = adminproposals.push( Proposal(victim, block.number, promotion, sillycompiler ) ); + } + // substract one, to make proppos actually reflect the position in the index. + proppos--; + // Make things a bit more easy to read by using a shorter variable name + Proposal memory prop = adminproposals[proppos]; + // Now look through the proposal and see if this sender already voted. + uint voterpos = prop.voters.length; + while ( voterpos>0 && prop.voters[voterpos-1] != msg.sender ) + voterpos--; + // Return if sender already voted + 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 ) + return; + // + // Is it a promotion or a demotion? + if ( prop.promotion ) + { + sealers.push( victim ); + } + else + { + uint i = sealers.length; + // 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 + emit adminChange( victim, promotion ); + } +}