voting.hpp 25.2 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
#include <cmath>
23 24 25 26 27 28

namespace eosiosystem {
   using eosio::indexed_by;
   using eosio::const_mem_fun;
   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

A
Anton Perkov 已提交
42
         static constexpr uint32_t max_inflation_rate = common<SystemAccount>::max_inflation_rate;
K
Khaled Al-Hassanieh 已提交
43
         static constexpr uint32_t blocks_per_year = 52*7*24*2*3600; // half seconds per year
44

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

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

K
Khaled Al-Hassanieh 已提交
59 60 61
            EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(prefs)(packed_key)
                              (per_block_payments)(last_rewards_claim)
                              (time_became_active)(last_produced_block_time) )
62 63
         };

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

68

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

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

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

87
         typedef eosio::multi_index< N(voters), voter_info>  voters_table;
88

A
Anton Perkov 已提交
89
         ACTION( SystemAccount, regproducer ) {
90 91
            account_name     producer;
            bytes            producer_key;
92
            eosio_parameters prefs;
93

A
Anton Perkov 已提交
94
            EOSLIB_SERIALIZE( regproducer, (producer)(producer_key)(prefs) )
95 96 97
         };

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

108
            producers_table producers_tbl( SystemAccount, SystemAccount );
A
Anton Perkov 已提交
109
            auto prod = producers_tbl.find( reg.producer );
110

A
Anton Perkov 已提交
111 112
            if ( prod != producers_tbl.end() ) {
               producers_tbl.modify( prod, reg.producer, [&]( producer_info& info ){
A
Anton Perkov 已提交
113
                     info.prefs = reg.prefs;
114
                     info.packed_key = reg.producer_key;
A
Anton Perkov 已提交
115 116 117 118 119 120 121 122 123
                  });
            } 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;
                  });
            }
124 125
         }

126
         ACTION( SystemAccount, unregprod ) {
127 128
            account_name producer;

129
            EOSLIB_SERIALIZE( unregprod, (producer) )
130 131
         };

132
         static void on( const unregprod& unreg ) {
133 134 135
            require_auth( unreg.producer );

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

A
Anton Perkov 已提交
139
            producers_tbl.modify( prod, 0, [&]( producer_info& info ){
140 141 142 143
                  info.packed_key.clear();
               });
         }

144 145
         static void increase_voting_power( account_name acnt, system_token_type amount ) {
            voters_table voters_tbl( SystemAccount, SystemAccount );
A
Anton Perkov 已提交
146
            auto voter = voters_tbl.find( acnt );
147

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

161
            const std::vector<account_name>* producers = nullptr;
162 163
            if ( voter->proxy ) {
               auto proxy = voters_tbl.find( voter->proxy );
A
Anton Perkov 已提交
164 165
               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; } );
166 167 168 169
               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 {
170
               producers = &voter->producers;
171
            }
172

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

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

            if ( 0 < amount.quantity ) {
               eosio_assert( amount <= voter->staked, "cannot unstake more than total stake amount" );
A
Anton Perkov 已提交
193
               voters_tbl.modify( voter, 0, [&](voter_info& a) {
194 195 196 197 198 199 200
                     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 已提交
201
                  voters_tbl.modify( proxy, 0, [&](voter_info& a) { a.proxied_votes -= amount.quantity; } );
202 203 204 205 206 207 208 209 210 211 212
                  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 已提交
213 214
                     eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption
                     producers_tbl.modify( prod, 0, [&]( auto& v ) {
215 216 217 218 219 220 221 222
                           v.total_votes -= amount.quantity;
                        });
                  }
               }
            } else {
               if (voter->deferred_trx_id) {
                  //XXX cancel_deferred_transaction(voter->deferred_trx_id);
               }
A
Anton Perkov 已提交
223
               voters_tbl.modify( voter, 0, [&](voter_info& a) {
224 225 226 227 228 229 230 231 232
                     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 已提交
233 234
         static system_token_type payment_per_block(uint32_t percent_of_max_inflation_rate) {
            const system_token_type token_supply = currency::get_total_supply();
235 236 237 238
            const double annual_rate = double(max_inflation_rate * percent_of_max_inflation_rate) / double(10000);
            double continuous_rate = std::log1p(annual_rate);
            uint64_t payment = static_cast<uint64_t>((continuous_rate * double(token_supply.quantity)) / double(blocks_per_year));
            return (system_token_type(payment));
K
Khaled Al-Hassanieh 已提交
239 240
         }

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

245 246 247 248 249 250
            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;
251
            std::array<uint64_t, 21> max_storage_size;
252 253 254 255 256 257
            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 已提交
258
            std::array<uint32_t, 21> percent_of_max_inflation_rate;
259
            std::array<uint32_t, 21> storage_reserve_ratio;
260

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

271 272 273 274 275 276 277 278 279
                  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;

280
                  max_storage_size[n] = it->prefs.max_storage_size;
281 282 283 284 285 286 287
                  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;

288
                  storage_reserve_ratio[n] = it->prefs.storage_reserve_ratio;
K
Khaled Al-Hassanieh 已提交
289
                  percent_of_max_inflation_rate[n] = it->prefs.percent_of_max_inflation_rate;
290 291
                  ++n;
               }
292
            }
293 294 295
            if ( n == 0 ) { //no active producers with votes > 0
               return;
            }
296 297 298 299 300 301 302 303 304 305 306 307
            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 );
