From 33a4759f31dbbfaf7bf285efc03f657fde0313b2 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sat, 15 Dec 2018 16:20:14 -0300 Subject: [PATCH 1/7] make GRAPHENE_TESTING_GENESIS_TIMESTAMP divisible by 3 --- .idea/misc.xml | 4 ++++ .idea/vcs.xml | 6 ++++++ tests/common/database_fixture.cpp | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..79b3c948 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index c6a21640..3da01662 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -59,7 +59,7 @@ using namespace graphene::chain::test; -uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP = 1431700000; +uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP = 1431700002; namespace graphene { namespace chain { From 8a0dc48321a46bc886dcc320c32782f403813dfb Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sat, 15 Dec 2018 16:21:53 -0300 Subject: [PATCH 2/7] fix identation in asset_create_evaluator::do_evaluate --- libraries/chain/asset_evaluator.cpp | 50 +++++++++++++++-------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 1346584c..7105eeba 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -57,35 +57,37 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o if( d.head_block_time() > HARDFORK_385_TIME ) { - - if( d.head_block_time() <= HARDFORK_409_TIME ) - { - auto dotpos = op.symbol.find( '.' ); - if( dotpos != std::string::npos ) + if( d.head_block_time() <= HARDFORK_409_TIME ) { - auto prefix = op.symbol.substr( 0, dotpos ); - auto asset_symbol_itr = asset_indx.find( op.symbol ); - FC_ASSERT( asset_symbol_itr != asset_indx.end(), "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered", - ("s",op.symbol)("p",prefix) ); - FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}", - ("s",op.symbol)("p",prefix)("i", op.issuer(d).name) ); + auto dotpos = op.symbol.find( '.' ); + if( dotpos != std::string::npos ) + { + auto prefix = op.symbol.substr( 0, dotpos ); + auto asset_symbol_itr = asset_indx.find( op.symbol ); + FC_ASSERT( asset_symbol_itr != asset_indx.end(), + "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered", + ("s",op.symbol)("p",prefix) ); + FC_ASSERT( asset_symbol_itr->issuer == op.issuer, + "Asset ${s} may only be created by issuer of ${p}, ${i}", + ("s",op.symbol)("p",prefix)("i", op.issuer(d).name) ); + } } - } - else - { - auto dotpos = op.symbol.rfind( '.' ); - if( dotpos != std::string::npos ) + else { - auto prefix = op.symbol.substr( 0, dotpos ); - auto asset_symbol_itr = asset_indx.find( prefix ); - FC_ASSERT( asset_symbol_itr != asset_indx.end(), "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered", - ("s",op.symbol)("p",prefix) ); - FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}", - ("s",op.symbol)("p",prefix)("i", op.issuer(d).name) ); + auto dotpos = op.symbol.rfind( '.' ); + if( dotpos != std::string::npos ) + { + auto prefix = op.symbol.substr( 0, dotpos ); + auto asset_symbol_itr = asset_indx.find( prefix ); + FC_ASSERT( asset_symbol_itr != asset_indx.end(), + "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered", + ("s",op.symbol)("p",prefix) ); + FC_ASSERT( asset_symbol_itr->issuer == op.issuer, + "Asset ${s} may only be created by issuer of ${p}, ${i}", + ("s",op.symbol)("p",prefix)("i", op.issuer(d).name) ); + } } } - - } else { auto dotpos = op.symbol.find( '.' ); From 100a41582508dd6fbe8ec5e727ba68d2e48cdb66 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sat, 15 Dec 2018 20:35:47 -0300 Subject: [PATCH 3/7] comment out fee_pool core check --- tests/common/database_fixture.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 3da01662..7f92dc54 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -170,8 +170,10 @@ string database_fixture::generate_anon_acct_name() void database_fixture::verify_asset_supplies( const database& db ) { //wlog("*** Begin asset supply verification ***"); - const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db); - BOOST_CHECK(core_asset_data.fee_pool == 0); + + // It seems peerplays by default DO have core fee pool in genesis so commenting this out + //const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db); + //BOOST_CHECK(core_asset_data.fee_pool == 0); const simple_index& statistics_index = db.get_index_type>(); const auto& balance_index = db.get_index_type().indices(); From 7606eb634a6456ffdefbe5a6b90aec4954b58bc9 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sat, 15 Dec 2018 20:36:35 -0300 Subject: [PATCH 4/7] move and fix dividend tests --- tests/tests/dividend_tests.cpp | 672 ++++++++++++++++++++++++++++++++ tests/tests/operation_tests.cpp | 627 ----------------------------- 2 files changed, 672 insertions(+), 627 deletions(-) create mode 100644 tests/tests/dividend_tests.cpp diff --git a/tests/tests/dividend_tests.cpp b/tests/tests/dividend_tests.cpp new file mode 100644 index 00000000..ecb2ee30 --- /dev/null +++ b/tests/tests/dividend_tests.cpp @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2018 oxarbitrage and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE( dividend_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( create_dividend_uia ) +{ + using namespace graphene; + try { + BOOST_TEST_MESSAGE("Creating dividend holder asset"); + { + asset_create_operation creator; + creator.issuer = account_id_type(); + creator.fee = asset(); + creator.symbol = "DIVIDEND"; + 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(); + } + + BOOST_TEST_MESSAGE("Creating test accounts"); + create_account("alice"); + create_account("bob"); + create_account("carol"); + create_account("dave"); + create_account("frank"); + + BOOST_TEST_MESSAGE("Creating test asset"); + { + asset_create_operation creator; + creator.issuer = account_id_type(); + creator.fee = asset(); + creator.symbol = "TESTB"; //cant use TEST + 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(); + } + generate_block(); + + BOOST_TEST_MESSAGE("Funding asset fee pool"); + { + asset_fund_fee_pool_operation fund_op; + fund_op.from_account = account_id_type(); + fund_op.asset_id = get_asset("TESTB").id; + fund_op.amount = 500000000; + trx.operations.push_back(std::move(fund_op)); + set_expiration(db, trx); + PUSH_TX( db, trx, ~0 ); + trx.operations.clear(); + } + + // our DIVIDEND asset should not yet be a divdend asset + const auto& dividend_holder_asset_object = get_asset("DIVIDEND"); + BOOST_CHECK(!dividend_holder_asset_object.dividend_data_id); + + BOOST_TEST_MESSAGE("Converting the new asset to a dividend holder asset"); + { + asset_update_dividend_operation op; + op.issuer = dividend_holder_asset_object.issuer; + op.asset_to_update = dividend_holder_asset_object.id; + op.new_options.next_payout_time = db.head_block_time() + fc::minutes(1); + op.new_options.payout_interval = 60 * 60 * 24 * 3; + + trx.operations.push_back(op); + set_expiration(db, trx); + PUSH_TX( db, trx, ~0 ); + trx.operations.clear(); + } + generate_block(); + + BOOST_TEST_MESSAGE("Verifying the dividend holder asset options"); + BOOST_REQUIRE(dividend_holder_asset_object.dividend_data_id); + const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); + { + BOOST_REQUIRE(dividend_data.options.payout_interval); + BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 60 * 60 * 24 * 3); + } + + const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); + BOOST_CHECK_EQUAL(dividend_distribution_account.name, "dividend-dividend-distribution"); + + // db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) + // { + // _gpo.parameters.current_fees->get().distribution_base_fee = 100; + // _gpo.parameters.current_fees->get().distribution_fee_per_holder = 100; + // } ); + + + } catch(fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( test_update_dividend_interval ) +{ + using namespace graphene; + try { + INVOKE( create_dividend_uia ); + + const auto& dividend_holder_asset_object = get_asset("DIVIDEND"); + const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); + + 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; + // 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 + } + }; + + BOOST_TEST_MESSAGE("Updating the payout interval"); + { + asset_update_dividend_operation op; + op.issuer = dividend_holder_asset_object.issuer; + op.asset_to_update = dividend_holder_asset_object.id; + op.new_options.next_payout_time = fc::time_point::now() + fc::minutes(1); + op.new_options.payout_interval = 60 * 60 * 24; // 1 days + trx.operations.push_back(op); + set_expiration(db, trx); + PUSH_TX( db, trx, ~0 ); + trx.operations.clear(); + } + generate_block(); + + BOOST_TEST_MESSAGE("Verifying the updated dividend holder asset options"); + { + BOOST_REQUIRE(dividend_data.options.payout_interval); + BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 60 * 60 * 24); + } + + BOOST_TEST_MESSAGE("Removing the payout interval"); + { + asset_update_dividend_operation op; + op.issuer = dividend_holder_asset_object.issuer; + op.asset_to_update = dividend_holder_asset_object.id; + op.new_options.next_payout_time = dividend_data.options.next_payout_time; + op.new_options.payout_interval = fc::optional(); + trx.operations.push_back(op); + set_expiration(db, trx); + PUSH_TX( db, trx, ~0 ); + trx.operations.clear(); + } + generate_block(); + BOOST_CHECK(!dividend_data.options.payout_interval); + advance_to_next_payout_time(); + BOOST_REQUIRE_MESSAGE(!dividend_data.options.next_payout_time, "A new payout was scheduled, but none should have been"); + } catch(fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution ) +{ + using namespace graphene; + try { + INVOKE( create_dividend_uia ); + + const auto& dividend_holder_asset_object = get_asset("DIVIDEND"); + 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 account_object& frank = get_account("frank"); + 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; + // 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 + } + }; + + // 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 each 100 DIVIDEND. + // Then deposit 300 TEST in the distribution account, and see that they + // each are credited 100 TEST. + issue_asset_to_account(dividend_holder_asset_object, alice, 100000); + issue_asset_to_account(dividend_holder_asset_object, bob, 100000); + issue_asset_to_account(dividend_holder_asset_object, carol, 100000); + + BOOST_TEST_MESSAGE("Issuing 300 TEST 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, 10000); + verify_pending_balance(carol, test_asset_object, 10000); + + // For the second test, issue carol more than the other two, so it's + // alice: 100 DIVIDND, bob: 100 DIVIDEND, carol: 200 DIVIDEND + // Then deposit 400 TEST in the distribution account, and see that alice + // and bob are credited with 100 TEST, and carol gets 200 TEST + BOOST_TEST_MESSAGE("Issuing carol twice as much of the holder asset"); + issue_asset_to_account(dividend_holder_asset_object, carol, 100000); // one thousand at two digits of precision + issue_asset_to_account(test_asset_object, dividend_distribution_account, 40000); // one thousand at two digits of precision + 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, 20000); + verify_pending_balance(bob, test_asset_object, 20000); + verify_pending_balance(carol, test_asset_object, 30000); + + fc::time_point_sec old_next_payout_scheduled_time = *dividend_data.options.next_payout_time; + advance_to_next_payout_time(); + + + BOOST_REQUIRE_MESSAGE(dividend_data.options.next_payout_time, "No new payout was scheduled"); + BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time != *dividend_data.options.next_payout_time, + "New payout was scheduled for the same time as the last payout"); + BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time + *dividend_data.options.payout_interval == *dividend_data.options.next_payout_time, + "New payout was not scheduled for the expected time"); + + 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), 20000); + verify_dividend_payout_operations(alice, asset(20000, test_asset_object.id)); + verify_pending_balance(alice, test_asset_object, 0); + + BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 20000); + verify_dividend_payout_operations(bob, asset(20000, test_asset_object.id)); + verify_pending_balance(bob, test_asset_object, 0); + + BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 30000); + verify_dividend_payout_operations(carol, asset(30000, test_asset_object.id)); + verify_pending_balance(carol, test_asset_object, 0); + } catch(fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution_to_core_asset ) +{ + using namespace graphene; + try { + BOOST_TEST_MESSAGE("Creating test accounts"); + create_account("alice"); + create_account("bob"); + create_account("carol"); + create_account("dave"); + create_account("frank"); + + 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(); + } + 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 account_object& frank = get_account("frank"); + 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 ) ); + + BOOST_TEST_MESSAGE("Issuing 300 TEST to the dividend account"); + issue_asset_to_account(test_asset_object, dividend_distribution_account, 40000); + + 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, 10000); + verify_pending_balance(carol, test_asset_object, 10000); + verify_pending_balance(dave, test_asset_object, 10000); + + // For the second test, issue dave more than the other two, so it's + // alice: 1/5 CORE, bob: 1/5 CORE, carol: 1/5 CORE, dave: 2/5 CORE + // Then deposit 500 TEST in the distribution account, and see that alice + // bob, and carol are credited with 100 TEST, and dave gets 200 TEST + BOOST_TEST_MESSAGE("Issuing dave twice as much of the holder asset"); + transfer( alice, dave, asset( 50000000000000 ) ); + transfer( bob, dave, asset( 50000000000000 ) ); + transfer( carol, dave, asset( 50000000000000 ) ); + issue_asset_to_account(test_asset_object, dividend_distribution_account, 50000); // 500 at two digits of precision + 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, 20000); + verify_pending_balance(bob, test_asset_object, 20000); + verify_pending_balance(carol, test_asset_object, 20000); + verify_pending_balance(dave, test_asset_object, 30000); + + fc::time_point_sec old_next_payout_scheduled_time = *dividend_data.options.next_payout_time; + advance_to_next_payout_time(); + + + BOOST_REQUIRE_MESSAGE(dividend_data.options.next_payout_time, "No new payout was scheduled"); + BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time != *dividend_data.options.next_payout_time, + "New payout was scheduled for the same time as the last payout"); + BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time + *dividend_data.options.payout_interval == *dividend_data.options.next_payout_time, + "New payout was not scheduled for the expected time"); + + 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), 20000); + verify_dividend_payout_operations(alice, asset(20000, test_asset_object.id)); + verify_pending_balance(alice, test_asset_object, 0); + + BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 20000); + verify_dividend_payout_operations(bob, asset(20000, test_asset_object.id)); + verify_pending_balance(bob, test_asset_object, 0); + + BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 20000); + verify_dividend_payout_operations(carol, asset(20000, test_asset_object.id)); + verify_pending_balance(carol, test_asset_object, 0); + + BOOST_CHECK_EQUAL(get_balance(dave, test_asset_object), 30000); + verify_dividend_payout_operations(dave, asset(30000, test_asset_object.id)); + verify_pending_balance(dave, test_asset_object, 0); + } catch(fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( test_dividend_distribution_interval ) +{ + using namespace graphene; + try { + INVOKE( create_dividend_uia ); + + const auto& dividend_holder_asset_object = get_asset("DIVIDEND"); + 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 account_object& frank = get_account("frank"); + const auto& test_asset_object = get_asset("TESTB"); + } catch(fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + + +BOOST_AUTO_TEST_CASE( check_dividend_corner_cases ) +{ + using namespace graphene; + try { + INVOKE( create_dividend_uia ); + + const auto& dividend_holder_asset_object = get_asset("DIVIDEND"); + 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 account_object& frank = get_account("frank"); + 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 reserve_asset_from_account = [&](const asset_object& asset_to_reserve, const account_object& from_account, int64_t amount_to_reserve) + { + asset_reserve_operation reserve_op; + reserve_op.payer = from_account.id; + reserve_op.amount_to_reserve = asset(amount_to_reserve, asset_to_reserve.id); + trx.operations.push_back(reserve_op); + set_expiration(db, trx); + PUSH_TX( db, trx, ~0 ); + trx.operations.clear(); + }; + 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; + // 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 + } + }; + + // 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(); + + BOOST_TEST_MESSAGE("Testing a payout interval when there are no users holding the dividend asset"); + BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0); + BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0); + BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0); + issue_asset_to_account(test_asset_object, dividend_distribution_account, 1000); + 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 + BOOST_TEST_MESSAGE("Verify that no pending payments were scheduled"); + verify_pending_balance(alice, test_asset_object, 0); + verify_pending_balance(bob, test_asset_object, 0); + verify_pending_balance(carol, test_asset_object, 0); + advance_to_next_payout_time(); + BOOST_TEST_MESSAGE("Verify that no actual payments took place"); + verify_pending_balance(alice, test_asset_object, 0); + verify_pending_balance(bob, test_asset_object, 0); + verify_pending_balance(carol, test_asset_object, 0); + BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 0); + BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 0); + BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 0); + BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, test_asset_object), 1000); + + BOOST_TEST_MESSAGE("Now give alice a small balance and see that she takes it all"); + issue_asset_to_account(dividend_holder_asset_object, alice, 1); + 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 + BOOST_TEST_MESSAGE("Verify that no alice received her payment of the entire amount"); + verify_pending_balance(alice, test_asset_object, 1000); + + // Test that we can pay out the dividend asset itself + issue_asset_to_account(dividend_holder_asset_object, bob, 1); + issue_asset_to_account(dividend_holder_asset_object, carol, 1); + issue_asset_to_account(dividend_holder_asset_object, dividend_distribution_account, 300); + generate_block(); + BOOST_CHECK_EQUAL(get_balance(alice, dividend_holder_asset_object), 1); + BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 1); + BOOST_CHECK_EQUAL(get_balance(carol, dividend_holder_asset_object), 1); + 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 + BOOST_TEST_MESSAGE("Verify that the dividend asset was shared out"); + verify_pending_balance(alice, dividend_holder_asset_object, 100); + verify_pending_balance(bob, dividend_holder_asset_object, 100); + verify_pending_balance(carol, dividend_holder_asset_object, 100); + } catch(fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index fd35c312..1f396156 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -1112,633 +1112,6 @@ BOOST_AUTO_TEST_CASE( uia_fees ) } } -BOOST_FIXTURE_TEST_SUITE( dividend_tests, database_fixture ) - -BOOST_AUTO_TEST_CASE( create_dividend_uia ) -{ - using namespace graphene; - try { - BOOST_TEST_MESSAGE("Creating dividend holder asset"); - { - asset_create_operation creator; - creator.issuer = account_id_type(); - creator.fee = asset(); - creator.symbol = "DIVIDEND"; - 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(); - } - - BOOST_TEST_MESSAGE("Creating test accounts"); - create_account("alice"); - create_account("bob"); - create_account("carol"); - create_account("dave"); - create_account("frank"); - - BOOST_TEST_MESSAGE("Creating test asset"); - { - asset_create_operation creator; - creator.issuer = account_id_type(); - creator.fee = asset(); - creator.symbol = "TEST"; - 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(); - } - generate_block(); - - BOOST_TEST_MESSAGE("Funding asset fee pool"); - { - asset_fund_fee_pool_operation fund_op; - fund_op.from_account = account_id_type(); - fund_op.asset_id = get_asset("TEST").id; - fund_op.amount = 500000000; - trx.operations.push_back(std::move(fund_op)); - set_expiration(db, trx); - PUSH_TX( db, trx, ~0 ); - trx.operations.clear(); - } - - // our DIVIDEND asset should not yet be a divdend asset - const auto& dividend_holder_asset_object = get_asset("DIVIDEND"); - BOOST_CHECK(!dividend_holder_asset_object.dividend_data_id); - - BOOST_TEST_MESSAGE("Converting the new asset to a dividend holder asset"); - { - asset_update_dividend_operation op; - op.issuer = dividend_holder_asset_object.issuer; - op.asset_to_update = dividend_holder_asset_object.id; - op.new_options.next_payout_time = db.head_block_time() + fc::minutes(1); - op.new_options.payout_interval = 60 * 60 * 24 * 3; - - trx.operations.push_back(op); - set_expiration(db, trx); - PUSH_TX( db, trx, ~0 ); - trx.operations.clear(); - } - generate_block(); - - BOOST_TEST_MESSAGE("Verifying the dividend holder asset options"); - BOOST_REQUIRE(dividend_holder_asset_object.dividend_data_id); - const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); - { - BOOST_REQUIRE(dividend_data.options.payout_interval); - BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 60 * 60 * 24 * 3); - } - - const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db); - BOOST_CHECK_EQUAL(dividend_distribution_account.name, "dividend-dividend-distribution"); - - // db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) - // { - // _gpo.parameters.current_fees->get().distribution_base_fee = 100; - // _gpo.parameters.current_fees->get().distribution_fee_per_holder = 100; - // } ); - - - } catch(fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( test_update_dividend_interval ) -{ - using namespace graphene; - try { - INVOKE( create_dividend_uia ); - - const auto& dividend_holder_asset_object = get_asset("DIVIDEND"); - const auto& dividend_data = dividend_holder_asset_object.dividend_data(db); - - 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; - // 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 - } - }; - - BOOST_TEST_MESSAGE("Updating the payout interval"); - { - asset_update_dividend_operation op; - op.issuer = dividend_holder_asset_object.issuer; - op.asset_to_update = dividend_holder_asset_object.id; - op.new_options.next_payout_time = fc::time_point::now() + fc::minutes(1); - op.new_options.payout_interval = 60 * 60 * 24; // 1 days - trx.operations.push_back(op); - set_expiration(db, trx); - PUSH_TX( db, trx, ~0 ); - trx.operations.clear(); - } - generate_block(); - - BOOST_TEST_MESSAGE("Verifying the updated dividend holder asset options"); - { - BOOST_REQUIRE(dividend_data.options.payout_interval); - BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 60 * 60 * 24); - } - - BOOST_TEST_MESSAGE("Removing the payout interval"); - { - asset_update_dividend_operation op; - op.issuer = dividend_holder_asset_object.issuer; - op.asset_to_update = dividend_holder_asset_object.id; - op.new_options.next_payout_time = dividend_data.options.next_payout_time; - op.new_options.payout_interval = fc::optional(); - trx.operations.push_back(op); - set_expiration(db, trx); - PUSH_TX( db, trx, ~0 ); - trx.operations.clear(); - } - generate_block(); - BOOST_CHECK(!dividend_data.options.payout_interval); - advance_to_next_payout_time(); - BOOST_REQUIRE_MESSAGE(!dividend_data.options.next_payout_time, "A new payout was scheduled, but none should have been"); - } catch(fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution ) -{ - using namespace graphene; - try { - INVOKE( create_dividend_uia ); - - const auto& dividend_holder_asset_object = get_asset("DIVIDEND"); - 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 account_object& frank = get_account("frank"); - const auto& test_asset_object = get_asset("TEST"); - - 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; - // 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 - } - }; - - // 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 each 100 DIVIDEND. - // Then deposit 300 TEST in the distribution account, and see that they - // each are credited 100 TEST. - issue_asset_to_account(dividend_holder_asset_object, alice, 100000); - issue_asset_to_account(dividend_holder_asset_object, bob, 100000); - issue_asset_to_account(dividend_holder_asset_object, carol, 100000); - - BOOST_TEST_MESSAGE("Issuing 300 TEST 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, 10000); - verify_pending_balance(carol, test_asset_object, 10000); - - // For the second test, issue carol more than the other two, so it's - // alice: 100 DIVIDND, bob: 100 DIVIDEND, carol: 200 DIVIDEND - // Then deposit 400 TEST in the distribution account, and see that alice - // and bob are credited with 100 TEST, and carol gets 200 TEST - BOOST_TEST_MESSAGE("Issuing carol twice as much of the holder asset"); - issue_asset_to_account(dividend_holder_asset_object, carol, 100000); // one thousand at two digits of precision - issue_asset_to_account(test_asset_object, dividend_distribution_account, 40000); // one thousand at two digits of precision - 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, 20000); - verify_pending_balance(bob, test_asset_object, 20000); - verify_pending_balance(carol, test_asset_object, 30000); - - fc::time_point_sec old_next_payout_scheduled_time = *dividend_data.options.next_payout_time; - advance_to_next_payout_time(); - - - BOOST_REQUIRE_MESSAGE(dividend_data.options.next_payout_time, "No new payout was scheduled"); - BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time != *dividend_data.options.next_payout_time, - "New payout was scheduled for the same time as the last payout"); - BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time + *dividend_data.options.payout_interval == *dividend_data.options.next_payout_time, - "New payout was not scheduled for the expected time"); - - 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), 20000); - verify_dividend_payout_operations(alice, asset(20000, test_asset_object.id)); - verify_pending_balance(alice, test_asset_object, 0); - - BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 20000); - verify_dividend_payout_operations(bob, asset(20000, test_asset_object.id)); - verify_pending_balance(bob, test_asset_object, 0); - - BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 30000); - verify_dividend_payout_operations(carol, asset(30000, test_asset_object.id)); - verify_pending_balance(carol, test_asset_object, 0); - } catch(fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution_to_core_asset ) -{ - using namespace graphene; - try { - BOOST_TEST_MESSAGE("Creating test accounts"); - create_account("alice"); - create_account("bob"); - create_account("carol"); - create_account("dave"); - create_account("frank"); - - BOOST_TEST_MESSAGE("Creating test asset"); - { - asset_create_operation creator; - creator.issuer = account_id_type(); - creator.fee = asset(); - creator.symbol = "TEST"; - 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(); - } - 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 account_object& frank = get_account("frank"); - const auto& test_asset_object = get_asset("TEST"); - - 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 ) ); - - BOOST_TEST_MESSAGE("Issuing 300 TEST to the dividend account"); - issue_asset_to_account(test_asset_object, dividend_distribution_account, 40000); - - 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, 10000); - verify_pending_balance(carol, test_asset_object, 10000); - verify_pending_balance(dave, test_asset_object, 10000); - - // For the second test, issue dave more than the other two, so it's - // alice: 1/5 CORE, bob: 1/5 CORE, carol: 1/5 CORE, dave: 2/5 CORE - // Then deposit 500 TEST in the distribution account, and see that alice - // bob, and carol are credited with 100 TEST, and dave gets 200 TEST - BOOST_TEST_MESSAGE("Issuing dave twice as much of the holder asset"); - transfer( alice, dave, asset( 50000000000000 ) ); - transfer( bob, dave, asset( 50000000000000 ) ); - transfer( carol, dave, asset( 50000000000000 ) ); - issue_asset_to_account(test_asset_object, dividend_distribution_account, 50000); // 500 at two digits of precision - 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, 20000); - verify_pending_balance(bob, test_asset_object, 20000); - verify_pending_balance(carol, test_asset_object, 20000); - verify_pending_balance(dave, test_asset_object, 30000); - - fc::time_point_sec old_next_payout_scheduled_time = *dividend_data.options.next_payout_time; - advance_to_next_payout_time(); - - - BOOST_REQUIRE_MESSAGE(dividend_data.options.next_payout_time, "No new payout was scheduled"); - BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time != *dividend_data.options.next_payout_time, - "New payout was scheduled for the same time as the last payout"); - BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time + *dividend_data.options.payout_interval == *dividend_data.options.next_payout_time, - "New payout was not scheduled for the expected time"); - - 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), 20000); - verify_dividend_payout_operations(alice, asset(20000, test_asset_object.id)); - verify_pending_balance(alice, test_asset_object, 0); - - BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 20000); - verify_dividend_payout_operations(bob, asset(20000, test_asset_object.id)); - verify_pending_balance(bob, test_asset_object, 0); - - BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 20000); - verify_dividend_payout_operations(carol, asset(20000, test_asset_object.id)); - verify_pending_balance(carol, test_asset_object, 0); - - BOOST_CHECK_EQUAL(get_balance(dave, test_asset_object), 30000); - verify_dividend_payout_operations(dave, asset(30000, test_asset_object.id)); - verify_pending_balance(dave, test_asset_object, 0); - } catch(fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( test_dividend_distribution_interval ) -{ - using namespace graphene; - try { - INVOKE( create_dividend_uia ); - - const auto& dividend_holder_asset_object = get_asset("DIVIDEND"); - 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 account_object& frank = get_account("frank"); - const auto& test_asset_object = get_asset("TEST"); - } catch(fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - - -BOOST_AUTO_TEST_CASE( check_dividend_corner_cases ) -{ - using namespace graphene; - try { - INVOKE( create_dividend_uia ); - - const auto& dividend_holder_asset_object = get_asset("DIVIDEND"); - 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 account_object& frank = get_account("frank"); - const auto& test_asset_object = get_asset("TEST"); - - 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 reserve_asset_from_account = [&](const asset_object& asset_to_reserve, const account_object& from_account, int64_t amount_to_reserve) - { - asset_reserve_operation reserve_op; - reserve_op.payer = from_account.id; - reserve_op.amount_to_reserve = asset(amount_to_reserve, asset_to_reserve.id); - trx.operations.push_back(reserve_op); - set_expiration(db, trx); - PUSH_TX( db, trx, ~0 ); - trx.operations.clear(); - }; - 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; - // 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 - } - }; - - // 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(); - - BOOST_TEST_MESSAGE("Testing a payout interval when there are no users holding the dividend asset"); - BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0); - BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0); - BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0); - issue_asset_to_account(test_asset_object, dividend_distribution_account, 1000); - 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 - BOOST_TEST_MESSAGE("Verify that no pending payments were scheduled"); - verify_pending_balance(alice, test_asset_object, 0); - verify_pending_balance(bob, test_asset_object, 0); - verify_pending_balance(carol, test_asset_object, 0); - advance_to_next_payout_time(); - BOOST_TEST_MESSAGE("Verify that no actual payments took place"); - verify_pending_balance(alice, test_asset_object, 0); - verify_pending_balance(bob, test_asset_object, 0); - verify_pending_balance(carol, test_asset_object, 0); - BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 0); - BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 0); - BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 0); - BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, test_asset_object), 1000); - - BOOST_TEST_MESSAGE("Now give alice a small balance and see that she takes it all"); - issue_asset_to_account(dividend_holder_asset_object, alice, 1); - 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 - BOOST_TEST_MESSAGE("Verify that no alice received her payment of the entire amount"); - verify_pending_balance(alice, test_asset_object, 1000); - - // Test that we can pay out the dividend asset itself - issue_asset_to_account(dividend_holder_asset_object, bob, 1); - issue_asset_to_account(dividend_holder_asset_object, carol, 1); - issue_asset_to_account(dividend_holder_asset_object, dividend_distribution_account, 300); - generate_block(); - BOOST_CHECK_EQUAL(get_balance(alice, dividend_holder_asset_object), 1); - BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 1); - BOOST_CHECK_EQUAL(get_balance(carol, dividend_holder_asset_object), 1); - 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 - BOOST_TEST_MESSAGE("Verify that the dividend asset was shared out"); - verify_pending_balance(alice, dividend_holder_asset_object, 100); - verify_pending_balance(bob, dividend_holder_asset_object, 100); - verify_pending_balance(carol, dividend_holder_asset_object, 100); - } catch(fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_SUITE_END() // end dividend_tests suite - BOOST_AUTO_TEST_CASE( cancel_limit_order_test ) { try { INVOKE( issue_uia ); From 0dfcfe8a0d3091eed18c23a750334a09c3a86e41 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sat, 15 Dec 2018 20:41:56 -0300 Subject: [PATCH 5/7] remove unintended xml files --- .idea/misc.xml | 4 ---- .idea/vcs.xml | 6 ------ 2 files changed, 10 deletions(-) delete mode 100644 .idea/misc.xml delete mode 100644 .idea/vcs.xml diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 79b3c948..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 8112a8347ad5054a44196c0c55e899c268f6f2ad Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sat, 15 Dec 2018 21:53:07 -0300 Subject: [PATCH 6/7] remove not needed includes --- tests/tests/dividend_tests.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/tests/dividend_tests.cpp b/tests/tests/dividend_tests.cpp index ecb2ee30..97e9ddbb 100644 --- a/tests/tests/dividend_tests.cpp +++ b/tests/tests/dividend_tests.cpp @@ -30,14 +30,6 @@ #include #include -#include -#include -#include -#include -#include -#include - -#include #include "../common/database_fixture.hpp" From 23750be3661f98c11b698ea70342f61ca13c1ff7 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sat, 15 Dec 2018 23:15:19 -0300 Subject: [PATCH 7/7] remove more not needed includes from dividend tests --- tests/tests/dividend_tests.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/tests/dividend_tests.cpp b/tests/tests/dividend_tests.cpp index 97e9ddbb..a3869b36 100644 --- a/tests/tests/dividend_tests.cpp +++ b/tests/tests/dividend_tests.cpp @@ -25,11 +25,6 @@ #include #include -#include -#include - -#include -#include #include "../common/database_fixture.hpp"