Move witness pay to VBO, update test #142

This commit is contained in:
theoreticalbts 2015-07-15 12:03:10 -04:00
parent b24006cca3
commit a751d90e00
17 changed files with 126 additions and 157 deletions

View file

@ -21,6 +21,7 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
namespace graphene { namespace chain {
@ -86,6 +87,54 @@ void database::adjust_core_in_orders( const account_object& acnt, asset delta )
}
}
optional< vesting_balance_id_type > database::deposit_lazy_vesting(
const optional< vesting_balance_id_type >& ovbid,
share_type amount, uint32_t req_vesting_seconds,
account_id_type req_owner,
bool require_vesting )
{
if( amount == 0 )
return optional< vesting_balance_id_type >();
fc::time_point_sec now = head_block_time();
while( true )
{
if( !ovbid.valid() )
break;
const vesting_balance_object& vbo = (*ovbid)(*this);
if( vbo.owner != req_owner )
break;
if( vbo.policy.which() != vesting_policy::tag< cdd_vesting_policy >::value )
break;
if( vbo.policy.get< cdd_vesting_policy >().vesting_seconds != req_vesting_seconds )
break;
modify( vbo, [&]( vesting_balance_object& _vbo )
{
if( require_vesting )
_vbo.deposit(now, amount);
else
_vbo.deposit_vested(now, amount);
} );
return optional< vesting_balance_id_type >();
}
const vesting_balance_object& vbo = create< vesting_balance_object >( [&]( vesting_balance_object& _vbo )
{
_vbo.owner = req_owner;
_vbo.balance = amount;
cdd_vesting_policy policy;
policy.vesting_seconds = req_vesting_seconds;
policy.coin_seconds_earned = require_vesting ? 0 : amount.value * policy.vesting_seconds;
policy.coin_seconds_earned_last_update = now;
_vbo.policy = policy;
} );
return vbo.id;
}
void database::deposit_cashback(const account_object& acct, share_type amount, bool require_vesting)
{
// If we don't have a VBO, or if it has the wrong maturity
@ -105,46 +154,43 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b
return;
}
uint32_t global_vesting_seconds = get_global_properties().parameters.cashback_vesting_period_seconds;
fc::time_point_sec now = head_block_time();
optional< vesting_balance_id_type > new_vbid = deposit_lazy_vesting(
acct.cashback_vb,
amount,
get_global_properties().parameters.cashback_vesting_period_seconds,
acct.id,
require_vesting );
while( true )
if( new_vbid.valid() )
{
if( !acct.cashback_vb.valid() )
break;
const vesting_balance_object& cashback_vb = (*acct.cashback_vb)(*this);
if( cashback_vb.policy.which() != vesting_policy::tag< cdd_vesting_policy >::value )
break;
if( cashback_vb.policy.get< cdd_vesting_policy >().vesting_seconds != global_vesting_seconds )
break;
modify( cashback_vb, [&]( vesting_balance_object& obj )
modify( acct, [&]( account_object& _acct )
{
if( require_vesting )
obj.deposit(now, amount);
else
obj.deposit_vested(now, amount);
_acct.cashback_vb = *new_vbid;
} );
return;
}
const vesting_balance_object& cashback_vb = create< vesting_balance_object >( [&]( vesting_balance_object& obj )
return;
}
void database::deposit_witness_pay(const witness_object& wit, share_type amount)
{
if( amount == 0 )
return;
optional< vesting_balance_id_type > new_vbid = deposit_lazy_vesting(
wit.pay_vb,
amount,
get_global_properties().parameters.witness_pay_vesting_seconds,
wit.witness_account,
true );
if( new_vbid.valid() )
{
obj.owner = acct.id;
obj.balance = amount;
cdd_vesting_policy policy;
policy.vesting_seconds = global_vesting_seconds;
policy.coin_seconds_earned = require_vesting? 0 : amount.value * policy.vesting_seconds;
policy.coin_seconds_earned_last_update = now;
obj.policy = policy;
} );
modify( acct, [&]( account_object& _acct )
{
_acct.cashback_vb = cashback_vb.id;
} );
modify( wit, [&]( witness_object& _wit )
{
_wit.pay_vb = *new_vbid;
} );
}
return;
}

View file

