voting.hpp 22.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#pragma once
#include <eosiolib/eosio.hpp>
#include <eosiolib/token.hpp>
#include <eosiolib/print.hpp>

#include <eosiolib/generic_currency.hpp>
#include <eosiolib/datastream.hpp>
#include <eosiolib/serialize.hpp>
#include <eosiolib/multi_index.hpp>
14
#include <eosiolib/privileged.hpp>
15
#include <eosiolib/singleton.hpp>
16
#include <eosiolib/transaction.hpp>
17

18
#include <array>
19 20 21 22

namespace eosiosystem {
   using eosio::indexed_by;
   using eosio::const_mem_fun;
23
   using eosio::member;
24 25
   using eosio::bytes;
   using eosio::print;
26
   using eosio::singleton;
27
   using eosio::transaction;
28 29 30 31 32 33 34 35

   template<account_name SystemAccount>
   class producers_election {
      public:
         static const account_name system_account = SystemAccount;
         typedef eosio::generic_currency< eosio::token<system_account,S(4,EOS)> > currency;
         typedef typename currency::token_type system_token_type;

36
         static constexpr uint32_t max_unstake_requests = 10;
37 38
         static constexpr uint32_t unstake_pay_period = 7*24*3600; // one per week
         static constexpr uint32_t unstake_payments = 26; // during 26 weeks
39

40 41
         struct producer_preferences : eosio::blockchain_parameters {
            uint32_t inflation_rate; // inflation coefficient * 10000 (i.e. inflation in percent * 1000)
42 43 44

            producer_preferences() { bzero(this, sizeof(*this)); }

45
            EOSLIB_SERIALIZE_DERIVED( producer_preferences, eosio::blockchain_parameters, (inflation_rate) )
46 47
         };

48 49 50
         struct producer_info {
            account_name      owner;
            uint64_t          padding = 0;
51
            uint128_t         total_votes = 0;
52
            producer_preferences prefs;
53
            eosio::bytes      packed_key; /// a packed public key object
54 55 56

            uint64_t    primary_key()const { return owner;       }
            uint128_t   by_votes()const    { return total_votes; }
57
            bool active() const { return !packed_key.empty(); }
58 59 60 61 62 63

            EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(prefs) )
         };

         typedef eosio::multi_index< N(producervote), producer_info,
                                     indexed_by<N(prototalvote), const_mem_fun<producer_info, uint128_t, &producer_info::by_votes>  >
64
                                     >  producers_table;
65

66 67
         typedef singleton<SystemAccount, N(inflation), SystemAccount, uint32_t>  inflation_singleton;

68
         struct account_votes {
69 70 71 72
            account_name                owner = 0;
            account_name                proxy = 0;
            uint32_t                    last_update = 0;
            uint32_t                    is_proxy = 0;
73
            system_token_type           staked;
74 75 76
            system_token_type           unstaking;
            system_token_type           unstake_per_week;
            uint128_t                   proxied_votes = 0;
77
            std::vector<account_name>   producers;
78 79
            uint32_t                    deferred_trx_id = 0;
            time                        last_unstake_time = 0; //uint32
80

81
            uint64_t primary_key()const { return owner; }
82

83
            EOSLIB_SERIALIZE( account_votes, (owner)(proxy)(last_update)(is_proxy)(staked)(unstaking)(unstake_per_week)(proxied_votes)(producers)(deferred_trx_id)(last_unstake_time) )
84
         };
85

86
         typedef eosio::multi_index< N(accountvotes), account_votes>  account_votes_table;
87 88 89


         struct producer_config {
90 91
            account_name      owner;
            eosio::bytes      packed_key; /// a packed public key object
92

93 94
            uint64_t primary_key()const { return owner;       }
            EOSLIB_SERIALIZE( producer_config, (owner)(packed_key) )
95 96
         };

97
         typedef eosio::multi_index< N(producercfg), producer_config>  producer_config_index_type;
98 99

