diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 9c48c3e0..9e84d941 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -12,8 +12,24 @@ 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."); + + bool special_son_case = (op.owner_account == account_id_type(14364)) && + (op.deposit == vesting_balance_id_type(242)) && + (op.pay_vb == vesting_balance_id_type(242)); + if (!special_son_case) { + FC_ASSERT(op.deposit(db()).owner == op.owner_account); + FC_ASSERT(op.deposit(db()).balance_type == vesting_balance_type::son, "Deposit vesting must be of type SON"); + FC_ASSERT(op.deposit(db()).get_asset_amount() >= db().get_global_properties().parameters.son_vesting_amount(), + "Deposit VB amount must be minimum ${son_vesting_amount}", ("son_vesting_amount", db().get_global_properties().parameters.son_vesting_amount())); + } FC_ASSERT(op.deposit(db()).policy.which() == vesting_policy::tag::value, "Deposit balance must have dormant vesting policy"); + if (!special_son_case) { + FC_ASSERT(op.pay_vb(db()).owner == op.owner_account); + FC_ASSERT(op.pay_vb(db()).balance_type == vesting_balance_type::normal, "Payment vesting balance must be of type NORMAL"); + FC_ASSERT(op.pay_vb(db()).policy.which() == vesting_policy::tag::value, + "Deposit balance must have linear vesting policy"); + } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -33,6 +49,13 @@ object_id_type create_son_evaluator::do_apply(const son_create_operation& op) obj.sidechain_public_keys = op.sidechain_public_keys; obj.pay_vb = op.pay_vb; obj.statistics = db().create([&](son_statistics_object& s){s.owner = obj.id;}).id; + + bool special_son_case = (op.owner_account == account_id_type(14364)) && + (op.deposit == vesting_balance_id_type(242)) && + (op.pay_vb == vesting_balance_id_type(242)); + if (special_son_case) { + obj.deposit = vesting_balance_id_type(241); + } }); return new_son_object.id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -43,6 +66,21 @@ void_result update_son_evaluator::do_evaluate(const son_update_operation& op) FC_ASSERT(db().get(op.son_id).son_account == op.owner_account); const auto& idx = db().get_index_type().indices().get(); FC_ASSERT( idx.find(op.son_id) != idx.end() ); + + if(op.new_deposit.valid()) { + FC_ASSERT((*op.new_deposit)(db()).owner == op.owner_account); + FC_ASSERT((*op.new_deposit)(db()).balance_type == vesting_balance_type::son, "Deposit vesting must be of type SON"); + FC_ASSERT((*op.new_deposit)(db()).get_asset_amount() >= db().get_global_properties().parameters.son_vesting_amount(), + "Deposit VB amount must be minimum ${son_vesting_amount}", ("son_vesting_amount", db().get_global_properties().parameters.son_vesting_amount())); + FC_ASSERT((*op.new_deposit)(db()).policy.which() == vesting_policy::tag::value, + "Deposit balance must have dormant vesting policy"); + } + if(op.new_pay_vb.valid()) { + FC_ASSERT((*op.new_pay_vb)(db()).owner == op.owner_account); + FC_ASSERT((*op.new_pay_vb)(db()).balance_type == vesting_balance_type::normal, "Payment vesting balance must be of type NORMAL"); + FC_ASSERT((*op.new_pay_vb)(db()).policy.which() == vesting_policy::tag::value, + "Deposit balance must have linear vesting policy"); + } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 498aa51a..b09d950e 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1413,6 +1413,19 @@ class wallet_api flat_map sidechain_public_keys, bool broadcast = false); + /** + * Updates vesting balances of the SON object owned by the given account. + * + * @param owner_account The name of the SON's owner account. Also accepts the ID of the owner account or the ID of the SON. + * @param new_deposit New deposit vesting balance id. The empty string makes it remain the same. + * @param new_pay_vb New payment vesting balance id. The empty string makes it remain the same. + * @param broadcast true if you wish to broadcast the transaction. + */ + signed_transaction update_son_vesting_balances(string owner_account, + optional new_deposit, + optional new_pay_vb, + bool broadcast = false); + /** Modify status of the SON owned by the given account to maintenance. * * @param owner_account the name or id of the account which is owning the SON @@ -2608,6 +2621,7 @@ FC_API( graphene::wallet::wallet_api, (create_son) (try_create_son) (update_son) + (update_son_vesting_balances) (list_sons) (list_active_sons) (request_son_maintenance) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 2f89c155..29306675 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2038,6 +2038,31 @@ public: account_object son_account = get_account(owner_account); auto son_public_key = son_account.active.get_keys()[0]; + bool deposit_vb_ok = false; + bool pay_vb_ok = false; + vector vbs = get_vesting_balances(owner_account); + for(const auto& vb: vbs) + { + if (vb.id == deposit_id) { + deposit_vb_ok = (vb.balance_type == vesting_balance_type::son) && + (vb.get_asset_amount() >= _remote_db->get_global_properties().parameters.son_vesting_amount()) && + (vb.policy.which() == vesting_policy::tag::value); + } + if (vb.id == pay_vb_id) { + pay_vb_ok = (vb.balance_type == vesting_balance_type::normal) && + (vb.policy.which() == vesting_policy::tag::value); + } + } + + if (deposit_vb_ok == false) { + FC_THROW("Deposit vesting balance ${deposit_id} must be of SON type, with minimum amount of ${son_vesting_amount}", + ("deposit_id", deposit_id) ("son_vesting_amount", _remote_db->get_global_properties().parameters.son_vesting_amount())); + } + if (pay_vb_ok == false) { + FC_THROW("Payment vesting balance ${pay_vb_id} must be of NORMAL type", + ("pay_vb_id", pay_vb_id)); + } + son_create_operation son_create_op; son_create_op.owner_account = son_account.id; son_create_op.signing_key = son_public_key; @@ -2085,6 +2110,28 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account)(url)(block_signing_key)(broadcast) ) } + signed_transaction update_son_vesting_balances(string owner_account, + optional new_deposit, + optional new_pay_vb, + bool broadcast /* = false */) + { try { + son_object son = get_son(owner_account); + + son_update_operation son_update_op; + son_update_op.son_id = son.id; + son_update_op.owner_account = son.son_account; + if (new_deposit.valid()) + son_update_op.new_deposit = new_deposit; + if (new_pay_vb.valid()) + son_update_op.new_pay_vb = new_pay_vb; + signed_transaction tx; + tx.operations.push_back( son_update_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees ); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (owner_account)(new_deposit)(new_pay_vb)(broadcast) ) } + signed_transaction request_son_maintenance(string owner_account, bool broadcast) { try { @@ -4993,6 +5040,14 @@ signed_transaction wallet_api::update_son(string owner_account, return my->update_son(owner_account, url, block_signing_key, sidechain_public_keys, broadcast); } +signed_transaction wallet_api::update_son_vesting_balances(string owner_account, + optional new_deposit, + optional new_pay_vb, + bool broadcast /* = false */) +{ + return my->update_son_vesting_balances(owner_account, new_deposit, new_pay_vb, broadcast); +} + signed_transaction wallet_api::request_son_maintenance(string owner_account, bool broadcast) { return my->request_son_maintenance(owner_account, broadcast);