Merge branch 'qa_gpos_18.04' into develop_gpos
This commit is contained in:
commit
f831f9527f
13 changed files with 505 additions and 72 deletions
|
|
@ -9,6 +9,8 @@ RUN \
|
|||
apt-get update -y && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
autoconf \
|
||||
gcc-5 \
|
||||
g++-5 \
|
||||
bash \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
|
|
@ -50,6 +52,7 @@ WORKDIR /peerplays-core
|
|||
# Compile Peerplays
|
||||
RUN \
|
||||
BOOST_ROOT=$HOME/boost_1_67_0 && \
|
||||
export CC=gcc-5 ; export CXX=g++-5\
|
||||
git submodule update --init --recursive && \
|
||||
mkdir build && \
|
||||
mkdir build/release && \
|
||||
|
|
|
|||
|
|
@ -2148,6 +2148,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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -700,7 +702,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,
|
||||
|
|
|
|||
|
|
@ -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.valid() && *flag))
|
||||
aso.last_vote_time = d.head_block_time();
|
||||
} );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -749,13 +749,10 @@ void deprecate_annual_members( database& db )
|
|||
return;
|
||||
}
|
||||
|
||||
double database::calculate_vesting_factor(const account_object& stake_account)
|
||||
uint32_t database::get_gpos_current_subperiod()
|
||||
{
|
||||
// get last time voted form stats
|
||||
const auto &stats = stake_account.statistics(*this);
|
||||
fc::time_point_sec last_date_voted = stats.last_vote_time;
|
||||
fc::time_point_sec last_date_voted;
|
||||
|
||||
// get global data related to gpos
|
||||
const auto &gpo = this->get_global_properties();
|
||||
const auto vesting_period = gpo.parameters.gpos_period();
|
||||
const auto vesting_subperiod = gpo.parameters.gpos_subperiod();
|
||||
|
|
@ -765,7 +762,6 @@ double database::calculate_vesting_factor(const account_object& stake_account)
|
|||
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);
|
||||
|
|
@ -781,7 +777,43 @@ double database::calculate_vesting_factor(const account_object& stake_account)
|
|||
current_subperiod = period;
|
||||
});
|
||||
|
||||
return current_subperiod;
|
||||
}
|
||||
|
||||
double database::calculate_vesting_factor(const account_object& stake_account)
|
||||
{
|
||||
fc::time_point_sec last_date_voted;
|
||||
// get last time voted form account stats
|
||||
// check last_vote_time of proxy voting account if proxy is set
|
||||
if (stake_account.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT)
|
||||
last_date_voted = stake_account.statistics(*this).last_vote_time;
|
||||
else
|
||||
last_date_voted = stake_account.options.voting_account(*this).statistics(*this).last_vote_time;
|
||||
|
||||
// get global data related to gpos
|
||||
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 auto number_of_subperiods = vesting_period / vesting_subperiod;
|
||||
double vesting_factor;
|
||||
|
||||
// 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;
|
||||
|
||||
// On starting new vesting period, all votes become zero until someone votes, To avoid a situation of zero votes,
|
||||
// changes were done to roll in GPOS rules, the vesting factor will be 1 for whoever votes in 6th sub-period of last vesting period
|
||||
// BLOCKBACK-174 fix
|
||||
if(current_subperiod == 1 && this->head_block_time() >= HARDFORK_GPOS_TIME + vesting_period) //Applicable only from 2nd vesting period
|
||||
{
|
||||
if(last_date_voted > period_start - vesting_subperiod)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(last_date_voted < period_start) return 0;
|
||||
|
||||
double numerator = number_of_subperiods;
|
||||
|
|
@ -853,7 +885,7 @@ void rolling_period_start(database& db)
|
|||
auto vesting_period = db.get_global_properties().parameters.gpos_period();
|
||||
|
||||
auto now = db.head_block_time();
|
||||
if(now.sec_since_epoch() > (period_start + vesting_period))
|
||||
if(now.sec_since_epoch() >= (period_start + vesting_period))
|
||||
{
|
||||
// roll
|
||||
db.modify(db.get_global_properties(), [now](global_property_object& p) {
|
||||
|
|
@ -1114,8 +1146,8 @@ void schedule_pending_dividend_balances(database& db,
|
|||
dlog("Crediting committee_account with ${amount}",
|
||||
("amount", asset(full_shares_to_credit - shares_to_credit, payout_asset_type)));
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
-(full_shares_to_credit - shares_to_credit));
|
||||
db.adjust_balance(account_id_type(0), full_shares_to_credit - shares_to_credit);
|
||||
-asset(full_shares_to_credit - shares_to_credit, payout_asset_type));
|
||||
db.adjust_balance(account_id_type(0), asset(full_shares_to_credit - shares_to_credit, payout_asset_type));
|
||||
}
|
||||
|
||||
remaining_amount_to_distribute = credit_account(db,
|
||||
|
|
@ -1415,10 +1447,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
distribute_fba_balances(*this);
|
||||
create_buyback_orders(*this);
|
||||
|
||||
rolling_period_start(*this);
|
||||
|
||||
process_dividend_assets(*this);
|
||||
|
||||
rolling_period_start(*this);
|
||||
|
||||
struct vote_tally_helper {
|
||||
database& d;
|
||||
const global_property_object& props;
|
||||
|
|
@ -1587,6 +1619,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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// GPOS HARDFORK Friday, March 15, 2019 11:57:28 PM
|
||||
// GPOS HARDFORK Tuesday, October 22, 2019 05:00:00 AM GMT
|
||||
#ifndef HARDFORK_GPOS_TIME
|
||||
#define HARDFORK_GPOS_TIME (fc::time_point_sec( 1552694248 ))
|
||||
#define HARDFORK_GPOS_TIME (fc::time_point_sec( 1571720400 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -525,6 +525,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
public:
|
||||
double calculate_vesting_factor(const account_object& stake_account);
|
||||
uint32_t get_gpos_current_subperiod();
|
||||
|
||||
template<class... Types>
|
||||
void perform_account_maintenance(std::tuple<Types...> helpers);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -295,7 +296,7 @@ FC_REFLECT( graphene::chain::account_create_operation,
|
|||
(name)(owner)(active)(options)(extensions)
|
||||
)
|
||||
|
||||
FC_REFLECT(graphene::chain::account_update_operation::ext, (null_ext)(owner_special_authority)(active_special_authority) )
|
||||
FC_REFLECT(graphene::chain::account_update_operation::ext, (null_ext)(owner_special_authority)(active_special_authority)(update_last_voting_time) )
|
||||
FC_REFLECT( graphene::chain::account_update_operation,
|
||||
(fee)(account)(owner)(active)(new_options)(extensions)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,16 @@ namespace graphene { namespace chain {
|
|||
|
||||
enum class vesting_balance_type { normal, gpos };
|
||||
|
||||
inline std::string get_vesting_balance_type(vesting_balance_type type) {
|
||||
switch (type) {
|
||||
case vesting_balance_type::normal:
|
||||
return "NORMAL";
|
||||
case vesting_balance_type::gpos:
|
||||
default:
|
||||
return "GPOS";
|
||||
}
|
||||
}
|
||||
|
||||
struct linear_vesting_policy_initializer
|
||||
{
|
||||
/** while vesting begins on begin_timestamp, none may be claimed before vesting_cliff_seconds have passed */
|
||||
|
|
@ -123,4 +133,3 @@ FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vestin
|
|||
FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos) )
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance
|
|||
if(d.head_block_time() < HARDFORK_GPOS_TIME) // Todo: can be removed after gpos hf time pass
|
||||
FC_ASSERT( op.balance_type == vesting_balance_type::normal);
|
||||
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
|
@ -146,7 +145,8 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan
|
|||
|
||||
const vesting_balance_object& vbo = op.vesting_balance( d );
|
||||
FC_ASSERT( op.owner == vbo.owner, "", ("op.owner", op.owner)("vbo.owner", vbo.owner) );
|
||||
FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "", ("now", now)("op", op)("vbo", vbo) );
|
||||
FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "${balance_type} Vested Balance cannot be withdrawn during the locking period",
|
||||
("balance_type", get_vesting_balance_type(vbo.balance_type))("now", now)("op", op)("vbo", vbo) );
|
||||
assert( op.amount <= vbo.balance ); // is_withdraw_allowed should fail before this check is reached
|
||||
|
||||
/* const account_object& owner_account = op.owner( d ); */
|
||||
|
|
|
|||
|
|
@ -1300,6 +1300,12 @@ class wallet_api
|
|||
*/
|
||||
witness_object get_witness(string owner_account);
|
||||
|
||||
/** Returns true if the account is witness, false otherwise
|
||||
* @param owner_account the name or id of the witness account owner, or the id of the witness
|
||||
* @returns true if account is witness, false otherwise
|
||||
*/
|
||||
bool is_witness(string owner_account);
|
||||
|
||||
/** Returns information about the given committee_member.
|
||||
* @param owner_account the name or id of the committee_member account owner, or the id of the committee_member
|
||||
* @returns the information about the committee_member stored in the block chain
|
||||
|
|
@ -2041,6 +2047,7 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(whitelist_account)
|
||||
(create_committee_member)
|
||||
(get_witness)
|
||||
(is_witness)
|
||||
(get_committee_member)
|
||||
(list_witnesses)
|
||||
(list_committee_members)
|
||||
|
|
|
|||
|
|
@ -1779,6 +1779,42 @@ public:
|
|||
FC_CAPTURE_AND_RETHROW( (owner_account) )
|
||||
}
|
||||
|
||||
bool is_witness(string owner_account)
|
||||
{
|
||||
try
|
||||
{
|
||||
fc::optional<witness_id_type> witness_id = maybe_id<witness_id_type>(owner_account);
|
||||
if (witness_id)
|
||||
{
|
||||
std::vector<witness_id_type> ids_to_get;
|
||||
ids_to_get.push_back(*witness_id);
|
||||
std::vector<fc::optional<witness_object>> witness_objects = _remote_db->get_witnesses(ids_to_get);
|
||||
if (witness_objects.front())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// then maybe it's the owner account
|
||||
try
|
||||
{
|
||||
account_id_type owner_account_id = get_account_id(owner_account);
|
||||
fc::optional<witness_object> witness = _remote_db->get_witness_by_account(owner_account_id);
|
||||
if (witness)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
catch (const fc::exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW( (owner_account) )
|
||||
}
|
||||
|
||||
committee_member_object get_committee_member(string owner_account)
|
||||
{
|
||||
try
|
||||
|
|
@ -2027,9 +2063,14 @@ public:
|
|||
fc::optional<vesting_balance_id_type> vbid = maybe_id<vesting_balance_id_type>(witness_name);
|
||||
if( !vbid )
|
||||
{
|
||||
witness_object wit = get_witness( witness_name );
|
||||
FC_ASSERT( wit.pay_vb );
|
||||
vbid = wit.pay_vb;
|
||||
if (is_witness(witness_name))
|
||||
{
|
||||
witness_object wit = get_witness( witness_name );
|
||||
FC_ASSERT( wit.pay_vb, "Account ${account} has no core Token ${TOKEN} vested and thus its not allowed to withdraw.", ("account", witness_name)("TOKEN", GRAPHENE_SYMBOL));
|
||||
vbid = wit.pay_vb;
|
||||
}
|
||||
else
|
||||
FC_THROW("Account ${account} has no core Token ${TOKEN} vested and thus its not allowed to withdraw.", ("account", witness_name)("TOKEN", GRAPHENE_SYMBOL));
|
||||
}
|
||||
|
||||
vesting_balance_object vbo = get_object< vesting_balance_object >( *vbid );
|
||||
|
|
@ -2069,12 +2110,8 @@ public:
|
|||
acct_id = get_account( account_name ).id;
|
||||
|
||||
vbos = _remote_db->get_vesting_balances( *acct_id );
|
||||
if( vbos.size() == 0 )
|
||||
{
|
||||
witness_object wit = get_witness( account_name );
|
||||
FC_ASSERT( wit.pay_vb );
|
||||
vbid = wit.pay_vb;
|
||||
}
|
||||
if( vbos.size() == 0 )
|
||||
FC_THROW("Account ${account} has no core TOKEN vested and thus its not allowed to withdraw.", ("account", account_name));
|
||||
}
|
||||
|
||||
//whether it is a witness or user, keep it in a container and iterate over to process all vesting balances and types
|
||||
|
|
@ -2138,18 +2175,29 @@ public:
|
|||
|
||||
vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;});
|
||||
if( vbo_info.size() == 0 || vbo_iter == vbo_info.end())
|
||||
FC_THROW("Account *** ${account} *** have insufficient or 0 vested balance(GPOS) to vote", ("account", voting_account));
|
||||
FC_THROW("Account ${account} has no core Token ${TOKEN} vested and thus she will not be allowed to vote for the committee member", ("account", voting_account)("TOKEN", GRAPHENE_SYMBOL));
|
||||
|
||||
account_object voting_account_object = get_account(voting_account);
|
||||
account_id_type committee_member_owner_account_id = get_account_id(committee_member);
|
||||
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
|
||||
{
|
||||
|
|
@ -2160,6 +2208,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 );
|
||||
|
|
@ -2179,28 +2228,41 @@ public:
|
|||
|
||||
vbo_iter = std::find_if(vbo_info.begin(), vbo_info.end(), [](vesting_balance_object_with_info const& obj){return obj.balance_type == vesting_balance_type::gpos;});
|
||||
if( vbo_info.size() == 0 || vbo_iter == vbo_info.end())
|
||||
FC_THROW("Account *** ${account} *** have insufficient or 0 vested balance(GPOS) to vote", ("account", voting_account));
|
||||
FC_THROW("Account ${account} has no core Token ${TOKEN} vested and thus she will not be allowed to vote for the witness", ("account", voting_account)("TOKEN", GRAPHENE_SYMBOL));
|
||||
|
||||
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)
|
||||
FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness));
|
||||
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)
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id);
|
||||
if (!votes_removed)
|
||||
FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness));
|
||||
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 );
|
||||
|
|
@ -4213,6 +4275,11 @@ witness_object wallet_api::get_witness(string owner_account)
|
|||
return my->get_witness(owner_account);
|
||||
}
|
||||
|
||||
bool wallet_api::is_witness(string owner_account)
|
||||
{
|
||||
return my->is_witness(owner_account);
|
||||
}
|
||||
|
||||
committee_member_object wallet_api::get_committee_member(string owner_account)
|
||||
{
|
||||
return my->get_committee_member(owner_account);
|
||||
|
|
|
|||
|
|
@ -95,12 +95,22 @@ struct gpos_fixture: database_fixture
|
|||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), vesting_subperiod);
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), period_start.sec_since_epoch());
|
||||
}
|
||||
|
||||
void update_maintenance_interval(uint32_t new_interval)
|
||||
{
|
||||
db.modify(db.get_global_properties(), [new_interval](global_property_object& p) {
|
||||
p.parameters.maintenance_interval = new_interval;
|
||||
});
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, new_interval);
|
||||
}
|
||||
|
||||
void vote_for(const account_id_type account_id, const vote_id_type vote_for, const fc::ecc::private_key& key)
|
||||
{
|
||||
account_update_operation op;
|
||||
op.account = account_id;
|
||||
op.new_options = account_id(db).options;
|
||||
op.new_options->votes.insert(vote_for);
|
||||
op.extensions.value.update_last_voting_time = true;
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
trx.validate();
|
||||
|
|
@ -339,11 +349,175 @@ BOOST_AUTO_TEST_CASE( dividends )
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( gpos_basic_dividend_distribution_to_core_asset )
|
||||
{
|
||||
using namespace graphene;
|
||||
ACTORS((alice)(bob)(carol)(dave));
|
||||
try {
|
||||
const auto& core = asset_id_type()(db);
|
||||
BOOST_TEST_MESSAGE("Creating test asset");
|
||||
{
|
||||
asset_create_operation creator;
|
||||
creator.issuer = account_id_type();
|
||||
creator.fee = asset();
|
||||
creator.symbol = "TESTB";
|
||||
creator.common_options.max_supply = 100000000;
|
||||
creator.precision = 2;
|
||||
creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/
|
||||
creator.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;
|
||||
creator.common_options.flags = charge_market_fee;
|
||||
creator.common_options.core_exchange_rate = price({asset(2),asset(1,asset_id_type(1))});
|
||||
trx.operations.push_back(std::move(creator));
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
|
||||
// pass hardfork
|
||||
generate_blocks( HARDFORK_GPOS_TIME );
|
||||
generate_block();
|
||||
|
||||
const auto& dividend_holder_asset_object = asset_id_type(0)(db);
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
|
||||
const account_object& alice = get_account("alice");
|
||||
const account_object& bob = get_account("bob");
|
||||
const account_object& carol = get_account("carol");
|
||||
const account_object& dave = get_account("dave");
|
||||
const auto& test_asset_object = get_asset("TESTB");
|
||||
|
||||
auto issue_asset_to_account = [&](const asset_object& asset_to_issue, const account_object& destination_account, int64_t amount_to_issue)
|
||||
{
|
||||
asset_issue_operation op;
|
||||
op.issuer = asset_to_issue.issuer;
|
||||
op.asset_to_issue = asset(amount_to_issue, asset_to_issue.id);
|
||||
op.issue_to_account = destination_account.id;
|
||||
trx.operations.push_back( op );
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
};
|
||||
|
||||
auto verify_pending_balance = [&](const account_object& holder_account_obj, const asset_object& payout_asset_obj, int64_t expected_balance) {
|
||||
int64_t pending_balance = get_dividend_pending_payout_balance(dividend_holder_asset_object.id,
|
||||
holder_account_obj.id,
|
||||
payout_asset_obj.id);
|
||||
BOOST_CHECK_EQUAL(pending_balance, expected_balance);
|
||||
};
|
||||
|
||||
auto advance_to_next_payout_time = [&]() {
|
||||
// Advance to the next upcoming payout time
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
idump((next_payout_scheduled_time));
|
||||
// generate blocks up to the next scheduled time
|
||||
generate_blocks(next_payout_scheduled_time);
|
||||
// if the scheduled time fell on a maintenance interval, then we should have paid out.
|
||||
// if not, we need to advance to the next maintenance interval to trigger the payout
|
||||
if (dividend_data.options.next_payout_time)
|
||||
{
|
||||
// we know there was a next_payout_time set when we entered this, so if
|
||||
// it has been cleared, we must have already processed payouts, no need to
|
||||
// further advance time.
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
if (*dividend_data.options.next_payout_time == next_payout_scheduled_time)
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
}
|
||||
idump((db.head_block_time()));
|
||||
};
|
||||
|
||||
// the first test will be testing pending balances, so we need to hit a
|
||||
// maintenance interval that isn't the payout interval. Payout is
|
||||
// every 3 days, maintenance interval is every 1 day.
|
||||
advance_to_next_payout_time();
|
||||
|
||||
// Set up the first test, issue alice, bob, and carol, and dave each 1/4 of the total
|
||||
// supply of the core asset.
|
||||
// Then deposit 400 TEST in the distribution account, and see that they
|
||||
// each are credited 100 TEST.
|
||||
transfer( committee_account(db), alice, asset( 250000000000000 ) );
|
||||
transfer( committee_account(db), bob, asset( 250000000000000 ) );
|
||||
transfer( committee_account(db), carol, asset( 250000000000000 ) );
|
||||
transfer( committee_account(db), dave, asset( 250000000000000 ) );
|
||||
|
||||
// create vesting balance
|
||||
// bob has not vested anything
|
||||
create_vesting(alice_id, core.amount(25000000), vesting_balance_type::gpos);
|
||||
create_vesting(carol_id, core.amount(25000000), vesting_balance_type::gpos);
|
||||
create_vesting(dave_id, core.amount(25000000), vesting_balance_type::gpos);
|
||||
|
||||
// need to vote to get paid
|
||||
// carol doesn't participate in voting
|
||||
auto witness1 = witness_id_type(1)(db);
|
||||
vote_for(alice_id, witness1.vote_id, alice_private_key);
|
||||
vote_for(bob_id, witness1.vote_id, bob_private_key);
|
||||
vote_for(dave_id, witness1.vote_id, dave_private_key);
|
||||
|
||||
// issuing 30000 TESTB to the dividend account
|
||||
// alice and dave should receive 10000 TESTB as they have gpos vesting and
|
||||
// participated in voting
|
||||
// bob should not receive any TESTB as he doesn't have gpos vested
|
||||
// carol should not receive any TESTB as she doesn't participated in voting
|
||||
// remaining 10000 TESTB should be deposited in commitee_accoount.
|
||||
BOOST_TEST_MESSAGE("Issuing 30000 TESTB to the dividend account");
|
||||
issue_asset_to_account(test_asset_object, dividend_distribution_account, 30000);
|
||||
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" );
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
|
||||
verify_pending_balance(alice, test_asset_object, 10000);
|
||||
verify_pending_balance(bob, test_asset_object, 0);
|
||||
verify_pending_balance(carol, test_asset_object, 0);
|
||||
verify_pending_balance(dave, test_asset_object, 10000);
|
||||
|
||||
advance_to_next_payout_time();
|
||||
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
|
||||
auto verify_dividend_payout_operations = [&](const account_object& destination_account, const asset& expected_payout)
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Verifying the virtual op was created");
|
||||
const account_transaction_history_index& hist_idx = db.get_index_type<account_transaction_history_index>();
|
||||
auto account_history_range = hist_idx.indices().get<by_seq>().equal_range(boost::make_tuple(destination_account.id));
|
||||
BOOST_REQUIRE(account_history_range.first != account_history_range.second);
|
||||
const operation_history_object& history_object = std::prev(account_history_range.second)->operation_id(db);
|
||||
const asset_dividend_distribution_operation& distribution_operation = history_object.op.get<asset_dividend_distribution_operation>();
|
||||
BOOST_CHECK(distribution_operation.account_id == destination_account.id);
|
||||
BOOST_CHECK(std::find(distribution_operation.amounts.begin(), distribution_operation.amounts.end(), expected_payout)
|
||||
!= distribution_operation.amounts.end());
|
||||
};
|
||||
|
||||
BOOST_TEST_MESSAGE("Verifying the payouts");
|
||||
BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 10000);
|
||||
verify_dividend_payout_operations(alice, asset(10000, test_asset_object.id));
|
||||
verify_pending_balance(alice, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 0);
|
||||
verify_pending_balance(bob, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 0);
|
||||
verify_pending_balance(carol, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(dave, test_asset_object), 10000);
|
||||
verify_dividend_payout_operations(dave, asset(10000, test_asset_object.id));
|
||||
verify_pending_balance(dave, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(account_id_type(0)(db), test_asset_object), 10000);
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( voting )
|
||||
{
|
||||
ACTORS((alice)(bob));
|
||||
try {
|
||||
|
||||
// move to hardfork
|
||||
generate_blocks( HARDFORK_GPOS_TIME );
|
||||
generate_block();
|
||||
|
|
@ -389,65 +563,97 @@ BOOST_AUTO_TEST_CASE( voting )
|
|||
auto witness2 = witness_id_type(2)(db);
|
||||
BOOST_CHECK_EQUAL(witness2.total_votes, 0);
|
||||
|
||||
// vote for witness1
|
||||
// vote for witness1 and witness2 - sub-period 1
|
||||
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);
|
||||
|
||||
//Bob votes for witness2 - sub-period 2
|
||||
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);
|
||||
|
||||
//Bob votes for witness2 - sub-period 3
|
||||
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
|
||||
|
||||
// Bob votes for witness2 - sub-period 4
|
||||
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
|
||||
|
||||
// Bob votes for witness2 - sub-period 5
|
||||
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
|
||||
|
||||
// Bob votes for witness2 - sub-period 6
|
||||
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);
|
||||
|
||||
// a new GPOS period is in but vote from user is before the start so his voting power is 0
|
||||
advance_x_maint(5);
|
||||
// a new GPOS period is in but vote from user is before the start. Whoever votes in 6th sub-period, votes will carry
|
||||
now = db.head_block_time();
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch());
|
||||
|
||||
generate_block();
|
||||
|
||||
// we are in the second GPOS period, at subperiod 1,
|
||||
witness1 = witness_id_type(1)(db);
|
||||
witness2 = witness_id_type(2)(db);
|
||||
BOOST_CHECK_EQUAL(witness1.total_votes, 0);
|
||||
//It's critical here, since bob votes in 6th sub-period of last vesting period, witness2 should retain his votes
|
||||
BOOST_CHECK_EQUAL(witness2.total_votes, 100);
|
||||
|
||||
// we are in the second GPOS period, at subperiod 2, lets vote here
|
||||
|
||||
// lets vote here from alice to generate votes for witness 1
|
||||
//vote from bob to reatin VF 1
|
||||
vote_for(alice_id, witness1.vote_id, alice_private_key);
|
||||
vote_for(bob_id, witness2.vote_id, bob_private_key);
|
||||
generate_block();
|
||||
|
||||
|
|
@ -457,7 +663,7 @@ BOOST_AUTO_TEST_CASE( voting )
|
|||
witness1 = witness_id_type(1)(db);
|
||||
witness2 = witness_id_type(2)(db);
|
||||
|
||||
BOOST_CHECK_EQUAL(witness1.total_votes, 0);
|
||||
BOOST_CHECK_EQUAL(witness1.total_votes, 100);
|
||||
BOOST_CHECK_EQUAL(witness2.total_votes, 100);
|
||||
|
||||
advance_x_maint(10);
|
||||
|
|
@ -465,16 +671,19 @@ BOOST_AUTO_TEST_CASE( voting )
|
|||
witness1 = witness_id_type(1)(db);
|
||||
witness2 = witness_id_type(2)(db);
|
||||
|
||||
BOOST_CHECK_EQUAL(witness1.total_votes, 0);
|
||||
BOOST_CHECK_EQUAL(witness1.total_votes, 83);
|
||||
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(witness1.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);
|
||||
|
|
@ -484,7 +693,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) {
|
||||
|
|
@ -497,33 +706,32 @@ BOOST_AUTO_TEST_CASE( rolling_period_start )
|
|||
{
|
||||
// period start rolls automatically after HF
|
||||
try {
|
||||
// advance to HF
|
||||
generate_blocks(HARDFORK_GPOS_TIME);
|
||||
generate_block();
|
||||
|
||||
// update default gpos global parameters to make this thing faster
|
||||
auto now = db.head_block_time();
|
||||
update_gpos_global(518400, 86400, now);
|
||||
update_gpos_global(518400, 86400, HARDFORK_GPOS_TIME);
|
||||
generate_blocks(HARDFORK_GPOS_TIME);
|
||||
update_maintenance_interval(3600); //update maintenance interval to 1hr to evaluate sub-periods
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, 3600);
|
||||
|
||||
auto vesting_period_1 = db.get_global_properties().parameters.gpos_period_start();
|
||||
|
||||
auto now = db.head_block_time();
|
||||
// moving outside period:
|
||||
while( db.head_block_time() <= now + fc::days(6) )
|
||||
{
|
||||
generate_block();
|
||||
}
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
|
||||
// rolling is here so getting the new now
|
||||
now = db.head_block_time();
|
||||
generate_block();
|
||||
|
||||
// period start rolled
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch());
|
||||
auto vesting_period_2 = db.get_global_properties().parameters.gpos_period_start();
|
||||
|
||||
//difference between start of two consecutive vesting periods should be 6 days
|
||||
BOOST_CHECK_EQUAL(vesting_period_1 + 518400, vesting_period_2);
|
||||
}
|
||||
catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( worker_dividends_voting )
|
||||
{
|
||||
try {
|
||||
|
|
@ -833,8 +1041,101 @@ BOOST_AUTO_TEST_CASE( competing_proposals )
|
|||
*/
|
||||
BOOST_AUTO_TEST_CASE( proxy_voting )
|
||||
{
|
||||
ACTORS((alice)(bob));
|
||||
try {
|
||||
// move to hardfork
|
||||
generate_blocks( HARDFORK_GPOS_TIME );
|
||||
generate_block();
|
||||
|
||||
// database api
|
||||
graphene::app::database_api db_api(db);
|
||||
|
||||
const auto& core = asset_id_type()(db);
|
||||
|
||||
// send some asset to alice and bob
|
||||
transfer( committee_account, alice_id, core.amount( 1000 ) );
|
||||
transfer( committee_account, bob_id, core.amount( 1000 ) );
|
||||
generate_block();
|
||||
|
||||
// add some vesting to alice and bob
|
||||
create_vesting(alice_id, core.amount(100), vesting_balance_type::gpos);
|
||||
generate_block();
|
||||
|
||||
// total balance is 100 rest of data at 0
|
||||
auto gpos_info = db_api.get_gpos_info(alice_id);
|
||||
BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0);
|
||||
BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0);
|
||||
BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 100);
|
||||
|
||||
create_vesting(bob_id, core.amount(100), vesting_balance_type::gpos);
|
||||
generate_block();
|
||||
|
||||
gpos_info = db_api.get_gpos_info(bob_id);
|
||||
BOOST_CHECK_EQUAL(gpos_info.vesting_factor, 0);
|
||||
BOOST_CHECK_EQUAL(gpos_info.award.amount.value, 0);
|
||||
BOOST_CHECK_EQUAL(gpos_info.total_amount.value, 200);
|
||||
|
||||
auto now = db.head_block_time();
|
||||
update_gpos_global(518400, 86400, now);
|
||||
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), 518400);
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), 86400);
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period_start(), now.sec_since_epoch());
|
||||
|
||||
// alice assign bob as voting account
|
||||
graphene::chain::account_update_operation op;
|
||||
op.account = alice_id;
|
||||
op.new_options = alice_id(db).options;
|
||||
op.new_options->voting_account = bob_id;
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
trx.validate();
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.clear();
|
||||
|
||||
generate_block();
|
||||
|
||||
// vote for witness1
|
||||
auto witness1 = witness_id_type(1)(db);
|
||||
vote_for(bob_id, witness1.vote_id, bob_private_key);
|
||||
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
|
||||
// check vesting factor of current subperiod
|
||||
BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 1);
|
||||
BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 1);
|
||||
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block();
|
||||
|
||||
// GPOS 2nd subperiod started.
|
||||
// vesting factor decay
|
||||
BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.83333333333333337);
|
||||
BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.83333333333333337);
|
||||
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block();
|
||||
|
||||
// GPOS 3rd subperiod started
|
||||
// vesting factor decay
|
||||
BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.66666666666666663);
|
||||
BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.66666666666666663);
|
||||
|
||||
// vote for witness2
|
||||
auto witness2 = witness_id_type(2)(db);
|
||||
vote_for(bob_id, witness2.vote_id, bob_private_key);
|
||||
|
||||
// vesting factor should be 1 for both alice and bob for the current subperiod
|
||||
BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 1);
|
||||
BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 1);
|
||||
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block();
|
||||
|
||||
// vesting factor decay
|
||||
BOOST_CHECK_EQUAL(db_api.get_gpos_info(alice_id).vesting_factor, 0.83333333333333337);
|
||||
BOOST_CHECK_EQUAL(db_api.get_gpos_info(bob_id).vesting_factor, 0.83333333333333337);
|
||||
}
|
||||
catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
|
|
@ -852,11 +1153,11 @@ BOOST_AUTO_TEST_CASE( no_proposal )
|
|||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( database_api )
|
||||
{
|
||||
ACTORS((alice)(bob));
|
||||
try {
|
||||
|
||||
// move to hardfork
|
||||
generate_blocks( HARDFORK_GPOS_TIME );
|
||||
generate_block();
|
||||
|
|
|
|||
Loading…
Reference in a new issue