308
               std::sort( max_inline_action_size.begin(), max_inline_action_size.begin()+n );
309 310 311 312 313
               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 );
            }

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

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

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

K
Khaled Al-Hassanieh 已提交
338 339 340
            // not voted on
            parameters.first_block_time_in_cycle = cycle_time;

K
Khaled Al-Hassanieh 已提交
341
            // derived parameters
K
Khaled Al-Hassanieh 已提交
342 343 344 345
            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);
346
            parameters.blocks_per_cycle = 6 * schedule.producers.size();
347 348 349 350

            if ( parameters.max_storage_size < parameters.total_storage_bytes_reserved ) {
               parameters.max_storage_size = parameters.total_storage_bytes_reserved;
            }
A
arhag 已提交
351

352 353
            auto issue_quantity = parameters.blocks_per_cycle * (parameters.payment_per_block + parameters.payment_to_eos_bucket);
            currency::inline_issue(SystemAccount, issue_quantity);
354
            set_blockchain_parameters(parameters);
355
            global_state_singleton::set(parameters);
356 357
         }

358
         ACTION( SystemAccount, voteproducer ) {
359 360 361 362
            account_name                voter;
            account_name                proxy;
            std::vector<account_name>   producers;

363
            EOSLIB_SERIALIZE( voteproducer, (voter)(proxy)(producers) )
364
         };
365 366 367 368 369 370 371 372

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

376 377 378
            //validate input
            if ( vp.proxy ) {
               eosio_assert( vp.producers.size() == 0, "cannot vote for producers and proxy at same time" );
379
               require_recipient( vp.proxy );
380 381
            } else {
               eosio_assert( vp.producers.size() <= 30, "attempt to vote for too many producers" );
382 383 384
               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" );
               }
385 386
            }

387 388
            voters_table voters_tbl( SystemAccount, SystemAccount );
            auto voter = voters_tbl.find( vp.voter );
389

A
Anton Perkov 已提交
390
            eosio_assert( voter != voters_tbl.end() && ( 0 < voter->staked.quantity || ( voter->is_proxy && 0 < voter->proxied_votes ) ), "no stake to vote" );
391
            if ( voter->is_proxy ) {
A
Anton Perkov 已提交
392
               eosio_assert( vp.proxy == 0 , "account registered as a proxy is not allowed to use a proxy" );
393 394 395 396
            }

            //find old producers, update old proxy if needed
            const std::vector<account_name>* old_producers = nullptr;
397 398
            if( voter->proxy ) {
               if ( voter->proxy == vp.proxy ) {
399
                  return; // nothing changed
400
               }
401
               auto old_proxy = voters_tbl.find( voter->proxy );
A
Anton Perkov 已提交
402 403
               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; } );
404 405 406
               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;
               }
407
            } else {
408
               old_producers = &voter->producers;
409 410
            }

411 412 413
            //find new producers, update new proxy if needed
            const std::vector<account_name>* new_producers = nullptr;
            if ( vp.proxy ) {
414
               auto new_proxy = voters_tbl.find( vp.proxy );
A
Anton Perkov 已提交
415 416
               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; } );
417 418 419 420 421
               new_producers = &new_proxy->producers;
            } else {
               new_producers = &vp.producers;
            }

