voting.hpp 26.7 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 <algorithm>
21
#include <array>
22 23 24 25

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

32

33
   template<account_name SystemAccount>
34
   class voting {
35
      public:
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;
A
Anton Perkov 已提交
40
         using global_state_singleton = typename common<SystemAccount>::global_state_singleton;
41

K
Khaled Al-Hassanieh 已提交
42
         static const uint32_t max_inflation_rate = common<SystemAccount>::max_inflation_rate;
43
         static constexpr uint32_t max_unstake_requests = 10;
44 45
         static constexpr uint32_t unstake_pay_period = 7*24*3600; // one per week
         static constexpr uint32_t unstake_payments = 26; // during 26 weeks
K
Khaled Al-Hassanieh 已提交
46
         static constexpr uint32_t blocks_per_year = 52*7*24*2*3600; // half seconds per year
47

48 49 50
         struct producer_info {
            account_name      owner;
            uint64_t          padding = 0;
51
            uint128_t         total_votes = 0;
K
Khaled Al-Hassanieh 已提交
52
            eosio_parameters  prefs;
53
            eosio::bytes      packed_key; /// a packed public key object
K
Khaled Al-Hassanieh 已提交
54
            system_token_type per_block_payments;
K
Khaled Al-Hassanieh 已提交
55 56
            time              last_rewards_claim = 0;
            time              time_became_active = 0;
K
Khaled Al-Hassanieh 已提交
57
            time              last_produced_block_time = 0;
58 59 60

            uint64_t    primary_key()const { return owner;       }
            uint128_t   by_votes()const    { return total_votes; }
A
Anton Perkov 已提交
61
            bool active() const { return packed_key.size() == sizeof(public_key); }
62

K
Khaled Al-Hassanieh 已提交
63 64 65
            EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(prefs)(packed_key)
                              (per_block_payments)(last_rewards_claim)
                              (time_became_active)(last_produced_block_time) )
66 67
         };

68
         typedef eosio::multi_index< N(producerinfo), producer_info,
69
                                     indexed_by<N(prototalvote), const_mem_fun<producer_info, uint128_t, &producer_info::by_votes>  >
70
                                     >  producers_table;
71

72

73
         struct voter_info {
74 75 76 77
            account_name                owner = 0;
            account_name                proxy = 0;
            uint32_t                    last_update = 0;
            uint32_t                    is_proxy = 0;
78
            system_token_type           staked;
79 80 81
            system_token_type           unstaking;
            system_token_type           unstake_per_week;
            uint128_t                   proxied_votes = 0;
82
            std::vector<account_name>   producers;
83 84
            uint32_t                    deferred_trx_id = 0;
            time                        last_unstake_time = 0; //uint32
85

86
            uint64_t primary_key()const { return owner; }
87

88
            EOSLIB_SERIALIZE( voter_info, (owner)(proxy)(last_update)(is_proxy)(staked)(unstaking)(unstake_per_week)(proxied_votes)(producers)(deferred_trx_id)(last_unstake_time) )
89
         };
90

91
         typedef eosio::multi_index< N(voters), voter_info>  voters_table;
92

A
Anton Perkov 已提交
93
         ACTION( SystemAccount, regproducer ) {
94 95
            account_name     producer;
            bytes            producer_key;
96
            eosio_parameters prefs;
97

A
Anton Perkov 已提交
98
            EOSLIB_SERIALIZE( regproducer, (producer)(producer_key)(prefs) )
99 100 101
         };

         /**
102
          *  This method will create a producer_config and producer_info object for 'producer' 
103 104 105 106 107 108
          *
          *  @pre producer is not already registered
          *  @pre producer to register is an account
          *  @pre authority of producer to register 
          *  
          */
