From 677c4681c385dcd6cad3d6e3cdea41567d9f079e Mon Sep 17 00:00:00 2001 From: maslenitsa93 Date: Wed, 16 Jan 2019 13:19:42 +0300 Subject: [PATCH] Implement payments for approved works #1024 --- libraries/chain/CMakeLists.txt | 2 + libraries/chain/database.cpp | 3 +- libraries/chain/database_worker_objects.cpp | 41 +++++++++++ libraries/chain/evaluator.cpp | 53 ++++++++++++++ .../chain/include/golos/chain/database.hpp | 4 +- .../chain/include/golos/chain/evaluator.hpp | 26 +++++++ .../include/golos/chain/worker_objects.hpp | 23 ++++--- libraries/chain/steem_evaluator.cpp | 69 ------------------- libraries/chain/worker_evaluators.cpp | 29 ++++++-- .../include/golos/protocol/operations.hpp | 4 +- .../protocol/steem_virtual_operations.hpp | 27 ++++++++ libraries/protocol/worker_operations.cpp | 14 ++++ .../plugins/mongo_db/mongo_db_operations.hpp | 2 + .../golos/plugins/mongo_db/mongo_db_state.hpp | 2 + plugins/mongo_db/mongo_db_operations.cpp | 10 +++ plugins/mongo_db/mongo_db_state.cpp | 8 +++ 16 files changed, 231 insertions(+), 86 deletions(-) create mode 100644 libraries/chain/evaluator.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 247eebba05..801e44502a 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -26,6 +26,7 @@ if(BUILD_SHARED_LIBRARIES) shared_authority.cpp # transaction_object.cpp block_log.cpp + evaluator.cpp proposal_object.cpp proposal_evaluator.cpp database_proposal_object.cpp @@ -80,6 +81,7 @@ else() shared_authority.cpp # transaction_object.cpp block_log.cpp + evaluator.cpp proposal_object.cpp proposal_evaluator.cpp database_proposal_object.cpp diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index f483adbd6f..1ef11a65dc 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -1309,7 +1309,7 @@ namespace golos { namespace chain { } } - inline const void database::push_virtual_operation(const operation &op, bool force) { + const void database::push_virtual_operation(const operation &op, bool force) { if (!force && _skip_virtual_ops ) { return; } @@ -3663,6 +3663,7 @@ namespace golos { namespace chain { process_funds(); process_conversions(); process_comment_cashout(); + process_worker_cashout(); process_vesting_withdrawals(); process_savings_withdraws(); pay_liquidity_reward(); diff --git a/libraries/chain/database_worker_objects.cpp b/libraries/chain/database_worker_objects.cpp index a170629495..991653df7d 100644 --- a/libraries/chain/database_worker_objects.cpp +++ b/libraries/chain/database_worker_objects.cpp @@ -101,4 +101,45 @@ namespace golos { namespace chain { return find(std::make_tuple(author, permlink)); } + void database::process_worker_cashout() { + if (!has_hardfork(STEEMIT_HARDFORK_0_21__1013)) { + return; + } + + const auto now = head_block_time(); + + const auto& wto_idx = get_index(); + + for (auto wto_itr = wto_idx.begin(); wto_itr != wto_idx.end() && wto_itr->next_cashout_time <= now; ++wto_itr) { + const auto& wpo = get_worker_proposal(wto_itr->worker_proposal_author, wto_itr->worker_proposal_permlink); + + const auto& reward = wto_itr->development_cost / wto_itr->payments_count; + + const auto& gpo = get_dynamic_global_properties(); + modify(gpo, [&](dynamic_global_property_object& gpo) { + gpo.total_worker_fund_steem -= reward; + }); + + adjust_balance(get_account(wto_itr->worker), reward); + + if (wto_itr->finished_payments_count+1 == wto_itr->payments_count) { + modify(*wto_itr, [&](worker_techspec_object& wto) { + wto.finished_payments_count++; + wto.next_cashout_time = time_point_sec::maximum(); + }); + + modify(wpo, [&](worker_proposal_object& wpo) { + wpo.state = worker_proposal_state::closed; + }); + } else { + modify(*wto_itr, [&](worker_techspec_object& wto) { + wto.finished_payments_count++; + wto.next_cashout_time = now + wto.payments_interval; + }); + } + + push_virtual_operation(worker_reward_operation(wto_itr->worker, wto_itr->author, to_string(wto_itr->permlink), reward)); + } + } + } } // golos::chain diff --git a/libraries/chain/evaluator.cpp b/libraries/chain/evaluator.cpp new file mode 100644 index 0000000000..79ebd68e50 --- /dev/null +++ b/libraries/chain/evaluator.cpp @@ -0,0 +1,53 @@ +#include + +namespace golos { namespace chain { + + asset get_balance(const account_object &account, balance_type type, asset_symbol_type symbol) { + switch(type) { + case MAIN_BALANCE: + switch (symbol) { + case STEEM_SYMBOL: + return account.balance; + case SBD_SYMBOL: + return account.sbd_balance; + default: + GOLOS_CHECK_VALUE(false, "invalid symbol"); + } + case SAVINGS: + switch (symbol) { + case STEEM_SYMBOL: + return account.savings_balance; + case SBD_SYMBOL: + return account.savings_sbd_balance; + default: + GOLOS_CHECK_VALUE(false, "invalid symbol"); + } + case VESTING: + GOLOS_CHECK_VALUE(symbol == VESTS_SYMBOL, "invalid symbol"); + return account.vesting_shares; + case EFFECTIVE_VESTING: + GOLOS_CHECK_VALUE(symbol == VESTS_SYMBOL, "invalid symbol"); + return account.effective_vesting_shares(); + case HAVING_VESTING: + GOLOS_CHECK_VALUE(symbol == VESTS_SYMBOL, "invalid symbol"); + return account.available_vesting_shares(false); + case AVAILABLE_VESTING: + GOLOS_CHECK_VALUE(symbol == VESTS_SYMBOL, "invalid symbol"); + return account.available_vesting_shares(true); + default: FC_ASSERT(false, "invalid balance type"); + } + } + + std::string get_balance_name(balance_type type) { + switch(type) { + case MAIN_BALANCE: return "fund"; + case SAVINGS: return "savings"; + case VESTING: return "vesting shares"; + case EFFECTIVE_VESTING: return "effective vesting shares"; + case HAVING_VESTING: return "having vesting shares"; + case AVAILABLE_VESTING: return "available vesting shares"; + default: FC_ASSERT(false, "invalid balance type"); + } + } + +} } // golos::chain diff --git a/libraries/chain/include/golos/chain/database.hpp b/libraries/chain/include/golos/chain/database.hpp index a12a68f3d0..cfa3663a06 100644 --- a/libraries/chain/include/golos/chain/database.hpp +++ b/libraries/chain/include/golos/chain/database.hpp @@ -307,7 +307,7 @@ namespace golos { namespace chain { void notify_post_apply_operation(const operation_notification ¬e); - inline const void push_virtual_operation(const operation &op, bool force = false); // vops are not needed for low mem. Force will push them on low mem. + const void push_virtual_operation(const operation &op, bool force = false); // vops are not needed for low mem. Force will push them on low mem. void notify_applied_block(const signed_block &block); void notify_on_pending_transaction(const signed_transaction &tx); @@ -449,6 +449,8 @@ namespace golos { namespace chain { void process_comment_cashout(); + void process_worker_cashout(); + void process_funds(); void process_conversions(); diff --git a/libraries/chain/include/golos/chain/evaluator.hpp b/libraries/chain/include/golos/chain/evaluator.hpp index 5db6ea0b14..2e92724f2a 100644 --- a/libraries/chain/include/golos/chain/evaluator.hpp +++ b/libraries/chain/include/golos/chain/evaluator.hpp @@ -2,6 +2,7 @@ #include #include +#include #define ASSERT_REQ_HF_IN_DB(HF, FEATURE, DATABASE) \ GOLOS_ASSERT(DATABASE.has_hardfork(HF), unsupported_operation, \ @@ -11,6 +12,18 @@ #define ASSERT_REQ_HF(HF, FEATURE) \ ASSERT_REQ_HF_IN_DB(HF, FEATURE, _db) +#define GOLOS_CHECK_BALANCE(ACCOUNT, TYPE, REQUIRED ...) \ + FC_EXPAND_MACRO( \ + FC_MULTILINE_MACRO_BEGIN \ + asset exist = get_balance(ACCOUNT, TYPE, (REQUIRED).symbol); \ + if( UNLIKELY( exist < (REQUIRED) )) { \ + FC_THROW_EXCEPTION( golos::insufficient_funds, \ + "Account \"${account}\" does not have enough ${balance}: required ${required}, exist ${exist}", \ + ("account",ACCOUNT.name)("balance",get_balance_name(TYPE))("required",REQUIRED)("exist",exist)); \ + } \ + FC_MULTILINE_MACRO_END \ + ) + namespace golos { namespace chain { @@ -52,6 +65,19 @@ namespace golos { database &_db; }; + enum balance_type { + MAIN_BALANCE, + SAVINGS, + VESTING, + EFFECTIVE_VESTING, + HAVING_VESTING, + AVAILABLE_VESTING + }; + + asset get_balance(const account_object &account, balance_type type, asset_symbol_type symbol); + + std::string get_balance_name(balance_type type); + } } diff --git a/libraries/chain/include/golos/chain/worker_objects.hpp b/libraries/chain/include/golos/chain/worker_objects.hpp index 678aa255bb..27e7ddf225 100644 --- a/libraries/chain/include/golos/chain/worker_objects.hpp +++ b/libraries/chain/include/golos/chain/worker_objects.hpp @@ -29,13 +29,8 @@ namespace golos { namespace chain { shared_string permlink; worker_proposal_type type; worker_proposal_state state; - asset deposit; account_name_type approved_techspec_author; shared_string approved_techspec_permlink; - account_name_type worker; - time_point_sec work_beginning_time; - uint8_t worker_payments_count; - time_point_sec payment_beginning_time; time_point_sec created; time_point_sec modified; }; @@ -62,10 +57,15 @@ namespace golos { namespace chain { uint32_t specification_eta; asset development_cost; uint32_t development_eta; - uint16_t payments_count; - uint32_t payments_interval; + account_name_type worker; + time_point_sec work_beginning_time; shared_string worker_result_permlink; time_point_sec completion_date; + uint16_t payments_count; + uint32_t payments_interval; + time_point_sec payment_beginning_time; + time_point_sec next_cashout_time = time_point_sec::maximum(); + uint8_t finished_payments_count = 0; }; class worker_techspec_approve_object : public object { @@ -125,6 +125,7 @@ namespace golos { namespace chain { struct by_worker_proposal; struct by_worker_result; + struct by_next_cashout_time; using worker_techspec_index = multi_index_container< worker_techspec_object, @@ -158,7 +159,13 @@ namespace golos { namespace chain { member>, composite_key_compare< std::less, - chainbase::strcmp_less>>>, + chainbase::strcmp_less>>, + ordered_unique< + tag, + composite_key< + worker_techspec_object, + member, + member>>>, allocator>; struct by_techspec_approver; diff --git a/libraries/chain/steem_evaluator.cpp b/libraries/chain/steem_evaluator.cpp index ce392f77c5..8bc66a09e2 100644 --- a/libraries/chain/steem_evaluator.cpp +++ b/libraries/chain/steem_evaluator.cpp @@ -5,18 +5,6 @@ #include #include -#define GOLOS_CHECK_BALANCE(ACCOUNT, TYPE, REQUIRED ...) \ - FC_EXPAND_MACRO( \ - FC_MULTILINE_MACRO_BEGIN \ - asset exist = get_balance(ACCOUNT, TYPE, (REQUIRED).symbol); \ - if( UNLIKELY( exist < (REQUIRED) )) { \ - FC_THROW_EXCEPTION( golos::insufficient_funds, \ - "Account \"${account}\" does not have enough ${balance}: required ${required}, exist ${exist}", \ - ("account",ACCOUNT.name)("balance",get_balance_name(TYPE))("required",REQUIRED)("exist",exist)); \ - } \ - FC_MULTILINE_MACRO_END \ - ) - #define GOLOS_CHECK_BANDWIDTH(NOW, NEXT, TYPE, MSG, ...) \ GOLOS_ASSERT((NOW) > (NEXT), golos::bandwidth_exception, MSG, \ ("bandwidth",TYPE)("now",NOW)("next",NEXT) __VA_ARGS__) @@ -25,63 +13,6 @@ namespace golos { namespace chain { using fc::uint128_t; - enum balance_type { - MAIN_BALANCE, - SAVINGS, - VESTING, - EFFECTIVE_VESTING, - HAVING_VESTING, - AVAILABLE_VESTING - }; - - asset get_balance(const account_object &account, balance_type type, asset_symbol_type symbol) { - switch(type) { - case MAIN_BALANCE: - switch (symbol) { - case STEEM_SYMBOL: - return account.balance; - case SBD_SYMBOL: - return account.sbd_balance; - default: - GOLOS_CHECK_VALUE(false, "invalid symbol"); - } - case SAVINGS: - switch (symbol) { - case STEEM_SYMBOL: - return account.savings_balance; - case SBD_SYMBOL: - return account.savings_sbd_balance; - default: - GOLOS_CHECK_VALUE(false, "invalid symbol"); - } - case VESTING: - GOLOS_CHECK_VALUE(symbol == VESTS_SYMBOL, "invalid symbol"); - return account.vesting_shares; - case EFFECTIVE_VESTING: - GOLOS_CHECK_VALUE(symbol == VESTS_SYMBOL, "invalid symbol"); - return account.effective_vesting_shares(); - case HAVING_VESTING: - GOLOS_CHECK_VALUE(symbol == VESTS_SYMBOL, "invalid symbol"); - return account.available_vesting_shares(false); - case AVAILABLE_VESTING: - GOLOS_CHECK_VALUE(symbol == VESTS_SYMBOL, "invalid symbol"); - return account.available_vesting_shares(true); - default: FC_ASSERT(false, "invalid balance type"); - } - } - - std::string get_balance_name(balance_type type) { - switch(type) { - case MAIN_BALANCE: return "fund"; - case SAVINGS: return "savings"; - case VESTING: return "vesting shares"; - case EFFECTIVE_VESTING: return "effective vesting shares"; - case HAVING_VESTING: return "having vesting shares"; - case AVAILABLE_VESTING: return "available vesting shares"; - default: FC_ASSERT(false, "invalid balance type"); - } - } - inline void validate_permlink_0_1(const string &permlink) { GOLOS_CHECK_VALUE(permlink.size() > STEEMIT_MIN_PERMLINK_LENGTH && permlink.size() < STEEMIT_MAX_PERMLINK_LENGTH, diff --git a/libraries/chain/worker_evaluators.cpp b/libraries/chain/worker_evaluators.cpp index 0fb3c4d130..fa26f153b1 100644 --- a/libraries/chain/worker_evaluators.cpp +++ b/libraries/chain/worker_evaluators.cpp @@ -186,13 +186,15 @@ namespace golos { namespace chain { } } - if (approvers >= STEEMIT_MAJOR_VOTED_WITNESSES) { - _db.modify(wpo, [&](worker_proposal_object& wpo) { - wpo.approved_techspec_author = o.author; - from_string(wpo.approved_techspec_permlink, o.permlink); - wpo.state = worker_proposal_state::techspec; - }); + if (approvers < STEEMIT_MAJOR_VOTED_WITNESSES) { + return; } + + _db.modify(wpo, [&](worker_proposal_object& wpo) { + wpo.approved_techspec_author = o.author; + from_string(wpo.approved_techspec_permlink, o.permlink); + wpo.state = worker_proposal_state::techspec; + }); } } @@ -335,6 +337,21 @@ namespace golos { namespace chain { _db.modify(wpo, [&](worker_proposal_object& wpo) { wpo.state = worker_proposal_state::payment; }); + + _db.modify(wto, [&](worker_techspec_object& wto) { + wto.next_cashout_time = _db.head_block_time() + wto.payments_interval; + wto.payment_beginning_time = wto.next_cashout_time; + }); + + const auto& gpo = _db.get_dynamic_global_properties(); + _db.modify(gpo, [&](dynamic_global_property_object& gpo) { + gpo.total_worker_fund_steem -= wto.specification_cost; + }); + + _db.adjust_balance(_db.get_account(wto.author), wto.specification_cost); + +// + _db.push_virtual_operation(techspec_reward_operation(wto.author, to_string(wto.permlink), wto.specification_cost)); } } } diff --git a/libraries/protocol/include/golos/protocol/operations.hpp b/libraries/protocol/include/golos/protocol/operations.hpp index 59008dbdec..a2dcb65701 100644 --- a/libraries/protocol/include/golos/protocol/operations.hpp +++ b/libraries/protocol/include/golos/protocol/operations.hpp @@ -96,7 +96,9 @@ namespace golos { namespace protocol { return_vesting_delegation_operation, producer_reward_operation, delegation_reward_operation, - auction_window_reward_operation + auction_window_reward_operation, + techspec_reward_operation, + worker_reward_operation > operation; /*void operation_get_required_authorities( const operation& op, diff --git a/libraries/protocol/include/golos/protocol/steem_virtual_operations.hpp b/libraries/protocol/include/golos/protocol/steem_virtual_operations.hpp index 68901a2aaf..49cdd24825 100644 --- a/libraries/protocol/include/golos/protocol/steem_virtual_operations.hpp +++ b/libraries/protocol/include/golos/protocol/steem_virtual_operations.hpp @@ -222,6 +222,31 @@ namespace golos { namespace protocol { asset vesting_shares; }; + struct techspec_reward_operation : public virtual_operation { + techspec_reward_operation() { + } + techspec_reward_operation(const account_name_type& a, const string& p, const asset& r) + : author(a), permlink(p), reward(r) { + } + + account_name_type author; + string permlink; + asset reward; + }; + + struct worker_reward_operation : public virtual_operation { + worker_reward_operation() { + } + worker_reward_operation(const account_name_type& w, const account_name_type& wpa, const string& wpp, const asset& r) + : worker(w), worker_proposal_author(wpa), worker_proposal_permlink(wpp), reward(r) { + } + + account_name_type worker; + account_name_type worker_proposal_author; + string worker_proposal_permlink; + asset reward; + }; + struct return_vesting_delegation_operation: public virtual_operation { return_vesting_delegation_operation() { } @@ -251,3 +276,5 @@ FC_REFLECT((golos::protocol::comment_benefactor_reward_operation), (benefactor)( FC_REFLECT((golos::protocol::return_vesting_delegation_operation), (account)(vesting_shares)) FC_REFLECT((golos::protocol::producer_reward_operation), (producer)(vesting_shares)) FC_REFLECT((golos::protocol::delegation_reward_operation), (delegator)(delegatee)(payout_strategy)(vesting_shares)) +FC_REFLECT((golos::protocol::techspec_reward_operation), (author)(permlink)(reward)) +FC_REFLECT((golos::protocol::worker_reward_operation), (worker)(worker_proposal_author)(worker_proposal_permlink)(reward)) diff --git a/libraries/protocol/worker_operations.cpp b/libraries/protocol/worker_operations.cpp index fe6014d947..78b703f95c 100644 --- a/libraries/protocol/worker_operations.cpp +++ b/libraries/protocol/worker_operations.cpp @@ -25,11 +25,25 @@ namespace golos { namespace protocol { GOLOS_CHECK_PARAM(worker_proposal_permlink, validate_permlink(worker_proposal_permlink)); GOLOS_CHECK_PARAM(specification_cost, { + GOLOS_CHECK_ASSET_GOLOS(specification_cost); GOLOS_CHECK_VALUE_GE(specification_cost.amount, 0); }); GOLOS_CHECK_PARAM(development_cost, { + GOLOS_CHECK_ASSET_GOLOS(development_cost); GOLOS_CHECK_VALUE_GE(development_cost.amount, 0); }); + + GOLOS_CHECK_PARAM(payments_count, { + GOLOS_CHECK_VALUE_GE(payments_count, 1); + }); + GOLOS_CHECK_PARAM(payments_interval, { + if (payments_count == 1) { + GOLOS_CHECK_VALUE_EQ(payments_interval, 0); + return; + } + GOLOS_CHECK_VALUE_GT(payments_interval, 0); + GOLOS_CHECK_VALUE_LE(payments_interval * payments_count, development_eta); + }); } void worker_techspec_delete_operation::validate() const { diff --git a/plugins/mongo_db/include/golos/plugins/mongo_db/mongo_db_operations.hpp b/plugins/mongo_db/include/golos/plugins/mongo_db/mongo_db_operations.hpp index db4c4b5d3b..8cfb290ea4 100644 --- a/plugins/mongo_db/include/golos/plugins/mongo_db/mongo_db_operations.hpp +++ b/plugins/mongo_db/include/golos/plugins/mongo_db/mongo_db_operations.hpp @@ -94,6 +94,8 @@ namespace mongo_db { result_type operator()(const chain_properties_update_operation& op); result_type operator()(const delegation_reward_operation& op); result_type operator()(const auction_window_reward_operation& op); + result_type operator()(const techspec_reward_operation& op); + result_type operator()(const worker_reward_operation& op); }; }}} // golos::plugins::mongo_db diff --git a/plugins/mongo_db/include/golos/plugins/mongo_db/mongo_db_state.hpp b/plugins/mongo_db/include/golos/plugins/mongo_db/mongo_db_state.hpp index 9d8b23a03a..db682a696c 100644 --- a/plugins/mongo_db/include/golos/plugins/mongo_db/mongo_db_state.hpp +++ b/plugins/mongo_db/include/golos/plugins/mongo_db/mongo_db_state.hpp @@ -96,6 +96,8 @@ namespace mongo_db { result_type operator()(const chain_properties_update_operation& op); result_type operator()(const delegation_reward_operation& op); result_type operator()(const auction_window_reward_operation& op); + result_type operator()(const techspec_reward_operation& op); + result_type operator()(const worker_reward_operation& op); void write_global_property_object(const dynamic_global_property_object& dgpo, bool history); diff --git a/plugins/mongo_db/mongo_db_operations.cpp b/plugins/mongo_db/mongo_db_operations.cpp index 1b5c33c1ab..54014b083d 100644 --- a/plugins/mongo_db/mongo_db_operations.cpp +++ b/plugins/mongo_db/mongo_db_operations.cpp @@ -816,4 +816,14 @@ namespace mongo_db { return body; } + auto operation_writer::operator()(const techspec_reward_operation& op) -> result_type { + result_type body; + return body; + } + + auto operation_writer::operator()(const worker_reward_operation& op) -> result_type { + result_type body; + return body; + } + }}} diff --git a/plugins/mongo_db/mongo_db_state.cpp b/plugins/mongo_db/mongo_db_state.cpp index ecbbb6742b..d83f34a903 100644 --- a/plugins/mongo_db/mongo_db_state.cpp +++ b/plugins/mongo_db/mongo_db_state.cpp @@ -1960,4 +1960,12 @@ namespace mongo_db { } + auto state_writer::operator()(const techspec_reward_operation& op) -> result_type { + + } + + auto state_writer::operator()(const worker_reward_operation& op) -> result_type { + + } + }}} \ No newline at end of file