         ACTION( SystemAccount, register_producer ) {
100 101
            account_name producer;
            bytes        producer_key;
102
            producer_preferences prefs;
103

104
            EOSLIB_SERIALIZE( register_producer, (producer)(producer_key)(prefs) )
105 106 107
         };

         /**
108
          *  This method will create a producer_config and producer_info object for 'producer' 
109 110 111 112 113 114
          *
          *  @pre producer is not already registered
          *  @pre producer to register is an account
          *  @pre authority of producer to register 
          *  
          */
115 116
         static void on( const register_producer& reg ) {
            require_auth( reg.producer );
117

118
            producers_table producers_tbl( SystemAccount, SystemAccount );
119
            const auto* existing = producers_tbl.find( reg.producer );
120 121
            eosio_assert( !existing, "producer already registered" );

122
            producers_tbl.emplace( reg.producer, [&]( producer_info& info ){
123 124 125
                  info.owner       = reg.producer;
                  info.total_votes = 0;
                  info.prefs = reg.prefs;
126 127 128
               });

            producer_config_index_type proconfig( SystemAccount, SystemAccount );
129 130
            proconfig.emplace( reg.producer, [&]( auto& pc ) {
                  pc.owner      = reg.producer;
131 132 133 134
                  pc.packed_key = reg.producer_key;
               });
         }

135 136 137 138 139 140 141 142 143 144 145
         ACTION( SystemAccount, change_producer_preferences ) {
            account_name producer;
            bytes        producer_key;
            producer_preferences prefs;

            EOSLIB_SERIALIZE( register_producer, (producer)(producer_key)(prefs) )
         };

