diff --git a/README.md b/README.md index a8f2b13f50e8ebce7a054ea1bb29769d5445de8b..e3d1ab9955ee2d8836e0f9791ce76841a0f9838e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ ## Sitio web: https://www.bfa.ar/ ## Repositorio: https://gitlab.bfa.ar/blockchain/nucleo.git +Este repositorio contiene lo nececario para instalar un "nodo BFA" (nodo sellador, nodo gateway, nodo transaccional "etc", son casi iguales). + Esta guÃa deberÃa funcionar en Debian o sus derivados. Testeado en *Debian* y *Ubuntu server* sin la GUI instalada en ninguno de ellos. ([Capturas de pantallas instalando un Ubuntu 18.04](https://gitlab.bfa.ar/blockchain/nucleo/wikis/Instalando-Ubuntu-Server-18.04)) @@ -17,15 +19,16 @@ Esta guÃa deberÃa funcionar en Debian o sus derivados. Testeado en *Debian* y Van a aparecer varios **warnings** mientras se instala web3. Esto parece ser "normal". Ignorarlo no parece causar problemas. 4. Cambiá al usuario `bfa` - como root: `su - bfa` -5. Crea una cuenta +5. Crea una cuenta (solamente nodos selladores y nodos transaccionales necesitan una cuenta) - como bfa: `admin.sh account` 6. Comenzá la sincronización. **Esto puede llevar un rato largo** (este script se ejecuta automáticamente cuando se reinicia el sistema). - como bfa: `start.sh` -7. Monitoreá los logs con `bfalog.sh`. Apretá CTRL-C en cualquier momento para detener el `tail -f`. -8. Cambiá la configuración de tu nodo usando `admin.sh syncmode` +7. `localstate.pl` muestra el estado actual del nodo. +8. Monitoreá los logs con `bfalog.sh`. Apretá CTRL-C en cualquier momento para detener el `tail -f`. +9. Cambiá la configuración de tu nodo usando `admin.sh syncmode` - Hacé esto antes de haber sincronizado mucho en el paso anterior, ya que esto podrÃa remover todos los datos de la cadena que hayas bajado y reiniciar la sincronización de la cadena. -9. Esperá a aque termine de sincronizar -10. Herramientas simples super básicas (más bien pruebas de concepto, para inspirar a los programadores): +10. Esperá a aque termine de sincronizar +11. Herramientas simples super básicas (más bien pruebas de concepto, para inspirar a los programadores): - `explorer.sh` : Sigue el bloque más nuevo "*lastest*" por default, pero podés especificar un número de bloque cualquiera como argumento, por ejemplo `explorer.sh 0` permite ver el génesis (bloque 0). - `walker.pl` : También toma un número de bloque para iniciar. Sigue esperando nuevos bloques. - `sealerwatch.pl` : Mira cuando los selladores firman. @@ -100,3 +103,6 @@ Argumento 1 es el nombre de archivo del *smart contract* a compilar. Ejemplo: `compile.and.deploy.contract src/TimestampAuthority.sol` +## localstate.pl + +Muestra varias detalles del entorno local. \ No newline at end of file diff --git a/bin/bfaupdate.sh b/bin/bfaupdate.sh index f6109cc0f79cc5b3572b8a89bbd1dd559285c4dd..bbc8ec83125f2972078088e8dfcb024199a60fcb 100755 --- a/bin/bfaupdate.sh +++ b/bin/bfaupdate.sh @@ -6,5 +6,6 @@ source ${BFAHOME}/bin/libbfa.sh || exit 1 set -x cd ${BFAHOME} +sudo apt -y install libclass-accessor-perl git pull npm rebuild diff --git a/bin/installbfa.sh b/bin/installbfa.sh index 8387c8522bcef4297f20cdbefe262f0564067f86..1ff8ec3ed1388d9e870890d6c89adfa22799847b 100755 --- a/bin/installbfa.sh +++ b/bin/installbfa.sh @@ -191,7 +191,7 @@ grep -q Ubuntu /etc/issue && apt-add-repository multiverse apt update # development tools aptinstall dirmngr apt-transport-https curl git curl build-essential sudo -aptinstall jq libjson-perl libwww-perl +aptinstall jq libjson-perl libwww-perl libclass-accessor-perl usersetup nodejsinstall web3install diff --git a/bin/sealerwatch.pl b/bin/sealerwatch.pl index c3c77eafaa17d768a223eb5d29b0fa8b7e5670ab..12d11ebccfb97e010cab2ed4769183964d52ac44 100755 --- a/bin/sealerwatch.pl +++ b/bin/sealerwatch.pl @@ -329,13 +329,18 @@ chdir "$ENV{BFAHOME}" or die $!; my $number = shift || 'latest'; my $tools = tools->new; my %cache; -my $lastblock; +my $latestvalidblock; my %signers; +# If we started with 'latest' then subtract 100, +# so we get an updated list of recent signers faster. +my $subtract = $number eq 'latest' ? 100 : 0; $libbfa = libbfa->new(); my $block = block->new( $libbfa )->get( $number ); die if not defined $block; $number = $block->number; +my $run_to = $number; +$number -= $subtract; print ansi::CUP().ansi::ED(); sub determine_colour @@ -409,14 +414,12 @@ sub presentation_top while ( defined $block || sleep 1 ) { my $parent = undef; - $block = block->new( $libbfa )->get( $number ); - if ( not defined $block ) - { - presentation_top( $lastblock ); - next; - } + my $prospect = block->new( $libbfa )->get( $number ); + $block = $prospect + if defined $prospect; presentation_top( $block ); - $lastblock = $block; + next + if not defined $prospect; $cache{$number}{'block'} = $block; $number = $block->number; if ( exists $cache{ $number - 1 }{'block'} ) @@ -488,5 +491,6 @@ while ( defined $block || sleep 1 ) print ansi::ED(0), ansi::CUP($maxy, 1); # $number = $block->number + 1; - select( undef, undef,undef, 0.2 ); + select( undef, undef,undef, 0.1 ) + if $number >= $run_to; } diff --git a/network/aliases b/network/aliases index 0bbc16c03e6aeff2e6f292fceec66951483e6911..277e0fb51005b45344368a233ac8d522dafda90f 100644 --- a/network/aliases +++ b/network/aliases @@ -21,3 +21,4 @@ 0xe191ac3108cb2c5d70d0e978876c048d4ba41b03 ANSV / Agencia Nacional de Seguridad Vial 0x52f8a89484947cd29903b6f52ec6beda69965e38 CABASE PSS / CABASE Posadas 0xb43b53af0db2c3fac788195f4b4dcf2b3d72aa44 IPLAN +0x354779914a94ad428d2b53ae96cce3010bb0ce1e Red Link diff --git a/src/Sealers.sol b/src/Majority.sol similarity index 59% rename from src/Sealers.sol rename to src/Majority.sol index bfdecdd62114bce247d237d357baeaceb9db7f3f..71a5515b03f57807ff9c24e03485377e880accab 100644 --- a/src/Sealers.sol +++ b/src/Majority.sol @@ -1,13 +1,17 @@ +// Robert Martin-Legene <robert@nic.ar> // 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 contract is supposed to maintain a list of accounts authorized +// to control members of "the club" using a majority (n/1+1). +// For instance, could be useful for +// - a list of sealers +// - a board of directors +// - a local chess club +// - even outsourcing this kind of management from another smart contract. +contract Majority { // 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 { @@ -16,41 +20,41 @@ contract Sealers { bool promotion; // true=promotion, false=demotion address[] voters; // List of voters. } - address[] private sealers; - Proposal[] private sealerproposals; + address[] public voters; + Proposal[] public voterproposals; event voteCast( address voter, address victim, bool promotionOrDemotion ); event adminChange( address admin, bool promotionOrDemotion ); constructor() public { - sealers.push( msg.sender ); + voters.push( msg.sender ); } - // This function is used to know how many sealers are in the sealers list. - function sealerLength() public view returns (uint) + // This function is used to know how many voters exist. + function votersLength() public view returns (uint) { - return sealers.length; + return voters.length; } - // This function is to be used to get the address from a specific position in the sealers list. + // This function is to be used to get the address from a specific position in the list. // Remember first position is 0. - // Last position is sealers.length-1. - function sealerPosition( uint idx ) public view returns (address) + // Last position is voters.length-1. + function voterPosition( uint idx ) public view returns (address) { - require( idx < sealers.length, "There are not that many sealers registered in the list." ); - return sealers[idx]; + require( idx < voters.length, "There are not that many entries in the list." ); + return voters[idx]; } - function isSealer( address subject ) public view returns (bool) + function isVoter( address subject ) public view returns (bool) { - return ( _findAddressInList( sealers, subject ) > 0 ); + return ( _findAddressInList( voters, subject ) > 0 ); } // Returns an index to the position of the needle inside haystack. // Beware that: // 0 = not found. - // 1 = first position. + // 1 = first position in a list (so this is actually the real list position + 1). function _findAddressInList( address[] haystack, address needle ) private pure returns (uint) { uint i = haystack.length; @@ -64,34 +68,34 @@ contract Sealers { { // Move all items in the list (after idx) one step closer to the // front of the list. - uint max = sealerproposals.length; + uint max = voterproposals.length; while ( ++idx < max ) - sealerproposals[idx-1] = sealerproposals[idx]; + voterproposals[idx-1] = voterproposals[idx]; // "pop" the end of the list, making the list shorter. - sealerproposals.length--; + voterproposals.length--; } - function _del_votes( address victim ) private + function _del_votes( address victim ) private { - uint i_max = sealerproposals.length; + uint i_max = voterproposals.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_max = voterproposals[i].voters.length; uint j = j_max; while ( j-- > 0 ) - if ( sealerproposals[i].voters[j] == victim ) + if ( voterproposals[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--; + voterproposals[i].voters[k-1] = voterproposals[i].voters[k]; + voterproposals[i].voters.length--; j_max--; - if ( sealerproposals[i].voters.length == 0 ) + if ( voterproposals[i].voters.length == 0 ) { _remove_proposal( i ); i_max--; @@ -100,69 +104,70 @@ contract Sealers { } } - function _remove_sealer( address victim ) private + function _remove_voter( 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 max = voters.length; uint i = max; while ( i-- > 0 ) - if ( sealers[i] == victim ) + if ( voters[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]; + voters[j-1] = voters[j]; // "pop" the end of the list, making the list shorter. - sealers.length--; + voters.length--; return; } } // This function sees if any proposals have overstayed their welcome and thus - // needs to be removed. + // needs to be removed. This is a bit like spring cleaning. We remove proposals + // that are over 30k blocks old. function _trimProposals() private { - uint i = sealerproposals.length; + uint i = voterproposals.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 ) + if ( voterproposals[i].votestart + 30000 <= block.number ) _remove_proposal( i ); } } // 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 + // requirements. Why the whole list? Because if a voter has been 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 ) + while ( prevlength != voterproposals.length ) { - uint i = sealerproposals.length; + uint i = voterproposals.length; prevlength = i; - uint majority = sealers.length / 2 + 1; + uint majority = voters.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 ) + if ( voterproposals[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 ); + if ( voterproposals[i].promotion ) + // Add victim to member list + voters.push( voterproposals[i].victim ); else - // Remove victim from sealer list - _remove_sealer( sealerproposals[i].victim ); + // Remove victim from member list + _remove_voter( voterproposals[i].victim ); // Send notification - emit adminChange( sealerproposals[i].victim, - sealerproposals[i].promotion ); + emit adminChange( voterproposals[i].victim, + voterproposals[i].promotion ); // Remove the proposal because the voting is complete. _remove_proposal( i ); } @@ -175,9 +180,9 @@ contract Sealers { // 1 = first position. function promotionIdx( address victim, bool promotion ) public view returns (uint) { - uint i = sealerproposals.length; + uint i = voterproposals.length; while ( i-- > 0) - if ( sealerproposals[i].victim == victim && sealerproposals[i].promotion == promotion ) + if ( voterproposals[i].victim == victim && voterproposals[i].promotion == promotion ) return i+1; return 0; } @@ -186,24 +191,22 @@ contract Sealers { // 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) ) + // Is caller a voter? + if ( !isVoter(voter) ) return false; - // Is already Sealer and want to promote him? - // Can't promote someone who is already a sealer. - bool victimSealer = isSealer( victim ); - if ( victimSealer && promotion ) + // Can't promote someone who is already a voter. + bool victimVoter = isVoter( victim ); + if ( victimVoter && promotion ) return false; - // Is not Sealer and want to demote him? - // Can't demote someone who is not a sealer. - if ( !victimSealer && !promotion ) + // Can't demote someone who is not a member. + if ( !victimVoter && !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 ) + if ( proppos > 0 && _findAddressInList( voterproposals[proppos-1].voters, voter ) > 0 ) return false; return true; } @@ -211,7 +214,7 @@ contract Sealers { // 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 + // The boolean must be true if you want to add a member // and false if you want to remove one. function vote( address victim, bool promotion ) public { @@ -227,18 +230,18 @@ contract Sealers { { // 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 = voterproposals.push( Proposal(victim, block.number, promotion, emptyAddressList ) ); } proppos--; // Add our vote - sealerproposals[proppos].voters.push( msg.sender ); + voterproposals[proppos].voters.push( msg.sender ); // See if we're ready to promote/demote anyone, based on the proposals. _promotedemote(); - // If we have no more sealers, we have no reason to live. - if ( sealers.length == 0 ) + // If we have no more voters, we have no reason to live. + if ( voters.length == 0 ) selfdestruct( msg.sender ); } }