voting.hpp 25.8 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
         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 已提交
39
         using global_state_singleton = typename common<SystemAccount>::global_state_singleton;
40

K
Khaled Al-Hassanieh 已提交
41
         static const uint32_t max_inflation_rate = common<SystemAccount>::max_inflation_rate;
42
         //         static const uint32_t blocks_per_cycle = common<SystemAccount>::blocks_per_cycle;
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
            const auto* prod = producers_tbl.find( reg.producer );
114

A
Anton Perkov 已提交
115 116 117
            if ( prod ) {
               producers_tbl.update( *prod, reg.producer, [&]( producer_info& info ){
                     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 140 141 142 143 144 145 146 147
            require_auth( unreg.producer );

            producers_table producers_tbl( SystemAccount, SystemAccount );
            const auto* prod = producers_tbl.find( unreg.producer );
            eosio_assert( bool(prod), "producer not found" );

            producers_tbl.update( *prod, 0, [&]( producer_info& info ){
                  info.packed_key.clear();
               });
         }

148 149 150
         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 );
151

152 153 154
            if( !voter ) {
               voter = &voters_tbl.emplace( acnt, [&]( voter_info& a ) {
                     a.owner = acnt;
155
                     a.last_update = now();
156
                     a.staked = amount;
157
                  });
158
            } else {
159
               voters_tbl.update( *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 168
            if ( voter->proxy ) {
               auto proxy = voters_tbl.find( voter->proxy );
               voters_tbl.update( *proxy, 0, [&](voter_info& a) { a.proxied_votes += amount.quantity; } );
169 170 171 172
               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 {
173
               producers = &voter->producers;
174
            }
175

176
            if ( producers ) {
177
               producers_table producers_tbl( SystemAccount, SystemAccount );
178
               for( auto p : *producers ) {
179 180 181
                  auto prod = producers_tbl.find( p );
                  eosio_assert( bool(prod), "never existed producer" ); //data corruption
                  producers_tbl.update( *prod, 0, [&]( auto& v ) {
182 183 184
                        v.total_votes += amount.quantity;
                     });
               }
185
            }
186
         }
187

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
         static void decrease_voting_power( account_name acnt, system_token_type amount ) {
            require_auth( acnt );
            voters_table voters_tbl( SystemAccount, SystemAccount );
            const auto* voter = voters_tbl.find( acnt );
            eosio_assert( bool(voter), "stake not found" );

            if ( 0 < amount.quantity ) {
               eosio_assert( amount <= voter->staked, "cannot unstake more than total stake amount" );
               /*
               if (voter->deferred_trx_id) {
                  //XXX cancel_deferred_transaction(voter->deferred_trx_id);
               }

               unstake_vote_deferred dt;
               dt.voter = acnt;
               uint32_t new_trx_id = 0;//XXX send_deferred(dt);

               avotes.update( *voter, 0, [&](voter_info& a) {
                     a.staked -= amount;
                     a.unstaking += a.unstaking + 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();
                  });
               */

               // Temporary code: immediate unstake
               voters_tbl.update( *voter, 0, [&](voter_info& a) {
                     a.staked -= amount;
                     a.last_update = now();
                  });
               //currency::inline_transfer( SystemAccount, acnt, amount, "unstake voting" );
               // end of temporary code

               const std::vector<account_name>* producers = nullptr;
               if ( voter->proxy ) {
                  auto proxy = voters_tbl.find( voter->proxy );
                  voters_tbl.update( *proxy, 0, [&](voter_info& 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 = &voter->producers;
               }

               if ( producers ) {
                  producers_table producers_tbl( SystemAccount, SystemAccount );
                  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 -= amount.quantity;
                        });
                  }
               }
            } else {
               if (voter->deferred_trx_id) {
                  //XXX cancel_deferred_transaction(voter->deferred_trx_id);
               }
               voters_tbl.update( *voter, 0, [&](voter_info& a) {
                     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 已提交
258 259
         static system_token_type payment_per_block(uint32_t percent_of_max_inflation_rate) {
            const system_token_type token_supply = currency::get_total_supply();
260 261 262 263 264
            /*
            prints("token_supply\n");
            printi(token_supply.quantity);
            prints("\ntoken_supply\n");
            */
K
Khaled Al-Hassanieh 已提交
265 266
            const auto inflation_rate = max_inflation_rate * percent_of_max_inflation_rate;
            const auto& inflation_ratio = int_logarithm_one_plus(inflation_rate);
267 268 269 270 271 272 273 274
            /*
            printi(inflation_rate);
            prints("\n");
            printi(inflation_ratio.first);
            prints("/");
            printi(inflation_ratio.second);
            prints("\n");
            */
K
Khaled Al-Hassanieh 已提交
275 276 277
            return (token_supply * inflation_ratio.first) / (inflation_ratio.second * blocks_per_year);
         }

K
Khaled Al-Hassanieh 已提交
278
         static void update_elected_producers(time cycle_time) {
279
            producers_table producers_tbl( SystemAccount, SystemAccount );
K
Khaled Al-Hassanieh 已提交
280
            auto idx = producers_tbl.template get_index<N(prototalvote)>();
281

282 283 284 285 286 287
            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;
288
            std::array<uint64_t, 21> max_storage_size;
289 290 291 292 293 294
            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 已提交
295
            std::array<uint32_t, 21> percent_of_max_inflation_rate;
296
            std::array<uint32_t, 21> storage_reserve_ratio;
297

A
Anton Perkov 已提交
298 299
            eosio::producer_schedule schedule;
            schedule.producers.reserve(21);
300

K
Khaled Al-Hassanieh 已提交
301
            auto it = idx.end();
302 303 304
            if (it == idx.begin()) {
                  return;
            }
K
Khaled Al-Hassanieh 已提交
305
            --it;
306 307 308
            size_t n = 0;
            while ( n < 21 ) {
               if ( it->active() ) {
A
Anton Perkov 已提交
309 310
                  schedule.producers.emplace_back();
                  schedule.producers.back().producer_name = it->owner;
K
Khaled Al-Hassanieh 已提交
311 312
                  eosio_assert( sizeof(schedule.producers.back().block_signing_key) == it->packed_key.size(), "size mismatch" );
                  std::copy(it->packed_key.begin(), it->packed_key.end(), schedule.producers.back().block_signing_key.data);
313

314 315 316 317 318 319 320 321 322
                  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;

323
                  max_storage_size[n] = it->prefs.max_storage_size;
324 325 326 327 328 329 330
                  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;

331
                  storage_reserve_ratio[n] = it->prefs.storage_reserve_ratio;
K
Khaled Al-Hassanieh 已提交
332
                  percent_of_max_inflation_rate[n] = it->prefs.percent_of_max_inflation_rate;
333 334
                  ++n;
               }
335 336 337 338 339 340

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

346 347
            auto parameters = global_state_singleton::exists() ? global_state_singleton::get()
                  : common<SystemAccount>::get_default_parameters();
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362

            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 已提交
363 364
            parameters.percent_of_max_inflation_rate = percent_of_max_inflation_rate[median];

K
Khaled Al-Hassanieh 已提交
365 366 367
            // not voted on
            parameters.first_block_time_in_cycle = cycle_time;

K
Khaled Al-Hassanieh 已提交
368
            // derived parameters
K
Khaled Al-Hassanieh 已提交
369 370 371 372
            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);
373
            parameters.blocks_per_cycle = 6 * schedule.producers.size();
374 375 376 377

            if ( parameters.max_storage_size < parameters.total_storage_bytes_reserved ) {
               parameters.max_storage_size = parameters.total_storage_bytes_reserved;
            }
378 379 380
            
            auto issue_quantity = parameters.blocks_per_cycle * (parameters.payment_per_block + parameters.payment_to_eos_bucket);
            currency::inline_issue(SystemAccount, issue_quantity);
381
            set_blockchain_parameters(&parameters);
382
            global_state_singleton::set(parameters);
383 384
         }

385 386 387 388 389 390
         ACTION(  SystemAccount, unstake_vote_deferred ) {
            account_name                voter;

            EOSLIB_SERIALIZE( unstake_vote_deferred, (voter) )
         };

391 392
         static void on( const unstake_vote_deferred& usv) {
            require_auth( usv.voter );
393 394 395
            voters_table voters_tbl( SystemAccount, SystemAccount );
            const auto* voter = voters_tbl.find( usv.voter );
            eosio_assert( bool(voter), "stake not found" );
396

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

401 402
            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;
403

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

406
            voters_tbl.update( *voter, 0, [&](voter_info& a) {
407 408 409 410
                  a.unstaking -= unstake_amount;
                  a.deferred_trx_id = new_trx_id;
                  a.last_unstake_time = a.last_unstake_time + weeks * unstake_pay_period;
               });
411 412
         }

413
         ACTION( SystemAccount, voteproducer ) {
414 415 416 417
            account_name                voter;
            account_name                proxy;
            std::vector<account_name>   producers;

418
            EOSLIB_SERIALIZE( voteproducer, (voter)(proxy)(producers) )
419
         };
420 421 422 423 424 425 426 427

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

431 432 433
            //validate input
            if ( vp.proxy ) {
               eosio_assert( vp.producers.size() == 0, "cannot vote for producers and proxy at same time" );
434
               require_recipient( vp.proxy );
435 436 437 438 439
            } 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" );
            }

440 441
            voters_table voters_tbl( SystemAccount, SystemAccount );
            auto voter = voters_tbl.find( vp.voter );
442

443 444
            eosio_assert( bool(voter), "no stake to vote" );
            if ( voter->is_proxy ) {
445 446 447 448 449
               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;
450 451
            if( voter->proxy ) {
               if ( voter->proxy == vp.proxy ) {
452
                  return; // nothing changed
453
               }
454 455
               auto old_proxy = voters_tbl.find( voter->proxy );
               voters_tbl.update( *old_proxy, 0, [&](auto& a) { a.proxied_votes -= voter->staked.quantity; } );
456 457 458
               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;
               }
459
            } else {
460
               old_producers = &voter->producers;
461 462
            }

463 464 465
            //find new producers, update new proxy if needed
            const std::vector<account_name>* new_producers = nullptr;
            if ( vp.proxy ) {
466
               auto new_proxy = voters_tbl.find( vp.proxy );
467
               eosio_assert( new_proxy->is_proxy, "selected proxy has not elected to be a proxy" );
468
               voters_tbl.update( *new_proxy, 0, [&](auto& a) { a.proxied_votes += voter->staked.quantity; } );
469 470 471 472 473
               new_producers = &new_proxy->producers;
            } else {
               new_producers = &vp.producers;
            }

474
            producers_table producers_tbl( SystemAccount, SystemAccount );
475

476 477 478 479 480
            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 ) {
481 482
                  auto prod = producers_tbl.find( *it );
                  eosio_assert( bool(prod), "never existed producer" ); //data corruption
483
                  producers_tbl.update( *prod, 0, [&]( auto& pi ) { pi.total_votes -= voter->staked.quantity; });
484
               }
485 486 487 488
            }

            //update newly elected
            std::vector<account_name> elected( new_producers->size() );
489
            auto end_it = std::set_difference( new_producers->begin(), new_producers->end(), old_producers->begin(), old_producers->end(), elected.begin() );
490
            for ( auto it = elected.begin(); it != end_it; ++it ) {
491 492 493 494 495
               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" );
               }
496
               producers_tbl.update( *prod, 0, [&]( auto& pi ) { pi.total_votes += voter->staked.quantity; });
497 498 499
            }

            // save new values to the account itself
500
            voters_tbl.update( *voter, 0, [&](voter_info& a) {
501 502 503
                  a.proxy = vp.proxy;
                  a.last_update = now();
                  a.producers = vp.producers;
504 505 506
               });
         }

507
         ACTION( SystemAccount, register_proxy ) {
508 509
            account_name proxy_to_register;

510
            EOSLIB_SERIALIZE( register_proxy, (proxy_to_register) )
511 512
         };

513
         static void on( const register_proxy& reg ) {
514 515
            require_auth( reg.proxy_to_register );

516 517
            voters_table voters_tbl( SystemAccount, SystemAccount );
            auto voter = voters_tbl.find( reg.proxy_to_register );
518 519 520
            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" );
521
               voters_tbl.update( *voter, 0, [&](voter_info& a) {
522
                     a.is_proxy = 1;
A
Anton Perkov 已提交
523
                     a.last_update = now();
524
                     //a.proxied_votes may be > 0, if the proxy has been unregistered, so we had to keep the value
525 526
                  });
            } else {
527
               voters_tbl.emplace( reg.proxy_to_register, [&]( voter_info& a ) {
528 529 530
                     a.owner = reg.proxy_to_register;
                     a.last_update = now();
                     a.proxy = 0;
531
                     a.is_proxy = 1;
532 533 534 535
                     a.proxied_votes = 0;
                     a.staked.quantity = 0;
                  });
            }
536
         }
A
Anton Perkov 已提交
537

538 539 540 541 542 543 544 545 546
         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 );

547 548
            voters_table voters_tbl( SystemAccount, SystemAccount );
            auto proxy = voters_tbl.find( reg.proxy_to_unregister );
549 550
            eosio_assert( bool(proxy), "proxy not found" );
            eosio_assert( proxy->is_proxy == 1, "account is already a proxy" );
551

552
            producers_table producers_tbl( SystemAccount, SystemAccount );
553
            for ( auto p : proxy->producers ) {
554 555 556
               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; });
557 558
            }

559
            voters_tbl.update( *proxy, 0, [&](voter_info& a) {
560 561 562 563 564 565
                     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
               });
         }

566 567
   };
}