From a8423f167d934167c7c48e72ef400b101ca2418f Mon Sep 17 00:00:00 2001 From: Sandip Patel Date: Fri, 25 Oct 2019 16:41:34 +0530 Subject: [PATCH] Fixed dividend distribution issue and added test case --- libraries/chain/db_maint.cpp | 4 +- tests/tests/gpos_tests.cpp | 170 +++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 2 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 182c04fc8..42c137058 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -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, diff --git a/tests/tests/gpos_tests.cpp b/tests/tests/gpos_tests.cpp index 5b0896852..76d27e466 100644 --- a/tests/tests/gpos_tests.cpp +++ b/tests/tests/gpos_tests.cpp @@ -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(); + auto account_history_range = hist_idx.indices().get().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(); + 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));