A
Anton Perkov 已提交
109
         static void on( const regproducer& reg ) {
110
            require_auth( reg.producer );
111

112
            producers_table producers_tbl( SystemAccount, SystemAccount );
A
Anton Perkov 已提交
113
            auto prod = producers_tbl.find( reg.producer );
114

A
Anton Perkov 已提交
115 116
            if ( prod != producers_tbl.end() ) {
               producers_tbl.modify( prod, reg.producer, [&]( producer_info& info ){
A
Anton Perkov 已提交
117
                     info.prefs = reg.prefs;
118
                     info.packed_key = reg.producer_key;
A
Anton Perkov 已提交
119 120 121 122 123 124 125 126 127
                  });
            } else {
               producers_tbl.emplace( reg.producer, [&]( producer_info& info ){
                     info.owner       = reg.producer;
                     info.total_votes = 0;
                     info.prefs       = reg.prefs;
                     info.packed_key  = reg.producer_key;
                  });
            }
128 129
         }

130
         ACTION( SystemAccount, unregprod ) {
131 132
            account_name producer;

133
            EOSLIB_SERIALIZE( unregprod, (producer) )
134 135
         };

136
         static void on( const unregprod& unreg ) {
137 138 139
            require_auth( unreg.producer );

            producers_table producers_tbl( SystemAccount, SystemAccount );
A
Anton Perkov 已提交
140 141
            auto prod = producers_tbl.find( unreg.producer );
            eosio_assert( prod != producers_tbl.end(), "producer not found" );
142

A
Anton Perkov 已提交
143
            producers_tbl.modify( prod, 0, [&]( producer_info& info ){
144 145 146 147
                  info.packed_key.clear();
               });
         }

