From 7d80c042bfe0043928110ca7270e8266a54d0f06 Mon Sep 17 00:00:00 2001 From: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Date: Fri, 31 Jul 2020 12:26:45 +0000 Subject: [PATCH] Unlisting offers, add result in offer history object --- libraries/chain/db_init.cpp | 1 + libraries/chain/db_notify.cpp | 3 + .../graphene/chain/offer_evaluator.hpp | 9 + .../include/graphene/chain/offer_object.hpp | 3 +- .../include/graphene/chain/protocol/offer.hpp | 31 +++- .../graphene/chain/protocol/operations.hpp | 1 + libraries/chain/offer_evaluator.cpp | 55 ++++++ libraries/chain/protocol/offer.cpp | 11 ++ .../wallet/include/graphene/wallet/wallet.hpp | 11 ++ libraries/wallet/wallet.cpp | 23 +++ programs/witness_node/genesis.json | 10 +- tests/tests/marketplace_tests.cpp | 165 ++++++++++++++++++ 12 files changed, 316 insertions(+), 7 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index b1e53a28..9ae1fb96 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -267,6 +267,7 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 0c0cfa6b..f56e3d8b 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -338,6 +338,9 @@ struct get_impacted_account_visitor void operator()( const bid_operation& op ) { _impacted.insert( op.bidder ); } + void operator()( const cancel_offer_operation& op ) { + _impacted.insert( op.issuer ); + } void operator()( const finalize_offer_operation& op ) { _impacted.insert( op.fee_paying_account ); } diff --git a/libraries/chain/include/graphene/chain/offer_evaluator.hpp b/libraries/chain/include/graphene/chain/offer_evaluator.hpp index 1db48b0d..46f7045b 100644 --- a/libraries/chain/include/graphene/chain/offer_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/offer_evaluator.hpp @@ -25,6 +25,15 @@ namespace graphene void_result do_apply(const bid_operation &o); }; + class cancel_offer_evaluator : public evaluator + { + public: + typedef cancel_offer_operation operation_type; + + void_result do_evaluate(const cancel_offer_operation &o); + void_result do_apply(const cancel_offer_operation &o); + }; + class finalize_offer_evaluator : public evaluator { public: diff --git a/libraries/chain/include/graphene/chain/offer_object.hpp b/libraries/chain/include/graphene/chain/offer_object.hpp index 5c757485..fe176332 100644 --- a/libraries/chain/include/graphene/chain/offer_object.hpp +++ b/libraries/chain/include/graphene/chain/offer_object.hpp @@ -48,6 +48,7 @@ namespace graphene bool buying_item; fc::time_point_sec offer_expiration_date; + result_type result; offer_history_id_type get_id() const { return id; } }; @@ -105,4 +106,4 @@ FC_REFLECT_DERIVED(graphene::chain::offer_object, (graphene::db::object), FC_REFLECT_DERIVED(graphene::chain::offer_history_object, (graphene::db::object), (issuer)(item_ids)(bidder)(bid_price)(minimum_price)( - maximum_price)(buying_item)(offer_expiration_date)) + maximum_price)(buying_item)(offer_expiration_date)(result)) diff --git a/libraries/chain/include/graphene/chain/protocol/offer.hpp b/libraries/chain/include/graphene/chain/protocol/offer.hpp index d6c98ea9..fcd0ab75 100644 --- a/libraries/chain/include/graphene/chain/protocol/offer.hpp +++ b/libraries/chain/include/graphene/chain/protocol/offer.hpp @@ -74,10 +74,32 @@ namespace graphene share_type calculate_fee(const fee_parameters_type &k) const; }; + struct cancel_offer_operation : public base_operation + { + struct fee_parameters_type + { + uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint32_t price_per_kbyte = + 10 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + + asset fee; + + account_id_type issuer; + offer_id_type offer_id; + + extensions_type extensions; + + account_id_type fee_payer() const { return issuer; } + void validate() const; + share_type calculate_fee(const fee_parameters_type &k) const; + }; + enum class result_type { Expired = 0, - ExpiredNoBid = 1 + ExpiredNoBid = 1, + Cancelled = 2 }; struct finalize_offer_operation : public base_operation @@ -114,7 +136,12 @@ FC_REFLECT(graphene::chain::bid_operation::fee_parameters_type, FC_REFLECT(graphene::chain::bid_operation, (fee)(bidder)(bid_price)(offer_id)(extensions)); -FC_REFLECT_ENUM(graphene::chain::result_type, (Expired)(ExpiredNoBid)); +FC_REFLECT(graphene::chain::cancel_offer_operation::fee_parameters_type, + (fee)(price_per_kbyte)); +FC_REFLECT(graphene::chain::cancel_offer_operation, + (fee)(issuer)(offer_id)(extensions)); + +FC_REFLECT_ENUM(graphene::chain::result_type, (Expired)(ExpiredNoBid)(Cancelled)); FC_REFLECT(graphene::chain::finalize_offer_operation::fee_parameters_type, (fee)); FC_REFLECT(graphene::chain::finalize_offer_operation, diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 746991d5..1285d353 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -148,6 +148,7 @@ namespace graphene { namespace chain { custom_account_authority_delete_operation, offer_operation, bid_operation, + cancel_offer_operation, finalize_offer_operation, nft_metadata_create_operation, nft_metadata_update_operation, diff --git a/libraries/chain/offer_evaluator.cpp b/libraries/chain/offer_evaluator.cpp index f6fd3138..79f7243b 100644 --- a/libraries/chain/offer_evaluator.cpp +++ b/libraries/chain/offer_evaluator.cpp @@ -137,6 +137,60 @@ namespace graphene FC_CAPTURE_AND_RETHROW((op)) } + void_result cancel_offer_evaluator::do_evaluate(const cancel_offer_operation &op) + { + try + { + const database &d = db(); + const auto &offer = op.offer_id(d); + op.issuer(d); + FC_ASSERT(op.issuer == offer.issuer, "Only offer issuer can cancel the offer"); + FC_ASSERT(offer.offer_expiration_date > d.head_block_time(), "Expiration should be in future when cancelling the offer"); + return void_result(); + } + FC_CAPTURE_AND_RETHROW((op)) + } + + void_result cancel_offer_evaluator::do_apply(const cancel_offer_operation &op) + { + try + { + database &d = db(); + + const auto &offer = op.offer_id(d); + if(offer.buying_item) + { + // Refund the max price to issuer + d.adjust_balance(offer.issuer, offer.maximum_price); + } + else + { + if (offer.bidder) + { + // Refund the bid price to the best bidder till now + d.adjust_balance(*offer.bidder, *offer.bid_price); + } + } + + d.create([&](offer_history_object &obj) { + obj.issuer = offer.issuer; + obj.item_ids = offer.item_ids; + obj.bidder = offer.bidder; + obj.bid_price = offer.bid_price; + obj.minimum_price = offer.minimum_price; + obj.maximum_price = offer.maximum_price; + obj.buying_item = offer.buying_item; + obj.offer_expiration_date = offer.offer_expiration_date; + obj.result = result_type::Cancelled; + }); + // This should unlock the item + d.remove(op.offer_id(d)); + + return void_result(); + } + FC_CAPTURE_AND_RETHROW((op)) + } + void_result finalize_offer_evaluator::do_evaluate(const finalize_offer_operation &op) { try @@ -265,6 +319,7 @@ namespace graphene obj.maximum_price = offer.maximum_price; obj.buying_item = offer.buying_item; obj.offer_expiration_date = offer.offer_expiration_date; + obj.result = op.result; }); // This should unlock the item d.remove(op.offer_id(d)); diff --git a/libraries/chain/protocol/offer.cpp b/libraries/chain/protocol/offer.cpp index 4fc60fc4..bd5dd20e 100644 --- a/libraries/chain/protocol/offer.cpp +++ b/libraries/chain/protocol/offer.cpp @@ -31,6 +31,17 @@ namespace graphene FC_ASSERT(bid_price.amount.value >= 0); } + void cancel_offer_operation::validate() const + { + FC_ASSERT(fee.amount.value >= 0); + } + + share_type cancel_offer_operation::calculate_fee(const fee_parameters_type &schedule) const + { + share_type core_fee_required = schedule.fee; + return core_fee_required; + } + void finalize_offer_operation::validate() const { FC_ASSERT(fee.amount.value >= 0); diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 6c075864..2d76159e 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -2071,6 +2071,12 @@ class wallet_api */ bool nft_is_approved_for_all(string owner_account_id_or_name, string operator_account_id_or_name) const; + /** + * @brief Returns all tokens + * @return Returns vector of NFT objects, empty vector if none + */ + vector nft_get_all_tokens() const; + signed_transaction create_offer(set item_ids, string issuer_accound_id_or_name, asset minimum_price, @@ -2083,6 +2089,9 @@ class wallet_api asset bid_price, offer_id_type offer_id, bool broadcast); + signed_transaction cancel_offer(string issuer_account_id_or_name, + offer_id_type offer_id, + bool broadcast); vector list_offers(uint32_t limit, optional lower_id) const; vector list_sell_offers(uint32_t limit, optional lower_id) const; vector list_buy_offers(uint32_t limit, optional lower_id) const; @@ -2355,8 +2364,10 @@ FC_API( graphene::wallet::wallet_api, (nft_set_approval_for_all) (nft_get_approved) (nft_is_approved_for_all) + (nft_get_all_tokens) (create_offer) (create_bid) + (cancel_offer) (list_offers) (list_sell_offers) (list_buy_offers) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 47452a62..77a00369 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -6563,6 +6563,11 @@ bool wallet_api::nft_is_approved_for_all(string owner_account_id_or_name, string return my->_remote_db->nft_is_approved_for_all(owner_account.id, operator_account.id); } +vector wallet_api::nft_get_all_tokens() const +{ + return my->_remote_db->nft_get_all_tokens(); +} + signed_transaction wallet_api::create_offer(set item_ids, string issuer_accound_id_or_name, asset minimum_price, @@ -6611,6 +6616,24 @@ signed_transaction wallet_api::create_bid(string bidder_account_id_or_name, return my->sign_transaction( trx, broadcast ); } +signed_transaction wallet_api::cancel_offer(string issuer_account_id_or_name, + offer_id_type offer_id, + bool broadcast) +{ + account_object issuer_account = my->get_account(issuer_account_id_or_name); + + cancel_offer_operation op; + op.issuer = issuer_account.id; + op.offer_id = offer_id; + + signed_transaction trx; + trx.operations.push_back(op); + my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees ); + trx.validate(); + + return my->sign_transaction( trx, broadcast ); +} + vector wallet_api::list_offers(uint32_t limit, optional lower_id) const { offer_id_type lb_id; diff --git a/programs/witness_node/genesis.json b/programs/witness_node/genesis.json index e07cec92..b09f3b50 100644 --- a/programs/witness_node/genesis.json +++ b/programs/witness_node/genesis.json @@ -333,9 +333,9 @@ "scale": 10000 }, "block_interval": 3, - "maintenance_interval": 86400, + "maintenance_interval": 240, "maintenance_skip_slots": 3, - "committee_proposal_review_period": 1209600, + "committee_proposal_review_period": 600, "maximum_transaction_size": 2048, "maximum_block_size": 1228800000, "maximum_time_until_expiration": 86400, @@ -384,7 +384,9 @@ "gpos_period": 15552000, "gpos_subperiod": 2592000, "gpos_period_start": 1601528400, - "gpos_vesting_lockin_period": 2592000 + "gpos_vesting_lockin_period": 2592000, + "rbac_max_permissions_per_account": 3, + "rbac_max_authorities_per_permission":3 } }, "initial_bts_accounts": [], @@ -526,4 +528,4 @@ "num_special_accounts": 0, "num_special_assets": 0 } -} \ No newline at end of file +} diff --git a/tests/tests/marketplace_tests.cpp b/tests/tests/marketplace_tests.cpp index b73d5dc7..28a186b1 100644 --- a/tests/tests/marketplace_tests.cpp +++ b/tests/tests/marketplace_tests.cpp @@ -402,6 +402,7 @@ BOOST_AUTO_TEST_CASE(best_buy_bid_for_sell_offer) BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price); BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date); BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids); + BOOST_CHECK(result_type::Expired == history_obj.result); } catch (fc::exception &e) { @@ -444,6 +445,7 @@ BOOST_AUTO_TEST_CASE(expire_with_bid_for_sell_offer_test) BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price); BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date); BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids); + BOOST_CHECK(result_type::Expired == history_obj.result); } BOOST_AUTO_TEST_CASE(expire_no_bid_for_sell_offer_test) @@ -474,6 +476,7 @@ BOOST_AUTO_TEST_CASE(expire_no_bid_for_sell_offer_test) BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price); BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date); BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids); + BOOST_CHECK(result_type::ExpiredNoBid == history_obj.result); } BOOST_AUTO_TEST_CASE(create_buy_offer_test) @@ -697,6 +700,7 @@ BOOST_AUTO_TEST_CASE(best_sell_bid_for_buy_offer) BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price); BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date); BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids); + BOOST_CHECK(result_type::Expired == history_obj.result); } catch (fc::exception &e) { @@ -742,6 +746,7 @@ BOOST_AUTO_TEST_CASE(expire_with_bid_for_buy_offer_test) BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price); BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date); BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids); + BOOST_CHECK(result_type::Expired == history_obj.result); } BOOST_AUTO_TEST_CASE(expire_no_bid_for_buy_offer_test) @@ -773,6 +778,166 @@ BOOST_AUTO_TEST_CASE(expire_no_bid_for_buy_offer_test) BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price); BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date); BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids); + BOOST_CHECK(result_type::ExpiredNoBid == history_obj.result); +} + +BOOST_AUTO_TEST_CASE(cancel_sell_offer_no_bid_test) +{ + try + { + INVOKE(create_sell_offer_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(operator1); + + const auto &offer_obj = sell_offer(db); + auto cached_offer_obj = offer_obj; + + cancel_offer_operation cancel_op; + cancel_op.offer_id = offer_obj.id; + // Add non-issuer + cancel_op.issuer = bob_id; + trx.clear(); + trx.operations.push_back(cancel_op); + sign(trx, bob_private_key); + BOOST_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + trx.clear(); + // Add issuer + cancel_op.issuer = alice_id; + trx.operations.push_back(cancel_op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + + const auto &oidx = db.get_index_type().indices().get(); + const auto &ohidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(oidx.size() == 0); + BOOST_REQUIRE(ohidx.size() == 1); + BOOST_CHECK(db.item_locked(nft_id_type(0)) == false); + BOOST_CHECK(db.item_locked(nft_id_type(1)) == false); + // Get offer history object + const auto &history_obj = offer_history_id_type(0)(db); + // History object data check + BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price); + BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder); + BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item); + BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer); + BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price); + BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price); + BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date); + BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids); + BOOST_CHECK(result_type::Cancelled == history_obj.result); + + } + catch (fc::exception &e) + { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE(cancel_sell_offer_with_bid_test) +{ + try + { + INVOKE(buy_bid_for_sell_offer_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(operator1); + + const auto &offer_obj = sell_offer(db); + auto cached_offer_obj = offer_obj; + int64_t bob_balance = get_balance(bob_id(db), asset_id_type()(db)); + + cancel_offer_operation cancel_op; + cancel_op.offer_id = offer_obj.id; + // Add issuer + cancel_op.issuer = alice_id; + trx.clear(); + trx.operations.push_back(cancel_op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + + const auto &oidx = db.get_index_type().indices().get(); + const auto &ohidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(oidx.size() == 0); + BOOST_REQUIRE(ohidx.size() == 1); + BOOST_CHECK(db.item_locked(nft_id_type(0)) == false); + BOOST_CHECK(db.item_locked(nft_id_type(1)) == false); + BOOST_CHECK_EQUAL(get_balance(bob_id(db), asset_id_type()(db)), + (bob_balance + (*cached_offer_obj.bid_price).amount).value); + // Get offer history object + const auto &history_obj = offer_history_id_type(0)(db); + // History object data check + BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price); + BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder); + BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item); + BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer); + BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price); + BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price); + BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date); + BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids); + BOOST_CHECK(result_type::Cancelled == history_obj.result); + + } + catch (fc::exception &e) + { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE(cancel_buy_offer_with_bid_test) +{ + try + { + INVOKE(sell_bid_for_buy_offer_test); + GET_ACTOR(alice); + GET_ACTOR(bob); + GET_ACTOR(operator1); + + const auto &offer_obj = buy_offer(db); + auto cached_offer_obj = offer_obj; + int64_t alice_balance = get_balance(alice_id(db), asset_id_type()(db)); + + cancel_offer_operation cancel_op; + cancel_op.offer_id = offer_obj.id; + cancel_op.issuer = alice_id; + + trx.clear(); + trx.operations.push_back(cancel_op); + sign(trx, alice_private_key); + PUSH_TX(db, trx); + trx.clear(); + + generate_block(); + + const auto &oidx = db.get_index_type().indices().get(); + const auto &ohidx = db.get_index_type().indices().get(); + BOOST_REQUIRE(oidx.size() == 0); + BOOST_REQUIRE(ohidx.size() == 2); + BOOST_CHECK(db.item_locked(nft_id_type(0)) == false); + BOOST_CHECK(db.item_locked(nft_id_type(1)) == false); + BOOST_CHECK_EQUAL(get_balance(alice_id(db), asset_id_type()(db)), + (alice_balance + cached_offer_obj.maximum_price.amount).value); + // Get offer history object + const auto &history_obj = offer_history_id_type(1)(db); + // History object data check + BOOST_CHECK(cached_offer_obj.bid_price == history_obj.bid_price); + BOOST_CHECK(cached_offer_obj.bidder == history_obj.bidder); + BOOST_CHECK(cached_offer_obj.buying_item == history_obj.buying_item); + BOOST_CHECK(cached_offer_obj.issuer == history_obj.issuer); + BOOST_CHECK(cached_offer_obj.maximum_price == history_obj.maximum_price); + BOOST_CHECK(cached_offer_obj.minimum_price == history_obj.minimum_price); + BOOST_CHECK(cached_offer_obj.offer_expiration_date == history_obj.offer_expiration_date); + BOOST_CHECK(cached_offer_obj.item_ids == history_obj.item_ids); + BOOST_CHECK(result_type::Cancelled == history_obj.result); + + } + catch (fc::exception &e) + { + edump((e.to_detail_string())); + throw; + } } BOOST_AUTO_TEST_SUITE_END()