voting.hpp 27.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;
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

59
            EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(packed_key)
K
Khaled Al-Hassanieh 已提交
60 61
                              (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

89

90 91
         static void increase_voting_power( account_name acnt, system_token_type amount ) {
            voters_table voters_tbl( SystemAccount, SystemAccount );
A
Anton Perkov 已提交
92
            auto voter = voters_tbl.find( acnt );
93

A
Anton Perkov 已提交
94 95
            if( voter == voters_tbl.end() ) {
               voter = voters_tbl.emplace( acnt, [&]( voter_info& a ) {
96
                     a.owner = acnt;
97
                     a.last_update = now();
98
                     a.staked = amount;
99
                  });
100
            } else {
A
Anton Perkov 已提交
101
               voters_tbl.modify( voter, 0, [&]( auto& av ) {
102 103 104
                     av.last_update = now();
                     av.staked += amount;
                  });
105 106
            }

107
            const std::vector<account_name>* producers = nullptr;
108 109
            if ( voter->proxy ) {
               auto proxy = voters_tbl.find( voter->proxy );
A
Anton Perkov 已提交
110 111
               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; } );
112 113 114 115
               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 {
116
               producers = &voter->producers;
117
            }
118

119
            if ( producers ) {
120
               producers_table producers_tbl( SystemAccount, SystemAccount );
121
               for( auto p : *producers ) {
122
                  auto prod = producers_tbl.find( p );
A
Anton Perkov 已提交
123 124
                  eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption
                  producers_tbl.modify( prod, 0, [&]( auto& v ) {
125 126 127
                        v.total_votes += amount.quantity;
                     });
               }
128
            }
129
         }
130