@ -72,11 +72,6 @@ void database::debug_dump()
total_balances[asset_obj.id] += asset_obj.dynamic_asset_data_id(db).accumulated_fees;
total_balances[asset_id_type()] += asset_obj.dynamic_asset_data_id(db).fee_pool;
}
for( const witness_object& witness_obj : db.get_index_type<witness_index>().indices() )
{
//idump((witness_obj));
total_balances[asset_id_type()] += witness_obj.accumulated_income;
}
if( total_balances[asset_id_type()].value != core_asset_data.current_supply.value )
{
edump( (total_balances[asset_id_type()].value)(core_asset_data.current_supply.value ));

View file

@ -142,7 +142,6 @@ void database::initialize_evaluators()
register_evaluator<proposal_update_evaluator>();
register_evaluator<proposal_delete_evaluator>();
register_evaluator<witness_create_evaluator>();
register_evaluator<witness_withdraw_pay_evaluator>();
register_evaluator<vesting_balance_create_evaluator>();
register_evaluator<vesting_balance_withdraw_evaluator>();
register_evaluator<withdraw_permission_create_evaluator>();

View file

@ -63,11 +63,12 @@ void database::update_signing_witness(const witness_object& signing_witness, con
_dpo.witness_budget -= witness_pay;
} );
deposit_witness_pay( signing_witness, witness_pay );
modify( signing_witness, [&]( witness_object& _wit )
{
_wit.previous_secret = new_block.previous_secret;
_wit.next_secret_hash = new_block.next_secret_hash;
_wit.accumulated_income += witness_pay;
} );
}

View file

@ -133,6 +133,7 @@
#define GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS 32
#define GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t( 10) )
#define GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS (60*60*24)
#define GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(500) * 1000 )
#define GRAPHENE_MAX_INTEREST_APR uint16_t( 10000 )

View file

