From b362ca23a693fdd1dd47912dc2a7b85c0d00d6e5 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Tue, 9 Feb 2021 03:43:42 +0000 Subject: [PATCH] Check vesting balance properties before creating/updating SON --- libraries/chain/son_evaluator.cpp | 36 ++++++++++++ .../wallet/include/graphene/wallet/wallet.hpp | 14 +++++ libraries/wallet/wallet.cpp | 55 +++++++++++++++++++ tests/tests/son_operations_tests.cpp | 7 ++- 4 files changed, 111 insertions(+), 1 deletion(-) diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 9c48c3e0..bf92b709 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -12,8 +12,27 @@ 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 = (db().get_chain_id().str() == "6b6b5f0ce7a36d323768e534f3edb41c6d6332a541a95725b98e28d140850134") && + (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) { + vesting_balance_object vbo = op.deposit(db()); + FC_ASSERT(vbo.owner == op.owner_account); + FC_ASSERT(vbo.balance_type == vesting_balance_type::son, "Deposit vesting must be of type SON"); + FC_ASSERT(vbo.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) { + vesting_balance_object vbo = op.pay_vb(db()); + FC_ASSERT(vbo.owner == op.owner_account); + FC_ASSERT(vbo.balance_type == vesting_balance_type::normal, "Payment vesting balance must be of type NORMAL"); + FC_ASSERT(vbo.policy.which() == vesting_policy::tag::value, + "Payment balance must have linear vesting policy"); + } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -43,6 +62,23 @@ 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()) { + vesting_balance_object vbo = (*op.new_deposit)(db()); + FC_ASSERT(vbo.owner == op.owner_account); + FC_ASSERT(vbo.balance_type == vesting_balance_type::son, "Deposit vesting must be of type SON"); + FC_ASSERT(vbo.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()) { + vesting_balance_object vbo = (*op.new_pay_vb)(db()); + FC_ASSERT(vbo.owner == op.owner_account); + FC_ASSERT(vbo.balance_type == vesting_balance_type::normal, "Payment vesting balance must be of type NORMAL"); + FC_ASSERT(vbo.policy.which() == vesting_policy::tag::value, + "Payment 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 4887f35b..38d510e0 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1373,6 +1373,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 @@ -2534,6 +2547,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 98dc122c..35fd9d96 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1942,6 +1942,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; @@ -1989,6 +2014,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 { @@ -4865,6 +4912,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); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 8029c58a..5fee05d5 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -39,6 +39,7 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { op.amount = asset(10*GRAPHENE_BLOCKCHAIN_PRECISION); op.balance_type = vesting_balance_type::son; op.policy = dormant_vesting_policy_initializer {}; + trx.clear(); trx.operations.push_back(op); // amount in the son balance need to be at least 50 @@ -68,9 +69,10 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { op.owner = alice_id; op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION); op.balance_type = vesting_balance_type::normal; - + op.policy = linear_vesting_policy_initializer {}; op.validate(); + trx.clear(); trx.operations.push_back(op); trx.validate(); processed_transaction ptx = PUSH_TX(db, trx, ~0); @@ -93,9 +95,12 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { op.pay_vb = payment; op.signing_key = alice_public_key; op.sidechain_public_keys = sidechain_public_keys; + + trx.clear(); trx.operations.push_back(op); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); + trx.clear(); } generate_block();