131 132 133
         static void decrease_voting_power( account_name acnt, system_token_type amount ) {
            require_auth( acnt );
            voters_table voters_tbl( SystemAccount, SystemAccount );
A
Anton Perkov 已提交
134 135
            auto voter = voters_tbl.find( acnt );
            eosio_assert( voter != voters_tbl.end(), "stake not found" );
136 137 138

            if ( 0 < amount.quantity ) {
               eosio_assert( amount <= voter->staked, "cannot unstake more than total stake amount" );
A
Anton Perkov 已提交
139
               voters_tbl.modify( voter, 0, [&](voter_info& a) {
140 141 142 143 144 145 146
                     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 已提交
147
                  voters_tbl.modify( proxy, 0, [&](voter_info& a) { a.proxied_votes -= amount.quantity; } );
148 149 150 151 152 153 154 155 156 157 158
                  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 已提交
159 160
                     eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption
                     producers_tbl.modify( prod, 0, [&]( auto& v ) {
161 162 163 164 165 166 167 168
                           v.total_votes -= amount.quantity;
                        });
                  }
               }
            } else {
               if (voter->deferred_trx_id) {
                  //XXX cancel_deferred_transaction(voter->deferred_trx_id);
               }
A
Anton Perkov 已提交
169
               voters_tbl.modify( voter, 0, [&](voter_info& a) {
170 171 172 173 174 175 176 177 178
                     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 已提交
179 180
         static system_token_type payment_per_block(uint32_t percent_of_max_inflation_rate) {
            const system_token_type token_supply = currency::get_total_supply();
181 182 183 184
            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 已提交
185 186
         }

A
arhag 已提交
187 188 189
         static void update_elected_producers(time cycle_time);

#if 0
K
Khaled Al-Hassanieh 已提交
190
         static void update_elected_producers(time cycle_time) {
191
            producers_table producers_tbl( SystemAccount, SystemAccount );
K
Khaled Al-Hassanieh 已提交
192
            auto idx = producers_tbl.template get_index<N(prototalvote)>();
193

194
            std::array<uint32_t, 21> max_block_net_usage;
A
arhag 已提交
195
            std::array<uint32_t, 21> target_block_net_usage_pct;
196
            std::array<uint32_t, 21> base_per_transaction_net_usage;
A
arhag 已提交
197
            std::array<uint32_t, 21> max_transaction_net_usage;
198 199
            std::array<uint32_t, 21> context_free_discount_net_usage_num;
            std::array<uint32_t, 21> context_free_discount_net_usage_den;
A
arhag 已提交
200

201
            std::array<uint32_t, 21> max_block_cpu_usage;
A
arhag 已提交
202 203
            std::array<uint32_t, 21> target_block_cpu_usage_pct;
            std::array<uint32_t, 21> max_transaction_cpu_usage;
204 205 206 207
            std::array<uint32_t, 21> base_per_transaction_cpu_usage;
            std::array<uint32_t, 21> base_per_action_cpu_usage;
            std::array<uint32_t, 21> base_setcode_cpu_usage;
            std::array<uint32_t, 21> per_signature_cpu_usage;
208 209
            std::array<uint32_t, 21> context_free_discount_cpu_usage_num;
            std::array<uint32_t, 21> context_free_discount_cpu_usage_den;
A
arhag 已提交
210

211
            std::array<uint32_t, 21> max_transaction_lifetime;
A
arhag 已提交
212 213
            std::array<uint32_t, 21> deferred_trx_expiration_window;
            std::array<uint32_t, 21> max_transaction_delay;
214
            std::array<uint32_t, 21> max_inline_action_size;
A
arhag 已提交
215 216
            std::array<uint16_t, 21> max_inline_action_depth;
            std::array<uint16_t, 21> max_authority_depth;
217
            std::array<uint32_t, 21> max_generated_transaction_count;
A
arhag 已提交
218 219

            std::array<uint32_t, 21> max_storage_size;
K
Khaled Al-Hassanieh 已提交
220
            std::array<uint32_t, 21> percent_of_max_inflation_rate;
221
            std::array<uint32_t, 21> storage_reserve_ratio;
222

223 224
            std::vector<eosio::producer_key> schedule;
            schedule.reserve(21);
225
            size_t n = 0;
226
            for ( auto it = idx.crbegin(); it != idx.crend() && n < 21 && 0 < it->total_votes; ++it ) {
227
               if ( it->active() ) {
228 229 230 231
                  schedule.emplace_back();
                  schedule.back().producer_name = it->owner;
                  eosio_assert( sizeof(schedule.back().block_signing_key) == it->packed_key.size(), "size mismatch" );
                  std::copy( it->packed_key.begin(), it->packed_key.end(), schedule.back().block_signing_key.data );
232

A
arhag 已提交
233 234 235
                  max_block_net_usage[n] = it->prefs.max_block_net_usage;
                  target_block_net_usage_pct[n] = it->prefs.target_block_net_usage_pct;
                  max_transaction_net_usage[n] = it->prefs.max_transaction_net_usage;
236
                  base_per_transaction_net_usage[n] = it->prefs.base_per_transaction_net_usage;
A
arhag 已提交
237 238 239 240 241 242
                  context_free_discount_net_usage_num[n] = it->prefs.context_free_discount_net_usage_num;
                  context_free_discount_net_usage_den[n] = it->prefs.context_free_discount_net_usage_den;

                  max_block_cpu_usage[n] = it->prefs.max_block_cpu_usage;
                  target_block_cpu_usage_pct[n] = it->prefs.target_block_cpu_usage_pct;
                  max_transaction_cpu_usage[n] = it->prefs.max_transaction_cpu_usage;
243 244 245 246
                  base_per_transaction_cpu_usage[n] = it->prefs.base_per_transaction_cpu_usage;
                  base_per_action_cpu_usage[n] = it->prefs.base_per_action_cpu_usage;
                  base_setcode_cpu_usage[n] = it->prefs.base_setcode_cpu_usage;
                  per_signature_cpu_usage[n] = it->prefs.per_signature_cpu_usage;
B
Bart Wyatt 已提交
247 248
                  context_free_discount_cpu_usage_num[n] = it->prefs.context_free_discount_cpu_usage_num;
                  context_free_discount_cpu_usage_den[n] = it->prefs.context_free_discount_cpu_usage_den;
A
arhag 已提交
249

250
                  max_transaction_lifetime[n] = it->prefs.max_transaction_lifetime;
A
arhag 已提交
251 252
                  deferred_trx_expiration_window[n] = it->prefs.deferred_trx_expiration_window;
                  max_transaction_delay[n] = it->prefs.max_transaction_delay;
253
                  max_inline_action_size[n] = it->prefs.max_inline_action_size;
A
arhag 已提交
254 255
                  max_inline_action_depth[n] = it->prefs.max_inline_action_depth;
                  max_authority_depth[n] = it->prefs.max_authority_depth;
256
                  max_generated_transaction_count[n] = it->prefs.max_generated_transaction_count;
257

A
arhag 已提交
258
                  max_storage_size[n] = it->prefs.max_storage_size;
259
                  storage_reserve_ratio[n] = it->prefs.storage_reserve_ratio;
K
Khaled Al-Hassanieh 已提交
260
                  percent_of_max_inflation_rate[n] = it->prefs.percent_of_max_inflation_rate;
261 262
                  ++n;
               }
263
            }
264 265 266
            if ( n == 0 ) { //no active producers with votes > 0
               return;
            }
267
            if ( 1 < n ) {
A
arhag 已提交
268 269 270
               std::sort( max_block_net_usage.begin(), max_block_net_usage.begin()+n );
               std::sort( target_block_net_usage_pct.begin(), target_block_net_usage_pct.begin()+n );
               std::sort( max_transaction_net_usage.begin(), max_transaction_net_usage.begin()+n );
271
               std::sort( base_per_transaction_net_usage.begin(), base_per_transaction_net_usage.begin()+n );
A
arhag 已提交
272 273 274 275 276 277
               std::sort( context_free_discount_net_usage_num.begin(), context_free_discount_net_usage_num.begin()+n );
               std::sort( context_free_discount_net_usage_den.begin(), context_free_discount_net_usage_den.begin()+n );

               std::sort( max_block_cpu_usage.begin(), max_block_cpu_usage.begin()+n );
               std::sort( target_block_cpu_usage_pct.begin(), target_block_cpu_usage_pct.begin()+n );
               std::sort( max_transaction_cpu_usage.begin(), max_transaction_cpu_usage.begin()+n );
278 279 280 281
               std::sort( base_per_transaction_cpu_usage.begin(), base_per_transaction_cpu_usage.begin()+n );
               std::sort( base_per_action_cpu_usage.begin(), base_per_action_cpu_usage.begin()+n );
               std::sort( base_setcode_cpu_usage.begin(), base_setcode_cpu_usage.begin()+n );
               std::sort( per_signature_cpu_usage.begin(), per_signature_cpu_usage.begin()+n );
B
Bart Wyatt 已提交
282 283
               std::sort( context_free_discount_cpu_usage_num.begin(), context_free_discount_cpu_usage_num.begin()+n );
               std::sort( context_free_discount_cpu_usage_den.begin(), context_free_discount_cpu_usage_den.begin()+n );
A
arhag 已提交
284

285
               std::sort( max_transaction_lifetime.begin(), max_transaction_lifetime.begin()+n );
A
arhag 已提交
286 287
               std::sort( deferred_trx_expiration_window.begin(), deferred_trx_expiration_window.begin()+n );
               std::sort( max_transaction_delay.begin(), max_transaction_delay.begin()+n );
288
               std::sort( max_inline_action_size.begin(), max_inline_action_size.begin()+n );
A
arhag 已提交
289 290
               std::sort( max_inline_action_depth.begin(), max_inline_action_depth.begin()+n );
               std::sort( max_authority_depth.begin(), max_authority_depth.begin()+n );
291
               std::sort( max_generated_transaction_count.begin(), max_generated_transaction_count.begin()+n );
A
arhag 已提交
292 293

               std::sort( max_storage_size.begin(), max_storage_size.begin()+n );
294 295 296 297
               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 );
            }

A
Anton Perkov 已提交
298 299
            bytes packed_schedule = pack(schedule);
            set_active_producers( packed_schedule.data(),  packed_schedule.size() );
300
            size_t median = n/2;
301

302 303
            auto parameters = global_state_singleton::exists() ? global_state_singleton::get()
                  : common<SystemAccount>::get_default_parameters();
304

A
arhag 已提交
305 306 307
            parameters.max_block_net_usage = max_block_net_usage[median];
            parameters.target_block_net_usage_pct = target_block_net_usage_pct[median];
            parameters.max_transaction_net_usage = max_transaction_net_usage[median];
308
            parameters.base_per_transaction_net_usage = base_per_transaction_net_usage[median];
A
arhag 已提交
309 310 311 312 313 314
            parameters.context_free_discount_net_usage_num = context_free_discount_net_usage_num[median];
            parameters.context_free_discount_net_usage_den = context_free_discount_net_usage_den[median];

            parameters.max_block_cpu_usage = max_block_cpu_usage[median];
            parameters.target_block_cpu_usage_pct = target_block_cpu_usage_pct[median];
            parameters.max_transaction_cpu_usage = max_transaction_cpu_usage[median];
315 316 317 318
            parameters.base_per_transaction_cpu_usage = base_per_transaction_cpu_usage[median];
            parameters.base_per_action_cpu_usage = base_per_action_cpu_usage[median];
            parameters.base_setcode_cpu_usage = base_setcode_cpu_usage[median];
            parameters.per_signature_cpu_usage = per_signature_cpu_usage[median];
B
Bart Wyatt 已提交
319 320
            parameters.context_free_discount_cpu_usage_num = context_free_discount_cpu_usage_num[median];
            parameters.context_free_discount_cpu_usage_den = context_free_discount_cpu_usage_den[median];
A
arhag 已提交
321

322
            parameters.max_transaction_lifetime = max_transaction_lifetime[median];
A
arhag 已提交
323 324
            parameters.deferred_trx_expiration_window = deferred_trx_expiration_window[median];
            parameters.max_transaction_delay = max_transaction_delay[median];
325
            parameters.max_inline_action_size = max_inline_action_size[median];
A
arhag 已提交
326 327
            parameters.max_inline_action_depth = max_inline_action_depth[median];
            parameters.max_authority_depth = max_authority_depth[median];
328
            parameters.max_generated_transaction_count = max_generated_transaction_count[median];
A
arhag 已提交
329 330

            parameters.max_storage_size = max_storage_size[median];
331
            parameters.storage_reserve_ratio = storage_reserve_ratio[median];
K
Khaled Al-Hassanieh 已提交
332 333
            parameters.percent_of_max_inflation_rate = percent_of_max_inflation_rate[median];

K
Khaled Al-Hassanieh 已提交
334 335 336
            // not voted on
            parameters.first_block_time_in_cycle = cycle_time;

K
Khaled Al-Hassanieh 已提交
337
            // derived parameters
K
Khaled Al-Hassanieh 已提交
338 339 340 341
            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);
342
            parameters.blocks_per_cycle = common<SystemAccount>::blocks_per_producer * schedule.size();
343 344 345 346

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

348 349
            auto issue_quantity = parameters.blocks_per_cycle * (parameters.payment_per_block + parameters.payment_to_eos_bucket);
            currency::inline_issue(SystemAccount, issue_quantity);
350
            set_blockchain_parameters(parameters);
351
            global_state_singleton::set(parameters);
352
         }
A
arhag 已提交
353
#endif
354

355
         ACTION( SystemAccount, voteproducer ) {
356 357 358 359
            account_name                voter;
            account_name                proxy;
            std::vector<account_name>   producers;

360
            EOSLIB_SERIALIZE( voteproducer, (voter)(proxy)(producers) )
361
         };
362 363 364 365 366 367 368 369

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

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

384 385
            voters_table voters_tbl( SystemAccount, SystemAccount );
            auto voter = voters_tbl.find( vp.voter );
386

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

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

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

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

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

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

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

461 462
         ACTION( SystemAccount, regproxy ) {
            account_name proxy;
463

464
            EOSLIB_SERIALIZE( regproxy, (proxy) )
465 466
         };

467 468
         static void on( const regproxy& reg ) {
            require_auth( reg.proxy );
469

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

500 501
         ACTION( SystemAccount, unregproxy ) {
            account_name proxy;
502

503
            EOSLIB_SERIALIZE( unregproxy, (proxy) )
504 505
         };

506 507
         static void on( const unregproxy& reg ) {
            require_auth( reg.proxy );
508

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

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

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

530 531
   };
}