Fix bug that prevented placing bets that were too small to stay on the books,
Add a get_objects() call to the bookie API to provide access to objects that have been removed from the blockchain, and a few tests to verify that they work.
This commit is contained in:
parent
26c2eb4c7a
commit
d13783a3c4
7 changed files with 300 additions and 201 deletions
|
|
@ -281,20 +281,7 @@ void_result bet_place_evaluator::do_evaluate(const bet_place_operation& op)
|
|||
FC_ASSERT(op.backer_multiplier % allowed_increment == 0, "Bet odds must be a multiple of ${allowed_increment}", ("allowed_increment", allowed_increment));
|
||||
}
|
||||
|
||||
// is it possible to match this bet
|
||||
FC_ASSERT(bet_object::get_exact_matching_amount(op.amount_to_bet.amount, op.backer_multiplier, op.back_or_lay) != 0,
|
||||
"Bet cannot be matched");
|
||||
|
||||
#if 0
|
||||
bet_object simulated_bet;
|
||||
simulated_bet.bettor_id = op.bettor_id;
|
||||
simulated_bet.betting_market_id = op.betting_market_id;
|
||||
simulated_bet.amount_to_bet = op.amount_to_bet;
|
||||
simulated_bet.backer_multiplier = op.backer_multiplier;
|
||||
simulated_bet.back_or_lay = op.back_or_lay;
|
||||
|
||||
share_type required_deposit = get_required_deposit_for_bet(simulated_bet);
|
||||
#endif
|
||||
FC_ASSERT(op.amount_to_bet.amount > share_type(), "Cannot place a bet with zero amount");
|
||||
|
||||
// do they have enough in their account to place the bet
|
||||
FC_ASSERT( d.get_balance( *fee_paying_account, *_asset ).amount >= op.amount_to_bet.amount, "insufficient balance",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <graphene/bookie/bookie_api.hpp>
|
||||
#include <graphene/bookie/bookie_plugin.hpp>
|
||||
#include <graphene/bookie/bookie_objects.hpp>
|
||||
|
||||
namespace graphene { namespace bookie {
|
||||
|
||||
|
|
@ -28,7 +29,8 @@ class bookie_api_impl
|
|||
binned_order_book get_binned_order_book(graphene::chain::betting_market_id_type betting_market_id, int32_t precision);
|
||||
std::shared_ptr<graphene::bookie::bookie_plugin> get_plugin();
|
||||
asset get_total_matched_bet_amount_for_betting_market_group(betting_market_group_id_type group_id);
|
||||
void get_events_containing_sub_string(std::vector<event_object>& events, const std::string& sub_string, const std::string& language);
|
||||
std::vector<event_object> get_events_containing_sub_string(const std::string& sub_string, const std::string& language);
|
||||
fc::variants get_objects(const vector<object_id_type>& ids) const;
|
||||
|
||||
graphene::app::application& app;
|
||||
};
|
||||
|
|
@ -122,6 +124,27 @@ binned_order_book bookie_api_impl::get_binned_order_book(graphene::chain::bettin
|
|||
return result;
|
||||
}
|
||||
|
||||
fc::variants bookie_api_impl::get_objects(const vector<object_id_type>& ids) const
|
||||
{
|
||||
std::shared_ptr<graphene::chain::database> db = app.chain_database();
|
||||
fc::variants result;
|
||||
result.reserve(ids.size());
|
||||
|
||||
std::transform(ids.begin(), ids.end(), std::back_inserter(result),
|
||||
[this, &db](object_id_type id) -> fc::variant {
|
||||
if (id.type() == bet_id_type::type_id)
|
||||
{
|
||||
auto& persistent_bets_by_bet_id = db->get_index_type<detail::persistent_bet_index>().indices().get<by_bet_id>();
|
||||
auto iter = persistent_bets_by_bet_id.find(id.as<bet_id_type>());
|
||||
if (iter != persistent_bets_by_bet_id.end())
|
||||
return iter->ephemeral_bet_object.to_variant();
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<graphene::bookie::bookie_plugin> bookie_api_impl::get_plugin()
|
||||
{
|
||||
return app.get_plugin<graphene::bookie::bookie_plugin>("bookie");
|
||||
|
|
@ -132,9 +155,9 @@ asset bookie_api_impl::get_total_matched_bet_amount_for_betting_market_group(bet
|
|||
return get_plugin()->get_total_matched_bet_amount_for_betting_market_group(group_id);
|
||||
}
|
||||
|
||||
void bookie_api_impl::get_events_containing_sub_string(std::vector<event_object>& events, const std::string& sub_string, const std::string& language)
|
||||
std::vector<event_object> bookie_api_impl::get_events_containing_sub_string(const std::string& sub_string, const std::string& language)
|
||||
{
|
||||
get_plugin()->get_events_containing_sub_string(events, sub_string, language);
|
||||
return get_plugin()->get_events_containing_sub_string(sub_string, language);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
|
@ -156,11 +179,13 @@ asset bookie_api::get_total_matched_bet_amount_for_betting_market_group(betting_
|
|||
|
||||
std::vector<event_object> bookie_api::get_events_containing_sub_string(const std::string& sub_string, const std::string& language)
|
||||
{
|
||||
std::vector<event_object> events;
|
||||
my->get_events_containing_sub_string(events, sub_string, language);
|
||||
return events;
|
||||
return my->get_events_containing_sub_string(sub_string, language);
|
||||
}
|
||||
|
||||
fc::variants bookie_api::get_objects(const vector<object_id_type>& ids) const
|
||||
{
|
||||
return my->get_objects(ids);
|
||||
}
|
||||
|
||||
} } // graphene::bookie
|
||||
|
||||
|
|
|
|||
|
|
@ -23,16 +23,15 @@
|
|||
*/
|
||||
|
||||
#include <graphene/bookie/bookie_plugin.hpp>
|
||||
#include <graphene/bookie/bookie_objects.hpp>
|
||||
|
||||
#include <graphene/app/impacted.hpp>
|
||||
|
||||
#include <graphene/chain/account_evaluator.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/config.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/operation_history_object.hpp>
|
||||
#include <graphene/chain/transaction_evaluation_state.hpp>
|
||||
|
||||
|
|
@ -54,144 +53,6 @@ namespace graphene { namespace bookie {
|
|||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
class persistent_event_object : public graphene::db::abstract_object<persistent_event_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = bookie_objects;
|
||||
static const uint8_t type_id = persistent_event_object_type;
|
||||
|
||||
event_id_type event_object_id;
|
||||
|
||||
internationalized_string_type name;
|
||||
|
||||
internationalized_string_type season;
|
||||
|
||||
optional<time_point_sec> start_time;
|
||||
|
||||
event_group_id_type event_group_id;
|
||||
|
||||
event_status status;
|
||||
vector<string> scores;
|
||||
};
|
||||
typedef object_id<bookie_objects, persistent_event_object_type, persistent_event_object> persistent_event_id_type;
|
||||
|
||||
struct by_event_id;
|
||||
typedef multi_index_container<
|
||||
persistent_event_object,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
|
||||
ordered_unique<tag<by_event_id>, member<persistent_event_object, event_id_type, &persistent_event_object::event_object_id> > > > persistent_event_object_multi_index_type;
|
||||
|
||||
typedef generic_index<persistent_event_object, persistent_event_object_multi_index_type> persistent_event_object_index;
|
||||
|
||||
#if 0 // we no longer have competitors, just leaving this here as an example of how to do a secondary index
|
||||
class events_by_competitor_index : public secondary_index
|
||||
{
|
||||
public:
|
||||
virtual ~events_by_competitor_index() {}
|
||||
|
||||
virtual void object_inserted( const object& obj ) override;
|
||||
virtual void object_removed( const object& obj ) override;
|
||||
virtual void about_to_modify( const object& before ) override;
|
||||
virtual void object_modified( const object& after ) override;
|
||||
protected:
|
||||
|
||||
map<competitor_id_type, set<persistent_event_id_type> > competitor_to_events;
|
||||
};
|
||||
|
||||
void events_by_competitor_index::object_inserted( const object& obj )
|
||||
{
|
||||
const persistent_event_object& event_obj = *boost::polymorphic_downcast<const persistent_event_object*>(&obj);
|
||||
for (const competitor_id_type& competitor_id : event_obj.competitors)
|
||||
competitor_to_events[competitor_id].insert(event_obj.id);
|
||||
for (const competitor_id_type& competitor_id : event_obj.competitors)
|
||||
competitor_to_events[competitor_id].insert(event_obj.id);
|
||||
}
|
||||
void events_by_competitor_index::object_removed( const object& obj )
|
||||
{
|
||||
const persistent_event_object& event_obj = *boost::polymorphic_downcast<const persistent_event_object*>(&obj);
|
||||
for (const competitor_id_type& competitor_id : event_obj.competitors)
|
||||
competitor_to_events[competitor_id].erase(event_obj.id);
|
||||
}
|
||||
void events_by_competitor_index::about_to_modify( const object& before )
|
||||
{
|
||||
object_removed(before);
|
||||
}
|
||||
void events_by_competitor_index::object_modified( const object& after )
|
||||
{
|
||||
object_inserted(after);
|
||||
}
|
||||
#endif
|
||||
|
||||
//////////// betting_market_group_object //////////////////
|
||||
class persistent_betting_market_group_object : public graphene::db::abstract_object<persistent_betting_market_group_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = bookie_objects;
|
||||
static const uint8_t type_id = persistent_betting_market_group_object_type;
|
||||
|
||||
betting_market_group_object ephemeral_betting_market_group_object;
|
||||
|
||||
share_type total_matched_bets_amount;
|
||||
|
||||
betting_market_group_id_type get_betting_market_group_id() const { return ephemeral_betting_market_group_object.id; }
|
||||
};
|
||||
|
||||
struct by_betting_market_group_id;
|
||||
typedef multi_index_container<
|
||||
persistent_betting_market_group_object,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
|
||||
ordered_unique<tag<by_betting_market_group_id>, const_mem_fun<persistent_betting_market_group_object, betting_market_group_id_type, &persistent_betting_market_group_object::get_betting_market_group_id> > > > persistent_betting_market_group_multi_index_type;
|
||||
|
||||
typedef generic_index<persistent_betting_market_group_object, persistent_betting_market_group_multi_index_type> persistent_betting_market_group_index;
|
||||
|
||||
//////////// betting_market_object //////////////////
|
||||
class persistent_betting_market_object : public graphene::db::abstract_object<persistent_betting_market_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = bookie_objects;
|
||||
static const uint8_t type_id = persistent_betting_market_object_type;
|
||||
|
||||
betting_market_object ephemeral_betting_market_object;
|
||||
|
||||
share_type total_matched_bets_amount;
|
||||
|
||||
betting_market_id_type get_betting_market_id() const { return ephemeral_betting_market_object.id; }
|
||||
};
|
||||
|
||||
struct by_betting_market_id;
|
||||
typedef multi_index_container<
|
||||
persistent_betting_market_object,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
|
||||
ordered_unique<tag<by_betting_market_id>, const_mem_fun<persistent_betting_market_object, betting_market_id_type, &persistent_betting_market_object::get_betting_market_id> > > > persistent_betting_market_multi_index_type;
|
||||
|
||||
typedef generic_index<persistent_betting_market_object, persistent_betting_market_multi_index_type> persistent_betting_market_index;
|
||||
|
||||
//////////// bet_object //////////////////
|
||||
class persistent_bet_object : public graphene::db::abstract_object<persistent_bet_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = bookie_objects;
|
||||
static const uint8_t type_id = persistent_bet_object_type;
|
||||
|
||||
bet_object ephemeral_bet_object;
|
||||
|
||||
bet_id_type get_bet_id() const { return ephemeral_bet_object.id; }
|
||||
};
|
||||
|
||||
struct by_bet_id;
|
||||
typedef multi_index_container<
|
||||
persistent_bet_object,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
|
||||
ordered_unique<tag<by_bet_id>, const_mem_fun<persistent_bet_object, bet_id_type, &persistent_bet_object::get_bet_id> > > > persistent_bet_multi_index_type;
|
||||
|
||||
|
||||
typedef generic_index<persistent_bet_object, persistent_bet_multi_index_type> persistent_bet_index;
|
||||
|
||||
/* As a plugin, we get notified of new/changed objects at the end of every block processed.
|
||||
* For most objects, that's fine, because we expect them to always be around until the end of
|
||||
* the block. However, with bet objects, it's possible that the user places a bet and it fills
|
||||
|
|
@ -259,7 +120,7 @@ class bookie_plugin_impl
|
|||
|
||||
void fill_localized_event_strings();
|
||||
|
||||
void get_events_containing_sub_string(std::vector<event_object>& events, const std::string& sub_string, const std::string& language);
|
||||
std::vector<event_object> get_events_containing_sub_string(const std::string& sub_string, const std::string& language);
|
||||
|
||||
graphene::chain::database& database()
|
||||
{
|
||||
|
|
@ -497,28 +358,30 @@ void bookie_plugin_impl::fill_localized_event_strings()
|
|||
}
|
||||
}
|
||||
|
||||
void bookie_plugin_impl::get_events_containing_sub_string(std::vector<event_object>& events, const std::string& sub_string, const std::string& language)
|
||||
std::vector<event_object> bookie_plugin_impl::get_events_containing_sub_string(const std::string& sub_string, const std::string& language)
|
||||
{
|
||||
graphene::chain::database& db = database();
|
||||
if (localized_event_strings.find(language) != localized_event_strings.end())
|
||||
{
|
||||
std::string lower_case_sub_string = boost::algorithm::to_lower_copy(sub_string);
|
||||
const event_string_set& language_set = localized_event_strings[language];
|
||||
for (const event_string& pair : language_set)
|
||||
{
|
||||
std::string lower_case_string = boost::algorithm::to_lower_copy(pair.second);
|
||||
if (lower_case_string.find(lower_case_sub_string) != std::string::npos)
|
||||
events.push_back(pair.first(db));
|
||||
}
|
||||
}
|
||||
graphene::chain::database& db = database();
|
||||
std::vector<event_object> events;
|
||||
if (localized_event_strings.find(language) != localized_event_strings.end())
|
||||
{
|
||||
std::string lower_case_sub_string = boost::algorithm::to_lower_copy(sub_string);
|
||||
const event_string_set& language_set = localized_event_strings[language];
|
||||
for (const event_string& pair : language_set)
|
||||
{
|
||||
std::string lower_case_string = boost::algorithm::to_lower_copy(pair.second);
|
||||
if (lower_case_string.find(lower_case_sub_string) != std::string::npos)
|
||||
events.push_back(pair.first(db));
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
asset bookie_plugin_impl::get_total_matched_bet_amount_for_betting_market_group(betting_market_group_id_type group_id)
|
||||
{
|
||||
graphene::chain::database& db = database();
|
||||
FC_ASSERT( db.find_object(group_id), "Invalid betting market group specified" );
|
||||
const betting_market_group_object& betting_market_group = group_id(db);
|
||||
return asset(betting_market_group.total_matched_bets_amount, betting_market_group.asset_id);
|
||||
graphene::chain::database& db = database();
|
||||
FC_ASSERT( db.find_object(group_id), "Invalid betting market group specified" );
|
||||
const betting_market_group_object& betting_market_group = group_id(db);
|
||||
return asset(betting_market_group.total_matched_bets_amount, betting_market_group.asset_id);
|
||||
}
|
||||
} // end namespace detail
|
||||
|
||||
|
|
@ -579,15 +442,11 @@ asset bookie_plugin::get_total_matched_bet_amount_for_betting_market_group(betti
|
|||
ilog("bookie plugin: get_total_matched_bet_amount_for_betting_market_group($group_id)", ("group_d", group_id));
|
||||
return my->get_total_matched_bet_amount_for_betting_market_group(group_id);
|
||||
}
|
||||
void bookie_plugin::get_events_containing_sub_string(std::vector<event_object>& events, const std::string& sub_string, const std::string& language)
|
||||
std::vector<event_object> bookie_plugin::get_events_containing_sub_string(const std::string& sub_string, const std::string& language)
|
||||
{
|
||||
ilog("bookie plugin: get_events_containing_sub_string($s, $l)", ("s", sub_string)("l", language));
|
||||
my->get_events_containing_sub_string(events, sub_string, language);
|
||||
ilog("bookie plugin: get_events_containing_sub_string(${sub_string}, ${language})", (sub_string)(language));
|
||||
return my->get_events_containing_sub_string(sub_string, language);
|
||||
}
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_event_object, (graphene::db::object), (event_object_id)(name)(season)(start_time)(event_group_id)(status)(scores) )
|
||||
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_group_object, (graphene::db::object), (ephemeral_betting_market_group_object)(total_matched_bets_amount) )
|
||||
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_object, (graphene::db::object), (ephemeral_betting_market_object) )
|
||||
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_bet_object, (graphene::db::object), (ephemeral_bet_object) )
|
||||
|
|
|
|||
|
|
@ -39,12 +39,13 @@ class bookie_api
|
|||
|
||||
/**
|
||||
* Returns the current order book, binned according to the given precision.
|
||||
* precision = 1 means bin using one decimal place: (1 - 1.1], (1.1 - 1.2], etc.
|
||||
* precision = 1 means bin using one decimal place. for backs, (1 - 1.1], (1.1 - 1.2], etc.
|
||||
* precision = 2 would bin on (1 - 1.01], (1.01 - 1.02]
|
||||
*/
|
||||
binned_order_book get_binned_order_book(graphene::chain::betting_market_id_type betting_market_id, int32_t precision);
|
||||
asset get_total_matched_bet_amount_for_betting_market_group(betting_market_group_id_type group_id);
|
||||
std::vector<event_object> get_events_containing_sub_string(const std::string& sub_string, const std::string& language);
|
||||
fc::variants get_objects(const vector<object_id_type>& ids)const;
|
||||
|
||||
std::shared_ptr<detail::bookie_api_impl> my;
|
||||
};
|
||||
|
|
@ -58,5 +59,6 @@ FC_API(graphene::bookie::bookie_api,
|
|||
(get_binned_order_book)
|
||||
(get_total_matched_bet_amount_for_betting_market_group)
|
||||
(get_events_containing_sub_string)
|
||||
(get_objects)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
|
||||
namespace graphene { namespace bookie {
|
||||
using namespace chain;
|
||||
|
||||
enum bookie_object_type
|
||||
{
|
||||
persistent_event_object_type,
|
||||
persistent_betting_market_group_object_type,
|
||||
persistent_betting_market_object_type,
|
||||
persistent_bet_object_type,
|
||||
BOOKIE_OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
class persistent_event_object : public graphene::db::abstract_object<persistent_event_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = bookie_objects;
|
||||
static const uint8_t type_id = persistent_event_object_type;
|
||||
|
||||
event_id_type event_object_id;
|
||||
|
||||
internationalized_string_type name;
|
||||
|
||||
internationalized_string_type season;
|
||||
|
||||
optional<time_point_sec> start_time;
|
||||
|
||||
event_group_id_type event_group_id;
|
||||
|
||||
event_status status;
|
||||
vector<string> scores;
|
||||
};
|
||||
typedef object_id<bookie_objects, persistent_event_object_type, persistent_event_object> persistent_event_id_type;
|
||||
|
||||
struct by_event_id;
|
||||
typedef multi_index_container<
|
||||
persistent_event_object,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
|
||||
ordered_unique<tag<by_event_id>, member<persistent_event_object, event_id_type, &persistent_event_object::event_object_id> > > > persistent_event_object_multi_index_type;
|
||||
|
||||
typedef generic_index<persistent_event_object, persistent_event_object_multi_index_type> persistent_event_object_index;
|
||||
|
||||
#if 0 // we no longer have competitors, just leaving this here as an example of how to do a secondary index
|
||||
class events_by_competitor_index : public secondary_index
|
||||
{
|
||||
public:
|
||||
virtual ~events_by_competitor_index() {}
|
||||
|
||||
virtual void object_inserted( const object& obj ) override;
|
||||
virtual void object_removed( const object& obj ) override;
|
||||
virtual void about_to_modify( const object& before ) override;
|
||||
virtual void object_modified( const object& after ) override;
|
||||
protected:
|
||||
|
||||
map<competitor_id_type, set<persistent_event_id_type> > competitor_to_events;
|
||||
};
|
||||
|
||||
void events_by_competitor_index::object_inserted( const object& obj )
|
||||
{
|
||||
const persistent_event_object& event_obj = *boost::polymorphic_downcast<const persistent_event_object*>(&obj);
|
||||
for (const competitor_id_type& competitor_id : event_obj.competitors)
|
||||
competitor_to_events[competitor_id].insert(event_obj.id);
|
||||
for (const competitor_id_type& competitor_id : event_obj.competitors)
|
||||
competitor_to_events[competitor_id].insert(event_obj.id);
|
||||
}
|
||||
void events_by_competitor_index::object_removed( const object& obj )
|
||||
{
|
||||
const persistent_event_object& event_obj = *boost::polymorphic_downcast<const persistent_event_object*>(&obj);
|
||||
for (const competitor_id_type& competitor_id : event_obj.competitors)
|
||||
competitor_to_events[competitor_id].erase(event_obj.id);
|
||||
}
|
||||
void events_by_competitor_index::about_to_modify( const object& before )
|
||||
{
|
||||
object_removed(before);
|
||||
}
|
||||
void events_by_competitor_index::object_modified( const object& after )
|
||||
{
|
||||
object_inserted(after);
|
||||
}
|
||||
#endif
|
||||
|
||||
//////////// betting_market_group_object //////////////////
|
||||
class persistent_betting_market_group_object : public graphene::db::abstract_object<persistent_betting_market_group_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = bookie_objects;
|
||||
static const uint8_t type_id = persistent_betting_market_group_object_type;
|
||||
|
||||
betting_market_group_object ephemeral_betting_market_group_object;
|
||||
|
||||
share_type total_matched_bets_amount;
|
||||
|
||||
betting_market_group_id_type get_betting_market_group_id() const { return ephemeral_betting_market_group_object.id; }
|
||||
};
|
||||
|
||||
struct by_betting_market_group_id;
|
||||
typedef multi_index_container<
|
||||
persistent_betting_market_group_object,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
|
||||
ordered_unique<tag<by_betting_market_group_id>, const_mem_fun<persistent_betting_market_group_object, betting_market_group_id_type, &persistent_betting_market_group_object::get_betting_market_group_id> > > > persistent_betting_market_group_multi_index_type;
|
||||
|
||||
typedef generic_index<persistent_betting_market_group_object, persistent_betting_market_group_multi_index_type> persistent_betting_market_group_index;
|
||||
|
||||
//////////// betting_market_object //////////////////
|
||||
class persistent_betting_market_object : public graphene::db::abstract_object<persistent_betting_market_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = bookie_objects;
|
||||
static const uint8_t type_id = persistent_betting_market_object_type;
|
||||
|
||||
betting_market_object ephemeral_betting_market_object;
|
||||
|
||||
share_type total_matched_bets_amount;
|
||||
|
||||
betting_market_id_type get_betting_market_id() const { return ephemeral_betting_market_object.id; }
|
||||
};
|
||||
|
||||
struct by_betting_market_id;
|
||||
typedef multi_index_container<
|
||||
persistent_betting_market_object,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
|
||||
ordered_unique<tag<by_betting_market_id>, const_mem_fun<persistent_betting_market_object, betting_market_id_type, &persistent_betting_market_object::get_betting_market_id> > > > persistent_betting_market_multi_index_type;
|
||||
|
||||
typedef generic_index<persistent_betting_market_object, persistent_betting_market_multi_index_type> persistent_betting_market_index;
|
||||
|
||||
//////////// bet_object //////////////////
|
||||
class persistent_bet_object : public graphene::db::abstract_object<persistent_bet_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = bookie_objects;
|
||||
static const uint8_t type_id = persistent_bet_object_type;
|
||||
|
||||
bet_object ephemeral_bet_object;
|
||||
|
||||
bet_id_type get_bet_id() const { return ephemeral_bet_object.id; }
|
||||
};
|
||||
|
||||
struct by_bet_id;
|
||||
typedef multi_index_container<
|
||||
persistent_bet_object,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
|
||||
ordered_unique<tag<by_bet_id>, const_mem_fun<persistent_bet_object, bet_id_type, &persistent_bet_object::get_bet_id> > > > persistent_bet_multi_index_type;
|
||||
|
||||
|
||||
typedef generic_index<persistent_bet_object, persistent_bet_multi_index_type> persistent_bet_index;
|
||||
|
||||
} } } //graphene::bookie::detail
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_event_object, (graphene::db::object), (event_object_id)(name)(season)(start_time)(event_group_id)(status)(scores) )
|
||||
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_group_object, (graphene::db::object), (ephemeral_betting_market_group_object)(total_matched_bets_amount) )
|
||||
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_object, (graphene::db::object), (ephemeral_betting_market_object) )
|
||||
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_bet_object, (graphene::db::object), (ephemeral_bet_object) )
|
||||
|
||||
|
|
@ -44,15 +44,6 @@ using namespace chain;
|
|||
enum spaces {
|
||||
bookie_objects = 6
|
||||
};
|
||||
enum bookie_object_type
|
||||
{
|
||||
persistent_event_object_type,
|
||||
persistent_betting_market_group_object_type,
|
||||
persistent_betting_market_object_type,
|
||||
persistent_bet_object_type,
|
||||
BOOKIE_OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
|
@ -73,7 +64,7 @@ class bookie_plugin : public graphene::app::plugin
|
|||
|
||||
flat_set<account_id_type> tracked_accounts()const;
|
||||
asset get_total_matched_bet_amount_for_betting_market_group(betting_market_group_id_type group_id);
|
||||
void get_events_containing_sub_string(std::vector<event_object>& events, const std::string& sub_string, const std::string& language);
|
||||
std::vector<event_object> get_events_containing_sub_string(const std::string& sub_string, const std::string& language);
|
||||
|
||||
friend class detail::bookie_plugin_impl;
|
||||
std::unique_ptr<detail::bookie_plugin_impl> my;
|
||||
|
|
|
|||
|
|
@ -309,22 +309,27 @@ BOOST_AUTO_TEST_CASE(inexact_odds)
|
|||
|
||||
transfer(account_id_type(), alice_id, asset(10000000));
|
||||
share_type alice_expected_balance = 10000000;
|
||||
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
|
||||
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
|
||||
|
||||
// lay 46 at 1.94 odds (50:47) -- this is too small to be placed on the books and there's
|
||||
// nothing for it to match, so it should be canceled
|
||||
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(46, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
|
||||
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
|
||||
|
||||
// lay 47 at 1.94 odds (50:47) -- this is an exact amount, nothing surprising should happen here
|
||||
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(47, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
|
||||
alice_expected_balance -= 47;
|
||||
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
|
||||
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
|
||||
|
||||
// lay 100 at 1.91 odds (100:91) -- this is an inexact match, we should get refunded 9 and leave a bet for 91 on the books
|
||||
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 191 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
|
||||
alice_expected_balance -= 91;
|
||||
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
|
||||
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
|
||||
|
||||
|
||||
transfer(account_id_type(), bob_id, asset(10000000));
|
||||
share_type bob_expected_balance = 10000000;
|
||||
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
|
||||
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
|
||||
|
||||
// now have bob match it with a back of 300 at 1.91
|
||||
// This should:
|
||||
|
|
@ -335,7 +340,50 @@ BOOST_AUTO_TEST_CASE(inexact_odds)
|
|||
// leaves a back bet of 100 @ 1.91 on the books
|
||||
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(300, asset_id_type()), 191 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
|
||||
bob_expected_balance -= 250;
|
||||
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
|
||||
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
|
||||
}
|
||||
FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(persistent_objects_test)
|
||||
{
|
||||
try
|
||||
{
|
||||
ACTORS( (alice)(bob) );
|
||||
CREATE_ICE_HOCKEY_BETTING_MARKET();
|
||||
graphene::bookie::bookie_api bookie_api(app);
|
||||
|
||||
transfer(account_id_type(), alice_id, asset(10000000));
|
||||
transfer(account_id_type(), bob_id, asset(10000000));
|
||||
share_type alice_expected_balance = 10000000;
|
||||
share_type bob_expected_balance = 10000000;
|
||||
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
|
||||
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
|
||||
|
||||
// lay 46 at 1.94 odds (50:47) -- this is too small to be placed on the books and there's
|
||||
// nothing for it to match, so it should be canceled
|
||||
bet_id_type automatically_canceled_bet_id = place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(46, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
|
||||
BOOST_CHECK_MESSAGE(!db.find(automatically_canceled_bet_id), "Bet should have been canceled, but the blockchain still knows about it");
|
||||
fc::variants objects_from_bookie = bookie_api.get_objects({automatically_canceled_bet_id});
|
||||
BOOST_REQUIRE_EQUAL(objects_from_bookie.size(), 1);
|
||||
BOOST_CHECK_MESSAGE(objects_from_bookie[0]["id"].as<bet_id_type>() == automatically_canceled_bet_id, "Bookie Plugin didn't return a deleted bet it");
|
||||
|
||||
// lay 47 at 1.94 odds (50:47) -- this bet should go on the order books normally
|
||||
bet_id_type first_bet_on_books = place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(47, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
|
||||
BOOST_CHECK_MESSAGE(db.find(first_bet_on_books), "Bet should exist on the blockchain");
|
||||
objects_from_bookie = bookie_api.get_objects({first_bet_on_books});
|
||||
BOOST_REQUIRE_EQUAL(objects_from_bookie.size(), 1);
|
||||
BOOST_CHECK_MESSAGE(objects_from_bookie[0]["id"].as<bet_id_type>() == first_bet_on_books, "Bookie Plugin didn't return a bet that is currently on the books");
|
||||
|
||||
// place a bet that exactly matches 'first_bet_on_books', should result in empty books (thus, no bet_objects from the blockchain)
|
||||
bet_id_type matching_bet = place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(50, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
|
||||
BOOST_CHECK_MESSAGE(!db.find(first_bet_on_books), "Bet should have been filled, but the blockchain still knows about it");
|
||||
BOOST_CHECK_MESSAGE(!db.find(matching_bet), "Bet should have been filled, but the blockchain still knows about it");
|
||||
|
||||
objects_from_bookie = bookie_api.get_objects({first_bet_on_books, matching_bet});
|
||||
BOOST_REQUIRE_EQUAL(objects_from_bookie.size(), 2);
|
||||
BOOST_CHECK_MESSAGE(objects_from_bookie[0]["id"].as<bet_id_type>() == first_bet_on_books, "Bookie Plugin didn't return a bet that has been filled");
|
||||
BOOST_CHECK_MESSAGE(objects_from_bookie[1]["id"].as<bet_id_type>() == matching_bet, "Bookie Plugin didn't return a bet that has been filled");
|
||||
}
|
||||
FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue