Cherry-picked commit b584ee1.

Separate out unit tests for dividend-assets into their own test suite
This commit is contained in:
Eric Frias 2016-06-30 12:05:16 -04:00 committed by Roman Olearski
parent 60f7dd798f
commit 5b437d7363
5 changed files with 308 additions and 144 deletions

View file

@ -24,6 +24,7 @@
#include <graphene/app/database_api.hpp>
#include <graphene/chain/get_config.hpp>
#include <graphene/chain/account_object.hpp>
#include <fc/bloom_filter.hpp>
#include <fc/smart_ref_impl.hpp>
@ -719,6 +720,10 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
acnt.withdraws.emplace_back(withdraw);
});
auto pending_payouts_range =
_db.get_index_type<pending_dividend_payout_balance_object_index>().indices().get<by_account_dividend_payout>().equal_range(boost::make_tuple(account->id));
std::copy(pending_payouts_range.first, pending_payouts_range.second, std::back_inserter(acnt.pending_dividend_payments));
results[account_name_or_id] = acnt;
}

View file

@ -48,6 +48,7 @@ namespace graphene { namespace app {
vector<proposal_object> proposals;
vector<asset_id_type> assets;
vector<withdraw_permission_object> withdraws;
vector<pending_dividend_payout_balance_object> pending_dividend_payments;
};
} }
@ -68,4 +69,6 @@ FC_REFLECT( graphene::app::full_account,
(proposals)
(assets)
(withdraws)
(proposals)
(pending_dividend_payments)
)

View file

