voting.hpp 24.0 KB
Newer Older
1 2 3 4 5
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#pragma once
6 7
#include "common.hpp"

8 9 10 11 12 13 14 15
#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>
16
#include <eosiolib/privileged.hpp>
17
#include <eosiolib/singleton.hpp>
18
#include <eosiolib/transaction.hpp>
19

20
#include <array>
21 22 23 24

namespace eosiosystem {
   using eosio::indexed_by;
   using eosio::const_mem_fun;
25
   using eosio::member;
26 27
   using eosio::bytes;
   using eosio::print;
28
   using eosio::singleton;
29
   using eosio::transaction;
30

31

32
   template<account_name SystemAccount>
33
   class voting {
34
      public:
35 36 37 38 39
         static constexpr account_name system_account = SystemAccount;
         using currency = typename common<SystemAccount>::currency;
         using system_token_type = typename common<SystemAccount>::system_token_type;
         using eosio_parameters = typename common<SystemAccount>::eosio_parameters;
         using eosio_parameters_singleton = typename common<SystemAccount>::eosio_parameters_singleton;
40

41
         static constexpr uint32_t max_unstake_requests = 10;
42 43
         static constexpr uint32_t unstake_pay_period = 7*24*3600; // one per week
         static constexpr uint32_t unstake_payments = 26; // during 26 weeks
44

45 46 47
         struct producer_info {
            account_name      owner;
            uint64_t          padding = 0;
48
            uint128_t         total_votes = 0;
49
            eosio_parameters prefs;
50
            eosio::bytes      packed_key; /// a packed public key object
51 52 53

            uint64_t    primary_key()const { return owner;       }
            uint128_t   by_votes()const    { return total_votes; }
54
            bool active() const { return packed_key.size() == 4 + 33 /*serialized key size*/; }
55

A
Anton Perkov 已提交
56
            EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(prefs)(packed_key) )
57 58 59 60
         };

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

63

64
         struct voter_info {
65 66 67 68
            account_name                owner = 0;
            account_name                proxy = 0;
            uint32_t                    last_update = 0;
            uint32_t                    is_proxy = 0;
69
            system_token_type           staked;
70 71 72
            system_token_type           unstaking;
            system_token_type           unstake_per_week;
            uint128_t                   proxied_votes = 0;
73
            std::vector<account_name>   producers;
74 75
            uint32_t                    deferred_trx_id = 0;
            time                        last_unstake_time = 0; //uint32
76

77
            uint64_t primary_key()const { return owner; }
78

79
            EOSLIB_SERIALIZE( voter_info, (owner)(proxy)(last_update)(is_proxy)(staked)(unstaking)(unstake_per_week)(proxied_votes)(producers)(deferred_trx_id)(last_unstake_time) )
80
         };
81

82
         typedef eosio::multi_index< N(voters), voter_info>  voters_table;
83

84
         ACTION( SystemAccount, register_producer ) {
85 86
            account_name producer;
            bytes        producer_key;
87
            eosio_parameters prefs;
88

89
            EOSLIB_SERIALIZE( register_producer, (producer)(producer_key)(prefs) )
90 91 92
         };

