From 4ed50c99c5a7c5677526337a48dcbc22590b85b6 Mon Sep 17 00:00:00 2001 From: Robert Martin-Legene <robert@nic.ar> Date: Wed, 12 Sep 2018 17:58:25 -0300 Subject: [PATCH] Bugs weeded out. Sealers.sol now needs testnet trials. --- bin/compile.and.deploy.contract | 70 +++++++++++++++++++++++ src/{contract.Sealers.sol => Sealers.sol} | 67 +++++++++++++++------- 2 files changed, 116 insertions(+), 21 deletions(-) create mode 100755 bin/compile.and.deploy.contract rename src/{contract.Sealers.sol => Sealers.sol} (76%) diff --git a/bin/compile.and.deploy.contract b/bin/compile.and.deploy.contract new file mode 100755 index 0000000..f7a36c0 --- /dev/null +++ b/bin/compile.and.deploy.contract @@ -0,0 +1,70 @@ +#!/bin/bash +# 20180618 Robert Martin-Legene <robert@nic.ar> + +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 + +function create +{ + workdir=$( mktemp -p . -d ) + cleanup "$workdir" + json=$( solc --optimize --combined-json abi,bin $filename ); test $? = 0 + prefix=$( echo "$json" | jq -M '.contracts | keys | .[0]' ); + abi=$( echo "$json" | jq -rM ".contracts.${prefix}.abi" ); test $? = 0 + bin=$( echo "$json" | jq -rM ".contracts.${prefix}.bin" ); test $? = 0 + # Save abi for future use + echo $abi > ${workdir}/abi + # We could save the bin, but we don't actually use it later, plus + # it gets stored in the blockchain + #echo $bin > ${workdir}/bin + js=$( mktemp ) + cleanup "$js" + cat > $js <<EOT +var mycontract = eth.contract($abi) +var transaction = mycontract.new( { from: eth.accounts[0], data: "0x${bin}", gas: 1000000 } ) +var rcpt +while ( !rcpt ) +{ + admin.sleepBlocks( 1 ) + rcpt = eth.getTransactionReceipt( transaction.transactionHash ) +} +var address = rcpt.contractAddress +var pubcontract = mycontract.at(address) +console.log( pubcontract.address ) +EOT + echo '*** Creating contract. This will take at least 16 seconds.' + outfile=$( mktemp ) + cleanup "$outfile" + geth_exec_file $js > $outfile + if [ ` wc -l < $outfile ` = 2 -a `tail -1 < $outfile` = "true" ] + then + addr=` head -1 < $outfile ` + mkdir -p ${BFANETWORKDIR}/contracts + mv ${workdir} ${BFANETWORKDIR}/contracts/${addr} + echo Your new contract can be found in ${BFANETWORKDIR}/contracts/${addr} + ln -snf ${addr} ${BFANETWORKDIR}/contracts/${contractname} + else + echo + echo ' *** INPUT ***' + echo + cat $js + echo + echo ' *** OUTPUT ***' + echo + cat $outfile + fi +} + +filename="$1" +if [ -z "$filename" -o ! -r "$filename" ] +then + echo "Specify a filename of a contract you wish to compile." + false +fi +contractname=${filename%%.sol} +contractname=${contractname##*/} +contractname=${contractname##contract.} +contractname=${contractname%.*} +bfaconfig max +prereq jq solc geth +create diff --git a/src/contract.Sealers.sol b/src/Sealers.sol similarity index 76% rename from src/contract.Sealers.sol rename to src/Sealers.sol index 2f1b811..0d53918 100644 --- a/src/contract.Sealers.sol +++ b/src/Sealers.sol @@ -19,7 +19,7 @@ contract Sealers { address[] public sealers; Proposal[] public sealerproposals; - event vote( address voter, address victim, bool promotionOrDemotion ); + event voteCast( address voter, address victim, bool promotionOrDemotion ); event adminChange( address admin, bool promotionOrDemotion ); constructor() public @@ -56,7 +56,7 @@ contract Sealers { // Beware that: // 0 = not found. // 1 = first position. - function _findAddressInList( address[] haystack, address needle ) private view returns (uint) + function _findAddressInList( address[] haystack, address needle ) private pure returns (uint) { uint i = haystack.length; while ( i-- > 0 ) @@ -130,7 +130,8 @@ contract Sealers { // needs to be removed. function _trimProposals() private { - for ( uint i = sealerproposals.length-1; i>=0; i-- ) + uint i = sealerproposals.length; + while ( i-- > 0 ) { // If a proposal is more than 30K blocks old, then remove it from the list. if ( sealerproposals[i].votestart + 30000 <= block.number ) @@ -138,6 +139,41 @@ contract Sealers { } } + // We run through the entire list of proposals, checking if they fulfill the + // requirements. Why the whole list? Because if a sealer is removed, whom has + // not yet voted for a proposal, that proposal may now have achieved majority. + function _promotedemote() private + { + uint prevlength = 0; + // Keep looping over the list until the number of proposals stops changing. + while ( prevlength != sealerproposals.length ) + { + uint i = sealerproposals.length; + prevlength = i; + uint majority = sealers.length / 2 + 1; + // Loop over all proposals + while ( i-- > 0 ) + { + // If we have enough votes to perform the actual promotion/demotion + if ( sealerproposals[i].voters.length >= majority ) + { + // Is it a promotion or a demotion? + if ( sealerproposals[i].promotion ) + // Add victim to sealer list + sealers.push( sealerproposals[i].victim ); + else + // Remove victim from sealer list + _remove_sealer( sealerproposals[i].victim ); + + // Send notification + emit adminChange( sealerproposals[i].victim, + sealerproposals[i].promotion ); + // Remove the proposal because the voting is complete. + _remove_proposal( i ); + } + } + } + } // Returns an index to the position of the proposal inside matching the [victim,promotion] tuple // Beware that: // 0 = not found. @@ -181,14 +217,14 @@ contract Sealers { // 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 + function vote( 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 ); + emit voteCast( msg.sender, victim, promotion ); uint proppos = promotionIdx( victim, promotion ); if ( proppos == 0 ) @@ -202,23 +238,12 @@ contract Sealers { // 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 ); + // See if we're ready to promote/demote anyone, based on the proposals. + _promotedemote(); - // Send notification - emit adminChange( victim, promotion ); + // If we have no more sealers, we have no reason to live. + if ( sealers.length == 0 ) + selfdestruct( msg.sender ); } } -- GitLab