@ -30,6 +30,7 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
@ -716,6 +717,10 @@ void deprecate_annual_members( database& db )
return;
}
// Schedules payouts from a dividend distribution account to the current holders of the
// dividend-paying asset. This takes any deposits made to the dividend distribution account
// since the last time it was called, and distributes them to the current owners of the
// dividend-paying asset according to the amount they own.
void schedule_pending_dividend_balances(database& db,
const asset_object& dividend_holder_asset_obj,
const asset_dividend_data_object& dividend_data,
@ -723,7 +728,8 @@ void schedule_pending_dividend_balances(database& db,
const distributed_dividend_balance_object_index& distributed_dividend_balance_index,
const pending_dividend_payout_balance_object_index& pending_payout_balance_index)
{
dlog("Processing dividend payments for dividend holder asset asset type ${holder_asset}", ("holder_asset", dividend_holder_asset_obj.symbol));
dlog("Processing dividend payments for dividend holder asset asset type ${holder_asset} at time ${t}",
("holder_asset", dividend_holder_asset_obj.symbol)("t", db.head_block_time()));
auto current_distribution_account_balance_range =
balance_index.indices().get<by_account_asset>().equal_range(boost::make_tuple(dividend_data.dividend_distribution_account));
auto previous_distribution_account_balance_range =
@ -786,7 +792,8 @@ void schedule_pending_dividend_balances(database& db,
share_type remaining_balance_of_dividend_asset = total_balance_of_dividend_asset;
for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_account_balance_range.first, holder_account_balance_range.second))
if (holder_balance_object.owner != dividend_data.dividend_distribution_account)
if (holder_balance_object.owner != dividend_data.dividend_distribution_account &&
holder_balance_object.balance.value)
{
// TODO: if holder_balance_object.owner is able to accept payout_asset_type
fc::uint128_t amount_to_credit(remaining_amount_to_distribute.value);
@ -902,6 +909,8 @@ void process_dividend_assets(database& db)
if (dividend_holder_asset_obj.dividend_data_id)
{
const asset_dividend_data_object& dividend_data = dividend_holder_asset_obj.dividend_data(db);
const account_object& dividend_distribution_account_object = dividend_data.dividend_distribution_account(db);
schedule_pending_dividend_balances(db, dividend_holder_asset_obj, dividend_data,
balance_index, distributed_dividend_balance_index, pending_payout_balance_index);
fc::time_point_sec current_head_block_time = db.head_block_time();
@ -923,10 +932,7 @@ void process_dividend_assets(database& db)
// and use this map to keep track of the total amount of each asset paid out.
// Afterwards, we decrease the distribution account's balance by the total amount paid out,
// and modify the distributed_balances accordingly
#ifndef NDEBUG
// for debugging, sum up our payouts here
std::map<asset_id_type, share_type> amounts_paid_out_by_asset;
#endif
auto pending_payouts_range =
pending_payout_balance_index.indices().get<by_dividend_account_payout>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id));
@ -935,6 +941,20 @@ void process_dividend_assets(database& db)
// virtual op
flat_set<asset> payouts_for_this_holder;
fc::optional<account_id_type> last_holder_account_id;
// cache the assets the distribution account is approved to send, we will be asking
// for these often
flat_map<asset_id_type, bool> approved_assets; // assets that the dividend distribution account is authorized to send/receive
auto is_asset_approved_for_distribution_account = [&](const asset_id_type& asset_id) {
auto approved_assets_iter = approved_assets.find(asset_id);
if (approved_assets_iter != approved_assets.end())
return approved_assets_iter->second;
bool is_approved = is_authorized_asset(db, dividend_distribution_account_object,
asset_id(db));
approved_assets[asset_id] = is_approved;
return is_approved;
};
for (auto pending_balance_object_iter = pending_payouts_range.first; pending_balance_object_iter != pending_payouts_range.second; )
{
const pending_dividend_payout_balance_object& pending_balance_object = *pending_balance_object_iter;
@ -947,26 +967,31 @@ void process_dividend_assets(database& db)
payouts_for_this_holder));
ilog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name));
payouts_for_this_holder.clear();
last_holder_account_id.reset();
}
ilog("Processing payout of ${asset} to account ${account}",
("asset", asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type))
("account", pending_balance_object.owner(db).name));
db.adjust_balance(pending_balance_object.owner,
asset(pending_balance_object.pending_balance,
pending_balance_object.dividend_payout_asset_type));
payouts_for_this_holder.insert(asset(pending_balance_object.pending_balance,
pending_balance_object.dividend_payout_asset_type));
last_holder_account_id = pending_balance_object.owner;
#ifndef NDEBUG
amounts_paid_out_by_asset[pending_balance_object.dividend_payout_asset_type] += pending_balance_object.pending_balance;
#endif
if (is_authorized_asset(db, pending_balance_object.owner(db), pending_balance_object.dividend_payout_asset_type(db)) &&
is_asset_approved_for_distribution_account(pending_balance_object.dividend_payout_asset_type))
{
ilog("Processing payout of ${asset} to account ${account}",
("asset", asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type))
("account", pending_balance_object.owner(db).name));
db.adjust_balance(pending_balance_object.owner,
asset(pending_balance_object.pending_balance,
pending_balance_object.dividend_payout_asset_type));
payouts_for_this_holder.insert(asset(pending_balance_object.pending_balance,
pending_balance_object.dividend_payout_asset_type));
last_holder_account_id = pending_balance_object.owner;
amounts_paid_out_by_asset[pending_balance_object.dividend_payout_asset_type] += pending_balance_object.pending_balance;
db.modify(pending_balance_object, [&]( pending_dividend_payout_balance_object& pending_balance ){
pending_balance.pending_balance = 0;
});
}
++pending_balance_object_iter;
db.modify(pending_balance_object, [&]( pending_dividend_payout_balance_object& pending_balance ){
pending_balance.pending_balance = 0;
});
}
// we will always be left with the last holder's data, generate the virtual op for it now.
if (last_holder_account_id)
@ -979,34 +1004,25 @@ void process_dividend_assets(database& db)
}
// now debit the total amount of dividends paid out from the distribution account
auto distributed_balance_range =
distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id));
// and reduce the distributed_balances accordingly
#ifndef NDEBUG
// validate that we actually paid out exactly as much as we had planned to
assert(amounts_paid_out_by_asset.size() == std::distance(distributed_balance_range.first, distributed_balance_range.second));
if (amounts_paid_out_by_asset.size() == std::distance(distributed_balance_range.first, distributed_balance_range.second))
for (const auto& value : amounts_paid_out_by_asset)
{
auto distributed_balance_object_iter = distributed_balance_range.first;
for (const auto& asset_and_amount_paid_out : amounts_paid_out_by_asset)
{
assert(distributed_balance_object_iter->dividend_payout_asset_type == asset_and_amount_paid_out.first);
assert(distributed_balance_object_iter->balance_at_last_maintenance_interval == asset_and_amount_paid_out.second);
++distributed_balance_object_iter;
}
}
#endif
const asset_id_type& asset_paid_out = value.first;
const share_type& amount_paid_out = value.second;
for (auto distributed_balance_object_iter = distributed_balance_range.first; distributed_balance_object_iter != distributed_balance_range.second; )
{
const distributed_dividend_balance_object& distributed_balance_object = *distributed_balance_object_iter;
db.adjust_balance(dividend_data.dividend_distribution_account,
asset(-distributed_balance_object.balance_at_last_maintenance_interval,
distributed_balance_object.dividend_payout_asset_type));
++distributed_balance_object_iter;
db.modify(distributed_balance_object, [&]( distributed_dividend_balance_object& obj ){
obj.balance_at_last_maintenance_interval = 0; // now they've been paid out, reset to zero
});
asset(-amount_paid_out,
asset_paid_out));
auto distributed_balance_iter =
distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().find(boost::make_tuple(dividend_holder_asset_obj.id,
asset_paid_out));
assert(distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end());
if (distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end())
db.modify(*distributed_balance_iter, [&]( distributed_dividend_balance_object& obj ){
obj.balance_at_last_maintenance_interval -= amount_paid_out; // now they've been paid out, reset to zero
});
}
// now schedule the next payout time

