Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
voting.cpp
Go to the documentation of this file.
1#include <sysio/crypto.hpp>
2#include <sysio/datastream.hpp>
3#include <sysio/sysio.hpp>
4#include <sysio/multi_index.hpp>
5#include <sysio/permission.hpp>
6#include <sysio/privileged.hpp>
7#include <sysio/serialize.hpp>
8#include <sysio/singleton.hpp>
9
12
13#include <type_traits>
14#include <limits>
15#include <set>
16#include <algorithm>
17#include <cmath>
18
19namespace sysiosystem {
20
21 using sysio::const_mem_fun;
22 using sysio::current_time_point;
23 using sysio::indexed_by;
25 using sysio::singleton;
26
27 void system_contract::register_producer( const name& producer, const sysio::block_signing_authority& producer_authority, const std::string& url, uint16_t location ) {
28 auto prod = _producers.find( producer.value );
29 const auto ct = current_time_point();
30
31 sysio::public_key producer_key{};
32
33 std::visit( [&](auto&& auth ) {
34 if( auth.keys.size() == 1 ) {
35 // if the producer_authority consists of a single key, use that key in the legacy producer_key field
36 producer_key = auth.keys[0].key;
37 }
38 }, producer_authority );
39
40 if ( prod != _producers.end() ) {
41 _producers.modify( prod, producer, [&]( producer_info& info ){
42 info.producer_key = producer_key;
43 info.is_active = true;
44 info.url = url;
45 info.location = location;
46 info.producer_authority.emplace( producer_authority );
47 if ( info.last_claim_time == time_point() )
48 info.last_claim_time = ct;
49 });
50
51 auto prod2 = _producers2.find( producer.value );
52 if ( prod2 == _producers2.end() ) {
53 _producers2.emplace( producer, [&]( producer_info2& info ){
54 info.owner = producer;
55 info.last_votepay_share_update = ct;
56 });
57 update_total_votepay_share( ct, 0.0, prod->total_votes );
58 // When introducing the producer2 table row for the first time, the producer's votes must also be accounted for in the global total_producer_votepay_share at the same time.
59 }
60 } else {
61 _producers.emplace( producer, [&]( producer_info& info ){
62 info.owner = producer;
63 info.total_votes = 0;
64 info.producer_key = producer_key;
65 info.is_active = true;
66 info.url = url;
67 info.location = location;
68 info.last_claim_time = ct;
69 info.producer_authority.emplace( producer_authority );
70 });
71 _producers2.emplace( producer, [&]( producer_info2& info ){
72 info.owner = producer;
73 info.last_votepay_share_update = ct;
74 });
75 }
76
77 }
78
79 void system_contract::regproducer( const name& producer, const sysio::public_key& producer_key, const std::string& url, uint16_t location ) {
80 require_auth( producer );
81 check( url.size() < 512, "url too long" );
82
83 register_producer( producer, convert_to_block_signing_authority( producer_key ), url, location );
84 }
85
86 void system_contract::regproducer2( const name& producer, const sysio::block_signing_authority& producer_authority, const std::string& url, uint16_t location ) {
87 require_auth( producer );
88 check( url.size() < 512, "url too long" );
89
90 std::visit( [&](auto&& auth ) {
91 check( auth.is_valid(), "invalid producer authority" );
92 }, producer_authority );
93
94 register_producer( producer, producer_authority, url, location );
95 }
96
97 void system_contract::unregprod( const name& producer ) {
98 require_auth( producer );
99
100 const auto& prod = _producers.get( producer.value, "producer not found" );
101 _producers.modify( prod, same_payer, [&]( producer_info& info ){
102 info.deactivate();
103 });
104 }
105
106 void system_contract::update_elected_producers( const block_timestamp& block_time ) {
107 _gstate.last_producer_schedule_update = block_time;
108
109 auto idx = _producers.get_index<"prototalvote"_n>();
110
111 using value_type = std::pair<sysio::producer_authority, uint16_t>;
112 std::vector< value_type > top_producers;
113 top_producers.reserve(21);
114
115 for( auto it = idx.cbegin(); it != idx.cend() && top_producers.size() < 21 && 0 < it->total_votes && it->active(); ++it ) {
116 top_producers.emplace_back(
118 .producer_name = it->owner,
119 .authority = it->get_producer_authority()
120 },
121 it->location
122 );
123 }
124
125 if( top_producers.size() == 0 || top_producers.size() < _gstate.last_producer_schedule_size ) {
126 return;
127 }
128
129 std::sort( top_producers.begin(), top_producers.end(), []( const value_type& lhs, const value_type& rhs ) {
130 return lhs.first.producer_name < rhs.first.producer_name; // sort by producer name
131 // return lhs.second < rhs.second; // sort by location
132 } );
133
134 std::vector<sysio::producer_authority> producers;
135
136 producers.reserve(top_producers.size());
137 for( auto& item : top_producers )
138 producers.push_back( std::move(item.first) );
139
140 if( set_proposed_producers( producers ) >= 0 ) {
141 _gstate.last_producer_schedule_size = static_cast<decltype(_gstate.last_producer_schedule_size)>( top_producers.size() );
142 }
143 }
144
145 double stake2vote( int64_t staked ) {
147 double weight = int64_t( (current_time_point().sec_since_epoch() - (block_timestamp::block_timestamp_epoch / 1000)) / (seconds_per_day * 7) ) / double( 52 );
148 return double(staked) * std::pow( 2, weight );
149 }
150
151 double system_contract::update_total_votepay_share( const time_point& ct,
152 double additional_shares_delta,
153 double shares_rate_delta )
154 {
155 double delta_total_votepay_share = 0.0;
156 if( ct > _gstate3.last_vpay_state_update ) {
157 delta_total_votepay_share = _gstate3.total_vpay_share_change_rate
158 * double( (ct - _gstate3.last_vpay_state_update).count() / 1E6 );
159 }
160
161 delta_total_votepay_share += additional_shares_delta;
162 if( delta_total_votepay_share < 0 && _gstate2.total_producer_votepay_share < -delta_total_votepay_share ) {
163 _gstate2.total_producer_votepay_share = 0.0;
164 } else {
165 _gstate2.total_producer_votepay_share += delta_total_votepay_share;
166 }
167
168 if( shares_rate_delta < 0 && _gstate3.total_vpay_share_change_rate < -shares_rate_delta ) {
169 _gstate3.total_vpay_share_change_rate = 0.0;
170 } else {
171 _gstate3.total_vpay_share_change_rate += shares_rate_delta;
172 }
173
174 _gstate3.last_vpay_state_update = ct;
175
176 return _gstate2.total_producer_votepay_share;
177 }
178
179 double system_contract::update_producer_votepay_share( const producers_table2::const_iterator& prod_itr,
180 const time_point& ct,
181 double shares_rate,
182 bool reset_to_zero )
183 {
184 double delta_votepay_share = 0.0;
185 if( shares_rate > 0.0 && ct > prod_itr->last_votepay_share_update ) {
186 delta_votepay_share = shares_rate * double( (ct - prod_itr->last_votepay_share_update).count() / 1E6 ); // cannot be negative
187 }
188
189 double new_votepay_share = prod_itr->votepay_share + delta_votepay_share;
190 _producers2.modify( prod_itr, same_payer, [&](auto& p) {
191 if( reset_to_zero )
192 p.votepay_share = 0.0;
193 else
194 p.votepay_share = new_votepay_share;
195
196 p.last_votepay_share_update = ct;
197 } );
198
199 return new_votepay_share;
200 }
201
202 void system_contract::voteproducer( const name& voter_name, const name& proxy, const std::vector<name>& producers ) {
203 if ( voter_name == "b1"_n ) {
204 require_auth("sysio"_n);
205 } else {
206 require_auth( voter_name );
207 }
208
209 vote_stake_updater( voter_name );
210 update_votes( voter_name, proxy, producers, true );
211 auto rex_itr = _rexbalance.find( voter_name.value );
212 if( rex_itr != _rexbalance.end() && rex_itr->rex_balance.amount > 0 ) {
213 check_voting_requirement( voter_name, "voter holding REX tokens must vote for at least 21 producers or for a proxy" );
214 }
215 }
216
217 void system_contract::voteupdate( const name& voter_name ) {
218 auto voter = _voters.find( voter_name.value );
219 check( voter != _voters.end(), "no voter found" );
220
221 int64_t new_staked = 0;
222
223 updaterex(voter_name);
224
225 // get rex bal
226 auto rex_itr = _rexbalance.find( voter_name.value );
227 if( rex_itr != _rexbalance.end() && rex_itr->rex_balance.amount > 0 ) {
228 new_staked += rex_itr->vote_stake.amount;
229 }
230 del_bandwidth_table del_tbl( get_self(), voter_name.value );
231
232 auto del_itr = del_tbl.begin();
233 while(del_itr != del_tbl.end()) {
234 new_staked += del_itr->net_weight.amount + del_itr->cpu_weight.amount;
235 del_itr++;
236 }
237
238 if( voter->staked != new_staked){
239 // check if staked and new_staked are different and only
240 _voters.modify( voter, same_payer, [&]( auto& av ) {
241 av.staked = new_staked;
242 });
243 }
244
245 update_votes(voter_name, voter->proxy, voter->producers, true);
246 } // voteupdate
247
248
249 void system_contract::update_votes( const name& voter_name, const name& proxy, const std::vector<name>& producers, bool voting ) {
250 //validate input
251 if ( proxy ) {
252 check( producers.size() == 0, "cannot vote for producers and proxy at same time" );
253 check( voter_name != proxy, "cannot proxy to self" );
254 } else {
255 check( producers.size() <= 30, "attempt to vote for too many producers" );
256 for( size_t i = 1; i < producers.size(); ++i ) {
257 check( producers[i-1] < producers[i], "producer votes must be unique and sorted" );
258 }
259 }
260
261 auto voter = _voters.find( voter_name.value );
262 check( voter != _voters.end(), "user must stake before they can vote" );
263 check( !proxy || !voter->is_proxy, "account registered as a proxy is not allowed to use a proxy" );
264
270 if( _gstate.thresh_activated_stake_time == time_point() && voter->last_vote_weight <= 0.0 ) {
271 _gstate.total_activated_stake += voter->staked;
272 if( _gstate.total_activated_stake >= min_activated_stake ) {
273 _gstate.thresh_activated_stake_time = current_time_point();
274 }
275 }
276
277 auto new_vote_weight = stake2vote( voter->staked );
278 if( voter->is_proxy ) {
279 new_vote_weight += voter->proxied_vote_weight;
280 }
281
282 std::map<name, std::pair<double, bool /*new*/> > producer_deltas;
283 if ( voter->last_vote_weight > 0 ) {
284 if( voter->proxy ) {
285 auto old_proxy = _voters.find( voter->proxy.value );
286 check( old_proxy != _voters.end(), "old proxy not found" ); //data corruption
287 _voters.modify( old_proxy, same_payer, [&]( auto& vp ) {
288 vp.proxied_vote_weight -= voter->last_vote_weight;
289 });
290 propagate_weight_change( *old_proxy );
291 } else {
292 for( const auto& p : voter->producers ) {
293 auto& d = producer_deltas[p];
294 d.first -= voter->last_vote_weight;
295 d.second = false;
296 }
297 }
298 }
299
300 if( proxy ) {
301 auto new_proxy = _voters.find( proxy.value );
302 check( new_proxy != _voters.end(), "invalid proxy specified" ); //if ( !voting ) { data corruption } else { wrong vote }
303 check( !voting || new_proxy->is_proxy, "proxy not found" );
304 if ( new_vote_weight >= 0 ) {
305 _voters.modify( new_proxy, same_payer, [&]( auto& vp ) {
306 vp.proxied_vote_weight += new_vote_weight;
307 });
308 propagate_weight_change( *new_proxy );
309 }
310 } else {
311 if( new_vote_weight >= 0 ) {
312 for( const auto& p : producers ) {
313 auto& d = producer_deltas[p];
314 d.first += new_vote_weight;
315 d.second = true;
316 }
317 }
318 }
319
320 const auto ct = current_time_point();
321 double delta_change_rate = 0.0;
322 double total_inactive_vpay_share = 0.0;
323 for( const auto& pd : producer_deltas ) {
324 auto pitr = _producers.find( pd.first.value );
325 if( pitr != _producers.end() ) {
326 if( voting && !pitr->active() && pd.second.second /* from new set */ ) {
327 check( false, ( "producer " + pitr->owner.to_string() + " is not currently registered" ).data() );
328 }
329 double init_total_votes = pitr->total_votes;
330 _producers.modify( pitr, same_payer, [&]( auto& p ) {
331 p.total_votes += pd.second.first;
332 if ( p.total_votes < 0 ) { // floating point arithmetics can give small negative numbers
333 p.total_votes = 0;
334 }
335 _gstate.total_producer_vote_weight += pd.second.first;
336 //check( p.total_votes >= 0, "something bad happened" );
337 });
338 auto prod2 = _producers2.find( pd.first.value );
339 if( prod2 != _producers2.end() ) {
340 const auto last_claim_plus_3days = pitr->last_claim_time + microseconds(3 * useconds_per_day);
341 bool crossed_threshold = (last_claim_plus_3days <= ct);
342 bool updated_after_threshold = (last_claim_plus_3days <= prod2->last_votepay_share_update);
343 // Note: updated_after_threshold implies cross_threshold
344
345 double new_votepay_share = update_producer_votepay_share( prod2,
346 ct,
347 updated_after_threshold ? 0.0 : init_total_votes,
348 crossed_threshold && !updated_after_threshold // only reset votepay_share once after threshold
349 );
350
351 if( !crossed_threshold ) {
352 delta_change_rate += pd.second.first;
353 } else if( !updated_after_threshold ) {
354 total_inactive_vpay_share += new_votepay_share;
355 delta_change_rate -= init_total_votes;
356 }
357 }
358 } else {
359 if( pd.second.second ) {
360 check( false, ( "producer " + pd.first.to_string() + " is not registered" ).data() );
361 }
362 }
363 }
364
365 update_total_votepay_share( ct, -total_inactive_vpay_share, delta_change_rate );
366
367 _voters.modify( voter, same_payer, [&]( auto& av ) {
368 av.last_vote_weight = new_vote_weight;
369 av.producers = producers;
370 av.proxy = proxy;
371 });
372 }
373
374 void system_contract::regproxy( const name& proxy, bool isproxy ) {
375 require_auth( proxy );
376
377 auto pitr = _voters.find( proxy.value );
378 if ( pitr != _voters.end() ) {
379 check( isproxy != pitr->is_proxy, "action has no effect" );
380 check( !isproxy || !pitr->proxy, "account that uses a proxy is not allowed to become a proxy" );
381 _voters.modify( pitr, same_payer, [&]( auto& p ) {
382 p.is_proxy = isproxy;
383 });
384 propagate_weight_change( *pitr );
385 } else {
386 _voters.emplace( proxy, [&]( auto& p ) {
387 p.owner = proxy;
388 p.is_proxy = isproxy;
389 });
390 }
391 }
392
393 void system_contract::propagate_weight_change( const voter_info& voter ) {
394 check( !voter.proxy || !voter.is_proxy, "account registered as a proxy is not allowed to use a proxy" );
395 double new_weight = stake2vote( voter.staked );
396 if ( voter.is_proxy ) {
397 new_weight += voter.proxied_vote_weight;
398 }
399
401 if ( fabs( new_weight - voter.last_vote_weight ) > 1 ) {
402 if ( voter.proxy ) {
403 auto& proxy = _voters.get( voter.proxy.value, "proxy not found" ); //data corruption
404 _voters.modify( proxy, same_payer, [&]( auto& p ) {
405 p.proxied_vote_weight += new_weight - voter.last_vote_weight;
406 }
407 );
408 propagate_weight_change( proxy );
409 } else {
410 auto delta = new_weight - voter.last_vote_weight;
411 const auto ct = current_time_point();
412 double delta_change_rate = 0;
413 double total_inactive_vpay_share = 0;
414 for ( auto acnt : voter.producers ) {
415 auto& prod = _producers.get( acnt.value, "producer not found" ); //data corruption
416 const double init_total_votes = prod.total_votes;
417 _producers.modify( prod, same_payer, [&]( auto& p ) {
418 p.total_votes += delta;
419 _gstate.total_producer_vote_weight += delta;
420 });
421 auto prod2 = _producers2.find( acnt.value );
422 if ( prod2 != _producers2.end() ) {
423 const auto last_claim_plus_3days = prod.last_claim_time + microseconds(3 * useconds_per_day);
424 bool crossed_threshold = (last_claim_plus_3days <= ct);
425 bool updated_after_threshold = (last_claim_plus_3days <= prod2->last_votepay_share_update);
426 // Note: updated_after_threshold implies cross_threshold
427
428 double new_votepay_share = update_producer_votepay_share( prod2,
429 ct,
430 updated_after_threshold ? 0.0 : init_total_votes,
431 crossed_threshold && !updated_after_threshold // only reset votepay_share once after threshold
432 );
433
434 if( !crossed_threshold ) {
435 delta_change_rate += delta;
436 } else if( !updated_after_threshold ) {
437 total_inactive_vpay_share += new_votepay_share;
438 delta_change_rate -= init_total_votes;
439 }
440 }
441 }
442
443 update_total_votepay_share( ct, -total_inactive_vpay_share, delta_change_rate );
444 }
445 }
446 _voters.modify( voter, same_payer, [&]( auto& v ) {
447 v.last_vote_weight = new_weight;
448 }
449 );
450 }
451
452}
const mie::Vuint & p
Definition bn.cpp:27
std::string name
contains only the public point of an elliptic curve key.
void regproducer2(const name &producer, const sysio::block_signing_authority &producer_authority, const std::string &url, uint16_t location)
Definition voting.cpp:86
void unregprod(const name &producer)
Definition voting.cpp:97
void voteproducer(const name &voter, const name &proxy, const std::vector< name > &producers)
Definition voting.cpp:202
void voteupdate(const name &voter_name)
Definition voting.cpp:217
void updaterex(const name &owner)
Definition rex.cpp:206
void regproxy(const name &proxy, bool isproxy)
Definition voting.cpp:374
void regproducer(const name &producer, const public_key &producer_key, const std::string &url, uint16_t location)
Definition voting.cpp:79
std::variant< block_signing_authority_v0 > block_signing_authority
sysio::multi_index< "delband"_n, delegated_bandwidth > del_bandwidth_table
double stake2vote(int64_t staked)
Definition voting.cpp:145
sysio::block_signing_authority convert_to_block_signing_authority(const sysio::public_key &producer_key)
string url
Definition main.cpp:166
schedule config_dir_name data_dir_name p2p_port http_port file_size name name keys peers producers(dont_start)) FC_REFLECT(testnet_def
unsigned short uint16_t
Definition stdint.h:125
signed __int64 int64_t
Definition stdint.h:135
Immutable except for fc::from_variant.
Definition name.hpp:43
int64_t total_activated_stake
all blocks which have been produced but not paid
block_timestamp last_producer_schedule_update
void fabs()
CK_ULONG d