         /**
93
          *  This method will create a producer_config and producer_info object for 'producer' 
94 95 96 97 98 99
          *
          *  @pre producer is not already registered
          *  @pre producer to register is an account
          *  @pre authority of producer to register 
          *  
          */
100 101
         static void on( const register_producer& reg ) {
            require_auth( reg.producer );
102

103
            producers_table producers_tbl( SystemAccount, SystemAccount );
104
            const auto* existing = producers_tbl.find( reg.producer );
105 106
            eosio_assert( !existing, "producer already registered" );

107
            producers_tbl.emplace( reg.producer, [&]( producer_info& info ){
108 109
                  info.owner       = reg.producer;
                  info.total_votes = 0;
A
Anton Perkov 已提交
110 111
                  info.prefs       = reg.prefs;
                  info.packed_key  = reg.producer_key;
112 113 114
               });
         }

115
         ACTION( SystemAccount, change_eosio_parameters ) {
116 117
            account_name producer;
            bytes        producer_key;
118
            eosio_parameters prefs;
119 120 121 122

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

123
         static void on( const change_eosio_parameters& change) {
124 125
            require_auth( change.producer );

126 127 128
            producers_table producers_tbl( SystemAccount, SystemAccount );
            const auto* prod = producers_tbl.find( change.producer );
            eosio_assert( bool(prod), "producer is not registered" );
129

130
            producers_tbl.update( *prod, change.producer, [&]( producer_info& info ){
131 132 133 134
                  info.prefs = change.prefs;
               });
         }

135
         ACTION( SystemAccount, stakevote ) {
136 137 138
            account_name      voter;
            system_token_type amount;

139
            EOSLIB_SERIALIZE( stakevote, (voter)(amount) )
140 141
         };

142 143 144
         static void increase_voting_power( account_name acnt, system_token_type amount ) {
            voters_table voters_tbl( SystemAccount, SystemAccount );
            const auto* voter = voters_tbl.find( acnt );
145

146 147 148
            if( !voter ) {
               voter = &voters_tbl.emplace( acnt, [&]( voter_info& a ) {
                     a.owner = acnt;
149
                     a.last_update = now();
150
                     a.staked = amount;
151
                  });
152
            } else {
153
               voters_tbl.update( *voter, 0, [&]( auto& av ) {
154 155 156
                     av.last_update = now();
                     av.staked += amount;
                  });
157 158
            }

159
            const std::vector<account_name>* producers = nullptr;
160 161 162
            if ( voter->proxy ) {
               auto proxy = voters_tbl.find( voter->proxy );
               voters_tbl.update( *proxy, 0, [&](voter_info& a) { a.proxied_votes += amount.quantity; } );
163 164 165 166
               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 {
167
               producers = &voter->producers;
168
            }
169

170
            if ( producers ) {
171
               producers_table producers_tbl( SystemAccount, SystemAccount );
172
               for( auto p : *producers ) {
173 174 175
                  auto prod = producers_tbl.find( p );
                  eosio_assert( bool(prod), "never existed producer" ); //data corruption
                  producers_tbl.update( *prod, 0, [&]( auto& v ) {
176 177 178
                        v.total_votes += amount.quantity;
                     });
               }
179
            }
180
         }
181

182
         static void update_elected_producers() {
183
            producers_table producers_tbl( SystemAccount, SystemAccount );
184 185
            auto& idx = producers_tbl.template get<>( N(prototalvote) );

186 187 188 189 190 191
            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;
192
            std::array<uint64_t, 21> max_storage_size;
193 194 195 196 197 198 199
            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;
200
            std::array<uint32_t, 21> storage_reserve_ratio;
201

A
Anton Perkov 已提交
202 203
            eosio::producer_schedule schedule;
            schedule.producers.reserve(21);
204

205
            auto it = std::prev( idx.end() );
206 207 208
            size_t n = 0;
            while ( n < 21 ) {
               if ( it->active() ) {
A
Anton Perkov 已提交
209 210 211 212
                  schedule.producers.emplace_back();
                  schedule.producers.back().producer_name = it->owner;
                  std::copy(it->packed_key.begin(), it->packed_key.end(),
                            schedule.producers.back().block_signing_key.begin());
213

214 215 216 217 218 219 220 221 222
                  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;

223
                  max_storage_size[n] = it->prefs.max_storage_size;
224 225 226 227 228 229 230
                  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;

231
                  storage_reserve_ratio[n] = it->prefs.storage_reserve_ratio;
232 233 234
                  inflation_rate[n] = it->prefs.inflation_rate;
                  ++n;
               }
235 236 237 238 239 240

               if (it == idx.begin()) {
                  break;
               }
               --it;
            }
241
            // should use producer_schedule_type from libraries/chain/include/eosio/chain/producer_schedule.hpp
A
Anton Perkov 已提交
242 243
            bytes packed_schedule = pack(schedule);
            set_active_producers( packed_schedule.data(),  packed_schedule.size() );
244
            size_t median = n/2;
245

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
            auto parameters = eosio_parameters_singleton::get();

            parameters.target_block_size = target_block_size[median];
            parameters.max_block_size = max_block_size[median];
            parameters.target_block_acts_per_scope = target_block_acts_per_scope[median];
            parameters.max_block_acts_per_scope = max_block_acts_per_scope[median];
            parameters.target_block_acts = target_block_acts[median];
            parameters.max_block_acts = max_block_acts[median];
            parameters.max_storage_size = max_storage_size[median];
            parameters.max_transaction_lifetime = max_transaction_lifetime[median];
            parameters.max_transaction_exec_time = max_transaction_exec_time[median];
            parameters.max_authority_depth = max_authority_depth[median];
            parameters.max_inline_depth = max_inline_depth[median];
            parameters.max_inline_action_size = max_inline_action_size[median];
            parameters.max_generated_transaction_size = max_generated_transaction_size[median];
            parameters.storage_reserve_ratio = storage_reserve_ratio[median];
            parameters.inflation_rate = inflation_rate[median];

            if ( parameters.max_storage_size < parameters.total_storage_bytes_reserved ) {
               parameters.max_storage_size = parameters.total_storage_bytes_reserved;
            }

            set_blockchain_parameters(&parameters);

            eosio_parameters_singleton::set( parameters );
271 272
         }

273
         static void on( const stakevote& sv ) {
274 275 276 277
            eosio_assert( sv.amount.quantity > 0, "must stake some tokens" );
            require_auth( sv.voter );

            increase_voting_power( sv.voter, sv.amount );
278 279 280
            currency::inline_transfer( sv.voter, SystemAccount, sv.amount, "stake for voting" );
         }

281
         ACTION( SystemAccount, unstakevote ) {
282 283 284
            account_name      voter;
            system_token_type amount;

285
            EOSLIB_SERIALIZE( unstakevote, (voter)(amount) )
286 287
         };

288 289 290 291 292 293
         ACTION(  SystemAccount, unstake_vote_deferred ) {
            account_name                voter;

            EOSLIB_SERIALIZE( unstake_vote_deferred, (voter) )
         };

294
         static void on( const unstakevote& usv ) {
295
            require_auth( usv.voter );
296 297 298
            voters_table voters_tbl( SystemAccount, SystemAccount );
            const auto* voter = voters_tbl.find( usv.voter );
            eosio_assert( bool(voter), "stake not found" );
299

300
            if ( 0 < usv.amount.quantity ) {
301
               eosio_assert( usv.amount <= voter->staked, "cannot unstake more than total stake amount" );
302
               /*
303 304
               if (voter->deferred_trx_id) {
                  //XXX cancel_deferred_transaction(voter->deferred_trx_id);
305
               }
306

307 308 309
               unstake_vote_deferred dt;
               dt.voter = usv.voter;
               uint32_t new_trx_id = 0;//XXX send_deferred(dt);
310

311
               avotes.update( *voter, 0, [&](voter_info& a) {
312 313 314 315 316 317 318
                     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();
                  });
319 320 321
               */

               // Temporary code: immediate unstake
322
               voters_tbl.update( *voter, 0, [&](voter_info& a) {
323 324 325
                     a.staked -= usv.amount;
                     a.last_update = now();
                  });
326
               currency::inline_transfer( SystemAccount, usv.voter, usv.amount, "unstake voting" );
327
               // end of temporary code
328 329

               const std::vector<account_name>* producers = nullptr;
330 331 332
               if ( voter->proxy ) {
                  auto proxy = voters_tbl.find( voter->proxy );
                  voters_tbl.update( *proxy, 0, [&](voter_info& a) { a.proxied_votes -= usv.amount.quantity; } );
333 334
                  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;
335
                  }
336
               } else {
337
                  producers = &voter->producers;
338
               }
339

340
               if ( producers ) {
341
                  producers_table producers_tbl( SystemAccount, SystemAccount );
342 343 344 345 346 347 348 349 350
                  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 {
351 352
               if (voter->deferred_trx_id) {
                  //XXX cancel_deferred_transaction(voter->deferred_trx_id);
353
               }
354
               voters_tbl.update( *voter, 0, [&](voter_info& a) {
355 356 357 358 359 360 361
                     a.staked += a.unstaking;
                     a.unstaking.quantity = 0;
                     a.unstake_per_week.quantity = 0;
                     a.deferred_trx_id = 0;
                     a.last_update = now();
                  });
            }
362 363
         }

364 365
         static void on( const unstake_vote_deferred& usv) {
            require_auth( usv.voter );
366 367 368
            voters_table voters_tbl( SystemAccount, SystemAccount );
            const auto* voter = voters_tbl.find( usv.voter );
            eosio_assert( bool(voter), "stake not found" );
369

370
            auto weeks = (now() - voter->last_unstake_time) / unstake_pay_period;
371
            eosio_assert( 0 == weeks, "less than one week passed since last transfer or unstake request" );
372
            eosio_assert( 0 < voter->unstaking.quantity, "no unstaking money to transfer" );
373

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

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

379
            voters_tbl.update( *voter, 0, [&](voter_info& a) {
380 381 382 383
                  a.unstaking -= unstake_amount;
                  a.deferred_trx_id = new_trx_id;
                  a.last_unstake_time = a.last_unstake_time + weeks * unstake_pay_period;
               });
384 385
         }

A
Anton Perkov 已提交
386
         ACTION( SystemAccount, vote_producer ) {
387 388 389 390
            account_name                voter;
            account_name                proxy;
            std::vector<account_name>   producers;

A
Anton Perkov 已提交
391
            EOSLIB_SERIALIZE( vote_producer, (voter)(proxy)(producers) )
392
         };
393 394 395 396 397 398 399 400

         /**
          *  @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 已提交
401
         static void on( const vote_producer& vp ) {
402 403
            require_auth( vp.voter );

404 405 406
            //validate input
            if ( vp.proxy ) {
               eosio_assert( vp.producers.size() == 0, "cannot vote for producers and proxy at same time" );
407
               require_recipient( vp.proxy );
408 409 410 411 412
            } 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" );
            }

413 414
            voters_table voters_tbl( SystemAccount, SystemAccount );
            auto voter = voters_tbl.find( vp.voter );
415

416 417
            eosio_assert( bool(voter), "no stake to vote" );
            if ( voter->is_proxy ) {
418 419 420 421 422
               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;
423 424
            if( voter->proxy ) {
               if ( voter->proxy == vp.proxy ) {
425
                  return; // nothing changed
426
               }
427 428
               auto old_proxy = voters_tbl.find( voter->proxy );
               voters_tbl.update( *old_proxy, 0, [&](auto& a) { a.proxied_votes -= voter->staked.quantity; } );
429 430 431
               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;
               }
432
            } else {
433
               old_producers = &voter->producers;
434 435
            }

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

447
            producers_table producers_tbl( SystemAccount, SystemAccount );
448

449 450 451 452 453
            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 ) {
454 455
                  auto prod = producers_tbl.find( *it );
                  eosio_assert( bool(prod), "never existed producer" ); //data corruption
456
                  producers_tbl.update( *prod, 0, [&]( auto& pi ) { pi.total_votes -= voter->staked.quantity; });
457
               }
458 459 460 461
            }

            //update newly elected
            std::vector<account_name> elected( new_producers->size() );
462
            auto end_it = std::set_difference( new_producers->begin(), new_producers->end(), old_producers->begin(), old_producers->end(), elected.begin() );
463
            for ( auto it = elected.begin(); it != end_it; ++it ) {
464 465 466 467 468
               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" );
               }
469
               producers_tbl.update( *prod, 0, [&]( auto& pi ) { pi.total_votes += voter->staked.quantity; });
470 471 472
            }

            // save new values to the account itself
473
            voters_tbl.update( *voter, 0, [&](voter_info& a) {
474 475 476
                  a.proxy = vp.proxy;
                  a.last_update = now();
                  a.producers = vp.producers;
477 478 479
               });
         }

480
         ACTION( SystemAccount, register_proxy ) {
481 482
            account_name proxy_to_register;

483
            EOSLIB_SERIALIZE( register_proxy, (proxy_to_register) )
484 485
         };

486
         static void on( const register_proxy& reg ) {
487 488
            require_auth( reg.proxy_to_register );

489 490
            voters_table voters_tbl( SystemAccount, SystemAccount );
            auto voter = voters_tbl.find( reg.proxy_to_register );
491 492 493
            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" );
494
               voters_tbl.update( *voter, 0, [&](voter_info& a) {
495
                     a.is_proxy = 1;
A
Anton Perkov 已提交
496
                     a.last_update = now();
497
                     //a.proxied_votes may be > 0, if the proxy has been unregistered, so we had to keep the value
498 499
                  });
            } else {
500
               voters_tbl.emplace( reg.proxy_to_register, [&]( voter_info& a ) {
501 502 503
                     a.owner = reg.proxy_to_register;
                     a.last_update = now();
                     a.proxy = 0;
504
                     a.is_proxy = 1;
505 506 507 508
                     a.proxied_votes = 0;
                     a.staked.quantity = 0;
                  });
            }
509
         }
A
Anton Perkov 已提交
510

511 512 513 514 515 516 517 518 519
         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 );

520 521
            voters_table voters_tbl( SystemAccount, SystemAccount );
            auto proxy = voters_tbl.find( reg.proxy_to_unregister );
522 523
            eosio_assert( bool(proxy), "proxy not found" );
            eosio_assert( proxy->is_proxy == 1, "account is already a proxy" );
524

525
            producers_table producers_tbl( SystemAccount, SystemAccount );
526
            for ( auto p : proxy->producers ) {
527 528 529
               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; });
530 531
            }

532
            voters_tbl.update( *proxy, 0, [&](voter_info& a) {
533 534 535 536 537 538
                     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 已提交
539 540 541
         struct block {};

         static void on( const block& ) {
542
            update_elected_producers();
A
Anton Perkov 已提交
543
         }
544 545
   };
}