@ -326,8 +326,30 @@ namespace graphene { namespace chain {
*/
void adjust_core_in_orders( const account_object& acnt, asset delta );
/**
* @brief Helper to make lazy deposit to CDD VBO.
*
* If the given optional VBID is not valid(),
* or it does not have a CDD vesting policy,
* or the owner / vesting_seconds of the policy
* does not match the parameter, then credit amount
* to newly created VBID and return it.
*
* Otherwise, credit amount to ovbid.
*
* @return ID of newly created VBO, but only if VBO was created.
*/
optional< vesting_balance_id_type > deposit_lazy_vesting(
const optional< vesting_balance_id_type >& ovbid,
share_type amount,
uint32_t req_vesting_seconds,
account_id_type req_owner,
bool require_vesting );
// helper to handle cashback rewards
void deposit_cashback(const account_object& acct, share_type amount, bool require_vesting = true);
// helper to handle witness pay
void deposit_witness_pay(const witness_object& wit, share_type amount);
//////////////////// db_debug.cpp ////////////////////

View file

@ -102,7 +102,6 @@ namespace graphene { namespace chain {
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_publish_feed );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( committee_member_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( witness_create );
//GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( witness_withdraw_pay );
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_create );

View file

@ -58,6 +58,7 @@ namespace graphene { namespace chain {
bool count_non_member_votes = true; ///< set to false to restrict voting privlegages to member accounts
bool allow_non_member_whitelists = false; ///< true if non-member accounts may set whitelists and blacklists; false otherwise
share_type witness_pay_per_block = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK; ///< CORE to be allocated to witnesses (per block)
uint32_t witness_pay_vesting_seconds = GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS; ///< vesting_seconds parameter for witness VBO's
share_type worker_budget_per_day = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY; ///< CORE to be allocated to workers (per day)
uint16_t max_predicate_opcode = GRAPHENE_DEFAULT_MAX_ASSERT_OPCODE; ///< predicate_opcode must be less than this number
share_type fee_liquidation_threshold = GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD; ///< value in CORE at which accumulated fees in blockchain-issued market assets should be liquidated

View file

@ -44,7 +44,6 @@ namespace graphene { namespace chain {
asset_global_settle_operation,
asset_publish_feed_operation,
witness_create_operation,
witness_withdraw_pay_operation,
proposal_create_operation,
proposal_update_operation,
proposal_delete_operation,

View file

@ -25,35 +25,10 @@ namespace graphene { namespace chain {
void validate()const;
};
/**
* @ingroup operations
* Used to move witness pay from accumulated_income to their account balance.
*
* TODO: remove this operation, send witness pay into a vesting balance object and
* have the witness claim the funds from there.
*/
struct witness_withdraw_pay_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
/// The account to pay. Must match from_witness->witness_account. This account pays the fee for this operation.
account_id_type to_account;
witness_id_type from_witness;
share_type amount;
account_id_type fee_payer()const { return to_account; }
void validate()const;
};
/// TODO: witness_resign_operation : public base_operation
} } // graphene::chain
FC_REFLECT( graphene::chain::witness_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::witness_withdraw_pay_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(url)(block_signing_key)(initial_secret) )
FC_REFLECT( graphene::chain::witness_withdraw_pay_operation, (fee)(from_witness)(to_account)(amount) )

View file

@ -30,15 +30,4 @@ namespace graphene { namespace chain {
object_id_type do_apply( const witness_create_operation& o );
};
class witness_withdraw_pay_evaluator : public evaluator<witness_withdraw_pay_evaluator>
{
public:
typedef witness_withdraw_pay_operation operation_type;
void_result do_evaluate( const operation_type& o );
void_result do_apply( const operation_type& o );
const witness_object* witness;
const account_object* to_account;
};
} } // graphene::chain

View file

@ -35,7 +35,7 @@ namespace graphene { namespace chain {
public_key_type signing_key;
secret_hash_type next_secret_hash;
secret_hash_type previous_secret;
share_type accumulated_income;
optional< vesting_balance_id_type > pay_vb;
vote_id_type vote_id;
string url;
@ -62,6 +62,6 @@ FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object),
(signing_key)
(next_secret_hash)
(previous_secret)
(accumulated_income)
(pay_vb)
(vote_id)
(url) )

View file

@ -3,17 +3,10 @@
namespace graphene { namespace chain {
void witness_create_operation::validate() const
{
FC_ASSERT(fee.amount >= 0);
FC_ASSERT(url.size() < GRAPHENE_MAX_URL_LENGTH );
}
void witness_withdraw_pay_operation::validate() const
{
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( amount >= 0 );
}
} } // graphene::chain

View file

@ -46,30 +46,4 @@ object_id_type witness_create_evaluator::do_apply( const witness_create_operatio
return new_witness_object.id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result witness_withdraw_pay_evaluator::do_evaluate(const witness_withdraw_pay_evaluator::operation_type& o)
{ try {
database& d = db();
witness = &d.get(o.from_witness);
FC_ASSERT( o.to_account == witness->witness_account );
FC_ASSERT( o.amount <= witness->accumulated_income, "Attempting to withdraw ${w}, but witness has only earned ${e}.",
("w", o.amount)("e", witness->accumulated_income) );
to_account = &d.get(o.to_account);
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result witness_withdraw_pay_evaluator::do_apply(const witness_withdraw_pay_evaluator::operation_type& o)
{ try {
database& d = db();
d.adjust_balance(o.to_account, asset(o.amount));
d.modify(*witness, [&o](witness_object& w) {
w.accumulated_income -= o.amount;
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
} } // graphene::chain

View file

@ -2196,8 +2196,6 @@ operation wallet_api::get_prototype_operation(string operation_name)
return graphene::chain::committee_member_create_operation();
if (operation_name == "witness_create_operation")
return graphene::chain::witness_create_operation();
if (operation_name == "witness_withdraw_pay_operation")
return graphene::chain::witness_withdraw_pay_operation();
if (operation_name == "committee_member_update_global_parameters_operation")
return graphene::chain::committee_member_update_global_parameters_operation();
if (operation_name == "transfer_operation")

View file

@ -181,10 +181,6 @@ void database_fixture::verify_asset_supplies( const database& db )
total_balances[bad.options.short_backing_asset] += bad.settlement_fund;
}
}
for( const witness_object& witness_obj : db.get_index_type<witness_index>().indices() )
{
total_balances[asset_id_type()] += witness_obj.accumulated_income;
}
for( const vesting_balance_object& vbo : db.get_index_type< simple_index<vesting_balance_object> >() )
total_balances[ vbo.balance.asset_id ] += vbo.balance.amount;

View file

@ -1100,7 +1100,7 @@ BOOST_AUTO_TEST_CASE( fill_order )
//o.calculate_fee(db.current_fee_schedule());
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test )
BOOST_AUTO_TEST_CASE( witness_pay_test )
{ try {
// there is an immediate maintenance interval in the first block
// which will initialize last_budget_time
@ -1112,9 +1112,17 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test )
transfer(account_id_type()(db), get_account("init3"), asset(20*CORE));
generate_block();
auto last_witness_vbo_balance = [&]() -> share_type
{
const witness_object& wit = db.fetch_block_by_number(db.head_block_num())->witness(db);
if( !wit.pay_vb.valid() )
return 0;
return (*wit.pay_vb)(db).balance.amount;
};
const asset_object* core = &asset_id_type()(db);
const account_object* nathan = &get_account("nathan");
enable_fees();//105000000);
enable_fees();
BOOST_CHECK_GT(db.current_fee_schedule().get<account_upgrade_operation>().membership_lifetime_fee, 0);
// Based on the size of the reserve fund later in the test, the witness budget will be set to this value
const uint64_t ref_budget =
@ -1156,9 +1164,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test )
generate_block();
nathan = &get_account("nathan");
core = &asset_id_type()(db);
const witness_object* witness = &db.fetch_block_by_number(db.head_block_num())->witness(db);
BOOST_CHECK_EQUAL(witness->accumulated_income.value, 0);
BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, 0 );
auto schedule_maint = [&]()
{
@ -1174,8 +1180,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test )
while( db.head_block_num() < 30 )
{
generate_block();
witness = &db.fetch_block_by_number(db.head_block_num())->witness(db);
BOOST_CHECK_EQUAL( witness->accumulated_income.value, 0 );
BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, 0 );
}
BOOST_CHECK_EQUAL( db.head_block_num(), 30 );
// maintenance will be in block 31. time of block 31 - time of block 1 = 30 * 5 seconds.
@ -1186,51 +1191,27 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test )
generate_block();
BOOST_CHECK_EQUAL( core->reserved(db).value, 999999406 );
BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, ref_budget );
witness = &db.fetch_block_by_number(db.head_block_num())->witness(db);
// first witness paid from old budget (so no pay)
BOOST_CHECK_EQUAL( witness->accumulated_income.value, 0 );
BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, 0 );
// second witness finally gets paid!
generate_block();
witness = &db.fetch_block_by_number(db.head_block_num())->witness(db);
const witness_object* paid_witness = witness;
BOOST_CHECK_EQUAL( witness->accumulated_income.value, witness_ppb );
BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, witness_ppb );
BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, ref_budget - witness_ppb );
generate_block();
witness = &db.fetch_block_by_number(db.head_block_num())->witness(db);
BOOST_CHECK_EQUAL( witness->accumulated_income.value, witness_ppb );
BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, witness_ppb );
BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, ref_budget - 2 * witness_ppb );
generate_block();
witness = &db.fetch_block_by_number(db.head_block_num())->witness(db);
BOOST_CHECK_LT( witness->accumulated_income.value, witness_ppb );
BOOST_CHECK_EQUAL( witness->accumulated_income.value, ref_budget - 2 * witness_ppb );
BOOST_CHECK_LT( last_witness_vbo_balance().value, witness_ppb );
BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, ref_budget - 2 * witness_ppb );
BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, 0 );
generate_block();
witness = &db.fetch_block_by_number(db.head_block_num())->witness(db);
BOOST_CHECK_EQUAL( witness->accumulated_income.value, 0 );
BOOST_CHECK_EQUAL( last_witness_vbo_balance().value, 0 );
BOOST_CHECK_EQUAL( db.get_dynamic_global_properties().witness_budget.value, 0 );
trx.set_expiration(db.head_block_time() + GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION);
// Withdraw the witness's pay
enable_fees();//1);
witness = paid_witness;
witness_withdraw_pay_operation wop;
wop.from_witness = witness->id;
wop.to_account = witness->witness_account;
wop.amount = witness->accumulated_income;
trx.operations.push_back(wop);
REQUIRE_THROW_WITH_VALUE(wop, amount, witness->accumulated_income.value * 2);
trx.operations.back() = wop;
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
trx.validate();
db.push_transaction(trx, database::skip_authority_check);
trx.clear();
BOOST_CHECK_EQUAL(get_balance(witness->witness_account(db), *core), witness_ppb );
BOOST_CHECK_EQUAL(core->reserved(db).value, 999999406 );
BOOST_CHECK_EQUAL(witness->accumulated_income.value, 0);
} FC_LOG_AND_RETHROW() }
/**