From c02a33a004c25bf78cb5946ff9d7001dce9ec1ca Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 15 Oct 2019 17:35:26 -0300 Subject: [PATCH 1/6] add son vesting config options --- libraries/chain/include/graphene/chain/config.hpp | 2 ++ .../graphene/chain/protocol/chain_parameters.hpp | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index dd25b6ad..16f528a7 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -232,6 +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 SON_VESTING_AMOUNT 50 // 50 PPY +#define SON_VESTING_PERIOD (60*60*24*30) // 2 days #define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT) #define SWEEPS_DEFAULT_DISTRIBUTION_ASSET (graphene::chain::asset_id_type(0)) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 2cfedb95..a71a0b18 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -41,6 +41,8 @@ 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_vesting_amount; + optional < uint32_t > son_vesting_period; }; struct chain_parameters @@ -124,6 +126,12 @@ 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 uint32_t son_vesting_amount()const { + return extensions.value.son_vesting_amount.valid() ? *extensions.value.son_vesting_amount : SON_VESTING_AMOUNT; /// current period start date + } + inline uint32_t son_vesting_period()const { + return extensions.value.son_vesting_period.valid() ? *extensions.value.son_vesting_period : SON_VESTING_PERIOD; /// current period start date + } }; } } // graphene::chain @@ -138,6 +146,8 @@ FC_REFLECT( graphene::chain::parameter_extension, (sweeps_distribution_percentage) (sweeps_distribution_asset) (sweeps_vesting_accumulator_account) + (son_vesting_amount) + (son_vesting_period) ) FC_REFLECT( graphene::chain::chain_parameters, From c94412cb7a06d842da65b51f685759fc8a07100c Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 15 Oct 2019 20:28:04 -0300 Subject: [PATCH 2/6] add vesting balance type support --- .../include/graphene/chain/protocol/chain_parameters.hpp | 2 ++ .../chain/include/graphene/chain/protocol/vesting.hpp | 9 +++++++-- .../include/graphene/chain/vesting_balance_object.hpp | 5 +++++ libraries/chain/vesting_balance_evaluator.cpp | 7 +++++++ tests/tests/son_operations_tests.cpp | 8 ++++---- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index a71a0b18..d152bddd 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -27,6 +27,8 @@ #include #include +#include + namespace graphene { namespace chain { struct fee_schedule; } } namespace graphene { namespace chain { diff --git a/libraries/chain/include/graphene/chain/protocol/vesting.hpp b/libraries/chain/include/graphene/chain/protocol/vesting.hpp index 4915b62e..731b8804 100644 --- a/libraries/chain/include/graphene/chain/protocol/vesting.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vesting.hpp @@ -24,7 +24,9 @@ #pragma once #include -namespace graphene { namespace chain { +namespace graphene { namespace chain { + + enum class vesting_balance_type { normal, gpos, son }; struct linear_vesting_policy_initializer { @@ -72,6 +74,7 @@ namespace graphene { namespace chain { account_id_type owner; ///< Who is able to withdraw the balance asset amount; vesting_policy_initializer policy; + vesting_balance_type balance_type; account_id_type fee_payer()const { return creator; } void validate()const @@ -112,9 +115,11 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::vesting_balance_create_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(policy) ) +FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(policy)(balance_type) ) FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount) ) FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) ) FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vesting_seconds) ) FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer ) + +FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos)(son) ) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index 789442fd..fc236272 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -24,6 +24,7 @@ #pragma once #include +#include #include #include @@ -140,6 +141,9 @@ namespace graphene { namespace chain { /// The vesting policy stores details on when funds vest, and controls when they may be withdrawn vesting_policy policy; + /// We can have 3 types of vesting, gpos, son and the rest + vesting_balance_type balance_type = vesting_balance_type::normal; + vesting_balance_object() {} asset_id_type get_asset_id() const { return balance.asset_id; } @@ -225,4 +229,5 @@ FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::objec (owner) (balance) (policy) + (balance_type) ) diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index ee918fd1..3bb798f3 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -42,6 +42,12 @@ void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance FC_ASSERT( d.get_balance( creator_account.id, op.amount.asset_id ) >= op.amount ); FC_ASSERT( !op.amount.asset_id(d).is_transfer_restricted() ); + if(d.head_block_time() < HARDFORK_SON_TIME) // Todo: can be removed after gpos hf time pass + FC_ASSERT( op.balance_type == vesting_balance_type::normal); + + if(d.head_block_time() >= HARDFORK_SON_TIME && op.balance_type == vesting_balance_type::son) // Todo: hf check can be removed after pass + FC_ASSERT( op.amount.amount >= d.get_global_properties().parameters.son_vesting_amount() ); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -92,6 +98,7 @@ object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance // If making changes to this logic, check if those changes should also be made there as well. obj.owner = op.owner; obj.balance = op.amount; + obj.balance_type = op.balance_type; op.policy.visit( init_policy_visitor( obj.policy, op.amount.amount, now ) ); } ); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index db992544..bd45d648 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -36,8 +36,8 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { vesting_balance_create_operation op; op.creator = alice_id; op.owner = alice_id; - op.amount = asset(10); - //op.balance_type = vesting_balance_type::unspecified; + op.amount = asset(50); + op.balance_type = vesting_balance_type::son; trx.operations.push_back(op); set_expiration(db, trx); @@ -51,8 +51,8 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { vesting_balance_create_operation op; op.creator = alice_id; op.owner = alice_id; - op.amount = asset(10); - //op.balance_type = vesting_balance_type::unspecified; + op.amount = asset(50); + op.balance_type = vesting_balance_type::normal; trx.operations.push_back(op); set_expiration(db, trx); From be8dc42d66e7c6772cf0bac8f63177e282ed2a95 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 15 Oct 2019 21:55:48 -0300 Subject: [PATCH 3/6] add dormant vesting policy for son --- .../graphene/chain/protocol/vesting.hpp | 12 ++++- .../graphene/chain/vesting_balance_object.hpp | 46 +++++++++++++++- libraries/chain/son_evaluator.cpp | 16 +++++- libraries/chain/vesting_balance_evaluator.cpp | 23 ++++++-- libraries/chain/vesting_balance_object.cpp | 53 ++++++++++++++++++ tests/tests/son_operations_tests.cpp | 54 +++++++++++++++---- 6 files changed, 186 insertions(+), 18 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/vesting.hpp b/libraries/chain/include/graphene/chain/protocol/vesting.hpp index 731b8804..57c22be9 100644 --- a/libraries/chain/include/graphene/chain/protocol/vesting.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vesting.hpp @@ -44,9 +44,16 @@ namespace graphene { namespace chain { cdd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){} }; - typedef fc::static_variant vesting_policy_initializer; - + struct dormant_vesting_policy_initializer + { + /** none may be claimed if dormant is true, otherwise this is a linear policy */ + bool dormant = true; + fc::time_point_sec begin_timestamp; + uint32_t vesting_cliff_seconds = 0; + uint32_t vesting_duration_seconds = 0; + }; + typedef fc::static_variant vesting_policy_initializer; /** * @brief Create a vesting balance. @@ -120,6 +127,7 @@ FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_b FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) ) FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vesting_seconds) ) +FC_REFLECT(graphene::chain::dormant_vesting_policy_initializer, (dormant)(begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) ) FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer ) FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos)(son) ) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index fc236272..a7a5f222 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -119,10 +119,44 @@ namespace graphene { namespace chain { void on_withdraw(const vesting_policy_context& ctx); }; + /** + * @brief Cant withdraw anything while dormant mode is true, linear policy after that changes. + * + * This vesting balance type is needed to register SON users where balance may be claimable only after + * the SON object is deleted(plus a linear policy). + * When deleting a SON member the dormant_mode will change and the linear policy will became active. + * + * @note New funds may not be added to a dormant vesting balance. + */ + struct dormant_vesting_policy + { + /// dormant mode flag indicates if we are in dormant mode or linear policy. + bool dormant_mode = true; + + /// This is the time at which funds begin vesting. + fc::time_point_sec begin_timestamp; + /// No amount may be withdrawn before this many seconds of the vesting period have elapsed. + uint32_t vesting_cliff_seconds = 0; + /// Duration of the vesting period, in seconds. Must be greater than 0 and greater than vesting_cliff_seconds. + uint32_t vesting_duration_seconds = 0; + /// The total amount of asset to vest. + share_type begin_balance; + + 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); + }; + typedef fc::static_variant< linear_vesting_policy, - cdd_vesting_policy - > vesting_policy; + cdd_vesting_policy, + dormant_vesting_policy + > vesting_policy; /** * Vesting balance object is a balance that is locked by the blockchain for a period of time. @@ -223,6 +257,14 @@ FC_REFLECT(graphene::chain::cdd_vesting_policy, (coin_seconds_earned_last_update) ) +FC_REFLECT(graphene::chain::dormant_vesting_policy, + (dormant_mode) + (begin_timestamp) + (vesting_cliff_seconds) + (vesting_duration_seconds) + (begin_balance) + ) + FC_REFLECT_TYPENAME( graphene::chain::vesting_policy ) FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::object), diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 6d70dc62..03221984 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -68,7 +69,20 @@ void_result delete_son_evaluator::do_evaluate(const son_delete_operation& op) void_result delete_son_evaluator::do_apply(const son_delete_operation& op) { try { const auto& idx = db().get_index_type().indices().get(); - db().remove(*idx.find(op.son_id)); + auto son = idx.find(op.son_id); + if(son != idx.end()) { + vesting_balance_object deposit = son->deposit(db()); + dormant_vesting_policy new_vesting_policy; + new_vesting_policy.dormant_mode = false; + new_vesting_policy.begin_timestamp = db().head_block_time(); + new_vesting_policy.vesting_cliff_seconds = db().get_global_properties().parameters.son_vesting_period(); + + db().modify(son->deposit(db()), [&new_vesting_policy](vesting_balance_object &vbo) { + vbo.policy = new_vesting_policy; + }); + + db().remove(*son); + } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index 3bb798f3..3f5cbca3 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -82,6 +82,16 @@ struct init_policy_visitor policy.coin_seconds_earned_last_update = now; p = policy; } + void operator()( const dormant_vesting_policy_initializer& i )const + { + dormant_vesting_policy policy; + policy.dormant_mode = i.dormant; + policy.begin_timestamp = i.begin_timestamp; + policy.vesting_cliff_seconds = i.vesting_cliff_seconds; + policy.vesting_duration_seconds = i.vesting_duration_seconds; + policy.begin_balance = init_balance; + p = policy; + } }; object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance_create_operation& op ) @@ -99,10 +109,17 @@ object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance obj.owner = op.owner; obj.balance = op.amount; obj.balance_type = op.balance_type; - op.policy.visit( init_policy_visitor( obj.policy, op.amount.amount, now ) ); + if(op.balance_type == vesting_balance_type::son) + { + const auto &gpo = d.get_global_properties(); + // forcing son dormant policy + dormant_vesting_policy p; + p.dormant_mode = true; + obj.policy = p; + } + else + op.policy.visit( init_policy_visitor( obj.policy, op.amount.amount, now ) ); } ); - - return vbo.id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/vesting_balance_object.cpp b/libraries/chain/vesting_balance_object.cpp index 73448e04..2463a78a 100644 --- a/libraries/chain/vesting_balance_object.cpp +++ b/libraries/chain/vesting_balance_object.cpp @@ -157,6 +157,59 @@ bool cdd_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)c return (ctx.amount <= get_allowed_withdraw(ctx)); } +asset dormant_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const +{ + share_type allowed_withdraw = 0; + + if( !dormant_mode && ctx.now > begin_timestamp) + { + const auto elapsed_seconds = (ctx.now - begin_timestamp).to_seconds(); + assert( elapsed_seconds > 0 ); + + if( elapsed_seconds >= vesting_cliff_seconds ) + { + share_type total_vested = 0; + if( elapsed_seconds < vesting_duration_seconds ) + { + total_vested = (fc::uint128_t( begin_balance.value ) * elapsed_seconds / vesting_duration_seconds).to_uint64(); + } + else + { + total_vested = begin_balance; + } + assert( total_vested >= 0 ); + + const share_type withdrawn_already = begin_balance - ctx.balance.amount; + assert( withdrawn_already >= 0 ); + + allowed_withdraw = total_vested - withdrawn_already; + assert( allowed_withdraw >= 0 ); + } + } + + return asset( allowed_withdraw, ctx.balance.asset_id ); +} + +void dormant_vesting_policy::on_deposit(const vesting_policy_context& ctx) +{ +} + +bool dormant_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); +} + +void dormant_vesting_policy::on_withdraw(const vesting_policy_context& ctx) +{ +} + +bool dormant_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const +{ + return (ctx.amount.asset_id == ctx.balance.asset_id) + && (ctx.amount <= get_allowed_withdraw(ctx)); +} + #define VESTING_VISITOR(NAME, MAYBE_CONST) \ struct NAME ## _visitor \ { \ diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index bd45d648..64ce7bba 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using namespace graphene::chain; using namespace graphene::chain::test; @@ -13,9 +14,6 @@ BOOST_FIXTURE_TEST_SUITE( son_operation_tests, database_fixture ) BOOST_AUTO_TEST_CASE( create_son_test ) { generate_blocks(HARDFORK_SON_TIME); - while (db.head_block_time() <= HARDFORK_SON_TIME) { - generate_block(); - } generate_block(); set_expiration(db, trx); @@ -36,31 +34,50 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { vesting_balance_create_operation op; op.creator = alice_id; op.owner = alice_id; - op.amount = asset(50); + op.amount = asset(10); op.balance_type = vesting_balance_type::son; + trx.operations.push_back(op); + + // amount in the son balance need to be at least 50 + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), fc::exception ); + + op.amount = asset(50); + trx.clear(); trx.operations.push_back(op); - set_expiration(db, trx); processed_transaction ptx = PUSH_TX(db, trx, ~0); - trx.clear(); deposit = ptx.operation_results[0].get(); + + auto deposit_vesting = db.get(ptx.operation_results[0].get()); + + BOOST_CHECK_EQUAL(deposit(db).balance.amount.value, 50); + auto now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit(db).is_withdraw_allowed(now, asset(50)), false); // cant withdraw } - // create payment vesting - vesting_balance_id_type payment; + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment ; { vesting_balance_create_operation op; op.creator = alice_id; op.owner = alice_id; - op.amount = asset(50); + op.amount = asset(1); op.balance_type = vesting_balance_type::normal; + op.validate(); + trx.operations.push_back(op); - set_expiration(db, trx); + trx.validate(); processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.clear(); payment = ptx.operation_results[0].get(); } + generate_block(); + set_expiration(db, trx); + // alice became son { son_create_operation op; @@ -116,6 +133,9 @@ BOOST_AUTO_TEST_CASE( delete_son_test ) { INVOKE(create_son_test); GET_ACTOR(alice); + auto deposit_vesting = db.get(vesting_balance_id_type(0)); + BOOST_CHECK_EQUAL(deposit_vesting.policy.get().dormant_mode, true); // dormant while active + { son_delete_operation op; op.owner_account = alice_id; @@ -129,6 +149,20 @@ BOOST_AUTO_TEST_CASE( delete_son_test ) { const auto& idx = db.get_index_type().indices().get(); BOOST_REQUIRE( idx.empty() ); + + deposit_vesting = db.get(vesting_balance_id_type(0)); + BOOST_CHECK_EQUAL(deposit_vesting.policy.get().dormant_mode, false); // not sleeping anymore + + auto now = db.head_block_time(); + + BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // but still cant withdraw + + generate_blocks(now + fc::seconds(db.get_global_properties().parameters.son_vesting_period())); + generate_block(); + + deposit_vesting = db.get(vesting_balance_id_type(0)); + now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), true); // after 2 days withdraw is allowed } BOOST_AUTO_TEST_CASE( update_delete_not_own ) { // fee payer needs to be the son object owner From 032c4c7a996a7e3d8452ef7f880afa8151ab660b Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Wed, 16 Oct 2019 14:59:49 -0300 Subject: [PATCH 4/6] add precision to son vesting amount --- libraries/chain/include/graphene/chain/config.hpp | 2 +- tests/tests/son_operations_tests.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 16f528a7..9b0ff036 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -232,7 +232,7 @@ #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 SON_VESTING_AMOUNT 50 // 50 PPY +#define SON_VESTING_AMOUNT (50*GRAPHENE_BLOCKCHAIN_PRECISION) // 50 PPY #define SON_VESTING_PERIOD (60*60*24*30) // 2 days #define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT) diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 64ce7bba..1e143d77 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -22,8 +22,8 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { upgrade_to_lifetime_member(alice); upgrade_to_lifetime_member(bob); - transfer( committee_account, alice_id, asset( 100000 ) ); - transfer( committee_account, bob_id, asset( 100000 ) ); + transfer( committee_account, alice_id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + transfer( committee_account, bob_id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); set_expiration(db, trx); std::string test_url = "https://create_son_test"; @@ -34,14 +34,14 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { vesting_balance_create_operation op; op.creator = alice_id; op.owner = alice_id; - op.amount = asset(10); + op.amount = asset(10*GRAPHENE_BLOCKCHAIN_PRECISION); op.balance_type = vesting_balance_type::son; trx.operations.push_back(op); // amount in the son balance need to be at least 50 GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), fc::exception ); - op.amount = asset(50); + op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION); trx.clear(); trx.operations.push_back(op); @@ -50,9 +50,9 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { auto deposit_vesting = db.get(ptx.operation_results[0].get()); - BOOST_CHECK_EQUAL(deposit(db).balance.amount.value, 50); + BOOST_CHECK_EQUAL(deposit(db).balance.amount.value, 50*GRAPHENE_BLOCKCHAIN_PRECISION); auto now = db.head_block_time(); - BOOST_CHECK_EQUAL(deposit(db).is_withdraw_allowed(now, asset(50)), false); // cant withdraw + BOOST_CHECK_EQUAL(deposit(db).is_withdraw_allowed(now, asset(50*GRAPHENE_BLOCKCHAIN_PRECISION)), false); // cant withdraw } generate_block(); set_expiration(db, trx); @@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { vesting_balance_create_operation op; op.creator = alice_id; op.owner = alice_id; - op.amount = asset(1); + op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION); op.balance_type = vesting_balance_type::normal; op.validate(); From 76120a5b76d66a30ea3b0e0aa80fb7efd858a35e Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 18 Oct 2019 00:01:40 -0300 Subject: [PATCH 5/6] abstraction of dormant vesting policy --- .../graphene/chain/protocol/vesting.hpp | 14 +++------- .../graphene/chain/vesting_balance_object.hpp | 26 +++-------------- libraries/chain/son_evaluator.cpp | 3 +- libraries/chain/vesting_balance_evaluator.cpp | 16 +---------- libraries/chain/vesting_balance_object.cpp | 28 +------------------ tests/tests/operation_tests.cpp | 6 ++-- tests/tests/operation_tests2.cpp | 4 +-- tests/tests/son_operations_tests.cpp | 16 +++++++---- 8 files changed, 27 insertions(+), 86 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/vesting.hpp b/libraries/chain/include/graphene/chain/protocol/vesting.hpp index 57c22be9..9fcbda66 100644 --- a/libraries/chain/include/graphene/chain/protocol/vesting.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vesting.hpp @@ -44,16 +44,10 @@ namespace graphene { namespace chain { cdd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){} }; - struct dormant_vesting_policy_initializer - { - /** none may be claimed if dormant is true, otherwise this is a linear policy */ - bool dormant = true; - fc::time_point_sec begin_timestamp; - uint32_t vesting_cliff_seconds = 0; - uint32_t vesting_duration_seconds = 0; - }; + struct dormant_vesting_policy_initializer {}; - typedef fc::static_variant vesting_policy_initializer; + typedef fc::static_variant vesting_policy_initializer; /** * @brief Create a vesting balance. @@ -127,7 +121,7 @@ FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_b FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) ) FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vesting_seconds) ) -FC_REFLECT(graphene::chain::dormant_vesting_policy_initializer, (dormant)(begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) ) +FC_REFLECT(graphene::chain::dormant_vesting_policy_initializer, ) FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer ) FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos)(son) ) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index a7a5f222..638519eb 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -120,28 +120,16 @@ namespace graphene { namespace chain { }; /** - * @brief Cant withdraw anything while dormant mode is true, linear policy after that changes. + * @brief Cant withdraw anything while balance is in this policy. * - * This vesting balance type is needed to register SON users where balance may be claimable only after + * This policy is needed to register SON users where balance may be claimable only after * the SON object is deleted(plus a linear policy). - * When deleting a SON member the dormant_mode will change and the linear policy will became active. + * When deleting a SON member the dormant mode will be replaced by a linear policy. * * @note New funds may not be added to a dormant vesting balance. */ struct dormant_vesting_policy { - /// dormant mode flag indicates if we are in dormant mode or linear policy. - bool dormant_mode = true; - - /// This is the time at which funds begin vesting. - fc::time_point_sec begin_timestamp; - /// No amount may be withdrawn before this many seconds of the vesting period have elapsed. - uint32_t vesting_cliff_seconds = 0; - /// Duration of the vesting period, in seconds. Must be greater than 0 and greater than vesting_cliff_seconds. - uint32_t vesting_duration_seconds = 0; - /// The total amount of asset to vest. - share_type begin_balance; - 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; } @@ -257,13 +245,7 @@ FC_REFLECT(graphene::chain::cdd_vesting_policy, (coin_seconds_earned_last_update) ) -FC_REFLECT(graphene::chain::dormant_vesting_policy, - (dormant_mode) - (begin_timestamp) - (vesting_cliff_seconds) - (vesting_duration_seconds) - (begin_balance) - ) +FC_REFLECT(graphene::chain::dormant_vesting_policy, ) FC_REFLECT_TYPENAME( graphene::chain::vesting_policy ) diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 03221984..26daba6a 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -72,8 +72,7 @@ void_result delete_son_evaluator::do_apply(const son_delete_operation& op) auto son = idx.find(op.son_id); if(son != idx.end()) { vesting_balance_object deposit = son->deposit(db()); - dormant_vesting_policy new_vesting_policy; - new_vesting_policy.dormant_mode = false; + linear_vesting_policy new_vesting_policy; new_vesting_policy.begin_timestamp = db().head_block_time(); new_vesting_policy.vesting_cliff_seconds = db().get_global_properties().parameters.son_vesting_period(); diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index 3f5cbca3..cc82aa3e 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -85,11 +85,6 @@ struct init_policy_visitor void operator()( const dormant_vesting_policy_initializer& i )const { dormant_vesting_policy policy; - policy.dormant_mode = i.dormant; - policy.begin_timestamp = i.begin_timestamp; - policy.vesting_cliff_seconds = i.vesting_cliff_seconds; - policy.vesting_duration_seconds = i.vesting_duration_seconds; - policy.begin_balance = init_balance; p = policy; } }; @@ -109,16 +104,7 @@ object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance obj.owner = op.owner; obj.balance = op.amount; obj.balance_type = op.balance_type; - if(op.balance_type == vesting_balance_type::son) - { - const auto &gpo = d.get_global_properties(); - // forcing son dormant policy - dormant_vesting_policy p; - p.dormant_mode = true; - obj.policy = p; - } - else - op.policy.visit( init_policy_visitor( obj.policy, op.amount.amount, now ) ); + op.policy.visit( init_policy_visitor( obj.policy, op.amount.amount, now ) ); } ); return vbo.id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/vesting_balance_object.cpp b/libraries/chain/vesting_balance_object.cpp index 2463a78a..742482ce 100644 --- a/libraries/chain/vesting_balance_object.cpp +++ b/libraries/chain/vesting_balance_object.cpp @@ -160,33 +160,6 @@ bool cdd_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)c asset dormant_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const { share_type allowed_withdraw = 0; - - if( !dormant_mode && ctx.now > begin_timestamp) - { - const auto elapsed_seconds = (ctx.now - begin_timestamp).to_seconds(); - assert( elapsed_seconds > 0 ); - - if( elapsed_seconds >= vesting_cliff_seconds ) - { - share_type total_vested = 0; - if( elapsed_seconds < vesting_duration_seconds ) - { - total_vested = (fc::uint128_t( begin_balance.value ) * elapsed_seconds / vesting_duration_seconds).to_uint64(); - } - else - { - total_vested = begin_balance; - } - assert( total_vested >= 0 ); - - const share_type withdrawn_already = begin_balance - ctx.balance.amount; - assert( withdrawn_already >= 0 ); - - allowed_withdraw = total_vested - withdrawn_already; - assert( allowed_withdraw >= 0 ); - } - } - return asset( allowed_withdraw, ctx.balance.asset_id ); } @@ -210,6 +183,7 @@ bool dormant_vesting_policy::is_withdraw_allowed(const vesting_policy_context& c && (ctx.amount <= get_allowed_withdraw(ctx)); } + #define VESTING_VISITOR(NAME, MAYBE_CONST) \ struct NAME ## _visitor \ { \ diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 443cd011..a6ded8ea 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -1561,7 +1561,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_create_test ) op.amount = test_asset.amount( 100 ); //op.vesting_seconds = 60*60*24; op.policy = cdd_vesting_policy_initializer{ 60*60*24 }; - //op.balance_type == vesting_balance_type::unspecified; + op.balance_type == vesting_balance_type::normal; // Fee must be non-negative REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(1) ); @@ -1581,7 +1581,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_create_test ) op.creator = alice_account.get_id(); op.owner = alice_account.get_id(); - //op.balance_type = vesting_balance_type::unspecified; + op.balance_type = vesting_balance_type::normal; account_id_type nobody = account_id_type(1234); @@ -1652,7 +1652,7 @@ BOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test ) create_op.owner = owner; create_op.amount = amount; create_op.policy = cdd_vesting_policy_initializer(vesting_seconds); - //create_op.balance_type = vesting_balance_type::unspecified; + create_op.balance_type = vesting_balance_type::normal; tx.operations.push_back( create_op ); set_expiration( db, tx ); diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index b68b34a7..2e175c9d 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -1316,7 +1316,7 @@ BOOST_AUTO_TEST_CASE(zero_second_vbo) create_op.owner = alice_id; create_op.amount = asset(500); create_op.policy = pinit; - //create_op.balance_type = vesting_balance_type::unspecified; + create_op.balance_type = vesting_balance_type::normal; signed_transaction create_tx; create_tx.operations.push_back( create_op ); @@ -1400,7 +1400,7 @@ BOOST_AUTO_TEST_CASE( vbo_withdraw_different ) create_op.owner = alice_id; create_op.amount = asset(100, stuff_id); create_op.policy = pinit; - //create_op.balance_type = vesting_balance_type::unspecified; + create_op.balance_type = vesting_balance_type::normal; signed_transaction create_tx; create_tx.operations.push_back( create_op ); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 1e143d77..2e79d272 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -36,6 +36,7 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { op.owner = alice_id; op.amount = asset(10*GRAPHENE_BLOCKCHAIN_PRECISION); op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; trx.operations.push_back(op); // amount in the son balance need to be at least 50 @@ -129,12 +130,13 @@ BOOST_AUTO_TEST_CASE( update_son_test ) { } BOOST_AUTO_TEST_CASE( delete_son_test ) { - +try { INVOKE(create_son_test); GET_ACTOR(alice); auto deposit_vesting = db.get(vesting_balance_id_type(0)); - BOOST_CHECK_EQUAL(deposit_vesting.policy.get().dormant_mode, true); // dormant while active + auto now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // cant withdraw { son_delete_operation op; @@ -151,10 +153,10 @@ BOOST_AUTO_TEST_CASE( delete_son_test ) { BOOST_REQUIRE( idx.empty() ); deposit_vesting = db.get(vesting_balance_id_type(0)); - BOOST_CHECK_EQUAL(deposit_vesting.policy.get().dormant_mode, false); // not sleeping anymore - - auto now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit_vesting.policy.get().vesting_cliff_seconds, + db.get_global_properties().parameters.son_vesting_period()); // in linear policy + now = db.head_block_time(); BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), false); // but still cant withdraw generate_blocks(now + fc::seconds(db.get_global_properties().parameters.son_vesting_period())); @@ -164,6 +166,10 @@ BOOST_AUTO_TEST_CASE( delete_son_test ) { now = db.head_block_time(); BOOST_CHECK_EQUAL(deposit_vesting.is_withdraw_allowed(now, asset(50)), true); // after 2 days withdraw is allowed } +catch (fc::exception &e) { + edump((e.to_detail_string())); + throw; +} } BOOST_AUTO_TEST_CASE( update_delete_not_own ) { // fee payer needs to be the son object owner try { From d29e4334882c6f2ea5ef046a2f45d4513572fb35 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 18 Oct 2019 00:27:13 -0300 Subject: [PATCH 6/6] force son create vesting balance to have dormant policy --- libraries/chain/son_evaluator.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 26daba6a..e88a5960 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -11,7 +11,9 @@ void_result create_son_evaluator::do_evaluate(const son_create_operation& op) { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT(db().get(op.owner_account).is_lifetime_member(), "Only Lifetime members may register a SON."); - return void_result(); + FC_ASSERT(op.deposit(db()).policy.which() == vesting_policy::tag::value, + "Deposit balance must have dormant vesting policy"); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type create_son_evaluator::do_apply(const son_create_operation& op)