voting.cpp 9.4 KB
Newer Older
1 2 3 4 5 6 7 8 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 37 38 39 40 41
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#include "eosio.system.hpp"

#include <eosiolib/eosio.hpp>
#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 <array>
#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;


   static constexpr uint32_t blocks_per_year = 52*7*24*2*3600; // half seconds per year
   static constexpr uint32_t blocks_per_producer = 12;


   /**
    *  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 已提交
42
   void system_contract::regproducer( const account_name producer, const eosio::public_key& producer_key, const std::string& url ) { //, const eosio_parameters& prefs ) {
43
      eosio_assert( url.size() < 512, "url too long" );
44
      //eosio::print("produce_key: ", producer_key.size(), ", sizeof(public_key): ", sizeof(public_key), "\n");
45 46
      require_auth( producer );

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

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

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

67
      const auto& prod = _producers.get( producer );
68

D
Daniel Larimer 已提交
69
      _producers.modify( prod, 0, [&]( producer_info& info ){
D
Daniel Larimer 已提交
70
         info.producer_key = eosio::public_key();
D
Daniel Larimer 已提交
71
      });
72 73
   }

D
Daniel Larimer 已提交
74

75
   eosio::asset system_contract::payment_per_block(uint32_t percent_of_max_inflation_rate) {
76
      const eosio::asset token_supply = eosio::token(N(eosio.token)).get_supply(eosio::symbol_type(system_token_symbol).name());
77
      const double annual_rate = double(max_inflation_rate * percent_of_max_inflation_rate) / double(10000);
78
      const double continuous_rate = std::log1p(annual_rate);
79
      int64_t payment = static_cast<int64_t>((continuous_rate * double(token_supply.amount)) / double(blocks_per_year));
80
      return eosio::asset(payment, system_token_symbol);
81 82 83
   }

   void system_contract::update_elected_producers(time cycle_time) {
D
Daniel Larimer 已提交
84
      auto idx = _producers.get_index<N(prototalvote)>();
85 86 87 88 89 90 91 92

      eosio::producer_schedule schedule;
      schedule.producers.reserve(21);
      size_t n = 0;
      for ( auto it = idx.crbegin(); it != idx.crend() && n < 21 && 0 < it->total_votes; ++it ) {
         if ( it->active() ) {
            schedule.producers.emplace_back();
            schedule.producers.back().producer_name = it->owner;
93
            schedule.producers.back().block_signing_key = it->producer_key; 
94 95 96 97 98 99 100 101 102 103
            ++n;
         }
      }
      if ( n == 0 ) { //no active producers with votes > 0
         return;
      }

      // should use producer_schedule_type from libraries/chain/include/eosio/chain/producer_schedule.hpp
      bytes packed_schedule = pack(schedule);
      set_active_producers( packed_schedule.data(),  packed_schedule.size() );
104

105
      // not voted on
D
Daniel Larimer 已提交
106
      _gstate.first_block_time_in_cycle = cycle_time;
107 108

      // derived parameters
D
Daniel Larimer 已提交
109 110 111 112 113 114
      auto half_of_percentage = _gstate.percent_of_max_inflation_rate / 2;
      auto other_half_of_percentage = _gstate.percent_of_max_inflation_rate - half_of_percentage;
      _gstate.payment_per_block = payment_per_block(half_of_percentage);
      _gstate.payment_to_eos_bucket = payment_per_block(other_half_of_percentage);
      _gstate.blocks_per_cycle = blocks_per_producer * schedule.producers.size();

115 116
      if (_gstate.max_ram_size <_gstate.total_ram_bytes_reserved ) {
         _gstate.max_ram_size =_gstate.total_ram_bytes_reserved;
117 118
      }

D
Daniel Larimer 已提交
119
      auto issue_quantity =_gstate.blocks_per_cycle * (_gstate.payment_per_block +_gstate.payment_to_eos_bucket);
120 121
      INLINE_ACTION_SENDER(eosio::token, issue)( N(eosio.token), {{N(eosio),N(active)}},
                                                 {N(eosio), issue_quantity, std::string("producer pay")} );
122

D
Daniel Larimer 已提交
123
      set_blockchain_parameters( _gstate );
124 125 126
   }

   /**
127
    *  @pre producers must be sorted from lowest to highest and must be registered and active
128
    *  @pre if proxy is set then no producers can be voted for
129
    *  @pre if proxy is set then proxy account must exist and be registered as a proxy
130 131 132
    *  @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
133 134 135 136 137 138 139 140
    *  @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.
141
    */
D
Daniel Larimer 已提交
142 143
   void system_contract::voteproducer( const account_name voter_name, const account_name proxy, const std::vector<account_name>& producers ) {
      require_auth( voter_name );
144 145 146 147

      //validate input
      if ( proxy ) {
         eosio_assert( producers.size() == 0, "cannot vote for producers and proxy at same time" );
D
Daniel Larimer 已提交
148
         eosio_assert( voter_name != proxy, "cannot proxy to self" );
149 150 151 152 153 154 155 156
         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 已提交
157 158 159 160
      auto voter = _voters.find(voter_name);
      eosio_assert( voter != _voters.end(), "user must stake before they can vote" ); /// staking creates voter object

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

163 164 165
      if( voter->is_proxy ) {
         new_vote_weight += voter->proxied_vote_weight;
      }
D
Daniel Larimer 已提交
166

167
      boost::container::flat_map<account_name, double> producer_deltas;
D
Daniel Larimer 已提交
168 169 170 171
      for( const auto& p : voter->producers ) {
         producer_deltas[p] -= voter->last_vote_weight;
      }

172 173 174
      if( new_vote_weight >= 0 ) {
         for( const auto& p : producers ) {
            producer_deltas[p] += new_vote_weight;
D
Daniel Larimer 已提交
175
         }
176
      }
D
Daniel Larimer 已提交
177

178 179 180 181
      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;
182
            print( "    vote weight: ", vp.last_vote_weight, "\n" );
183
         });
D
Daniel Larimer 已提交
184 185
      }

186 187 188 189 190
      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;
191
            print( "    vote weight: ", vp.last_vote_weight, "\n" );
192 193
         });
      }
D
Daniel Larimer 已提交
194 195

      _voters.modify( voter, 0, [&]( auto& av ) {
196
                      print( "new_vote_weight: ", new_vote_weight, "\n" );
D
Daniel Larimer 已提交
197 198 199
         av.last_vote_weight = new_vote_weight;
         av.producers = producers;
         av.proxy     = proxy;
200
         print( "    vote weight: ", av.last_vote_weight, "\n" );
D
Daniel Larimer 已提交
201 202
      });

203 204 205 206 207 208 209 210
      print( __FILE__, ":", __LINE__, "   ");
      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" );
211 212 213 214 215
            });
         }
      }
   }

216 217 218 219 220 221 222 223 224 225
   /**
    *  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 ) {
226 227
      require_auth( proxy );

228 229 230 231
      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" );
232

233 234
      _voters.modify( pitr, 0, [&]( auto& p ) {
         p.is_proxy = isproxy;
235
         print( "    vote weight: ", p.last_vote_weight, "\n" );
236
      });
237 238 239
   }

}