Merge pull request #193 from peerplays-network/BLOCKBACK-155

BLOCKBACK- 155: allow token holder to vote in each subperiod without unvote
This commit is contained in:
pbattu123 2019-10-25 10:23:24 -03:00 committed by GitHub
commit 46dab2545c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 123 additions and 34 deletions

View file

@ -2039,6 +2039,8 @@ graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type
{
gpos_info result;
result.vesting_factor = _db.calculate_vesting_factor(account(_db));
result.current_subperiod = _db.get_gpos_current_subperiod();
result.last_voted_time = account(_db).statistics(_db).last_vote_time;
const auto& dividend_data = asset_id_type()(_db).dividend_data(_db);
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(_db);

View file

@ -118,6 +118,8 @@ struct gpos_info {
double vesting_factor;
asset award;
share_type total_amount;
uint32_t current_subperiod;
fc::time_point_sec last_voted_time;
};
/**
@ -672,7 +674,7 @@ FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) );
FC_REFLECT( graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) );
FC_REFLECT( graphene::app::market_volume, (base)(quote)(base_volume)(quote_volume) );
FC_REFLECT( graphene::app::market_trade, (date)(price)(amount)(value) );
FC_REFLECT( graphene::app::gpos_info, (vesting_factor)(award)(total_amount) );
FC_REFLECT( graphene::app::gpos_info, (vesting_factor)(award)(total_amount)(current_subperiod)(last_voted_time) );
FC_API(graphene::app::database_api,

View file

@ -284,8 +284,10 @@ void_result account_update_evaluator::do_apply( const account_update_operation&
{
d.modify( acnt->statistics( d ), [&]( account_statistics_object& aso )
{
fc::optional< bool > flag = o.extensions.value.update_last_voting_time;
if((o.new_options->votes != acnt->options.votes ||
o.new_options->voting_account != acnt->options.voting_account))
o.new_options->voting_account != acnt->options.voting_account) ||
flag)
aso.last_vote_time = d.head_block_time();
} );
}

View file

@ -725,6 +725,37 @@ void deprecate_annual_members( database& db )
return;
}
uint32_t database::get_gpos_current_subperiod()
{
fc::time_point_sec last_date_voted;
const auto &gpo = this->get_global_properties();
const auto vesting_period = gpo.parameters.gpos_period();
const auto vesting_subperiod = gpo.parameters.gpos_subperiod();
const auto period_start = fc::time_point_sec(gpo.parameters.gpos_period_start());
// variables needed
const fc::time_point_sec period_end = period_start + vesting_period;
const auto number_of_subperiods = vesting_period / vesting_subperiod;
const auto now = this->head_block_time();
auto seconds_since_period_start = now.sec_since_epoch() - period_start.sec_since_epoch();
FC_ASSERT(period_start <= now && now <= period_end);
// get in what sub period we are
uint32_t current_subperiod = 0;
std::list<uint32_t> period_list(number_of_subperiods);
std::iota(period_list.begin(), period_list.end(), 1);
std::for_each(period_list.begin(), period_list.end(),[&](uint32_t period) {
if(seconds_since_period_start >= vesting_subperiod * (period - 1) &&
seconds_since_period_start < vesting_subperiod * period)
current_subperiod = period;
});
return current_subperiod;
}
double database::calculate_vesting_factor(const account_object& stake_account)
{
fc::time_point_sec last_date_voted;
@ -742,25 +773,12 @@ double database::calculate_vesting_factor(const account_object& stake_account)
const auto period_start = fc::time_point_sec(gpo.parameters.gpos_period_start());
// variables needed
const fc::time_point_sec period_end = period_start + vesting_period;
const auto number_of_subperiods = vesting_period / vesting_subperiod;
const auto now = this->head_block_time();
double vesting_factor;
auto seconds_since_period_start = now.sec_since_epoch() - period_start.sec_since_epoch();
FC_ASSERT(period_start <= now && now <= period_end);
// get in what sub period we are
uint32_t current_subperiod = 0;
std::list<uint32_t> period_list(number_of_subperiods);
std::iota(period_list.begin(), period_list.end(), 1);
std::for_each(period_list.begin(), period_list.end(),[&](uint32_t period) {
if(seconds_since_period_start >= vesting_subperiod * (period - 1) &&
seconds_since_period_start < vesting_subperiod * period)
current_subperiod = period;
});
// get in what sub period we are
uint32_t current_subperiod = get_gpos_current_subperiod();
if(current_subperiod == 0 || current_subperiod > number_of_subperiods) return 0;
if(last_date_voted < period_start) return 0;
@ -1562,6 +1580,12 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
p.pending_parameters->extensions.value.permitted_betting_odds_increments = p.parameters.extensions.value.permitted_betting_odds_increments;
if( !p.pending_parameters->extensions.value.live_betting_delay_time.valid() )
p.pending_parameters->extensions.value.live_betting_delay_time = p.parameters.extensions.value.live_betting_delay_time;
if( !p.pending_parameters->extensions.value.gpos_period.valid() )
p.pending_parameters->extensions.value.gpos_period = p.parameters.extensions.value.gpos_period;
if( !p.pending_parameters->extensions.value.gpos_subperiod.valid() )
p.pending_parameters->extensions.value.gpos_subperiod = p.parameters.extensions.value.gpos_subperiod;
if( !p.pending_parameters->extensions.value.gpos_vesting_lockin_period.valid() )
p.pending_parameters->extensions.value.gpos_vesting_lockin_period = p.parameters.extensions.value.gpos_vesting_lockin_period;
p.parameters = std::move(*p.pending_parameters);
p.pending_parameters.reset();
}

View file

@ -500,6 +500,7 @@ namespace graphene { namespace chain {
void update_worker_votes();
public:
double calculate_vesting_factor(const account_object& stake_account);
uint32_t get_gpos_current_subperiod();
template<class... Types>

View file

@ -140,6 +140,7 @@ namespace graphene { namespace chain {
optional< void_t > null_ext;
optional< special_authority > owner_special_authority;
optional< special_authority > active_special_authority;
optional< bool > update_last_voting_time = false;
};
struct fee_parameters_type

View file

@ -2124,11 +2124,22 @@ public:
fc::optional<committee_member_object> committee_member_obj = _remote_db->get_committee_member_by_account(committee_member_owner_account_id);
if (!committee_member_obj)
FC_THROW("Account ${committee_member} is not registered as a committee_member", ("committee_member", committee_member));
bool update_vote_time = false;
if (approve)
{
account_id_type stake_account = get_account_id(voting_account);
const auto gpos_info = _remote_db->get_gpos_info(stake_account);
const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod();
const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start());
const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod;
auto insert_result = voting_account_object.options.votes.insert(committee_member_obj->vote_id);
if (!insert_result.second)
FC_THROW("Account ${account} was already voting for committee_member ${committee_member}", ("account", voting_account)("committee_member", committee_member));
if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time))
FC_THROW("Account ${account} was already voting for committee_member ${committee_member} in the current GPOS sub-period", ("account", voting_account)("committee_member", committee_member));
else
update_vote_time = true; //Allow user to vote in each sub-period(Update voting time, which is reference in calculating VF)
}
else
{
@ -2139,6 +2150,7 @@ public:
account_update_operation account_update_op;
account_update_op.account = voting_account_object.id;
account_update_op.new_options = voting_account_object.options;
account_update_op.extensions.value.update_last_voting_time = update_vote_time;
signed_transaction tx;
tx.operations.push_back( account_update_op );
@ -2162,12 +2174,26 @@ public:
account_object voting_account_object = get_account(voting_account);
account_id_type witness_owner_account_id = get_account_id(witness);
fc::optional<witness_object> witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id);
if (!witness_obj)
FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness));
bool update_vote_time = false;
if (approve)
{
account_id_type stake_account = get_account_id(voting_account);
const auto gpos_info = _remote_db->get_gpos_info(stake_account);
const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod();
const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start());
const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod;
auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id);
if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time))
FC_THROW("Account ${account} was already voting for witness ${witness} in the current GPOS sub-period", ("account", voting_account)("witness", witness));
else
update_vote_time = true; //Allow user to vote in each sub-period(Update voting time, which is reference in calculating VF)
if (!insert_result.second)
FC_THROW("Account ${account} has already voted for witness ${witness}", ("account", voting_account)("witness", witness));
}
@ -2177,9 +2203,11 @@ public:
if (!votes_removed)
FC_THROW("Account ${account} has not voted for witness ${witness}", ("account", voting_account)("witness", witness));
}
account_update_operation account_update_op;
account_update_op.account = voting_account_object.id;
account_update_op.new_options = voting_account_object.options;
account_update_op.extensions.value.update_last_voting_time = update_vote_time;
signed_transaction tx;
tx.operations.push_back( account_update_op );

View file

@ -399,53 +399,77 @@ BOOST_AUTO_TEST_CASE( voting )
// vote for witness1
vote_for(alice_id, witness1.vote_id, alice_private_key);
vote_for(bob_id, witness2.vote_id, bob_private_key);
// go to maint
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
// vote is the same as amount in the first subperiod since voting
witness1 = witness_id_type(1)(db);
witness2 = witness_id_type(2)(db);
BOOST_CHECK_EQUAL(witness1.total_votes, 100);
BOOST_CHECK_EQUAL(witness2.total_votes, 100);
advance_x_maint(10);
//vote bob tot witness2 in each subperiod and verify votes
vote_for(bob_id, witness2.vote_id, bob_private_key);
// go to maint
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
// vote decay as time pass
witness1 = witness_id_type(1)(db);
witness2 = witness_id_type(2)(db);
BOOST_CHECK_EQUAL(witness1.total_votes, 83);
BOOST_CHECK_EQUAL(witness2.total_votes, 100);
advance_x_maint(10);
vote_for(bob_id, witness2.vote_id, bob_private_key);
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
// decay more
witness1 = witness_id_type(1)(db);
witness2 = witness_id_type(2)(db);
BOOST_CHECK_EQUAL(witness1.total_votes, 66);
BOOST_CHECK_EQUAL(witness2.total_votes, 100);
advance_x_maint(10);
// more
vote_for(bob_id, witness2.vote_id, bob_private_key);
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
// decay more
witness1 = witness_id_type(1)(db);
witness2 = witness_id_type(2)(db);
BOOST_CHECK_EQUAL(witness1.total_votes, 50);
BOOST_CHECK_EQUAL(witness2.total_votes, 100);
advance_x_maint(10);
// more
vote_for(bob_id, witness2.vote_id, bob_private_key);
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
// decay more
witness1 = witness_id_type(1)(db);
witness2 = witness_id_type(2)(db);
BOOST_CHECK_EQUAL(witness1.total_votes, 33);
BOOST_CHECK_EQUAL(witness2.total_votes, 100);
advance_x_maint(10);
// more
vote_for(bob_id, witness2.vote_id, bob_private_key);
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
// decay more
witness1 = witness_id_type(1)(db);
witness2 = witness_id_type(2)(db);
BOOST_CHECK_EQUAL(witness1.total_votes, 16);
BOOST_CHECK_EQUAL(witness2.total_votes, 100);
// we are still in gpos period 1
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch());
advance_x_maint(10);
// until 0
witness1 = witness_id_type(1)(db);
BOOST_CHECK_EQUAL(witness1.total_votes, 0);
advance_x_maint(5);
// a new GPOS period is in but vote from user is before the start so his voting power is 0
now = db.head_block_time();
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch());
@ -453,7 +477,9 @@ BOOST_AUTO_TEST_CASE( voting )
generate_block();
witness1 = witness_id_type(1)(db);
witness2 = witness_id_type(2)(db);
BOOST_CHECK_EQUAL(witness1.total_votes, 0);
BOOST_CHECK_EQUAL(witness2.total_votes, 0);
// we are in the second GPOS period, at subperiod 2, lets vote here
vote_for(bob_id, witness2.vote_id, bob_private_key);
@ -476,13 +502,16 @@ BOOST_AUTO_TEST_CASE( voting )
BOOST_CHECK_EQUAL(witness1.total_votes, 0);
BOOST_CHECK_EQUAL(witness2.total_votes, 83);
vote_for(bob_id, witness2.vote_id, bob_private_key);
generate_block();
advance_x_maint(10);
witness1 = witness_id_type(1)(db);
witness2 = witness_id_type(2)(db);
BOOST_CHECK_EQUAL(witness1.total_votes, 0);
BOOST_CHECK_EQUAL(witness2.total_votes, 66);
BOOST_CHECK_EQUAL(witness2.total_votes, 83);
// alice votes again, now for witness 2, her vote worth 100 now
vote_for(alice_id, witness2.vote_id, alice_private_key);
@ -492,7 +521,7 @@ BOOST_AUTO_TEST_CASE( voting )
witness2 = witness_id_type(2)(db);
BOOST_CHECK_EQUAL(witness1.total_votes, 100);
BOOST_CHECK_EQUAL(witness2.total_votes, 166);
BOOST_CHECK_EQUAL(witness2.total_votes, 183);
}
catch (fc::exception &e) {