Unlisting offers, add result in offer history object

This commit is contained in:
sierra19XX 2020-07-31 12:26:45 +00:00
parent 82e9d92089
commit 7d80c042bf
12 changed files with 316 additions and 7 deletions

View file

@ -267,6 +267,7 @@ void database::initialize_evaluators()
register_evaluator<delete_custom_account_authority_evaluator>();
register_evaluator<offer_evaluator>();
register_evaluator<bid_evaluator>();
register_evaluator<cancel_offer_evaluator>();
register_evaluator<finalize_offer_evaluator>();
register_evaluator<nft_metadata_create_evaluator>();
register_evaluator<nft_metadata_update_evaluator>();

View file

@ -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 );
}

View file

@ -25,6 +25,15 @@ namespace graphene
void_result do_apply(const bid_operation &o);
};
class cancel_offer_evaluator : public evaluator<cancel_offer_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<finalize_offer_evaluator>
{
public:

View file

@ -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))

View file

@ -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,

View file

@ -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,

View file

@ -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>([&](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));

View file

@ -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);

View file

@ -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_object> nft_get_all_tokens() const;
signed_transaction create_offer(set<nft_id_type> 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<offer_object> list_offers(uint32_t limit, optional<offer_id_type> lower_id) const;
vector<offer_object> list_sell_offers(uint32_t limit, optional<offer_id_type> lower_id) const;
vector<offer_object> list_buy_offers(uint32_t limit, optional<offer_id_type> 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)

View file

@ -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<nft_object> wallet_api::nft_get_all_tokens() const
{
return my->_remote_db->nft_get_all_tokens();
}
signed_transaction wallet_api::create_offer(set<nft_id_type> 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<offer_object> wallet_api::list_offers(uint32_t limit, optional<offer_id_type> lower_id) const
{
offer_id_type lb_id;

View file

@ -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
}
}
}

View file

@ -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<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
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<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
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<offer_index>().indices().get<by_id>();
const auto &ohidx = db.get_index_type<offer_history_index>().indices().get<by_id>();
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()