         static void on( const change_producer_preferences& change) {
            require_auth( change.producer );

146 147 148
            producers_table producers_tbl( SystemAccount, SystemAccount );
            const auto* prod = producers_tbl.find( change.producer );
            eosio_assert( bool(prod), "producer is not registered" );
149

150
            producers_tbl.update( *prod, change.producer, [&]( producer_info& info ){
151 152 153 154
                  info.prefs = change.prefs;
               });
         }

A
Anton Perkov 已提交
155
         ACTION( SystemAccount, stake_vote ) {
156 157 158
            account_name      voter;
            system_token_type amount;

A
Anton Perkov 已提交
159
            EOSLIB_SERIALIZE( stake_vote, (voter)(amount) )
160 161
         };

162
         static void increase_voting_power( account_name voter, system_token_type amount ) {
163
            account_votes_table avotes( SystemAccount, SystemAccount );
164
            const auto* acv = avotes.find( voter );
165 166

            if( !acv ) {
167 168
               acv = &avotes.emplace( voter, [&]( account_votes& a ) {
                     a.owner = voter;
169
                     a.last_update = now();
170
                     a.staked = amount;
171
                  });
172 173 174 175 176 177
            } else {
               avotes.update( *acv, 0, [&]( auto& av ) {
                     av.last_update = now();
                     av.staked += amount;
                  });

178 179
            }

180 181 182 183 184 185 186 187 188 189
            const std::vector<account_name>* producers = nullptr;
            if ( acv->proxy ) {
               auto proxy = avotes.find( acv->proxy );
               avotes.update( *proxy, 0, [&](account_votes& a) { a.proxied_votes += amount.quantity; } );
               if ( proxy->is_proxy ) { //only if proxy is still active. if proxy has been unregistered, we update proxied_votes, but don't propagate to producers
                  producers = &proxy->producers;
               }
            } else {
               producers = &acv->producers;
            }
190

191
            if ( producers ) {
192
               producers_table producers_tbl( SystemAccount, SystemAccount );
193
               for( auto p : *producers ) {
194 195 196
                  auto prod = producers_tbl.find( p );
                  eosio_assert( bool(prod), "never existed producer" ); //data corruption
                  producers_tbl.update( *prod, 0, [&]( auto& v ) {
197 198 199
                        v.total_votes += amount.quantity;
                     });
               }
200
            }
201
         }
202

203
         static void update_elected_producers() {
204
            producers_table producers_tbl( SystemAccount, SystemAccount );
205 206
            auto& idx = producers_tbl.template get<>( N(prototalvote) );

207 208 209 210 211 212
            std::array<uint32_t, 21> target_block_size;
            std::array<uint32_t, 21> max_block_size;
            std::array<uint32_t, 21> target_block_acts_per_scope;
            std::array<uint32_t, 21> max_block_acts_per_scope;
            std::array<uint32_t, 21> target_block_acts;
            std::array<uint32_t, 21> max_block_acts;
213
            std::array<uint64_t, 21> max_storage_size;
214 215 216 217 218 219 220
            std::array<uint32_t, 21> max_transaction_lifetime;
            std::array<uint16_t, 21> max_authority_depth;
            std::array<uint32_t, 21> max_transaction_exec_time;
            std::array<uint16_t, 21> max_inline_depth;
            std::array<uint32_t, 21> max_inline_action_size;
            std::array<uint32_t, 21> max_generated_transaction_size;
            std::array<uint32_t, 21> inflation_rate;
221 222

            std::array<account_name, 21> elected;
223

224
            auto it = std::prev( idx.end() );
225 226 227 228 229
            size_t n = 0;
            while ( n < 21 ) {
               if ( it->active() ) {
                  elected[n] = it->owner;

230 231 232 233 234 235 236 237 238
                  target_block_size[n] = it->prefs.target_block_size;
                  max_block_size[n] = it->prefs.max_block_size;

                  target_block_acts_per_scope[n] = it->prefs.target_block_acts_per_scope;
                  max_block_acts_per_scope[n] = it->prefs.max_block_acts_per_scope;

                  target_block_acts[n] = it->prefs.target_block_acts;
                  max_block_acts[n] = it->prefs.max_block_acts;

239
                  max_storage_size[n] = it->prefs.max_storage_size;
240 241 242 243 244 245 246
                  max_transaction_lifetime[n] = it->prefs.max_transaction_lifetime;
                  max_authority_depth[n] = it->prefs.max_authority_depth;
                  max_transaction_exec_time[n] = it->prefs.max_transaction_exec_time;
                  max_inline_depth[n] = it->prefs.max_inline_depth;
                  max_inline_action_size[n] = it->prefs.max_inline_action_size;
                  max_generated_transaction_size[n] = it->prefs.max_generated_transaction_size;

247 248 249
                  inflation_rate[n] = it->prefs.inflation_rate;
                  ++n;
               }
250 251 252 253 254 255

               if (it == idx.begin()) {
                  break;
               }
               --it;
            }
256 257
            set_active_producers( elected.data(), n );
            size_t median = n/2;
258

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
            ::blockchain_parameters concensus = {
               target_block_size[median],
               max_block_size[median],
               target_block_acts_per_scope[median],
               max_block_acts_per_scope[median],
               target_block_acts[median],
               max_block_acts[median],
               max_storage_size[median],
               max_transaction_lifetime[median],
               max_transaction_exec_time[median],
               max_authority_depth[median],
               max_inline_depth[median],
               max_inline_action_size[median],
               max_generated_transaction_size[median]
            };

            set_blockchain_parameters(&concensus);
276 277

            inflation_singleton::set( inflation_rate[median] );
278 279
         }

280
         static void on( const stake_vote& sv ) {
281 282 283 284
            eosio_assert( sv.amount.quantity > 0, "must stake some tokens" );
            require_auth( sv.voter );

            increase_voting_power( sv.voter, sv.amount );
285 286 287
            currency::inline_transfer( sv.voter, SystemAccount, sv.amount, "stake for voting" );
         }

A
Anton Perkov 已提交
288
         ACTION( SystemAccount, unstake_vote ) {
289 290 291
            account_name      voter;
            system_token_type amount;

A
Anton Perkov 已提交
292
            EOSLIB_SERIALIZE( unstake_vote, (voter)(amount) )
293 294
         };

A
Anton Perkov 已提交
295
         static void on( const unstake_vote& usv ) {
296
            require_auth( usv.voter );
297
            account_votes_table avotes( SystemAccount, SystemAccount );
298 299 300
            const auto* acv = avotes.find( usv.voter );
            eosio_assert( bool(acv), "stake not found" );

301 302
            if ( 0 < usv.amount.quantity ) {
               eosio_assert( acv->staked < usv.amount, "cannot unstake more than total stake amount" );
303

304 305
               if (acv->deferred_trx_id) {
                  //XXX cancel_deferred_transaction(acv->deferred_trx_id);
306
               }
307

308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
               uint32_t new_trx_id = 0;//XXX send_deferred();

               avotes.update( *acv, 0, [&](account_votes& a) {
                     a.staked -= usv.amount;
                     a.unstaking += a.unstaking + usv.amount;
                     //round up to guarantee that there will be no unpaid balance after 26 weeks, and we are able refund amount < unstake_payments
                     a.unstake_per_week = system_token_type( a.unstaking.quantity /unstake_payments + a.unstaking.quantity % unstake_payments );
                     a.deferred_trx_id = new_trx_id;
                     a.last_update = now();
                  });

               const std::vector<account_name>* producers = nullptr;
               if ( acv->proxy ) {
                  auto proxy = avotes.find( acv->proxy );
                  avotes.update( *proxy, 0, [&](account_votes& a) { a.proxied_votes -= usv.amount.quantity; } );
                  if ( proxy->is_proxy ) { //only if proxy is still active. if proxy has been unregistered, we update proxied_votes, but don't propagate to producers
                     producers = &proxy->producers;
325
                  }
326 327 328
               } else {
                  producers = &acv->producers;
               }
329

330
               if ( producers ) {
331
                  producers_table producers_tbl( SystemAccount, SystemAccount );
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
                  for( auto p : *producers ) {
                     auto prod = producers_tbl.find( p );
                     eosio_assert( bool(prod), "never existed producer" ); //data corruption
                     producers_tbl.update( *prod, 0, [&]( auto& v ) {
                           v.total_votes -= usv.amount.quantity;
                        });
                  }
               }
            } else {
               if (acv->deferred_trx_id) {
                  //XXX cancel_deferred_transaction(acv->deferred_trx_id);
               }
               avotes.update( *acv, 0, [&](account_votes& a) {
                     a.staked += a.unstaking;
                     a.unstaking.quantity = 0;
                     a.unstake_per_week.quantity = 0;
                     a.deferred_trx_id = 0;
                     a.last_update = now();
                  });
            }
352 353
         }

354 355
         ACTION(  SystemAccount, unstake_vote_deferred ) {
            account_name                voter;
356

357
            EOSLIB_SERIALIZE( unstake_vote_deferred, (voter) )
358 359
         };

360 361
         static void on( const unstake_vote_deferred& usv) {
            require_auth( usv.voter );
362
            account_votes_table avotes( SystemAccount, SystemAccount );
363 364
            const auto* acv = avotes.find( usv.voter );
            eosio_assert( bool(acv), "stake not found" );
365

366 367 368 369 370 371 372 373 374 375 376 377 378
            auto weeks = (now() - acv->last_unstake_time) / unstake_pay_period;
            eosio_assert( 0 == weeks, "less than one week since last unstaking balance transfer" );

            auto unstake_amount = std::min(weeks * acv->unstake_per_week, acv->unstaking);
            uint32_t new_trx_id = unstake_amount < acv->unstaking ? /* XXX send_deferred() */ 0 : 0;

            currency::inline_transfer( usv.voter, SystemAccount, unstake_amount, "unstake voting" );

            avotes.update( *acv, 0, [&](account_votes& a) {
                  a.unstaking -= unstake_amount;
                  a.deferred_trx_id = new_trx_id;
                  a.last_unstake_time = a.last_unstake_time + weeks * unstake_pay_period;
               });
379 380
         }

A
Anton Perkov 已提交
381
         ACTION( SystemAccount, vote_producer ) {
382 383 384 385
            account_name                voter;
            account_name                proxy;
            std::vector<account_name>   producers;

A
Anton Perkov 已提交
386
            EOSLIB_SERIALIZE( vote_producer, (voter)(proxy)(producers) )
387
         };
388 389 390 391 392 393 394 395