148 149
         static void increase_voting_power( account_name acnt, system_token_type amount ) {
            voters_table voters_tbl( SystemAccount, SystemAccount );
A
Anton Perkov 已提交
150
            auto voter = voters_tbl.find( acnt );
151

A
Anton Perkov 已提交
152 153
            if( voter == voters_tbl.end() ) {
               voter = voters_tbl.emplace( acnt, [&]( voter_info& a ) {
154
                     a.owner = acnt;
155
                     a.last_update = now();
156
                     a.staked = amount;
157
                  });
158
            } else {
A
Anton Perkov 已提交
159
               voters_tbl.modify( voter, 0, [&]( auto& av ) {
160 161 162
                     av.last_update = now();
                     av.staked += amount;
                  });
163 164
            }

165
            const std::vector<account_name>* producers = nullptr;
166 167
            if ( voter->proxy ) {
               auto proxy = voters_tbl.find( voter->proxy );
A
Anton Perkov 已提交
168 169
               eosio_assert( proxy != voters_tbl.end(), "selected proxy not found" ); //data corruption
               voters_tbl.modify( proxy, 0, [&](voter_info& a) { a.proxied_votes += amount.quantity; } );
170 171 172 173
               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 {
174
               producers = &voter->producers;
175
            }
176

177
            if ( producers ) {
178
               producers_table producers_tbl( SystemAccount, SystemAccount );
179
               for( auto p : *producers ) {
180
                  auto prod = producers_tbl.find( p );
A
Anton Perkov 已提交
181 182
                  eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption
                  producers_tbl.modify( prod, 0, [&]( auto& v ) {
183 184 185
                        v.total_votes += amount.quantity;
                     });
               }
186
            }
187
         }
188

189 190 191
         static void decrease_voting_power( account_name acnt, system_token_type amount ) {
            require_auth( acnt );
            voters_table voters_tbl( SystemAccount, SystemAccount );
A
Anton Perkov 已提交
192 193
            auto voter = voters_tbl.find( acnt );
            eosio_assert( voter != voters_tbl.end(), "stake not found" );
194 195 196

            if ( 0 < amount.quantity ) {
               eosio_assert( amount <= voter->staked, "cannot unstake more than total stake amount" );
A
Anton Perkov 已提交
197
               voters_tbl.modify( voter, 0, [&](voter_info& a) {
198 199 200 201 202 203 204
                     a.staked -= amount;
                     a.last_update = now();
                  });

               const std::vector<account_name>* producers = nullptr;
               if ( voter->proxy ) {
                  auto proxy = voters_tbl.find( voter->proxy );
A
Anton Perkov 已提交
205
                  voters_tbl.modify( proxy, 0, [&](voter_info& a) { a.proxied_votes -= amount.quantity; } );
206 207 208 209 210 211 212 213 214 215 216
                  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 = &voter->producers;
               }

               if ( producers ) {
                  producers_table producers_tbl( SystemAccount, SystemAccount );
                  for( auto p : *producers ) {
                     auto prod = producers_tbl.find( p );
A
Anton Perkov 已提交
217 218
                     eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption
                     producers_tbl.modify( prod, 0, [&]( auto& v ) {
219 220 221 222 223 224 225 226
                           v.total_votes -= amount.quantity;
                        });
                  }
               }
            } else {
               if (voter->deferred_trx_id) {
                  //XXX cancel_deferred_transaction(voter->deferred_trx_id);
               }
A
Anton Perkov 已提交
227
               voters_tbl.modify( voter, 0, [&](voter_info& a) {
228 229 230 231 232 233 234 235 236
                     a.staked += a.unstaking;
                     a.unstaking.quantity = 0;
                     a.unstake_per_week.quantity = 0;
                     a.deferred_trx_id = 0;
                     a.last_update = now();
                  });
            }
         }

K
Khaled Al-Hassanieh 已提交
237 238 239 240 241 242 243
         static system_token_type payment_per_block(uint32_t percent_of_max_inflation_rate) {
            const system_token_type token_supply = currency::get_total_supply();
            const auto inflation_rate = max_inflation_rate * percent_of_max_inflation_rate;
            const auto& inflation_ratio = int_logarithm_one_plus(inflation_rate);
            return (token_supply * inflation_ratio.first) / (inflation_ratio.second * blocks_per_year);
         }

K
Khaled Al-Hassanieh 已提交
244
         static void update_elected_producers(time cycle_time) {
245
            producers_table producers_tbl( SystemAccount, SystemAccount );
K
Khaled Al-Hassanieh 已提交
246
            auto idx = producers_tbl.template get_index<N(prototalvote)>();
247

248 249 250 251 252 253
            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;
254
            std::array<uint64_t, 21> max_storage_size;
255 256 257 258 259 260
            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;
K
Khaled Al-Hassanieh 已提交
261
            std::array<uint32_t, 21> percent_of_max_inflation_rate;
262
            std::array<uint32_t, 21> storage_reserve_ratio;
263

A
Anton Perkov 已提交
264 265
            eosio::producer_schedule schedule;
            schedule.producers.reserve(21);
266
            size_t n = 0;
267
            for ( auto it = idx.crbegin(); it != idx.crend() && n < 21 && 0 < it->total_votes; ++it ) {
268
               if ( it->active() ) {
A
Anton Perkov 已提交
269 270
                  schedule.producers.emplace_back();
                  schedule.producers.back().producer_name = it->owner;
K
Khaled Al-Hassanieh 已提交
271
                  eosio_assert( sizeof(schedule.producers.back().block_signing_key) == it->packed_key.size(), "size mismatch" );
272
                  std::copy( it->packed_key.begin(), it->packed_key.end(), schedule.producers.back().block_signing_key.data );
273

274 275 276 277 278 279 280 281 282
                  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;

283
                  max_storage_size[n] = it->prefs.max_storage_size;
284 285 286 287 288 289 290
                  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;

291
                  storage_reserve_ratio[n] = it->prefs.storage_reserve_ratio;
K
Khaled Al-Hassanieh 已提交
292
                  percent_of_max_inflation_rate[n] = it->prefs.percent_of_max_inflation_rate;
293 294
                  ++n;
               }
295
            }
296 297 298
            if ( n == 0 ) { //no active producers with votes > 0
               return;
            }
299 300 301 302 303 304 305 306 307 308 309 310
            if ( 1 < n ) {
               std::sort( target_block_size.begin(), target_block_size.begin()+n );
               std::sort( max_block_size.begin(), max_block_size.begin()+n );
               std::sort( target_block_acts_per_scope.begin(), target_block_acts_per_scope.begin()+n );
               std::sort( max_block_acts_per_scope.begin(), max_block_acts_per_scope.begin()+n );
               std::sort( target_block_acts.begin(), target_block_acts.begin()+n );
               std::sort( max_block_acts.begin(), max_block_acts.begin()+n );
               std::sort( max_storage_size.begin(), max_storage_size.begin()+n );
               std::sort( max_transaction_lifetime.begin(), max_transaction_lifetime.begin()+n );
               std::sort( max_transaction_exec_time.begin(), max_transaction_exec_time.begin()+n );
               std::sort( max_authority_depth.begin(), max_authority_depth.begin()+n );
               std::sort( max_inline_depth.begin(), max_inline_depth.begin()+n );
311
               std::sort( max_inline_action_size.begin(), max_inline_action_size.begin()+n );
312 313 314 315 316
               std::sort( max_generated_transaction_size.begin(), max_generated_transaction_size.begin()+n );
               std::sort( storage_reserve_ratio.begin(), storage_reserve_ratio.begin()+n );
               std::sort( percent_of_max_inflation_rate.begin(), percent_of_max_inflation_rate.begin()+n );
            }

317
            // should use producer_schedule_type from libraries/chain/include/eosio/chain/producer_schedule.hpp
A
Anton Perkov 已提交
318 319
            bytes packed_schedule = pack(schedule);
            set_active_producers( packed_schedule.data(),  packed_schedule.size() );
320
            size_t median = n/2;
321

322 323
            auto parameters = global_state_singleton::exists() ? global_state_singleton::get()
                  : common<SystemAccount>::get_default_parameters();
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

            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];
K
Khaled Al-Hassanieh 已提交
339 340
            parameters.percent_of_max_inflation_rate = percent_of_max_inflation_rate[median];

K
Khaled Al-Hassanieh 已提交
341 342 343
            // not voted on
            parameters.first_block_time_in_cycle = cycle_time;

K
Khaled Al-Hassanieh 已提交
344
            // derived parameters
K
Khaled Al-Hassanieh 已提交
345 346 347 348
            auto half_of_percentage = parameters.percent_of_max_inflation_rate / 2;
            auto other_half_of_percentage = parameters.percent_of_max_inflation_rate - half_of_percentage;
            parameters.payment_per_block = payment_per_block(half_of_percentage);
            parameters.payment_to_eos_bucket = payment_per_block(other_half_of_percentage);
349
            parameters.blocks_per_cycle = 6 * schedule.producers.size();
350 351 352 353

            if ( parameters.max_storage_size < parameters.total_storage_bytes_reserved ) {
               parameters.max_storage_size = parameters.total_storage_bytes_reserved;
            }
354 355 356
            
            auto issue_quantity = parameters.blocks_per_cycle * (parameters.payment_per_block + parameters.payment_to_eos_bucket);
            currency::inline_issue(SystemAccount, issue_quantity);
357
            set_blockchain_parameters(&parameters);
358
            global_state_singleton::set(parameters);
359 360
         }

