voting.cpp 8.5 KB
Newer Older
1 2 3 4 5 6 7
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#include "eosio.system.hpp"

#include <eosiolib/eosio.hpp>
D
Daniel Larimer 已提交
8
#include <eosiolib/crypto.h>
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#include <eosiolib/print.hpp>
#include <eosiolib/datastream.hpp>
#include <eosiolib/serialize.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/privileged.hpp>
#include <eosiolib/singleton.hpp>
#include <eosiolib/transaction.hpp>
#include <eosio.token/eosio.token.hpp>

#include <algorithm>
#include <cmath>

namespace eosiosystem {
   using eosio::indexed_by;
   using eosio::const_mem_fun;
   using eosio::bytes;
   using eosio::print;
   using eosio::singleton;
   using eosio::transaction;

   /**
    *  This method will create a producer_config and producer_info object for 'producer'
    *
    *  @pre producer is not already registered
    *  @pre producer to register is an account
    *  @pre authority of producer to register
    *
    */
D
Daniel Larimer 已提交
37
   void system_contract::regproducer( const account_name producer, const eosio::public_key& producer_key, const std::string& url ) { //, const eosio_parameters& prefs ) {
38
      eosio_assert( url.size() < 512, "url too long" );
39
      //eosio::print("produce_key: ", producer_key.size(), ", sizeof(public_key): ", sizeof(public_key), "\n");
40 41
      require_auth( producer );

D
Daniel Larimer 已提交
42
      auto prod = _producers.find( producer );
43

D
Daniel Larimer 已提交
44
      if ( prod != _producers.end() ) {
45
         if( producer_key != prod->producer_key ) {
D
Daniel Larimer 已提交
46
             _producers.modify( prod, producer, [&]( producer_info& info ){
47
                  info.producer_key = producer_key;
D
Daniel Larimer 已提交
48
             });
49
         }
50
      } else {
D
Daniel Larimer 已提交
51
         _producers.emplace( producer, [&]( producer_info& info ){
52 53
               info.owner       = producer;
               info.total_votes = 0;
54
               info.producer_key =  producer_key;
D
Daniel Larimer 已提交
55
         });
56 57 58 59 60 61
      }
   }

