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>
21 using sysio::const_mem_fun;
22 using sysio::current_time_point;
23 using sysio::indexed_by;
25 using sysio::singleton;
28 auto prod = _producers.find( producer.value );
29 const auto ct = current_time_point();
33 std::visit( [&](
auto&& auth ) {
34 if( auth.keys.size() == 1 ) {
36 producer_key = auth.keys[0].key;
38 }, producer_authority );
40 if ( prod != _producers.end() ) {
41 _producers.modify( prod, producer, [&]( producer_info& info ){
42 info.producer_key = producer_key;
43 info.is_active =
true;
45 info.location = location;
46 info.producer_authority.emplace( producer_authority );
48 info.last_claim_time = ct;
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;
57 update_total_votepay_share( ct, 0.0, prod->total_votes );
61 _producers.emplace( producer, [&]( producer_info& info ){
62 info.owner = producer;
64 info.producer_key = producer_key;
65 info.is_active =
true;
67 info.location = location;
68 info.last_claim_time = ct;
69 info.producer_authority.emplace( producer_authority );
71 _producers2.emplace( producer, [&]( producer_info2& info ){
72 info.owner = producer;
73 info.last_votepay_share_update = ct;
80 require_auth( producer );
81 check(
url.size() < 512,
"url too long" );
87 require_auth( producer );
88 check(
url.size() < 512,
"url too long" );
90 std::visit( [&](
auto&& auth ) {
91 check( auth.is_valid(),
"invalid producer authority" );
92 }, producer_authority );
94 register_producer( producer, producer_authority,
url, location );
98 require_auth( producer );
100 const auto& prod = _producers.get( producer.value,
"producer not found" );
101 _producers.modify( prod, same_payer, [&](
producer_info& info ){
106 void system_contract::update_elected_producers(
const block_timestamp& block_time ) {
109 auto idx = _producers.get_index<
"prototalvote"_n>();
111 using value_type = std::pair<sysio::producer_authority, uint16_t>;
112 std::vector< value_type > top_producers;
113 top_producers.reserve(21);
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()
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;
134 std::vector<sysio::producer_authority>
producers;
137 for(
auto& item : top_producers )
138 producers.push_back( std::move(item.first) );
140 if( set_proposed_producers(
producers ) >= 0 ) {
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 );
151 double system_contract::update_total_votepay_share(
const time_point& ct,
152 double additional_shares_delta,
153 double shares_rate_delta )
155 double delta_total_votepay_share = 0.0;
161 delta_total_votepay_share += additional_shares_delta;
179 double system_contract::update_producer_votepay_share(
const producers_table2::const_iterator& prod_itr,
180 const time_point& ct,
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 );
189 double new_votepay_share = prod_itr->votepay_share + delta_votepay_share;
190 _producers2.modify( prod_itr, same_payer, [&](
auto&
p) {
192 p.votepay_share = 0.0;
194 p.votepay_share = new_votepay_share;
196 p.last_votepay_share_update = ct;
199 return new_votepay_share;
203 if ( voter_name ==
"b1"_n ) {
204 require_auth(
"sysio"_n);
206 require_auth( voter_name );
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" );
218 auto voter = _voters.find( voter_name.value );
219 check( voter != _voters.end(),
"no voter found" );
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;
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;
238 if( voter->staked != new_staked){
240 _voters.modify( voter, same_payer, [&](
auto& av ) {
241 av.staked = new_staked;
245 update_votes(voter_name, voter->proxy, voter->producers,
true);
249 void system_contract::update_votes(
const name& voter_name,
const name& proxy,
const std::vector<name>&
producers,
bool voting ) {
252 check(
producers.size() == 0,
"cannot vote for producers and proxy at same time" );
253 check( voter_name != proxy,
"cannot proxy to self" );
255 check(
producers.size() <= 30,
"attempt to vote for too many producers" );
256 for(
size_t i = 1; i <
producers.size(); ++i ) {
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" );
277 auto new_vote_weight =
stake2vote( voter->staked );
278 if( voter->is_proxy ) {
279 new_vote_weight += voter->proxied_vote_weight;
282 std::map<
name, std::pair<double,
bool > > producer_deltas;
283 if ( voter->last_vote_weight > 0 ) {
285 auto old_proxy = _voters.find( voter->proxy.value );
286 check( old_proxy != _voters.end(),
"old proxy not found" );
287 _voters.modify( old_proxy, same_payer, [&](
auto& vp ) {
288 vp.proxied_vote_weight -= voter->last_vote_weight;
290 propagate_weight_change( *old_proxy );
292 for(
const auto&
p : voter->producers ) {
293 auto&
d = producer_deltas[
p];
294 d.first -= voter->last_vote_weight;
301 auto new_proxy = _voters.find( proxy.value );
302 check( new_proxy != _voters.end(),
"invalid proxy specified" );
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;
308 propagate_weight_change( *new_proxy );
311 if( new_vote_weight >= 0 ) {
313 auto&
d = producer_deltas[
p];
314 d.first += new_vote_weight;
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 ) {
327 check(
false, (
"producer " + pitr->owner.to_string() +
" is not currently registered" ).data() );
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 ) {
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);
345 double new_votepay_share = update_producer_votepay_share( prod2,
347 updated_after_threshold ? 0.0 : init_total_votes,
348 crossed_threshold && !updated_after_threshold
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;
359 if( pd.second.second ) {
360 check(
false, (
"producer " + pd.first.to_string() +
" is not registered" ).data() );
365 update_total_votepay_share( ct, -total_inactive_vpay_share, delta_change_rate );
367 _voters.modify( voter, same_payer, [&](
auto& av ) {
368 av.last_vote_weight = new_vote_weight;
375 require_auth( proxy );
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;
384 propagate_weight_change( *pitr );
386 _voters.emplace( proxy, [&](
auto&
p ) {
388 p.is_proxy = isproxy;
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;
401 if (
fabs( new_weight - voter.last_vote_weight ) > 1 ) {
403 auto& proxy = _voters.get( voter.proxy.value,
"proxy not found" );
404 _voters.modify( proxy, same_payer, [&](
auto&
p ) {
405 p.proxied_vote_weight += new_weight - voter.last_vote_weight;
408 propagate_weight_change( proxy );
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" );
416 const double init_total_votes = prod.total_votes;
417 _producers.modify( prod, same_payer, [&](
auto&
p ) {
418 p.total_votes += delta;
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);
428 double new_votepay_share = update_producer_votepay_share( prod2,
430 updated_after_threshold ? 0.0 : init_total_votes,
431 crossed_threshold && !updated_after_threshold
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;
443 update_total_votepay_share( ct, -total_inactive_vpay_share, delta_change_rate );
446 _voters.modify( voter, same_payer, [&](
auto& v ) {
447 v.last_vote_weight = new_weight;
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)
void unregprod(const name &producer)
void voteproducer(const name &voter, const name &proxy, const std::vector< name > &producers)
void voteupdate(const name &voter_name)
void updaterex(const name &owner)
void regproxy(const name &proxy, bool isproxy)
void regproducer(const name &producer, const public_key &producer_key, const std::string &url, uint16_t location)
std::variant< block_signing_authority_v0 > block_signing_authority
sysio::multi_index< "delband"_n, delegated_bandwidth > del_bandwidth_table
double stake2vote(int64_t staked)
sysio::block_signing_authority convert_to_block_signing_authority(const sysio::public_key &producer_key)
sysio::time_point time_point
schedule config_dir_name data_dir_name p2p_port http_port file_size name name keys peers producers(dont_start)) FC_REFLECT(testnet_def
Immutable except for fc::from_variant.
double total_producer_votepay_share
time_point last_vpay_state_update
double total_vpay_share_change_rate
int64_t total_activated_stake
all blocks which have been produced but not paid
uint16_t last_producer_schedule_size
time_point thresh_activated_stake_time
block_timestamp last_producer_schedule_update
double total_producer_vote_weight