361 362 363 364 365 366
         ACTION(  SystemAccount, unstake_vote_deferred ) {
            account_name                voter;

            EOSLIB_SERIALIZE( unstake_vote_deferred, (voter) )
         };

367 368
         static void on( const unstake_vote_deferred& usv) {
            require_auth( usv.voter );
369
            voters_table voters_tbl( SystemAccount, SystemAccount );
A
Anton Perkov 已提交
370 371
            auto voter = voters_tbl.find( usv.voter );
            eosio_assert( voter != voters_tbl.end(), "stake not found" );
372

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

377 378
            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;
379

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

A
Anton Perkov 已提交
382
            voters_tbl.modify( voter, 0, [&](voter_info& a) {
383 384 385 386
                  a.unstaking -= unstake_amount;
                  a.deferred_trx_id = new_trx_id;
                  a.last_unstake_time = a.last_unstake_time + weeks * unstake_pay_period;
               });
387 388
         }

389
         ACTION( SystemAccount, voteproducer ) {
390 391 392 393
            account_name                voter;
            account_name                proxy;
            std::vector<account_name>   producers;

394
            EOSLIB_SERIALIZE( voteproducer, (voter)(proxy)(producers) )
395
         };
396 397 398 399 400 401 402 403

         /**
          *  @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
          */
