diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 5775713c..59ba30aa 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -36,4 +36,24 @@ void account_balance_object::adjust_balance(const asset& delta) balance += delta.amount; } +uint16_t account_statistics_object::calculate_bulk_discount_percent(const chain_parameters& params) const +{ + uint64_t bulk_discount_percent = 0; + if( lifetime_fees_paid >= params.bulk_discount_threshold_max ) + bulk_discount_percent = params.max_bulk_discount_percent_of_fee; + else if(params.bulk_discount_threshold_max.value != + params.bulk_discount_threshold_min.value) + { + bulk_discount_percent = + (params.max_bulk_discount_percent_of_fee * + (lifetime_fees_paid.value - + params.bulk_discount_threshold_min.value)) / + (params.bulk_discount_threshold_max.value - + params.bulk_discount_threshold_min.value); + } + assert( bulk_discount_percent <= GRAPHENE_100_PERCENT ); + + return bulk_discount_percent; +} + } } // graphene::chain diff --git a/libraries/chain/account_operations.cpp b/libraries/chain/account_operations.cpp deleted file mode 100644 index efd8e65b..00000000 --- a/libraries/chain/account_operations.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include -#include -#include - -namespace graphene { namespace chain { -} } diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index 2c8736e6..791d8488 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -89,7 +89,7 @@ void database::adjust_core_in_orders( const account_object& acnt, asset delta ) } } -void database::deposit_cashback( const account_object& acct, share_type amount ) +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 // due to a policy change, cut it loose. @@ -112,7 +112,10 @@ void database::deposit_cashback( const account_object& acct, share_type amount ) modify( cashback_vb, [&]( vesting_balance_object& obj ) { - obj.deposit( now, amount ); + if( require_vesting ) + obj.deposit(now, amount); + else + obj.deposit_vested(now, amount); } ); return; } @@ -124,7 +127,7 @@ void database::deposit_cashback( const account_object& acct, share_type amount ) cdd_vesting_policy policy; policy.vesting_seconds = global_vesting_seconds; - policy.coin_seconds_earned = 0; + policy.coin_seconds_earned = require_vesting? 0 : amount.value * policy.vesting_seconds; policy.coin_seconds_earned_last_update = now; obj.policy = policy; diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 84a3597d..0db0b6f6 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -357,86 +357,81 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g process_fees_helper(database& d, const global_property_object& gpo) : d(d), props(gpo) {} + share_type cut_fee(share_type a, uint16_t p)const + { + if( a == 0 || p == 0 ) + return 0; + if( p == GRAPHENE_100_PERCENT ) + return a; + + fc::uint128 r(a.value); + r *= p; + r /= GRAPHENE_100_PERCENT; + return r.to_uint64(); + } + + void pay_out_fees(const account_object& account, share_type core_fee_total, bool require_vesting) + { + share_type network_cut = cut_fee(core_fee_total, account.network_fee_percentage); + assert( network_cut <= core_fee_total ); + share_type burned = cut_fee(network_cut, props.parameters.burn_percent_of_fee); + share_type accumulated = network_cut - burned; + assert( accumulated + burned == network_cut ); + share_type lifetime_cut = cut_fee(core_fee_total, account.lifetime_referrer_fee_percentage); + share_type referral = core_fee_total - network_cut - lifetime_cut; + + d.modify(dynamic_asset_data_id_type()(d), [network_cut](asset_dynamic_data_object& d) { + d.accumulated_fees += network_cut; + }); + + // Potential optimization: Skip some of this math and object lookups by special casing on the account type. + // For example, if the account is a lifetime member, we can skip all this and just deposit the referral to + // it directly. + share_type referrer_cut = cut_fee(referral, account.referrer_rewards_percentage); + share_type registrar_cut = referral - referrer_cut; + + d.deposit_cashback(d.get(account.lifetime_referrer), lifetime_cut, require_vesting); + d.deposit_cashback(d.get(account.referrer), referrer_cut, require_vesting); + d.deposit_cashback(d.get(account.registrar), registrar_cut, require_vesting); + + assert( referrer_cut + registrar_cut + accumulated + burned + lifetime_cut == core_fee_total ); + } + void operator()(const account_object& a) { const account_statistics_object& stats = a.statistics(d); - auto cut_fee = [](share_type a, uint16_t p) -> share_type - { - if( a == 0 || p == 0 ) - return 0; - if( p == GRAPHENE_100_PERCENT ) - return a; - - fc::uint128 r(a.value); - r *= p; - r /= GRAPHENE_100_PERCENT; - return r.to_uint64(); - }; - if( stats.pending_fees > 0 ) { - share_type core_fee_subtotal(stats.pending_fees); - share_type bulk_cashback = share_type(0); + share_type vesting_fee_subtotal(stats.pending_fees); + share_type vested_fee_subtotal(stats.pending_vested_fees); + share_type vesting_cashback, vested_cashback; + if( stats.lifetime_fees_paid > props.parameters.bulk_discount_threshold_min && a.is_member(d.head_block_time()) ) { - uint64_t bulk_discount_percent = 0; - if( stats.lifetime_fees_paid >= props.parameters.bulk_discount_threshold_max ) - bulk_discount_percent = props.parameters.max_bulk_discount_percent_of_fee; - else if(props.parameters.bulk_discount_threshold_max.value != - props.parameters.bulk_discount_threshold_min.value) - { - bulk_discount_percent = - (props.parameters.max_bulk_discount_percent_of_fee * - (stats.lifetime_fees_paid.value - - props.parameters.bulk_discount_threshold_min.value)) / - (props.parameters.bulk_discount_threshold_max.value - - props.parameters.bulk_discount_threshold_min.value); - } - assert( bulk_discount_percent <= GRAPHENE_100_PERCENT ); - assert( bulk_discount_percent >= 0 ); + auto bulk_discount_rate = stats.calculate_bulk_discount_percent(props.parameters); + vesting_cashback = cut_fee(vesting_fee_subtotal, bulk_discount_rate); + vesting_fee_subtotal -= vesting_cashback; - bulk_cashback = cut_fee(core_fee_subtotal, bulk_discount_percent); - assert( bulk_cashback <= core_fee_subtotal ); + vested_cashback = cut_fee(vested_fee_subtotal, bulk_discount_rate); + vested_fee_subtotal -= vested_cashback; } - share_type core_fee_total = core_fee_subtotal - bulk_cashback; - share_type network_cut = cut_fee(core_fee_total, a.network_fee_percentage); - assert( network_cut <= core_fee_total ); - share_type burned = cut_fee(network_cut, props.parameters.burn_percent_of_fee); - share_type accumulated = network_cut - burned; - assert( accumulated + burned == network_cut ); - share_type lifetime_cut = cut_fee(core_fee_total, a.lifetime_referrer_fee_percentage); - share_type referral = core_fee_total - network_cut - lifetime_cut; + pay_out_fees(a, vesting_fee_subtotal, true); + d.deposit_cashback(a, vesting_cashback, true); + pay_out_fees(a, vested_fee_subtotal, false); + d.deposit_cashback(a, vested_cashback, false); - d.modify(dynamic_asset_data_id_type()(d), [network_cut](asset_dynamic_data_object& d) { - d.accumulated_fees += network_cut; - }); - - d.modify(a.statistics(d), [core_fee_total](account_statistics_object& s) { - s.lifetime_fees_paid += core_fee_total; + d.modify(stats, [vested_fee_subtotal, vesting_fee_subtotal](account_statistics_object& s) { + s.lifetime_fees_paid += vested_fee_subtotal + vesting_fee_subtotal; s.pending_fees = 0; + s.pending_vested_fees = 0; }); - - d.deposit_cashback( a, bulk_cashback ); - - // Potential optimization: Skip some of this math and object lookups by special casing on the account type. - // For example, if the account is a lifetime member, we can skip all this and just deposit the referral to - // it directly. - share_type referrer_cut = cut_fee(referral, a.referrer_rewards_percentage); - share_type registrar_cut = referral - referrer_cut; - - d.deposit_cashback(d.get(a.lifetime_referrer), lifetime_cut); - d.deposit_cashback(d.get(a.referrer), referrer_cut); - d.deposit_cashback(d.get(a.registrar), registrar_cut); - - idump((referrer_cut)(registrar_cut)(bulk_cashback)(accumulated)(burned)(lifetime_cut)(core_fee_subtotal)); - assert( referrer_cut + registrar_cut + bulk_cashback + accumulated + burned + lifetime_cut == core_fee_subtotal ); - } + } } - } fees_helper(*this, gpo); + } fee_helper(*this, gpo); - perform_account_maintenance(std::tie(tally_helper, fees_helper)); + perform_account_maintenance(std::tie(tally_helper, fee_helper)); struct clear_canary { clear_canary(vector& target): target(target){} @@ -451,9 +446,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g update_active_witnesses(); update_active_delegates(); - const global_property_object& global_properties = get_global_properties(); - if( global_properties.pending_parameters ) - modify(get_global_properties(), [](global_property_object& p) { + if( gpo.pending_parameters ) + modify(gpo, [](global_property_object& p) { p.parameters = std::move(*p.pending_parameters); p.pending_parameters.reset(); }); @@ -472,7 +466,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g } auto next_maintenance_time = get(dynamic_global_property_id_type()).next_maintenance_time; - auto maintenance_interval = get_global_properties().parameters.maintenance_interval; + auto maintenance_interval = gpo.parameters.maintenance_interval; if( next_maintenance_time <= next_block.timestamp ) { diff --git a/libraries/chain/evaluator.cpp b/libraries/chain/evaluator.cpp index 74cf5c14..46117211 100644 --- a/libraries/chain/evaluator.cpp +++ b/libraries/chain/evaluator.cpp @@ -67,7 +67,10 @@ namespace graphene { namespace chain { d.fee_pool -= core_fee_paid; }); db().modify(*fee_paying_account_statistics, [&](account_statistics_object& s) { - s.pending_fees += core_fee_paid; + if( core_fee_paid > db().get_global_properties().parameters.cashback_vesting_threshold ) + s.pending_fees += core_fee_paid; + else + s.pending_vested_fees += core_fee_paid; }); } FC_CAPTURE_AND_RETHROW() } diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index be74e87d..c41a15fc 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -62,11 +62,20 @@ namespace graphene { namespace chain { /** * Tracks the fees paid by this account which have not been disseminated to the various parties that receive * them yet (registrar, referrer, lifetime referrer, network, etc). This is used as an optimization to avoid - * doing massive amounts of uint128 math on each and every operation. + * doing massive amounts of uint128 arithmetic on each and every operation. * - * These fees will be paid out and this counter will reset during the maintenance interval. + *These fees will be paid out as vesting cash-back, and this counter will reset during the maintenance + *interval. */ share_type pending_fees; + /** + * Same as @ref pending_fees, except these fees will be paid out as pre-vested cash-back (immediately + * available for withdrawal) rather than requiring the normal vesting period. + */ + share_type pending_vested_fees; + + /// @brief Calculate the percentage discount this user receives on his fees + uint16_t calculate_bulk_discount_percent(const chain_parameters& params)const; }; /** @@ -290,5 +299,6 @@ FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (graphene::chain (most_recent_op) (total_core_in_orders) (lifetime_fees_paid) + (pending_fees)(pending_vested_fees) ) diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 020ae45d..469a78ad 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -254,7 +254,7 @@ namespace graphene { namespace chain { void adjust_core_in_orders( const account_object& acnt, asset delta ); // helper to handle cashback rewards - void deposit_cashback( const account_object& acct, share_type amount ); + void deposit_cashback(const account_object& acct, share_type amount, bool require_vesting = true); //////////////////// db_debug.cpp //////////////////// diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index f2465573..c0e32347 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -35,9 +35,9 @@ namespace graphene { namespace chain { vesting_policy_context( asset _balance, fc::time_point_sec _now, - asset _amount ) - : balance( _balance ), now( _now ), amount( _amount ) {} - + asset _amount) + : balance(_balance), now(_now), amount(_amount) {} + asset balance; fc::time_point_sec now; asset amount; @@ -53,11 +53,14 @@ namespace graphene { namespace chain { share_type begin_balance; // same asset as balance share_type total_withdrawn; // same asset as balance - asset get_allowed_withdraw( const vesting_policy_context& ctx )const; - bool is_deposit_allowed( const vesting_policy_context& ctx )const; - bool is_withdraw_allowed( const vesting_policy_context& ctx )const; - void on_deposit( const vesting_policy_context& ctx ); - void on_withdraw( const vesting_policy_context& ctx ); + asset get_allowed_withdraw(const vesting_policy_context& ctx)const; + bool is_deposit_allowed(const vesting_policy_context& ctx)const; + bool is_deposit_vested_allowed(const vesting_policy_context&)const { return false; } + bool is_withdraw_allowed(const vesting_policy_context& ctx)const; + void on_deposit(const vesting_policy_context& ctx); + void on_deposit_vested(const vesting_policy_context&) + { FC_THROW( "May not deposit vested into a linear vesting balance." ); } + void on_withdraw(const vesting_policy_context& ctx); }; struct cdd_vesting_policy @@ -71,20 +74,22 @@ namespace graphene { namespace chain { * non-destructively figure out how many coin seconds * are available. */ - fc::uint128_t compute_coin_seconds_earned( const vesting_policy_context& ctx )const; + fc::uint128_t compute_coin_seconds_earned(const vesting_policy_context& ctx)const; /** * Update coin_seconds_earned and * coin_seconds_earned_last_update fields; called by both * on_deposit() and on_withdraw(). */ - void update_coin_seconds_earned( const vesting_policy_context& ctx ); + void update_coin_seconds_earned(const vesting_policy_context& ctx); - asset get_allowed_withdraw( const vesting_policy_context& ctx )const; - bool is_deposit_allowed( const vesting_policy_context& ctx )const; - bool is_withdraw_allowed( const vesting_policy_context& ctx )const; - void on_deposit( const vesting_policy_context& ctx ); - void on_withdraw( const vesting_policy_context& ctx ); + asset get_allowed_withdraw(const vesting_policy_context& ctx)const; + bool is_deposit_allowed(const vesting_policy_context& ctx)const; + bool is_deposit_vested_allowed(const vesting_policy_context& ctx)const; + bool is_withdraw_allowed(const vesting_policy_context& ctx)const; + void on_deposit(const vesting_policy_context& ctx); + void on_deposit_vested(const vesting_policy_context& ctx); + void on_withdraw(const vesting_policy_context& ctx); }; typedef fc::static_variant< @@ -102,17 +107,21 @@ namespace graphene { namespace chain { static const uint8_t space_id = protocol_ids; static const uint8_t type_id = vesting_balance_object_type; - account_id_type owner; - asset balance; - vesting_policy policy; + account_id_type owner; + asset balance; + vesting_policy policy; vesting_balance_object() {} /** * Used to increase existing vesting balances. */ - void deposit( const fc::time_point_sec& now, const asset& amount ); - bool is_deposit_allowed( const fc::time_point_sec& now, const asset& amount )const; + void deposit(const fc::time_point_sec& now, const asset& amount); + bool is_deposit_allowed(const fc::time_point_sec& now, const asset& amount)const; + + /// @brief Deposit amount into vesting balance, making the new funds vest immediately + void deposit_vested(const fc::time_point_sec& now, const asset& amount); + bool is_deposit_vested_allowed(const fc::time_point_sec& now, const asset& amount)const; /** * Used to remove a vesting balance from the VBO. As well @@ -122,27 +131,27 @@ namespace graphene { namespace chain { * The money doesn't "go" anywhere; the caller is responsible * for crediting it to the proper account. */ - void withdraw( const fc::time_point_sec& now, const asset& amount ); - bool is_withdraw_allowed( const fc::time_point_sec& now, const asset& amount )const; + void withdraw(const fc::time_point_sec& now, const asset& amount); + bool is_withdraw_allowed(const fc::time_point_sec& now, const asset& amount)const; }; } } // graphene::chain -FC_REFLECT( graphene::chain::linear_vesting_policy, - (vesting_seconds) - (begin_date) - (begin_balance) - (total_withdrawn) -) +FC_REFLECT(graphene::chain::linear_vesting_policy, + (vesting_seconds) + (begin_date) + (begin_balance) + (total_withdrawn) + ) -FC_REFLECT( graphene::chain::cdd_vesting_policy, - (vesting_seconds) - (coin_seconds_earned) - (coin_seconds_earned_last_update) -) +FC_REFLECT(graphene::chain::cdd_vesting_policy, + (vesting_seconds) + (coin_seconds_earned) + (coin_seconds_earned_last_update) + ) -FC_REFLECT_DERIVED( graphene::chain::vesting_balance_object, (graphene::db::object), - (owner) - (balance) - (policy) -) +FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::object), + (owner) + (balance) + (policy) + ) diff --git a/libraries/chain/vesting_balance_object.cpp b/libraries/chain/vesting_balance_object.cpp index 022e892d..bfc91de5 100644 --- a/libraries/chain/vesting_balance_object.cpp +++ b/libraries/chain/vesting_balance_object.cpp @@ -20,66 +20,64 @@ namespace graphene { namespace chain { -inline bool sum_below_max_shares( const asset& a, const asset& b ) +inline bool sum_below_max_shares(const asset& a, const asset& b) { - assert( GRAPHENE_MAX_SHARE_SUPPLY + GRAPHENE_MAX_SHARE_SUPPLY > GRAPHENE_MAX_SHARE_SUPPLY ); - return ( a.amount <= GRAPHENE_MAX_SHARE_SUPPLY) + assert(GRAPHENE_MAX_SHARE_SUPPLY + GRAPHENE_MAX_SHARE_SUPPLY > GRAPHENE_MAX_SHARE_SUPPLY); + return (a.amount <= GRAPHENE_MAX_SHARE_SUPPLY) && ( b.amount <= GRAPHENE_MAX_SHARE_SUPPLY) && ((a.amount + b.amount) <= GRAPHENE_MAX_SHARE_SUPPLY) ; } -asset linear_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx ) const +asset linear_vesting_policy::get_allowed_withdraw(const vesting_policy_context& ctx) const { - if( ctx.now <= begin_date ) - return asset( 0, ctx.balance.asset_id ); - if( vesting_seconds == 0 ) + if(ctx.now <= begin_date) + return asset(0, ctx.balance.asset_id); + if(vesting_seconds == 0) return ctx.balance; int64_t elapsed_seconds = (ctx.now - begin_date).to_seconds(); // if elapsed_seconds <= 0, then ctx.now <= begin_date, // and we should have returned above. - assert( elapsed_seconds > 0 ); + assert(elapsed_seconds > 0); fc::uint128_t total_allowed = begin_balance.value; - total_allowed *= uint64_t( elapsed_seconds ); + total_allowed *= uint64_t(elapsed_seconds); total_allowed /= vesting_seconds; - if( total_allowed <= total_withdrawn.value ) - return asset( 0, ctx.balance.asset_id ); + if(total_allowed <= total_withdrawn.value) + return asset(0, ctx.balance.asset_id); total_allowed -= total_withdrawn.value; - FC_ASSERT( total_allowed <= GRAPHENE_MAX_SHARE_SUPPLY ); - return asset( total_allowed.to_uint64(), ctx.balance.asset_id ); + FC_ASSERT(total_allowed <= GRAPHENE_MAX_SHARE_SUPPLY); + return asset(total_allowed.to_uint64(), ctx.balance.asset_id); } -void linear_vesting_policy::on_deposit( const vesting_policy_context& ctx ) +void linear_vesting_policy::on_deposit(const vesting_policy_context& ctx) { - return; } -bool linear_vesting_policy::is_deposit_allowed( const vesting_policy_context& ctx )const +bool linear_vesting_policy::is_deposit_allowed(const vesting_policy_context& ctx)const { return (ctx.amount.asset_id == ctx.balance.asset_id) - && sum_below_max_shares( ctx.amount, ctx.balance ); + && sum_below_max_shares(ctx.amount, ctx.balance); } -void linear_vesting_policy::on_withdraw( const vesting_policy_context& ctx ) +void linear_vesting_policy::on_withdraw(const vesting_policy_context& ctx) { total_withdrawn += ctx.amount.amount; - return; } -bool linear_vesting_policy::is_withdraw_allowed( const vesting_policy_context& ctx )const +bool linear_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const { - return ( ctx.amount <= get_allowed_withdraw( ctx ) ); + return (ctx.amount <= get_allowed_withdraw(ctx)); } -fc::uint128_t cdd_vesting_policy::compute_coin_seconds_earned( const vesting_policy_context& ctx )const +fc::uint128_t cdd_vesting_policy::compute_coin_seconds_earned(const vesting_policy_context& ctx)const { - assert( ctx.now >= coin_seconds_earned_last_update ); + assert(ctx.now >= coin_seconds_earned_last_update); int64_t delta_seconds = (ctx.now - coin_seconds_earned_last_update).to_seconds(); - assert( delta_seconds >= 0 ); + assert(delta_seconds >= 0); fc::uint128_t delta_coin_seconds = ctx.balance.amount.value; delta_coin_seconds *= delta_seconds; @@ -87,113 +85,132 @@ fc::uint128_t cdd_vesting_policy::compute_coin_seconds_earned( const vesting_pol fc::uint128_t coin_seconds_earned_cap = ctx.balance.amount.value; coin_seconds_earned_cap *= vesting_seconds; - return std::min( - coin_seconds_earned + delta_coin_seconds, - coin_seconds_earned_cap - ); + return std::min(coin_seconds_earned + delta_coin_seconds, coin_seconds_earned_cap); } -void cdd_vesting_policy::update_coin_seconds_earned( const vesting_policy_context& ctx ) +void cdd_vesting_policy::update_coin_seconds_earned(const vesting_policy_context& ctx) { - coin_seconds_earned = compute_coin_seconds_earned( ctx ); + coin_seconds_earned = compute_coin_seconds_earned(ctx); coin_seconds_earned_last_update = ctx.now; - return; } -asset cdd_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const +asset cdd_vesting_policy::get_allowed_withdraw(const vesting_policy_context& ctx)const { - fc::uint128_t cs_earned = compute_coin_seconds_earned( ctx ); + fc::uint128_t cs_earned = compute_coin_seconds_earned(ctx); fc::uint128_t withdraw_available = cs_earned / vesting_seconds; - assert( withdraw_available <= ctx.balance.amount.value ); - return asset( withdraw_available.to_uint64(), ctx.balance.asset_id ); + assert(withdraw_available <= ctx.balance.amount.value); + return asset(withdraw_available.to_uint64(), ctx.balance.asset_id); } -void cdd_vesting_policy::on_deposit( const vesting_policy_context& ctx ) +void cdd_vesting_policy::on_deposit(const vesting_policy_context& ctx) { - update_coin_seconds_earned( ctx ); - return; + update_coin_seconds_earned(ctx); } -void cdd_vesting_policy::on_withdraw( const vesting_policy_context& ctx ) +void cdd_vesting_policy::on_deposit_vested(const vesting_policy_context& ctx) { - update_coin_seconds_earned( ctx ); + on_deposit(ctx); + coin_seconds_earned += ctx.amount.amount.value * vesting_seconds; +} + +void cdd_vesting_policy::on_withdraw(const vesting_policy_context& ctx) +{ + update_coin_seconds_earned(ctx); fc::uint128_t coin_seconds_needed = ctx.amount.amount.value; coin_seconds_needed *= vesting_seconds; // is_withdraw_allowed should forbid any withdrawal that // would trigger this assert - assert( coin_seconds_needed <= coin_seconds_earned ); + assert(coin_seconds_needed <= coin_seconds_earned); coin_seconds_earned -= coin_seconds_needed; - return; } -bool cdd_vesting_policy::is_deposit_allowed( const vesting_policy_context& ctx )const +bool cdd_vesting_policy::is_deposit_allowed(const vesting_policy_context& ctx)const { return (ctx.amount.asset_id == ctx.balance.asset_id) - && sum_below_max_shares( ctx.amount, ctx.balance ); + && sum_below_max_shares(ctx.amount, ctx.balance); } -bool cdd_vesting_policy::is_withdraw_allowed( const vesting_policy_context& ctx )const +bool cdd_vesting_policy::is_deposit_vested_allowed(const vesting_policy_context& ctx) const { - return ( ctx.amount <= get_allowed_withdraw( ctx ) ); + return is_deposit_allowed(ctx); } -#define VESTING_VISITOR( NAME, MAYBE_CONST ) \ +bool cdd_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const +{ + return (ctx.amount <= get_allowed_withdraw(ctx)); +} + +#define VESTING_VISITOR(NAME, MAYBE_CONST) \ struct NAME ## _visitor \ { \ typedef decltype( \ std::declval().NAME( \ std::declval()) \ - ) result_type; \ + ) result_type; \ \ NAME ## _visitor( \ const asset& balance, \ const time_point_sec& now, \ const asset& amount \ - ) \ - : ctx( balance, now, amount ) {} \ + ) \ + : ctx(balance, now, amount) {} \ \ template< typename Policy > \ result_type \ - operator()( MAYBE_CONST Policy& policy ) MAYBE_CONST \ + operator()(MAYBE_CONST Policy& policy) MAYBE_CONST \ { \ - return policy.NAME( ctx ); \ + return policy.NAME(ctx); \ } \ \ vesting_policy_context ctx; \ } -VESTING_VISITOR( on_deposit, ); -VESTING_VISITOR( on_withdraw, ); -VESTING_VISITOR( is_deposit_allowed, const ); -VESTING_VISITOR( is_withdraw_allowed, const ); +VESTING_VISITOR(on_deposit,); +VESTING_VISITOR(on_deposit_vested,); +VESTING_VISITOR(on_withdraw,); +VESTING_VISITOR(is_deposit_allowed, const); +VESTING_VISITOR(is_deposit_vested_allowed, const); +VESTING_VISITOR(is_withdraw_allowed, const); bool vesting_balance_object::is_deposit_allowed(const time_point_sec& now, const asset& amount)const { - return policy.visit( is_deposit_allowed_visitor( balance, now, amount ) ); + return policy.visit(is_deposit_allowed_visitor(balance, now, amount)); } bool vesting_balance_object::is_withdraw_allowed(const time_point_sec& now, const asset& amount)const { - bool result = policy.visit( is_withdraw_allowed_visitor( balance, now, amount ) ); + bool result = policy.visit(is_withdraw_allowed_visitor(balance, now, amount)); // if some policy allows you to withdraw more than your balance, // there's a programming bug in the policy algorithm - assert( (amount <= balance) || (!result) ); + assert((amount <= balance) || (!result)); return result; } void vesting_balance_object::deposit(const time_point_sec& now, const asset& amount) { - on_deposit_visitor vtor( balance, now, amount ); - policy.visit( vtor ); + on_deposit_visitor vtor(balance, now, amount); + policy.visit(vtor); balance += amount; } +void vesting_balance_object::deposit_vested(const time_point_sec& now, const asset& amount) +{ + on_deposit_vested_visitor vtor(balance, now, amount); + policy.visit(vtor); + balance += amount; +} + +bool vesting_balance_object::is_deposit_vested_allowed(const time_point_sec& now, const asset& amount) const +{ + return policy.visit(is_deposit_vested_allowed_visitor(balance, now, amount)); +} + void vesting_balance_object::withdraw(const time_point_sec& now, const asset& amount) { - assert( amount <= balance ); - on_withdraw_visitor vtor( balance, now, amount ); - policy.visit( vtor ); + assert(amount <= balance); + on_withdraw_visitor vtor(balance, now, amount); + policy.visit(vtor); balance -= amount; } diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 6f74f777..1717a04a 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -119,7 +119,7 @@ void database_fixture::verify_asset_supplies( )const for( const account_statistics_object& a : statistics_index ) { reported_core_in_orders += a.total_core_in_orders; - total_balances[asset_id_type()] += a.pending_fees; + total_balances[asset_id_type()] += a.pending_fees + a.pending_vested_fees; } for( const limit_order_object& o : db.get_index_type().indices() ) {