Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BLOCKBACK-166] GPOS - dividend distribution for UIAs #200

Merged
merged 1 commit into from
Oct 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions libraries/chain/db_maint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1090,8 +1090,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,
Expand Down
170 changes: 170 additions & 0 deletions tests/tests/gpos_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,176 @@ 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));
Expand Down