404
         static void on( const voteproducer& vp ) {
405 406
            require_auth( vp.voter );

407 408 409
            //validate input
            if ( vp.proxy ) {
               eosio_assert( vp.producers.size() == 0, "cannot vote for producers and proxy at same time" );
410
               require_recipient( vp.proxy );
411 412
            } else {
               eosio_assert( vp.producers.size() <= 30, "attempt to vote for too many producers" );
413 414 415
               for( size_t i = 1; i < vp.producers.size(); ++i ) {
                  eosio_assert( vp.producers[i-1] < vp.producers[i], "producer votes must be unique and sorted" );
               }
416 417
            }

418 419
            voters_table voters_tbl( SystemAccount, SystemAccount );
            auto voter = voters_tbl.find( vp.voter );
420

A
Anton Perkov 已提交
421
            eosio_assert( voter != voters_tbl.end() && ( 0 < voter->staked.quantity || ( voter->is_proxy && 0 < voter->proxied_votes ) ), "no stake to vote" );
422
            if ( voter->is_proxy ) {
A
Anton Perkov 已提交
423
               eosio_assert( vp.proxy == 0 , "account registered as a proxy is not allowed to use a proxy" );
424 425 426 427
            }

            //find old producers, update old proxy if needed
            const std::vector<account_name>* old_producers = nullptr;
428 429
            if( voter->proxy ) {
               if ( voter->proxy == vp.proxy ) {
430
                  return; // nothing changed
431
               }
432
               auto old_proxy = voters_tbl.find( voter->proxy );
A
Anton Perkov 已提交
433 434
               eosio_assert( old_proxy != voters_tbl.end(), "old proxy not found" ); //data corruption
               voters_tbl.modify( old_proxy, 0, [&](auto& a) { a.proxied_votes -= voter->staked.quantity; } );
435 436 437
               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;
               }
438
            } else {
439
               old_producers = &voter->producers;
440 441
            }

442 443 444
            //find new producers, update new proxy if needed
            const std::vector<account_name>* new_producers = nullptr;
            if ( vp.proxy ) {
445
               auto new_proxy = voters_tbl.find( vp.proxy );
A
Anton Perkov 已提交
446 447
               eosio_assert( new_proxy != voters_tbl.end() && new_proxy->is_proxy, "proxy not found" );
               voters_tbl.modify( new_proxy, 0, [&](auto& a) { a.proxied_votes += voter->staked.quantity; } );
448 449 450 451 452
               new_producers = &new_proxy->producers;
            } else {
               new_producers = &vp.producers;
            }

453
            producers_table producers_tbl( SystemAccount, SystemAccount );
454 455 456 457
            uint128_t votes = voter->staked.quantity;
            if ( voter->is_proxy ) {
               votes += voter->proxied_votes;
            }
458

459
            if ( old_producers ) { //old_producers == 0 if proxy has stoped being a proxy and votes were taken back from the producers at that moment
460 461 462 463
               //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 ) {
464
                  auto prod = producers_tbl.find( *it );
A
Anton Perkov 已提交
465 466
                  eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption
                  producers_tbl.modify( prod, 0, [&]( auto& pi ) { pi.total_votes -= votes; } );
467
               }
468 469 470 471
            }

            //update newly elected
            std::vector<account_name> elected( new_producers->size() );
472
            auto end_it = std::set_difference( new_producers->begin(), new_producers->end(), old_producers->begin(), old_producers->end(), elected.begin() );
