From e2c6a8b02843240d0e2aba1358b00e2ffb7279f8 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 13 Feb 2018 15:26:43 -0500 Subject: [PATCH] progress on system contract producer voting --- contracts/eosio.system/eosio.system.hpp | 177 +++++++++++++++++++++++- 1 file changed, 173 insertions(+), 4 deletions(-) diff --git a/contracts/eosio.system/eosio.system.hpp b/contracts/eosio.system/eosio.system.hpp index f1b07ceda..e4419c96a 100644 --- a/contracts/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/eosio.system.hpp @@ -14,13 +14,58 @@ #include #include +#include +#include + namespace eosiosystem { + using eosio::index_by; + using eosio::const_mem_fun; + using eosio::bytes; + using std::map; + using std::pair; template class contract { public: static const account_name system_account = SystemAccount; typedef eosio::generic_currency< eosio::token > currency; + typedef typename currency::token_type system_token_type; + + struct producer_votes { + account_name owner; + uint128_t total_votes; + + uint64_t primary_key()const { return owner; } + uint128_t by_votes()const { return total_votes; } + + EOSLIB_SERIALIZE( producer_votes, (owner)(total_votes) ); + }; + typedef eosio::multi_index< N(producervote), producer_votes, + index_by<0, N(prototalvote), producer_votes, const_mem_fun > + > producer_votes_index_type; + + struct account_votes { + account_name owner; + account_name proxy; + uint32_t last_update; + system_token_type staked; + std::vector producers; + + uint64_t primary_key()const { return owner; } + + EOSLIB_SERIALIZE( account_votes, (owner)(proxy)(last_update)(staked)(producers) ); + }; + typedef eosio::multi_index< N(accountvotes), account_votes> account_votes_index_type; + + + struct producer_config { + account_name owner; + eosio::bytes packed_key; /// a packed public key object + + uint64_t primary_key()const { return owner; } + EOSLIB_SERIALIZE( producer_config, (owner)(packed_key) ); + }; + typedef eosio::multi_index< N(producercfg), producer_config> producer_config_index_type; struct total_resources { account_name owner; @@ -34,7 +79,6 @@ namespace eosiosystem { }; - /** * Every user 'from' has a scope/table that uses every receipient 'to' as the primary key. */ @@ -73,8 +117,9 @@ namespace eosiosystem { ACTION( SystemAccount, regproducer ) { account_name producer_to_register; + bytes producer_key; - EOSLIB_SERIALIZE( regproducer, (producer_to_register) ); + EOSLIB_SERIALIZE( regproducer, (producer_to_register)(producer_key) ); }; ACTION( SystemAccount, regproxy ) { @@ -204,12 +249,132 @@ namespace eosiosystem { + /** + * This method will create a producr_config and producer_votes object for 'producer_to_register' + * + * @pre producer is not already registered + * @pre producer to register is an account + * @pre authority of producer to register + * + */ static void on( const regproducer& reg ) { - require_auth( reg.producer_to_register ); + auto producer = reg.producer_to_register; + require_auth( producer ); + + producer_votes_index_type votes( SystemAccount, SystemAccount ); + const auto* existing = votes.find( producer ); + eosio_assert( !existing, "producer already registered" ); + + votes.emplace( producer, [&]( auto& pv ){ + pv.owner = producer; + pv.total_votes = 0; + }); + + producer_config_index_type proconfig( SystemAccount, SystemAccount ); + proconfig.emplace( producer, [&]( auto& pc ) { + pc.owner = producer; + pc.packed_key = reg.producer_key; + }); + } + + ACTION( SystemAccount, stakevote ) { + account_name voter; + system_token_type amount_to_stake; + + EOSLIB_SERIALIZE( stakevote, (voter)(amount_to_stake) ) + }; + + static void on( const stakevote& sv ) { + eosio_assert( sv.amount_to_stake.quantity > 0, "must stake some tokens" ); + require_auth( sv.voter ); + + account_votes_index_type avotes( SystemAccount, SystemAccount ); + + const auto* acv = avotes.find( sv.voter ); + if( !acv ) { + acv = &avotes.emplace( sv.voter, [&]( auto& av ) { + av.owner = sv.voter; + av.last_update = now(); + av.proxy = 0; + }); + } + + uint128_t old_weight = acv->staked.quantity; + uint128_t new_weight = old_weight + sv.amount_to_stake.quantity; + + producer_votes_index_type votes( SystemAccount, SystemAccount ); + + for( auto p : acv->producers ) { + votes.update( votes.get( p ), 0, [&]( auto& v ) { + v.total_votes -= old_weight; + v.total_votes += new_weight; + }); + } + + avotes.update( *acv, 0, [&]( auto av ) { + av.last_update = now(); + av.staked += sv.amount_to_stake; + }); + + currency::inline_transfer( sv.voter, SystemAccount, sv.amount_to_stake, "stake for voting" ); + }; + + ACTION( SystemAccount, voteproducer ) { + account_name voter; + account_name proxy; + std::vector producers; + + EOSLIB_SERIALIZE( voteproducer, (voter)(proxy)(producers) ) + }; + + /** + * @pre vp.producers must be sorted from lowest to highest + * @pre if proxy is set then no producers can be voted for + * @pre every listed producer or proxy must have been previously registered + * @pre vp.voter must authorize this action + * @pre voter must have previously staked some EOS for voting + */ + static void on( const voteproducer& vp ) { + eosio_assert( std::is_sorted( vp.producers.begin(), vp.producers.end() ), "producer votes must be sorted" ); + eosio_assert( vp.producers.size() <= 30, "attempt to vote for too many producers" ); + if( vp.proxy != 0 ) eosio_assert( vp.producers.size() == 0, "cannot vote for producers and proxy at same time" ); + + require_auth( vp.voter ); + + account_votes_index_type avotes( SystemAccount, SystemAccount ); + const auto& existing = avotes.get( vp.voter ); + + std::map > producer_vote_changes; + + uint128_t old_weight = existing.staked.quantity; /// old time + uint128_t new_weight = old_weight; /// TODO: update for current weight + + for( const auto& p : existing.producers ) + producer_vote_changes[p].first = old_weight; + for( const auto& p : vp.producers ) + producer_vote_changes[p].second = new_weight; + + producer_votes_index_type votes( SystemAccount, SystemAccount ); + for( const auto& delta : producer_vote_changes ) { + if( delta.second.first != delta.second.second ) { + const auto& provote = votes.get( delta.first ); + votes.update( provote, 0, [&]( auto& pv ){ + pv.total_votes -= delta.second.first; + pv.total_votes += delta.second.second; + }); + } + } + + avotes.update( existing, 0, [&]( auto& av ) { + av.proxy = vp.proxy; + av.last_update = now(); + av.producers = vp.producers; + }); } static void on( const regproxy& reg ) { require_auth( reg.proxy_to_register ); + } static void on( const nonce& ) { @@ -217,7 +382,11 @@ namespace eosiosystem { static void apply( account_name code, action_name act ) { - if( !eosio::dispatch( code, act) ) { + if( !eosio::dispatch( code, act) ) { if ( !eosio::dispatch( code, act ) ) { eosio::print("Unexpected action: ", eosio::name(act), "\n"); eosio_assert( false, "received unexpected action"); -- GitLab