SON118-Add Budget for SON (#165)
* SON118-Add Budget for SON * SON118 - Compilation errors fix * SON118 - Proper commenting around pay_sons function * SON118 - Comment correction, SON statistics object implementation type correction * SON118 - Add missing index init and reflect enums * SON118 - Correcting the indentation * SON118 SON144 - Add unit test, code fixes and resolve failures for existing tests * SON118 SON144 - Removing extra spaces added
This commit is contained in:
parent
b3b994c6ea
commit
ee7aae56da
14 changed files with 343 additions and 3 deletions
|
|
@ -114,6 +114,7 @@ add_library( graphene_chain
|
|||
affiliate_payout.cpp
|
||||
|
||||
son_evaluator.cpp
|
||||
son_object.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
|
|
|
|||
|
|
@ -317,6 +317,7 @@ void database::initialize_indexes()
|
|||
|
||||
add_index< primary_index<lottery_balance_index > >();
|
||||
add_index< primary_index<sweeps_vesting_balance_index > >();
|
||||
add_index< primary_index<son_stats_index > >();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,6 +117,53 @@ void database::update_worker_votes()
|
|||
}
|
||||
}
|
||||
|
||||
void database::pay_sons()
|
||||
{
|
||||
time_point_sec now = head_block_time();
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
// Current requirement is that we have to pay every 24 hours, so the following check
|
||||
if( dpo.son_budget.value > 0 && now - dpo.last_son_payout_time >= fc::days(1)) {
|
||||
uint64_t total_txs_signed = 0;
|
||||
share_type son_budget = dpo.son_budget;
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &total_txs_signed](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
total_txs_signed += s.txs_signed;
|
||||
});
|
||||
|
||||
|
||||
// Now pay off each SON proportional to the number of transactions signed.
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &total_txs_signed, &dpo, &son_budget](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
if(s.txs_signed > 0){
|
||||
auto son_params = get_global_properties().parameters;
|
||||
share_type pay = (s.txs_signed * son_budget.value)/total_txs_signed;
|
||||
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find( s.owner );
|
||||
modify( *son_obj, [&]( son_object& _son_obj)
|
||||
{
|
||||
_son_obj.pay_son_fee(pay, *this);
|
||||
});
|
||||
//Remove the amount paid out to SON from global SON Budget
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.son_budget -= pay;
|
||||
} );
|
||||
//Reset the tx counter in each son statistics object
|
||||
modify( s, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.txs_signed = 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
//Note the last son pay out time
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.last_son_payout_time = now;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::pay_workers( share_type& budget )
|
||||
{
|
||||
// ilog("Processing payroll! Available budget is ${b}", ("b", budget));
|
||||
|
|
@ -505,6 +552,21 @@ void database::process_budget()
|
|||
rec.witness_budget = witness_budget;
|
||||
available_funds -= witness_budget;
|
||||
|
||||
// We should not factor-in the son budget before SON HARDFORK
|
||||
share_type son_budget = 0;
|
||||
if(now >= HARDFORK_SON_TIME){
|
||||
// Before making a budget we should pay out SONs for the last day
|
||||
// This function should check if its time to pay sons
|
||||
// and modify the global son funds accordingly, whatever is left is passed on to next budget
|
||||
pay_sons();
|
||||
rec.leftover_son_funds = dpo.son_budget;
|
||||
available_funds += rec.leftover_son_funds;
|
||||
son_budget = gpo.parameters.son_pay_daily_max();
|
||||
son_budget = std::min(son_budget, available_funds);
|
||||
rec.son_budget = son_budget;
|
||||
available_funds -= son_budget;
|
||||
}
|
||||
|
||||
fc::uint128_t worker_budget_u128 = gpo.parameters.worker_budget_per_day.value;
|
||||
worker_budget_u128 *= uint64_t(time_to_maint);
|
||||
worker_budget_u128 /= 60*60*24;
|
||||
|
|
@ -524,9 +586,11 @@ void database::process_budget()
|
|||
|
||||
rec.supply_delta = rec.witness_budget
|
||||
+ rec.worker_budget
|
||||
+ rec.son_budget
|
||||
- rec.leftover_worker_funds
|
||||
- rec.from_accumulated_fees
|
||||
- rec.from_unused_witness_budget;
|
||||
- rec.from_unused_witness_budget
|
||||
- rec.leftover_son_funds;
|
||||
|
||||
modify(core, [&]( asset_dynamic_data_object& _core )
|
||||
{
|
||||
|
|
@ -535,9 +599,11 @@ void database::process_budget()
|
|||
assert( rec.supply_delta ==
|
||||
witness_budget
|
||||
+ worker_budget
|
||||
+ son_budget
|
||||
- leftover_worker_funds
|
||||
- _core.accumulated_fees
|
||||
- dpo.witness_budget
|
||||
- dpo.son_budget
|
||||
);
|
||||
_core.accumulated_fees = 0;
|
||||
});
|
||||
|
|
@ -548,6 +614,7 @@ void database::process_budget()
|
|||
// available_funds, we replace it with witness_budget
|
||||
// instead of adding it.
|
||||
_dpo.witness_budget = witness_budget;
|
||||
_dpo.son_budget = son_budget;
|
||||
_dpo.last_budget_time = now;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -46,9 +46,11 @@ struct budget_record
|
|||
// sinks of budget, should sum up to total_budget
|
||||
share_type witness_budget = 0;
|
||||
share_type worker_budget = 0;
|
||||
share_type son_budget = 0;
|
||||
|
||||
// unused budget
|
||||
share_type leftover_worker_funds = 0;
|
||||
share_type leftover_son_funds = 0;
|
||||
|
||||
// change in supply due to budget operations
|
||||
share_type supply_delta = 0;
|
||||
|
|
|
|||
|
|
@ -232,8 +232,8 @@
|
|||
#define TOURNAMENT_MAX_START_TIME_IN_FUTURE (60*60*24*7*4) // 1 month
|
||||
#define TOURNAMENT_MAX_START_DELAY (60*60*24*7) // 1 week
|
||||
#define MIN_SON_MEMBER_COUNT 15
|
||||
|
||||
#define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT)
|
||||
#define SWEEPS_DEFAULT_DISTRIBUTION_ASSET (graphene::chain::asset_id_type(0))
|
||||
#define SWEEPS_VESTING_BALANCE_MULTIPLIER 100000000
|
||||
#define SWEEPS_ACCUMULATOR_ACCOUNT (graphene::chain::account_id_type(0))
|
||||
#define MIN_SON_PAY_DAILY_MAX (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(200))
|
||||
|
|
|
|||
|
|
@ -516,6 +516,7 @@ namespace graphene { namespace chain {
|
|||
void initialize_budget_record( fc::time_point_sec now, budget_record& rec )const;
|
||||
void process_budget();
|
||||
void pay_workers( share_type& budget );
|
||||
void pay_sons();
|
||||
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
|
||||
void update_active_witnesses();
|
||||
void update_active_committee_members();
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@ namespace graphene { namespace chain {
|
|||
time_point_sec next_maintenance_time;
|
||||
time_point_sec last_budget_time;
|
||||
share_type witness_budget;
|
||||
//Last SON Payout time, it can be different to the maintenance interval time
|
||||
time_point_sec last_son_payout_time;
|
||||
share_type son_budget = 0;
|
||||
uint32_t accounts_registered_this_interval = 0;
|
||||
/**
|
||||
* Every time a block is missed this increases by
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ namespace graphene { namespace chain {
|
|||
optional< uint16_t > sweeps_distribution_percentage;
|
||||
optional< asset_id_type > sweeps_distribution_asset;
|
||||
optional< account_id_type > sweeps_vesting_accumulator_account;
|
||||
optional < uint32_t > son_pay_daily_max;
|
||||
};
|
||||
|
||||
struct chain_parameters
|
||||
|
|
@ -124,6 +125,9 @@ namespace graphene { namespace chain {
|
|||
inline uint16_t son_count()const {
|
||||
return extensions.value.son_count.valid() ? *extensions.value.son_count : MIN_SON_MEMBER_COUNT;
|
||||
}
|
||||
inline uint16_t son_pay_daily_max()const {
|
||||
return extensions.value.son_pay_daily_max.valid() ? *extensions.value.son_pay_daily_max : MIN_SON_PAY_DAILY_MAX;
|
||||
}
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -138,6 +142,7 @@ FC_REFLECT( graphene::chain::parameter_extension,
|
|||
(sweeps_distribution_percentage)
|
||||
(sweeps_distribution_asset)
|
||||
(sweeps_vesting_accumulator_account)
|
||||
(son_pay_daily_max)
|
||||
)
|
||||
|
||||
FC_REFLECT( graphene::chain::chain_parameters,
|
||||
|
|
|
|||
|
|
@ -174,7 +174,8 @@ namespace graphene { namespace chain {
|
|||
impl_betting_market_position_object_type,
|
||||
impl_global_betting_statistics_object_type,
|
||||
impl_lottery_balance_object_type,
|
||||
impl_sweeps_vesting_balance_object_type
|
||||
impl_sweeps_vesting_balance_object_type,
|
||||
impl_son_statistics_object_type
|
||||
};
|
||||
|
||||
//typedef fc::unsigned_int object_id_type;
|
||||
|
|
@ -256,6 +257,7 @@ namespace graphene { namespace chain {
|
|||
class global_betting_statistics_object;
|
||||
class lottery_balance_object;
|
||||
class sweeps_vesting_balance_object;
|
||||
class son_statistics_object;
|
||||
|
||||
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
|
||||
|
|
@ -284,6 +286,7 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< implementation_ids, impl_global_betting_statistics_object_type, global_betting_statistics_object > global_betting_statistics_id_type;
|
||||
typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type;
|
||||
|
||||
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
|
||||
typedef fc::ripemd160 block_id_type;
|
||||
|
|
@ -441,6 +444,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
|||
(impl_global_betting_statistics_object_type)
|
||||
(impl_lottery_balance_object_type)
|
||||
(impl_sweeps_vesting_balance_object_type)
|
||||
(impl_son_statistics_object_type)
|
||||
)
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::share_type )
|
||||
|
|
|
|||
|
|
@ -6,6 +6,25 @@
|
|||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class son_statistics_object
|
||||
* @ingroup object
|
||||
* @ingroup implementation
|
||||
*
|
||||
* This object contains regularly updated statistical data about an SON. It is provided for the purpose of
|
||||
* separating the SON transaction data that changes frequently from the SON object data that is mostly static.
|
||||
*/
|
||||
class son_statistics_object : public graphene::db::abstract_object<son_statistics_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_son_statistics_object_type;
|
||||
|
||||
son_id_type owner;
|
||||
// Transactions signed since the last son payouts
|
||||
uint64_t txs_signed = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class son_object
|
||||
* @brief tracks information about a SON account.
|
||||
|
|
@ -24,6 +43,9 @@ namespace graphene { namespace chain {
|
|||
vesting_balance_id_type deposit;
|
||||
public_key_type signing_key;
|
||||
vesting_balance_id_type pay_vb;
|
||||
son_statistics_id_type statistics;
|
||||
|
||||
void pay_son_fee(share_type pay, database& db);
|
||||
};
|
||||
|
||||
struct by_account;
|
||||
|
|
@ -43,7 +65,22 @@ namespace graphene { namespace chain {
|
|||
>
|
||||
>;
|
||||
using son_index = generic_index<son_object, son_multi_index_type>;
|
||||
|
||||
using son_stats_multi_index_type = multi_index_container<
|
||||
son_statistics_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >
|
||||
>
|
||||
>;
|
||||
|
||||
using son_stats_index = generic_index<son_statistics_object, son_stats_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object),
|
||||
(son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_statistics_object,
|
||||
(graphene::db::object),
|
||||
(owner)
|
||||
(txs_signed)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ object_id_type create_son_evaluator::do_apply(const son_create_operation& op)
|
|||
obj.deposit = op.deposit;
|
||||
obj.signing_key = op.signing_key;
|
||||
obj.pay_vb = op.pay_vb;
|
||||
obj.statistics = db().create<son_statistics_object>([&](son_statistics_object& s){s.owner = obj.id;}).id;
|
||||
});
|
||||
return new_son_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
|
|||
8
libraries/chain/son_object.cpp
Normal file
8
libraries/chain/son_object.cpp
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
void son_object::pay_son_fee(share_type pay, database& db) {
|
||||
db.adjust_balance(son_account, pay);
|
||||
}
|
||||
}}
|
||||
|
|
@ -272,6 +272,7 @@ void database_fixture::verify_asset_supplies( const database& db )
|
|||
|
||||
total_balances[db.get_global_properties().parameters.sweeps_distribution_asset()] += sweeps_vestings / SWEEPS_VESTING_BALANCE_MULTIPLIER;
|
||||
total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget;
|
||||
total_balances[asset_id_type()] += db.get_dynamic_global_properties().son_budget;
|
||||
|
||||
for( const auto& item : total_debts )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -182,5 +182,214 @@ catch (fc::exception &e) {
|
|||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( son_pay_test )
|
||||
{
|
||||
try
|
||||
{
|
||||
const dynamic_global_property_object& dpo = db.get_dynamic_global_properties();
|
||||
const auto block_interval = db.get_global_properties().parameters.block_interval;
|
||||
BOOST_CHECK( dpo.son_budget.value == 0);
|
||||
generate_blocks(HARDFORK_SON_TIME);
|
||||
while (db.head_block_time() <= HARDFORK_SON_TIME) {
|
||||
generate_block();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
ACTORS((alice)(bob));
|
||||
// Send some core to the actors
|
||||
transfer( committee_account, alice_id, asset( 20000 * 100000) );
|
||||
transfer( committee_account, bob_id, asset( 20000 * 100000) );
|
||||
|
||||
generate_block();
|
||||
// Enable default fee schedule to collect fees
|
||||
enable_fees();
|
||||
// Make SON Budget small for testing purposes
|
||||
// Make witness budget zero so that amount can be allocated to SON
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
_gpo.parameters.extensions.value.son_pay_daily_max = 200;
|
||||
_gpo.parameters.witness_pay_per_block = 0;
|
||||
} );
|
||||
// Upgrades pay fee and this goes to reserve
|
||||
upgrade_to_lifetime_member(alice);
|
||||
upgrade_to_lifetime_member(bob);
|
||||
// Note payment time just to generate enough blocks to make budget
|
||||
auto pay_fee_time = db.head_block_time().sec_since_epoch();
|
||||
generate_block();
|
||||
// Do maintenance from the upcoming block
|
||||
auto schedule_maint = [&]()
|
||||
{
|
||||
db.modify( db.get_dynamic_global_properties(), [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.next_maintenance_time = db.head_block_time() + 1;
|
||||
} );
|
||||
};
|
||||
|
||||
// Generate enough blocks to make budget
|
||||
while( db.head_block_time().sec_since_epoch() - pay_fee_time < 100 * block_interval )
|
||||
{
|
||||
generate_block();
|
||||
}
|
||||
|
||||
// Enough blocks generated schedule maintenance now
|
||||
schedule_maint();
|
||||
// This block triggers maintenance
|
||||
generate_block();
|
||||
|
||||
// Check that the SON Budget is allocated and Witness budget is zero
|
||||
BOOST_CHECK( dpo.son_budget.value == 200);
|
||||
BOOST_CHECK( dpo.witness_budget.value == 0);
|
||||
|
||||
// Now create SONs
|
||||
std::string test_url1 = "https://create_son_test1";
|
||||
std::string test_url2 = "https://create_son_test2";
|
||||
|
||||
// create deposit vesting
|
||||
vesting_balance_id_type deposit1;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(10);
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
set_expiration(db, trx);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
deposit1 = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
// create payment vesting
|
||||
vesting_balance_id_type payment1;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(10);
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
set_expiration(db, trx);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment1 = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
// create deposit vesting
|
||||
vesting_balance_id_type deposit2;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = bob_id;
|
||||
op.owner = bob_id;
|
||||
op.amount = asset(10);
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
set_expiration(db, trx);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
deposit2 = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
// create payment vesting
|
||||
vesting_balance_id_type payment2;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = bob_id;
|
||||
op.owner = bob_id;
|
||||
op.amount = asset(10);
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
set_expiration(db, trx);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment2 = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
// alice becomes son
|
||||
{
|
||||
son_create_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.url = test_url1;
|
||||
op.deposit = deposit1;
|
||||
op.pay_vb = payment1;
|
||||
op.fee = asset(0);
|
||||
op.signing_key = alice_public_key;
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
// bob becomes son
|
||||
{
|
||||
son_create_operation op;
|
||||
op.owner_account = bob_id;
|
||||
op.url = test_url2;
|
||||
op.deposit = deposit2;
|
||||
op.pay_vb = payment2;
|
||||
op.fee = asset(0);
|
||||
op.signing_key = bob_public_key;
|
||||
trx.operations.push_back(op);
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
sign(trx, bob_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
generate_block();
|
||||
// Check if SONs are created properly
|
||||
const auto& idx = db.get_index_type<son_index>().indices().get<by_account>();
|
||||
BOOST_REQUIRE( idx.size() == 2 );
|
||||
// Alice's SON
|
||||
auto obj1 = idx.find( alice_id );
|
||||
BOOST_REQUIRE( obj1 != idx.end() );
|
||||
BOOST_CHECK( obj1->url == test_url1 );
|
||||
BOOST_CHECK( obj1->signing_key == alice_public_key );
|
||||
BOOST_CHECK( obj1->deposit.instance == deposit1.instance.value );
|
||||
BOOST_CHECK( obj1->pay_vb.instance == payment1.instance.value );
|
||||
// Bob's SON
|
||||
auto obj2 = idx.find( bob_id );
|
||||
BOOST_REQUIRE( obj2 != idx.end() );
|
||||
BOOST_CHECK( obj2->url == test_url2 );
|
||||
BOOST_CHECK( obj2->signing_key == bob_public_key );
|
||||
BOOST_CHECK( obj2->deposit.instance == deposit2.instance.value );
|
||||
BOOST_CHECK( obj2->pay_vb.instance == payment2.instance.value );
|
||||
// Get the statistics object for the SONs
|
||||
const auto& sidx = db.get_index_type<son_stats_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( sidx.size() == 2 );
|
||||
auto son_stats_obj1 = sidx.find( obj1->statistics );
|
||||
auto son_stats_obj2 = sidx.find( obj2->statistics );
|
||||
BOOST_REQUIRE( son_stats_obj1 != sidx.end() );
|
||||
BOOST_REQUIRE( son_stats_obj2 != sidx.end() );
|
||||
// Modify the transaction signed statistics of Alice's SON
|
||||
db.modify( *son_stats_obj1, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.txs_signed = 2;
|
||||
});
|
||||
// Modify the transaction signed statistics of Bob's SON
|
||||
db.modify( *son_stats_obj2, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.txs_signed = 3;
|
||||
});
|
||||
|
||||
// Note the balances before the maintenance
|
||||
int64_t obj1_balance = db.get_balance(obj1->son_account, asset_id_type()).amount.value;
|
||||
int64_t obj2_balance = db.get_balance(obj2->son_account, asset_id_type()).amount.value;
|
||||
// Next maintenance triggerred
|
||||
generate_blocks(dpo.next_maintenance_time);
|
||||
generate_block();
|
||||
// Check if the signed transaction statistics are reset for both SONs
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed, 0);
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed, 0);
|
||||
// Check that Alice and Bob are paid for signing the transactions in the previous day/cycle
|
||||
BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 80+obj1_balance);
|
||||
BOOST_REQUIRE_EQUAL(db.get_balance(obj2->son_account, asset_id_type()).amount.value, 120+obj2_balance);
|
||||
// Check the SON Budget is again allocated after maintenance
|
||||
BOOST_CHECK( dpo.son_budget.value == 200);
|
||||
BOOST_CHECK( dpo.witness_budget.value == 0);
|
||||
}FC_LOG_AND_RETHROW()
|
||||
|
||||
} BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
Loading…
Reference in a new issue