   void system_contract::unregprod( const account_name producer ) {
      require_auth( producer );

62
      const auto& prod = _producers.get( producer );
63

D
Daniel Larimer 已提交
64
      _producers.modify( prod, 0, [&]( producer_info& info ){
D
Daniel Larimer 已提交
65
         info.producer_key = eosio::public_key();
D
Daniel Larimer 已提交
66
      });
67 68
   }

K
Khaled Al-Hassanieh 已提交
69
   void system_contract::update_elected_producers( block_timestamp block_time ) {
D
Daniel Larimer 已提交
70 71
      _gstate.last_producer_schedule_update = block_time;

D
Daniel Larimer 已提交
72
      auto idx = _producers.get_index<N(prototalvote)>();
73

D
Daniel Larimer 已提交
74 75
      std::vector< std::pair<eosio::producer_key,uint16_t> > top_producers;
      top_producers.reserve(21);
K
Khaled Al-Hassanieh 已提交
76

D
Daniel Larimer 已提交
77 78 79 80
      for ( auto it = idx.cbegin(); it != idx.cend() && top_producers.size() < 21 && 0 < it->total_votes; ++it ) {
         if( !it->active() ) continue;
         top_producers.emplace_back( std::pair<eosio::producer_key,uint16_t>({{it->owner, it->producer_key}, it->location}));
      }
K
Khaled Al-Hassanieh 已提交
81 82


D
Daniel Larimer 已提交
83 84
      /// sort by producer name
      std::sort( top_producers.begin(), top_producers.end() );
K
Khaled Al-Hassanieh 已提交
85

D
Daniel Larimer 已提交
86
      std::vector<eosio::producer_key> producers;
K
Khaled Al-Hassanieh 已提交
87

D
Daniel Larimer 已提交
88 89 90
      producers.reserve(top_producers.size());
      for( const auto& item : top_producers )
         producers.push_back(item.first);
91

D
Daniel Larimer 已提交
92 93 94
      bytes packed_schedule = pack(producers);
      checksum160 new_id;
      sha1( packed_schedule.data(), packed_schedule.size(), &new_id );
95

D
Daniel Larimer 已提交
96 97 98 99
      if( new_id != _gstate.last_producer_schedule_id ) {
         _gstate.last_producer_schedule_id = new_id;
         set_active_producers( packed_schedule.data(),  packed_schedule.size() );
      }
100
      _gstate.last_producer_schedule_update = block_time;
101 102
   }

103

104
   /**
105
    *  @pre producers must be sorted from lowest to highest and must be registered and active
106
    *  @pre if proxy is set then no producers can be voted for
107
    *  @pre if proxy is set then proxy account must exist and be registered as a proxy
108 109 110
    *  @pre every listed producer or proxy must have been previously registered
    *  @pre voter must authorize this action
    *  @pre voter must have previously staked some EOS for voting
111 112 113 114 115 116 117 118
    *  @pre voter->staked must be up to date
    *
    *  @post every producer previously voted for will have vote reduced by previous vote weight 
    *  @post every producer newly voted for will have vote increased by new vote amount
    *  @post prior proxy will proxied_vote_weight decremented by previous vote weight
    *  @post new proxy will proxied_vote_weight incremented by new vote weight
    *
    *  If voting for a proxy, the producer votes will not change until the proxy updates their own vote.
119
    */
D
Daniel Larimer 已提交
120 121
   void system_contract::voteproducer( const account_name voter_name, const account_name proxy, const std::vector<account_name>& producers ) {
      require_auth( voter_name );
122 123 124 125

      //validate input
      if ( proxy ) {
         eosio_assert( producers.size() == 0, "cannot vote for producers and proxy at same time" );
D
Daniel Larimer 已提交
126
         eosio_assert( voter_name != proxy, "cannot proxy to self" );
127 128 129 130 131 132 133 134
         require_recipient( proxy );
      } else {
         eosio_assert( producers.size() <= 30, "attempt to vote for too many producers" );
         for( size_t i = 1; i < producers.size(); ++i ) {
            eosio_assert( producers[i-1] < producers[i], "producer votes must be unique and sorted" );
         }
      }

D
Daniel Larimer 已提交
135 136 137
      auto voter = _voters.find(voter_name);
      eosio_assert( voter != _voters.end(), "user must stake before they can vote" ); /// staking creates voter object

138 139 140 141 142 143 144 145 146
      /**
       * The first time someone votes we calculate and set last_vote_weight, since they cannot unstake until
       * after total_activiated_stake hits threshold, we can use last_vote_weight to determine that this is
       * their first vote and should consider their stake activated.
       */
      if( voter->last_vote_weight <= 0.0 ) {
         _gstate.total_activiated_stake += voter->staked;
      }

D
Daniel Larimer 已提交
147
      auto weight = int64_t(now() / (seconds_per_day * 7)) / double( 52 );
148
      double new_vote_weight = double(voter->staked) * std::pow(2,weight);
D
Daniel Larimer 已提交
149

150 151 152
      if( voter->is_proxy ) {
         new_vote_weight += voter->proxied_vote_weight;
      }
D
Daniel Larimer 已提交
153

154
      boost::container::flat_map<account_name, double> producer_deltas;
D
Daniel Larimer 已提交
155 156 157 158
      for( const auto& p : voter->producers ) {
         producer_deltas[p] -= voter->last_vote_weight;
      }

159 160 161
      if( new_vote_weight >= 0 ) {
         for( const auto& p : producers ) {
            producer_deltas[p] += new_vote_weight;
D
Daniel Larimer 已提交
162
         }
163
      }
D
Daniel Larimer 已提交
164

165 166 167 168
      if( voter->proxy != account_name() ) {
         auto old_proxy = _voters.find( voter->proxy );
         _voters.modify( old_proxy, 0, [&]( auto& vp ) {
             vp.proxied_vote_weight -= voter->last_vote_weight;
169
            print( "    vote weight: ", vp.last_vote_weight, "\n" );
170
         });
D
Daniel Larimer 已提交
171 172
      }

173 174 175 176 177
      if( proxy != account_name() && new_vote_weight > 0 ) {
         auto new_proxy = _voters.find( voter->proxy );
         eosio_assert( new_proxy != _voters.end() && new_proxy->is_proxy, "invalid proxy specified" );
         _voters.modify( new_proxy, 0, [&]( auto& vp ) {
             vp.proxied_vote_weight += new_vote_weight;
178
            print( "    vote weight: ", vp.last_vote_weight, "\n" );
179 180
         });
      }
D
Daniel Larimer 已提交
181 182

      _voters.modify( voter, 0, [&]( auto& av ) {
183
                      print( "new_vote_weight: ", new_vote_weight, "\n" );
D
Daniel Larimer 已提交
184 185 186
         av.last_vote_weight = new_vote_weight;
         av.producers = producers;
         av.proxy     = proxy;
187
         print( "    vote weight: ", av.last_vote_weight, "\n" );
D
Daniel Larimer 已提交
188 189
      });

190 191 192 193 194 195 196
      for( const auto& pd : producer_deltas ) {
         auto pitr = _producers.find( pd.first );
         if( pitr != _producers.end() ) {
            _producers.modify( pitr, 0, [&]( auto& p ) {
               p.total_votes += pd.second;
               eosio_assert( p.total_votes >= 0, "something bad happened" );
               eosio_assert( p.active(), "producer is not active" );
197 198 199 200 201
            });
         }
      }
   }

202 203 204 205 206 207 208 209 210 211
   /**
    *  An account marked as a proxy can vote with the weight of other accounts which
    *  have selected it as a proxy. Other accounts must refresh their voteproducer to
    *  update the proxy's weight.    
    *
    *  @param isproxy - true if proxy wishes to vote on behalf of others, false otherwise
    *  @pre proxy must have something staked (existing row in voters table)
    *  @pre new state must be different than current state
    */
   void system_contract::regproxy( const account_name proxy, bool isproxy ) {
212 213
      require_auth( proxy );

214 215 216 217
      auto pitr = _voters.find(proxy);
      eosio_assert( pitr != _voters.end(), "proxy must have some stake first" );
      eosio_assert( !pitr->is_proxy, "account is already a proxy" );
      eosio_assert( pitr->is_proxy != isproxy, "action has no effect" );
218

219 220
      _voters.modify( pitr, 0, [&]( auto& p ) {
         p.is_proxy = isproxy;
221
         print( "    vote weight: ", p.last_vote_weight, "\n" );
222
      });
223 224
   }

225
} /// namespace eosiosystem