473
            for ( auto it = elected.begin(); it != end_it; ++it ) {
474
               auto prod = producers_tbl.find( *it );
A
Anton Perkov 已提交
475
               eosio_assert( prod != producers_tbl.end(), "producer is not registered" );
476
               if ( vp.proxy == 0 ) { //direct voting, in case of proxy voting update total_votes even for inactive producers
477
                  eosio_assert( prod->active(), "producer is not currently registered" );
478
               }
A
Anton Perkov 已提交
479
               producers_tbl.modify( prod, 0, [&]( auto& pi ) { pi.total_votes += votes; } );
480 481 482
            }

            // save new values to the account itself
A
Anton Perkov 已提交
483
            voters_tbl.modify( voter, 0, [&](voter_info& a) {
484 485 486
                  a.proxy = vp.proxy;
                  a.last_update = now();
                  a.producers = vp.producers;
487 488 489
               });
         }

490 491
         ACTION( SystemAccount, regproxy ) {
            account_name proxy;
492

493
            EOSLIB_SERIALIZE( regproxy, (proxy) )
494 495
         };

496 497
         static void on( const regproxy& reg ) {
            require_auth( reg.proxy );
498

499
            voters_table voters_tbl( SystemAccount, SystemAccount );
500
            auto proxy = voters_tbl.find( reg.proxy );
A
Anton Perkov 已提交
501
            if ( proxy != voters_tbl.end() ) {
502 503
               eosio_assert( proxy->is_proxy == 0, "account is already a proxy" );
               eosio_assert( proxy->proxy == 0, "account that uses a proxy is not allowed to become a proxy" );
A
Anton Perkov 已提交
504
               voters_tbl.modify( proxy, 0, [&](voter_info& a) {
505
                     a.is_proxy = 1;
A
Anton Perkov 已提交
506
                     a.last_update = now();
507
                     //a.proxied_votes may be > 0, if the proxy has been unregistered, so we had to keep the value
508
                  });
509 510 511 512
               if ( 0 < proxy->proxied_votes ) {
                  producers_table producers_tbl( SystemAccount, SystemAccount );
                  for ( auto p : proxy->producers ) {
                     auto prod = producers_tbl.find( p );
A
Anton Perkov 已提交
513 514
                     eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption
                     producers_tbl.modify( prod, 0, [&]( auto& pi ) { pi.total_votes += proxy->proxied_votes; });
515 516
                  }
               }
517
            } else {
518 519
               voters_tbl.emplace( reg.proxy, [&]( voter_info& a ) {
                     a.owner = reg.proxy;
520 521
                     a.last_update = now();
                     a.proxy = 0;
522
                     a.is_proxy = 1;
523 524 525 526
                     a.proxied_votes = 0;
                     a.staked.quantity = 0;
                  });
            }
527
         }
A
Anton Perkov 已提交
528

529 530
         ACTION( SystemAccount, unregproxy ) {
            account_name proxy;
531

532
            EOSLIB_SERIALIZE( unregproxy, (proxy) )
533 534
         };

535 536
         static void on( const unregproxy& reg ) {
            require_auth( reg.proxy );
537

538
            voters_table voters_tbl( SystemAccount, SystemAccount );
539
            auto proxy = voters_tbl.find( reg.proxy );
540
            eosio_assert( proxy != voters_tbl.end(), "proxy not found" );
A
Anton Perkov 已提交
541
            eosio_assert( proxy->is_proxy == 1, "account is not a proxy" );
542

A
Anton Perkov 已提交
543
            voters_tbl.modify( proxy, 0, [&](voter_info& a) {
544 545 546 547
                     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
               });
548 549 550 551 552

            if ( 0 < proxy->proxied_votes ) {
               producers_table producers_tbl( SystemAccount, SystemAccount );
               for ( auto p : proxy->producers ) {
                  auto prod = producers_tbl.find( p );
A
Anton Perkov 已提交
553 554
                  eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption
                  producers_tbl.modify( prod, 0, [&]( auto& pi ) { pi.total_votes -= proxy->proxied_votes; });
555 556
               }
            }
557 558
         }

559 560
   };
}