         /**
          *  @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
          */
A
Anton Perkov 已提交
396
         static void on( const vote_producer& vp ) {
397 398
            require_auth( vp.voter );

399 400 401
            //validate input
            if ( vp.proxy ) {
               eosio_assert( vp.producers.size() == 0, "cannot vote for producers and proxy at same time" );
402
               require_recipient( vp.proxy );
403 404 405 406 407
            } else {
               eosio_assert( vp.producers.size() <= 30, "attempt to vote for too many producers" );
               eosio_assert( std::is_sorted( vp.producers.begin(), vp.producers.end() ), "producer votes must be sorted" );
            }

408 409
            account_votes_table avotes( SystemAccount, SystemAccount );
            auto voter = avotes.find( vp.voter );
410

411 412
            eosio_assert( bool(voter), "no stake to vote" );
            if ( voter->is_proxy ) {
413 414 415 416 417
               eosio_assert( vp.proxy == 0 , "accounts elected to be proxy are not allowed to use another proxy" );
            }

            //find old producers, update old proxy if needed
            const std::vector<account_name>* old_producers = nullptr;
418 419
            if( voter->proxy ) {
               if ( voter->proxy == vp.proxy ) {
420
                  return; // nothing changed
421
               }
422 423
               auto old_proxy = avotes.find( voter->proxy );
               avotes.update( *old_proxy, 0, [&](auto& a) { a.proxied_votes -= voter->staked.quantity; } );
424 425 426
               if ( old_proxy->is_proxy ) { //if proxy stoped being proxy, the votes were already taken back from producers by on( const unregister_proxy& )
                  old_producers = &old_proxy->producers;
               }
427
            } else {
428
               old_producers = &voter->producers;
429 430
            }

431 432 433
            //find new producers, update new proxy if needed
            const std::vector<account_name>* new_producers = nullptr;
            if ( vp.proxy ) {
434 435
               auto new_proxy = avotes.find( vp.proxy );
               eosio_assert( new_proxy->is_proxy, "selected proxy has not elected to be a proxy" );
436
               avotes.update( *new_proxy, 0, [&](auto& a) { a.proxied_votes += voter->staked.quantity; } );
437 438 439 440 441
               new_producers = &new_proxy->producers;
            } else {
               new_producers = &vp.producers;
            }

442
            producers_table producers_tbl( SystemAccount, SystemAccount );
443

444 445 446 447 448
            if ( old_producers ) { //old_producers == 0 if proxy has stoped being a proxy and votes were taken back from producers at that moment
               //revoke votes only from no longer elected
               std::vector<account_name> revoked( old_producers->size() );
               auto end_it = std::set_difference( old_producers->begin(), old_producers->end(), new_producers->begin(), new_producers->end(), revoked.begin() );
               for ( auto it = revoked.begin(); it != end_it; ++it ) {
449 450
                  auto prod = producers_tbl.find( *it );
                  eosio_assert( bool(prod), "never existed producer" ); //data corruption
451
                  producers_tbl.update( *prod, 0, [&]( auto& pi ) { pi.total_votes -= voter->staked.quantity; });
452
               }
453 454 455 456
            }

            //update newly elected
            std::vector<account_name> elected( new_producers->size() );
457
            auto end_it = std::set_difference( new_producers->begin(), new_producers->end(), old_producers->begin(), old_producers->end(), elected.begin() );
458
            for ( auto it = elected.begin(); it != end_it; ++it ) {
459 460 461 462 463
               auto prod = producers_tbl.find( *it );
               eosio_assert( bool(prod), "never existed producer" ); //data corruption
               if ( vp.proxy == 0 ) { //direct voting, in case of proxy voting update total_votes even for inactive producers
                  eosio_assert( prod->active(), "can vote only for active producers" );
               }
464
               producers_tbl.update( *prod, 0, [&]( auto& pi ) { pi.total_votes += voter->staked.quantity; });
465 466 467
            }

            // save new values to the account itself
468
            avotes.update( *voter, 0, [&](account_votes& a) {
469 470 471
                  a.proxy = vp.proxy;
                  a.last_update = now();
                  a.producers = vp.producers;
472 473 474
               });
         }

475
         ACTION( SystemAccount, register_proxy ) {
476 477
            account_name proxy_to_register;

478
            EOSLIB_SERIALIZE( register_proxy, (proxy_to_register) )
479 480
         };

481
         static void on( const register_proxy& reg ) {
482 483
            require_auth( reg.proxy_to_register );

484 485 486 487 488 489
            account_votes_table avotes( SystemAccount, SystemAccount );
            auto voter = avotes.find( reg.proxy_to_register );
            if ( voter ) {
               eosio_assert( voter->is_proxy == 0, "account is already a proxy" );
               eosio_assert( voter->proxy == 0, "account that uses a proxy is not allowed to become a proxy" );
               avotes.update( *voter, 0, [&](account_votes& a) {
490
                     a.is_proxy = 1;
A
Anton Perkov 已提交
491
                     a.last_update = now();
492
                     //a.proxied_votes may be > 0, if the proxy has been unregistered, so we had to keep the value
493 494 495 496 497 498
                  });
            } else {
               avotes.emplace( reg.proxy_to_register, [&]( account_votes& a ) {
                     a.owner = reg.proxy_to_register;
                     a.last_update = now();
                     a.proxy = 0;
499
                     a.is_proxy = 1;
500 501 502 503
                     a.proxied_votes = 0;
                     a.staked.quantity = 0;
                  });
            }
504
         }
A
Anton Perkov 已提交
505

506 507 508 509 510 511 512 513 514
         ACTION( SystemAccount, unregister_proxy ) {
            account_name proxy_to_unregister;

            EOSLIB_SERIALIZE( unregister_proxy, (proxy_to_unregister) )
         };

         static void on( const unregister_proxy& reg ) {
            require_auth( reg.proxy_to_unregister );

515
            account_votes_table avotes( SystemAccount, SystemAccount );
516 517 518
            auto proxy = avotes.find( reg.proxy_to_unregister );
            eosio_assert( bool(proxy), "proxy not found" );
            eosio_assert( proxy->is_proxy == 1, "account is already a proxy" );
519

520
            producers_table producers_tbl( SystemAccount, SystemAccount );
521
            for ( auto p : proxy->producers ) {
522 523 524
               auto prod = producers_tbl.find( p );
               eosio_assert( bool(prod), "never existed producer" ); //data corruption
               producers_tbl.update( *prod, 0, [&]( auto& pi ) { pi.total_votes -= proxy->proxied_votes; });
525 526
            }

527
            avotes.update( *proxy, 0, [&](account_votes& a) {
528 529 530 531 532 533
                     a.is_proxy = 0;
                     a.last_update = now();
                     //a.proxied_votes should be kept in order to be able to reenable this proxy in the future
               });
         }

A
Anton Perkov 已提交
534 535 536
         struct block {};

         static void on( const block& ) {
537
            update_elected_producers();
A
Anton Perkov 已提交
538
         }
539 540
   };
}