View file

@ -392,8 +392,9 @@ namespace graphene { namespace chain {
*/
typedef generic_index<account_object, account_multi_index_type> account_index;
struct by_dividend_payout_account{};
struct by_dividend_account_payout{};
struct by_dividend_payout_account{}; // use when calculating pending payouts
struct by_dividend_account_payout{}; // use when doing actual payouts
struct by_account_dividend_payout{}; // use in get_full_accounts()
/**
* @ingroup object_index
@ -417,6 +418,14 @@ namespace graphene { namespace chain {
member<pending_dividend_payout_balance_object, account_id_type, &pending_dividend_payout_balance_object::owner>,
member<pending_dividend_payout_balance_object, asset_id_type, &pending_dividend_payout_balance_object::dividend_payout_asset_type>
>
>,
ordered_unique< tag<by_account_dividend_payout>,
composite_key<
pending_dividend_payout_balance_object,
member<pending_dividend_payout_balance_object, account_id_type, &pending_dividend_payout_balance_object::owner>,
member<pending_dividend_payout_balance_object, asset_id_type, &pending_dividend_payout_balance_object::dividend_holder_asset_type>,
member<pending_dividend_payout_balance_object, asset_id_type, &pending_dividend_payout_balance_object::dividend_payout_asset_type>
>
>
>
> pending_dividend_payout_balance_object_multi_index_type;

View file

@ -1112,6 +1112,8 @@ BOOST_AUTO_TEST_CASE( uia_fees )
}
}
BOOST_FIXTURE_TEST_SUITE( dividend_tests, database_fixture )
BOOST_AUTO_TEST_CASE( create_dividend_uia )
{
using namespace graphene;
@ -1133,60 +1135,6 @@ BOOST_AUTO_TEST_CASE( create_dividend_uia )
PUSH_TX( db, trx, ~0 );
trx.operations.clear();
}
generate_block();
// it 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 * 7; // one week
trx.operations.push_back(op);
set_expiration(db, trx);
PUSH_TX( db, trx, ~0 );
trx.operations.clear();
}
generate_block();
//const auto& test_readback = get_asset("TEST");
//BOOST_REQUIRE(test_readback.dividend_data_id);
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 * 7);
}
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; // one day
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);
}
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
BOOST_CHECK_EQUAL(dividend_distribution_account.name, "dividend-dividend-distribution");
BOOST_TEST_MESSAGE("Creating test accounts");
create_account("alice");
@ -1194,12 +1142,6 @@ BOOST_AUTO_TEST_CASE( create_dividend_uia )
create_account("carol");
create_account("dave");
create_account("frank");
generate_block();
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");
BOOST_TEST_MESSAGE("Creating test asset");
{
@ -1220,7 +1162,90 @@ BOOST_AUTO_TEST_CASE( create_dividend_uia )
}
generate_block();
// it should not yet be a divdend asset
// 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");
} 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);
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);
}
} 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)
@ -1235,49 +1260,12 @@ BOOST_AUTO_TEST_CASE( create_dividend_uia )
trx.operations.clear();
};
// 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(); // get the maintenance skip slots out of the way
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
// for (const auto& pending_payout : db.get_index_type<pending_dividend_payout_balance_object_index>().indices())
// dlog("In test, pending payout: ${account_name} -> ${amount}", ("account_name", pending_payout.owner(db).name)("amount", pending_payout.pending_balance));
dlog("Test asset object symbol is ${symbol}", ("symbol", test_asset_object.get_id()(db).symbol));
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);
};
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);
auto advance_to_next_payout_time = [&]() {
// Advance to the next upcoming payout time
@ -1299,6 +1287,45 @@ BOOST_AUTO_TEST_CASE( create_dividend_uia )
}
};
// 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();
@ -1333,8 +1360,111 @@ BOOST_AUTO_TEST_CASE( create_dividend_uia )
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( 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 pending payments were scheduled");
verify_pending_balance(alice, test_asset_object, 1000);
BOOST_TEST_MESSAGE("Removing the payout interval");
{
@ -1358,6 +1488,7 @@ BOOST_AUTO_TEST_CASE( create_dividend_uia )
throw;
}
}
BOOST_AUTO_TEST_SUITE_END() // end dividend_tests suite
BOOST_AUTO_TEST_CASE( cancel_limit_order_test )
{ try {