422
            producers_table producers_tbl( SystemAccount, SystemAccount );
423 424 425 426
            uint128_t votes = voter->staked.quantity;
            if ( voter->is_proxy ) {
               votes += voter->proxied_votes;
            }
427

428
            if ( old_producers ) { //old_producers == 0 if proxy has stoped being a proxy and votes were taken back from the producers at that moment
429 430 431 432
               //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 ) {
433
                  auto prod = producers_tbl.find( *it );
A
Anton Perkov 已提交
434 435
                  eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption
                  producers_tbl.modify( prod, 0, [&]( auto& pi ) { pi.total_votes -= votes; } );
436
               }
437 438 439 440
            }

            //update newly elected
            std::vector<account_name> elected( new_producers->size() );
441
            auto end_it = std::set_difference( new_producers->begin(), new_producers->end(), old_producers->begin(), old_producers->end(), elected.begin() );
442
            for ( auto it = elected.begin(); it != end_it; ++it ) {
443
               auto prod = producers_tbl.find( *it );
A
Anton Perkov 已提交
444
               eosio_assert( prod != producers_tbl.end(), "producer is not registered" );
445
               if ( vp.proxy == 0 ) { //direct voting, in case of proxy voting update total_votes even for inactive producers
446
                  eosio_assert( prod->active(), "producer is not currently registered" );
447
               }
A
Anton Perkov 已提交
448
               producers_tbl.modify( prod, 0, [&]( auto& pi ) { pi.total_votes += votes; } );
449 450 451
            }

            // save new values to the account itself
A
Anton Perkov 已提交
452
            voters_tbl.modify( voter, 0, [&](voter_info& a) {
453 454 455
                  a.proxy = vp.proxy;
                  a.last_update = now();
                  a.producers = vp.producers;
456 457 458
               });
         }

459 460
         ACTION( SystemAccount, regproxy ) {
            account_name proxy;
461

462
            EOSLIB_SERIALIZE( regproxy, (proxy) )
463 464
         };

465 466
         static void on( const regproxy& reg ) {
            require_auth( reg.proxy );
467

468
            voters_table voters_tbl( SystemAccount, SystemAccount );
469
            auto proxy = voters_tbl.find( reg.proxy );
A
Anton Perkov 已提交
470
            if ( proxy != voters_tbl.end() ) {
471 472
               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 已提交
473
               voters_tbl.modify( proxy, 0, [&](voter_info& a) {
474
                     a.is_proxy = 1;
A
Anton Perkov 已提交
475
                     a.last_update = now();
476
                     //a.proxied_votes may be > 0, if the proxy has been unregistered, so we had to keep the value
477
                  });
478 479 480 481
               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 已提交
482 483
                     eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption
                     producers_tbl.modify( prod, 0, [&]( auto& pi ) { pi.total_votes += proxy->proxied_votes; });
484 485
                  }
               }
486
            } else {
487 488
               voters_tbl.emplace( reg.proxy, [&]( voter_info& a ) {
                     a.owner = reg.proxy;
489 490
                     a.last_update = now();
                     a.proxy = 0;
491
                     a.is_proxy = 1;
492 493 494 495
                     a.proxied_votes = 0;
                     a.staked.quantity = 0;
                  });
            }
496
         }
A
Anton Perkov 已提交
497

498 499
         ACTION( SystemAccount, unregproxy ) {
            account_name proxy;
500

501
            EOSLIB_SERIALIZE( unregproxy, (proxy) )
502 503
         };

504 505
         static void on( const unregproxy& reg ) {
            require_auth( reg.proxy );
506

507
            voters_table voters_tbl( SystemAccount, SystemAccount );
508
            auto proxy = voters_tbl.find( reg.proxy );
509
            eosio_assert( proxy != voters_tbl.end(), "proxy not found" );
A
Anton Perkov 已提交
510
            eosio_assert( proxy->is_proxy == 1, "account is not a proxy" );
511

A
Anton Perkov 已提交
512
            voters_tbl.modify( proxy, 0, [&](voter_info& a) {
513 514 515 516
                     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
               });
517 518 519 520 521

            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 已提交
522 523
                  eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption
                  producers_tbl.modify( prod, 0, [&]( auto& pi ) { pi.total_votes -= proxy->proxied_votes; });
524 525
               }
            }
526 527
         }

528 529
   };
}