Change the behavior of the betting markets from being controlled by
a collection of flags (is_live, is_closed) to a single status field. The status changes in an event can trickle down to the market groups, and the status changes in market groups can bubble up to events.
This commit is contained in:
parent
9fcebf8bc0
commit
3b3a0905ff
21 changed files with 3530 additions and 825 deletions
10
genesis.json
10
genesis.json
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"initial_timestamp": "2017-07-18T18:33:30",
|
||||
"initial_timestamp": "2018-02-01T00:00:00",
|
||||
"max_core_supply": "1000000000000000",
|
||||
"initial_parameters": {
|
||||
"current_fees": {
|
||||
|
|
@ -243,7 +243,7 @@
|
|||
}
|
||||
],[
|
||||
57,{
|
||||
"fee": 100000
|
||||
"fee": 1
|
||||
}
|
||||
],[
|
||||
58,{
|
||||
|
|
@ -261,7 +261,7 @@
|
|||
62,{}
|
||||
],[
|
||||
63,{
|
||||
"fee": 100000
|
||||
"fee": 0
|
||||
}
|
||||
],[
|
||||
64,{}
|
||||
|
|
@ -295,7 +295,7 @@
|
|||
],
|
||||
"scale": 10000
|
||||
},
|
||||
"block_interval": 5,
|
||||
"block_interval": 3,
|
||||
"maintenance_interval": 600,
|
||||
"maintenance_skip_slots": 3,
|
||||
"committee_proposal_review_period": 900,
|
||||
|
|
@ -536,4 +536,4 @@
|
|||
"num_special_accounts": 0,
|
||||
"num_special_assets": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,10 +102,11 @@ add_library( graphene_chain
|
|||
event_group_evaluator.cpp
|
||||
protocol/event.cpp
|
||||
event_evaluator.cpp
|
||||
event_object.cpp
|
||||
protocol/betting_market.cpp
|
||||
betting_market_evaluator.cpp
|
||||
betting_market_object.cpp
|
||||
db_bet.cpp
|
||||
betting_market_group_object.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#define DEFAULT_LOGGER "betting"
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
#include <graphene/chain/betting_market_evaluator.hpp>
|
||||
|
|
@ -109,8 +110,8 @@ object_id_type betting_market_group_create_evaluator::do_apply(const betting_mar
|
|||
betting_market_group_obj.rules_id = _rules_id;
|
||||
betting_market_group_obj.description = op.description;
|
||||
betting_market_group_obj.asset_id = op.asset_id;
|
||||
betting_market_group_obj.frozen = false;
|
||||
betting_market_group_obj.delay_bets = false;
|
||||
betting_market_group_obj.never_in_play = op.never_in_play;
|
||||
betting_market_group_obj.delay_before_settling = op.delay_before_settling;
|
||||
});
|
||||
return new_betting_market_group.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
@ -121,12 +122,9 @@ void_result betting_market_group_update_evaluator::do_evaluate(const betting_mar
|
|||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
_betting_market_group = &op.betting_market_group_id(d);
|
||||
|
||||
FC_ASSERT(op.new_description.valid() ||
|
||||
op.new_rules_id.valid() ||
|
||||
op.freeze.valid() ||
|
||||
op.delay_bets.valid(), "nothing to change");
|
||||
FC_ASSERT(op.new_description || op.new_rules_id || op.status, "nothing to change");
|
||||
|
||||
if (op.new_rules_id.valid())
|
||||
if (op.new_rules_id)
|
||||
{
|
||||
// the rules_id in the operation can be a relative id. If it is,
|
||||
// resolve it and verify that it is truly rules
|
||||
|
|
@ -141,11 +139,9 @@ void_result betting_market_group_update_evaluator::do_evaluate(const betting_mar
|
|||
FC_ASSERT(d.find_object(_rules_id), "invalid rules specified");
|
||||
}
|
||||
|
||||
if (op.freeze.valid())
|
||||
FC_ASSERT(_betting_market_group->frozen != *op.freeze, "freeze would not change the state of the betting market group");
|
||||
if (op.status)
|
||||
FC_ASSERT(_betting_market_group->get_status() != *op.status, "status would not change the state of the betting market group");
|
||||
|
||||
if (op.delay_bets.valid())
|
||||
FC_ASSERT(_betting_market_group->delay_bets != *op.delay_bets, "delay_bets would not change the state of the betting market group");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
|
@ -153,39 +149,40 @@ void_result betting_market_group_update_evaluator::do_apply(const betting_market
|
|||
{ try {
|
||||
database& d = db();
|
||||
d.modify(*_betting_market_group, [&](betting_market_group_object& betting_market_group) {
|
||||
if (op.new_description.valid())
|
||||
if (op.new_description)
|
||||
betting_market_group.description = *op.new_description;
|
||||
if (op.new_rules_id.valid())
|
||||
if (op.new_rules_id)
|
||||
betting_market_group.rules_id = _rules_id;
|
||||
if (op.freeze.valid())
|
||||
betting_market_group.frozen = *op.freeze;
|
||||
if (op.delay_bets.valid())
|
||||
|
||||
bool bets_were_delayed = betting_market_group.bets_are_delayed();
|
||||
if (op.status)
|
||||
betting_market_group.dispatch_new_status(d, *op.status);
|
||||
|
||||
bool bets_are_delayed = betting_market_group.bets_are_delayed();
|
||||
|
||||
// if we have transitioned from in-play to not-in-play-but-still-accepting-bets,
|
||||
// place all delayed bets now
|
||||
if (betting_market_group.bets_are_allowed() &&
|
||||
bets_were_delayed && !bets_are_delayed)
|
||||
{
|
||||
assert(_betting_market_group->delay_bets != *op.delay_bets); // we checked this in evaluate
|
||||
betting_market_group.delay_bets = *op.delay_bets;
|
||||
if (!*op.delay_bets)
|
||||
const auto& bet_odds_idx = d.get_index_type<bet_object_index>().indices().get<by_odds>();
|
||||
auto bet_iter = bet_odds_idx.begin();
|
||||
bool last = bet_iter == bet_odds_idx.end() || !bet_iter->end_of_delay;
|
||||
while (!last)
|
||||
{
|
||||
// we have switched from delayed to not-delayed. if there are any delayed bets,
|
||||
// push them through now.
|
||||
const auto& bet_odds_idx = d.get_index_type<bet_object_index>().indices().get<by_odds>();
|
||||
auto bet_iter = bet_odds_idx.begin();
|
||||
bool last = bet_iter == bet_odds_idx.end() || !bet_iter->end_of_delay;
|
||||
while (!last)
|
||||
const bet_object& delayed_bet = *bet_iter;
|
||||
++bet_iter;
|
||||
last = bet_iter == bet_odds_idx.end() || !bet_iter->end_of_delay;
|
||||
|
||||
const betting_market_object& betting_market = delayed_bet.betting_market_id(d);
|
||||
if (betting_market.group_id == op.betting_market_group_id)
|
||||
{
|
||||
const bet_object& delayed_bet = *bet_iter;
|
||||
++bet_iter;
|
||||
last = bet_iter == bet_odds_idx.end() || !bet_iter->end_of_delay;
|
||||
d.modify(delayed_bet, [](bet_object& bet_obj) {
|
||||
// clear the end_of_delay, which will re-sort the bet into its place in the book
|
||||
bet_obj.end_of_delay.reset();
|
||||
});
|
||||
|
||||
const betting_market_object& betting_market = delayed_bet.betting_market_id(d);
|
||||
if (betting_market.group_id == op.betting_market_group_id && !_betting_market_group->frozen)
|
||||
{
|
||||
d.modify(delayed_bet, [](bet_object& bet_obj) {
|
||||
// clear the end_of_delay, which will re-sort the bet into its place in the book
|
||||
bet_obj.end_of_delay.reset();
|
||||
});
|
||||
|
||||
d.place_bet(delayed_bet);
|
||||
}
|
||||
d.place_bet(delayed_bet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -271,7 +268,9 @@ void_result bet_place_evaluator::do_evaluate(const bet_place_operation& op)
|
|||
FC_ASSERT( op.amount_to_bet.asset_id == _betting_market_group->asset_id,
|
||||
"Asset type bet does not match the market's asset type" );
|
||||
|
||||
FC_ASSERT( !_betting_market_group->frozen, "Unable to place bets while the market is frozen" );
|
||||
ddump((_betting_market_group->get_status()));
|
||||
FC_ASSERT( _betting_market_group->get_status() != betting_market_group_status::frozen,
|
||||
"Unable to place bets while the market is frozen" );
|
||||
|
||||
_asset = &_betting_market_group->asset_id(d);
|
||||
FC_ASSERT( is_authorized_asset( d, *fee_paying_account, *_asset ) );
|
||||
|
|
@ -312,15 +311,19 @@ object_id_type bet_place_evaluator::do_apply(const bet_place_operation& op)
|
|||
bet_obj.amount_to_bet = op.amount_to_bet;
|
||||
bet_obj.backer_multiplier = op.backer_multiplier;
|
||||
bet_obj.back_or_lay = op.back_or_lay;
|
||||
if (_betting_market_group->delay_bets)
|
||||
bet_obj.end_of_delay = d.head_block_time() + _current_params->live_betting_delay_time;
|
||||
if (_betting_market_group->bets_are_delayed()) {
|
||||
// the bet will be included in the block at time `head_block_time() + block_interval`, so make the delay relative
|
||||
// to the time it's included in a block
|
||||
bet_obj.end_of_delay = d.head_block_time() + _current_params->block_interval + _current_params->live_betting_delay_time;
|
||||
}
|
||||
});
|
||||
|
||||
bet_id_type new_bet_id = new_bet.id; // save the bet id here, new_bet may be deleted during place_bet()
|
||||
|
||||
d.adjust_balance(fee_paying_account->id, -op.amount_to_bet);
|
||||
|
||||
if (!_betting_market_group->delay_bets || _current_params->live_betting_delay_time <= 0)
|
||||
ddump((_betting_market_group->bets_are_delayed())(_current_params->live_betting_delay_time));
|
||||
if (!_betting_market_group->bets_are_delayed() || _current_params->live_betting_delay_time <= 0)
|
||||
d.place_bet(new_bet);
|
||||
|
||||
return new_bet_id;
|
||||
|
|
|
|||
541
libraries/chain/betting_market_group_object.cpp
Normal file
541
libraries/chain/betting_market_group_object.cpp
Normal file
|
|
@ -0,0 +1,541 @@
|
|||
#define DEFAULT_LOGGER "betting"
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <boost/math/common_factor_rt.hpp>
|
||||
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/archive/binary_oarchive.hpp>
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/msm/back/tools.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
enum class betting_market_group_state {
|
||||
upcoming,
|
||||
frozen_upcoming,
|
||||
in_play,
|
||||
frozen_in_play,
|
||||
closed,
|
||||
graded,
|
||||
canceled,
|
||||
settled
|
||||
};
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::betting_market_group_state,
|
||||
(upcoming)
|
||||
(frozen_upcoming)
|
||||
(in_play)
|
||||
(frozen_in_play)
|
||||
(closed)
|
||||
(graded)
|
||||
(canceled)
|
||||
(settled))
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
// betting market object implementation
|
||||
namespace
|
||||
{
|
||||
// Events -- most events happen when the witnesses publish an update operation with a new
|
||||
// status, so if they publish an event with the status set to `frozen`, we'll generate a `frozen_event`
|
||||
struct upcoming_event
|
||||
{
|
||||
database& db;
|
||||
upcoming_event(database& db) : db(db) {}
|
||||
};
|
||||
struct frozen_event
|
||||
{
|
||||
database& db;
|
||||
frozen_event(database& db) : db(db) {}
|
||||
};
|
||||
struct in_play_event
|
||||
{
|
||||
database& db;
|
||||
in_play_event(database& db) : db(db) {}
|
||||
};
|
||||
struct closed_event
|
||||
{
|
||||
database& db;
|
||||
bool closed_by_event;
|
||||
closed_event(database& db, bool closed_by_event) : db(db), closed_by_event(closed_by_event) {}
|
||||
};
|
||||
struct graded_event
|
||||
{
|
||||
database& db;
|
||||
graded_event(database& db) : db(db) {}
|
||||
};
|
||||
struct re_grading_event
|
||||
{
|
||||
database& db;
|
||||
re_grading_event(database& db) : db(db) {}
|
||||
};
|
||||
struct settled_event
|
||||
{
|
||||
database& db;
|
||||
settled_event(database& db) : db(db) {}
|
||||
};
|
||||
struct canceled_event
|
||||
{
|
||||
database& db;
|
||||
|
||||
// true if this was triggered by setting event to canceled state,
|
||||
// false if this was triggered directly on this betting market group
|
||||
bool canceled_by_event;
|
||||
|
||||
canceled_event(database& db, bool canceled_by_event = false) : db(db), canceled_by_event(canceled_by_event) {}
|
||||
};
|
||||
|
||||
// Events
|
||||
struct betting_market_group_state_machine_ : public msm::front::state_machine_def<betting_market_group_state_machine_>
|
||||
{
|
||||
// disable a few state machine features we don't use for performance
|
||||
typedef int no_exception_thrown;
|
||||
typedef int no_message_queue;
|
||||
|
||||
// States
|
||||
struct upcoming : public msm::front::state<>
|
||||
{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> upcoming", ("id", fsm.betting_market_group_obj->id));
|
||||
// when a betting market group goes from frozen -> upcoming, transition the markets from frozen -> unresolved
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_unresolved_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
}
|
||||
}
|
||||
};
|
||||
struct frozen_upcoming : public msm::front::state<>
|
||||
{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> frozen_upcoming", ("id", fsm.betting_market_group_obj->id));
|
||||
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_frozen_event(event.db);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
struct in_play : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> in_play", ("id", fsm.betting_market_group_obj->id));
|
||||
// when an event goes in-play, cancel all unmatched bets in its betting markets
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.cancel_all_unmatched_bets(event.db);
|
||||
try {
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_unresolved_event(event.db);
|
||||
});
|
||||
} catch (const graphene::chain::no_transition&) {
|
||||
// if this wasn't a transition from frozen state, this wasn't necessary
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
struct frozen_in_play : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> frozen_in_play", ("id", fsm.betting_market_group_obj->id));
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_frozen_event(event.db);
|
||||
});
|
||||
}
|
||||
};
|
||||
struct closed : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& fsm_event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> closed", ("id", fsm.betting_market_group_obj->id));
|
||||
auto& betting_market_index = fsm_event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
fsm_event.db.modify(betting_market, [&fsm_event](betting_market_object& betting_market) {
|
||||
betting_market.cancel_all_unmatched_bets(fsm_event.db);
|
||||
betting_market.on_closed_event(fsm_event.db);
|
||||
});
|
||||
|
||||
// then notify the event that this betting market is now closed so it can change its status accordingly
|
||||
if (!fsm_event.closed_by_event) {
|
||||
const event_object& event = fsm.betting_market_group_obj->event_id(fsm_event.db);
|
||||
fsm_event.db.modify(event, [&fsm_event,&fsm](event_object& event_obj) {
|
||||
event_obj.on_betting_market_group_closed(fsm_event.db, fsm.betting_market_group_obj->id);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
struct graded : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> graded", ("id", fsm.betting_market_group_obj->id));
|
||||
fsm.betting_market_group_obj->settling_time = event.db.head_block_time() + fsm.betting_market_group_obj->delay_before_settling;
|
||||
dlog("grading complete, setting settling time to ${settling_time}", ("settling_time", fsm.betting_market_group_obj->settling_time));
|
||||
}
|
||||
};
|
||||
struct re_grading : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> re_grading", ("id", fsm.betting_market_group_obj->id));
|
||||
}
|
||||
};
|
||||
struct settled : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& fsm_event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> settled", ("id", fsm.betting_market_group_obj->id));
|
||||
// TODO: what triggers this? I guess it will be the blockchain when its settling delay expires. So in that case, it should
|
||||
// trigger the payout in the betting markets
|
||||
auto& betting_market_index = fsm_event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
fsm_event.db.modify(betting_market, [&fsm_event](betting_market_object& betting_market) {
|
||||
betting_market.on_settled_event(fsm_event.db);
|
||||
});
|
||||
|
||||
// then notify the event that this betting market is now resolved so it can change its status accordingly
|
||||
const event_object& event = fsm.betting_market_group_obj->event_id(fsm_event.db);
|
||||
fsm_event.db.modify(event, [&fsm_event,&fsm](event_object& event_obj) {
|
||||
event_obj.on_betting_market_group_resolved(fsm_event.db, fsm.betting_market_group_obj->id, false);
|
||||
});
|
||||
}
|
||||
};
|
||||
struct canceled : public msm::front::state<>{
|
||||
void on_entry(const canceled_event& fsm_event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> canceled", ("id", fsm.betting_market_group_obj->id));
|
||||
auto& betting_market_index = fsm_event.db.get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
auto betting_markets_in_group = boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id));
|
||||
|
||||
for (const betting_market_object& betting_market : betting_markets_in_group)
|
||||
fsm_event.db.modify(betting_market, [&fsm_event](betting_market_object& betting_market_obj) {
|
||||
betting_market_obj.on_canceled_event(fsm_event.db);
|
||||
});
|
||||
|
||||
if (!fsm_event.canceled_by_event) {
|
||||
const event_object& event = fsm.betting_market_group_obj->event_id(fsm_event.db);
|
||||
fsm_event.db.modify(event, [&fsm_event,&fsm](event_object& event_obj) {
|
||||
event_obj.on_betting_market_group_resolved(fsm_event.db, fsm.betting_market_group_obj->id, true);
|
||||
});
|
||||
}
|
||||
|
||||
fsm.betting_market_group_obj->settling_time = fsm_event.db.head_block_time();
|
||||
dlog("cancel complete, setting settling time to ${settling_time}", ("settling_time", fsm.betting_market_group_obj->settling_time));
|
||||
}
|
||||
};
|
||||
|
||||
typedef upcoming initial_state;
|
||||
|
||||
// actions
|
||||
void cancel_all_unmatched_bets(const in_play_event& event) {
|
||||
event.db.cancel_all_unmatched_bets_on_betting_market_group(*betting_market_group_obj);
|
||||
}
|
||||
|
||||
// guards
|
||||
bool in_play_is_allowed(const in_play_event& event) {
|
||||
return !betting_market_group_obj->never_in_play;
|
||||
}
|
||||
|
||||
typedef betting_market_group_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
// Transition table for betting market
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < upcoming, frozen_event, frozen_upcoming >,
|
||||
row < upcoming, in_play_event, in_play, &x::cancel_all_unmatched_bets, &x::in_play_is_allowed >,
|
||||
_row < upcoming, closed_event, closed >,
|
||||
_row < upcoming, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < frozen_upcoming, upcoming_event, upcoming >,
|
||||
_row < frozen_upcoming, in_play_event, upcoming >,
|
||||
row < frozen_upcoming, in_play_event, in_play, &x::cancel_all_unmatched_bets, &x::in_play_is_allowed >,
|
||||
_row < frozen_upcoming, closed_event, closed >,
|
||||
_row < frozen_upcoming, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < in_play, frozen_event, frozen_in_play >,
|
||||
_row < in_play, closed_event, closed >,
|
||||
_row < in_play, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < frozen_in_play, in_play_event, in_play >,
|
||||
_row < frozen_in_play, closed_event, closed >,
|
||||
_row < frozen_in_play, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < closed, graded_event, graded >,
|
||||
_row < closed, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
//_row < graded re_grading_event, re_grading >,
|
||||
_row < graded, settled_event, settled >,
|
||||
_row < graded, canceled_event, canceled >
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
//_row < re_grading, graded_event, graded >,
|
||||
//_row < re_grading, canceled_event, canceled >
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
> {};
|
||||
|
||||
template <class Fsm,class Event>
|
||||
void no_transition(Event const& e, Fsm& ,int state)
|
||||
{
|
||||
FC_THROW_EXCEPTION(graphene::chain::no_transition, "No transition");
|
||||
}
|
||||
|
||||
betting_market_group_object* betting_market_group_obj;
|
||||
betting_market_group_state_machine_(betting_market_group_object* betting_market_group_obj) : betting_market_group_obj(betting_market_group_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<betting_market_group_state_machine_> betting_market_group_state_machine;
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
class betting_market_group_object::impl {
|
||||
public:
|
||||
betting_market_group_state_machine state_machine;
|
||||
|
||||
impl(betting_market_group_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
betting_market_group_object::betting_market_group_object() :
|
||||
my(new impl(this))
|
||||
{
|
||||
dlog("betting_market_group_object ctor");
|
||||
}
|
||||
|
||||
betting_market_group_object::betting_market_group_object(const betting_market_group_object& rhs) :
|
||||
graphene::db::abstract_object<betting_market_group_object>(rhs),
|
||||
description(rhs.description),
|
||||
event_id(rhs.event_id),
|
||||
rules_id(rhs.rules_id),
|
||||
asset_id(rhs.asset_id),
|
||||
total_matched_bets_amount(rhs.total_matched_bets_amount),
|
||||
never_in_play(rhs.never_in_play),
|
||||
delay_before_settling(rhs.delay_before_settling),
|
||||
settling_time(rhs.settling_time),
|
||||
my(new impl(this))
|
||||
{
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.betting_market_group_obj = this;
|
||||
}
|
||||
|
||||
betting_market_group_object& betting_market_group_object::operator=(const betting_market_group_object& rhs)
|
||||
{
|
||||
//graphene::db::abstract_object<betting_market_group_object>::operator=(rhs);
|
||||
id = rhs.id;
|
||||
description = rhs.description;
|
||||
event_id = rhs.event_id;
|
||||
rules_id = rhs.rules_id;
|
||||
asset_id = rhs.asset_id;
|
||||
total_matched_bets_amount = rhs.total_matched_bets_amount;
|
||||
never_in_play = rhs.never_in_play;
|
||||
delay_before_settling = rhs.delay_before_settling;
|
||||
settling_time = rhs.settling_time;
|
||||
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.betting_market_group_obj = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
betting_market_group_object::~betting_market_group_object()
|
||||
{
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool verify_betting_market_group_status_constants()
|
||||
{
|
||||
unsigned error_count = 0;
|
||||
typedef msm::back::generate_state_set<betting_market_group_state_machine::stt>::type all_states;
|
||||
static char const* filled_state_names[mpl::size<all_states>::value];
|
||||
mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >
|
||||
(msm::back::fill_state_names<betting_market_group_state_machine::stt>(filled_state_names));
|
||||
for (unsigned i = 0; i < mpl::size<all_states>::value; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
|
||||
// at least contain the string we're looking for
|
||||
const char* fc_reflected_value_name = fc::reflector<betting_market_group_state>::to_string((betting_market_group_state)i);
|
||||
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
|
||||
fc_elog(fc::logger::get("default"),
|
||||
"Error, state string mismatch between fc and boost::msm for int value ${int_value}: boost::msm -> ${boost_string}, fc::reflect -> ${fc_string}",
|
||||
("int_value", i)("boost_string", filled_state_names[i])("fc_string", fc_reflected_value_name));
|
||||
}
|
||||
catch (const fc::bad_cast_exception&)
|
||||
{
|
||||
fc_elog(fc::logger::get("default"),
|
||||
"Error, no reflection for value ${int_value} in enum betting_market_group_status",
|
||||
("int_value", i));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
dlog("Done checking constants");
|
||||
|
||||
return error_count == 0;
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
betting_market_group_status betting_market_group_object::get_status() const
|
||||
{
|
||||
static bool state_constants_are_correct = verify_betting_market_group_status_constants();
|
||||
(void)&state_constants_are_correct;
|
||||
betting_market_group_state state = (betting_market_group_state)my->state_machine.current_state()[0];
|
||||
|
||||
ddump((state));
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case betting_market_group_state::upcoming:
|
||||
return betting_market_group_status::upcoming;
|
||||
case betting_market_group_state::frozen_upcoming:
|
||||
return betting_market_group_status::frozen;
|
||||
case betting_market_group_state::in_play:
|
||||
return betting_market_group_status::in_play;
|
||||
case betting_market_group_state::frozen_in_play:
|
||||
return betting_market_group_status::frozen;
|
||||
case betting_market_group_state::closed:
|
||||
return betting_market_group_status::closed;
|
||||
case betting_market_group_state::graded:
|
||||
return betting_market_group_status::graded;
|
||||
case betting_market_group_state::canceled:
|
||||
return betting_market_group_status::canceled;
|
||||
case betting_market_group_state::settled:
|
||||
return betting_market_group_status::settled;
|
||||
default:
|
||||
FC_THROW("Unexpected betting market group state");
|
||||
};
|
||||
}
|
||||
|
||||
void betting_market_group_object::pack_impl(std::ostream& stream) const
|
||||
{
|
||||
boost::archive::binary_oarchive oa(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
oa << my->state_machine;
|
||||
}
|
||||
|
||||
void betting_market_group_object::unpack_impl(std::istream& stream)
|
||||
{
|
||||
boost::archive::binary_iarchive ia(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
ia >> my->state_machine;
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_upcoming_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(upcoming_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_in_play_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(in_play_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_frozen_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(frozen_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_closed_event(database& db, bool closed_by_event)
|
||||
{
|
||||
my->state_machine.process_event(closed_event(db, closed_by_event));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_graded_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(graded_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_re_grading_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(re_grading_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_settled_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(settled_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_canceled_event(database& db, bool canceled_by_event)
|
||||
{
|
||||
my->state_machine.process_event(canceled_event(db, canceled_by_event));
|
||||
}
|
||||
|
||||
// These are the only statuses that can be explicitly set by witness operations.
|
||||
// Other states can only be reached indirectly (i.e., settling happens a fixed
|
||||
// delay after grading)
|
||||
void betting_market_group_object::dispatch_new_status(database& db, betting_market_group_status new_status)
|
||||
{
|
||||
switch (new_status) {
|
||||
case betting_market_group_status::upcoming: // by witnesses to unfreeze a bmg
|
||||
on_upcoming_event(db);
|
||||
break;
|
||||
case betting_market_group_status::in_play: // by witnesses to make a bmg in-play
|
||||
on_in_play_event(db);
|
||||
break;
|
||||
case betting_market_group_status::closed: // by witnesses to close a bmg
|
||||
on_closed_event(db, false);
|
||||
break;
|
||||
case betting_market_group_status::frozen: // by witnesses to freeze a bmg
|
||||
on_frozen_event(db);
|
||||
break;
|
||||
case betting_market_group_status::canceled: // by witnesses to cancel a bmg
|
||||
on_canceled_event(db, false);
|
||||
break;
|
||||
default:
|
||||
FC_THROW("The status ${new_status} cannot be set directly", ("new_status", new_status));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc {
|
||||
// Manually reflect betting_market_group_object to variant to properly reflect "state"
|
||||
void to_variant(const graphene::chain::betting_market_group_object& betting_market_group_obj, fc::variant& v)
|
||||
{
|
||||
fc::mutable_variant_object o;
|
||||
o("id", betting_market_group_obj.id)
|
||||
("description", betting_market_group_obj.description)
|
||||
("event_id", betting_market_group_obj.event_id)
|
||||
("rules_id", betting_market_group_obj.rules_id)
|
||||
("asset_id", betting_market_group_obj.asset_id)
|
||||
("total_matched_bets_amount", betting_market_group_obj.total_matched_bets_amount)
|
||||
("never_in_play", betting_market_group_obj.never_in_play)
|
||||
("delay_before_settling", betting_market_group_obj.delay_before_settling)
|
||||
("settling_time", betting_market_group_obj.settling_time)
|
||||
("status", betting_market_group_obj.get_status());
|
||||
|
||||
v = o;
|
||||
}
|
||||
|
||||
// Manually reflect betting_market_group_object to variant to properly reflect "state"
|
||||
void from_variant(const fc::variant& v, graphene::chain::betting_market_group_object& betting_market_group_obj)
|
||||
{
|
||||
betting_market_group_obj.id = v["id"].as<graphene::chain::betting_market_group_id_type>();
|
||||
betting_market_group_obj.description = v["description"].as<graphene::chain::internationalized_string_type>();
|
||||
betting_market_group_obj.event_id = v["event_id"].as<graphene::chain::event_id_type>();
|
||||
betting_market_group_obj.asset_id = v["asset_id"].as<graphene::chain::asset_id_type>();
|
||||
betting_market_group_obj.total_matched_bets_amount = v["total_matched_bets_amount"].as<graphene::chain::share_type>();
|
||||
betting_market_group_obj.never_in_play = v["never_in_play"].as<bool>();
|
||||
betting_market_group_obj.delay_before_settling = v["delay_before_settling"].as<uint32_t>();
|
||||
betting_market_group_obj.settling_time = v["settling_time"].as<fc::optional<fc::time_point_sec>>();
|
||||
graphene::chain::betting_market_group_status status = v["status"].as<graphene::chain::betting_market_group_status>();
|
||||
const_cast<int*>(betting_market_group_obj.my->state_machine.current_state())[0] = (int)status;
|
||||
}
|
||||
} //end namespace fc
|
||||
|
||||
|
|
@ -1,7 +1,37 @@
|
|||
#define DEFAULT_LOGGER "betting"
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <boost/math/common_factor_rt.hpp>
|
||||
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/archive/binary_oarchive.hpp>
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/msm/back/tools.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
enum class betting_market_state {
|
||||
unresolved,
|
||||
frozen,
|
||||
closed,
|
||||
canceled,
|
||||
graded,
|
||||
settled
|
||||
};
|
||||
} }
|
||||
FC_REFLECT_ENUM(graphene::chain::betting_market_state,
|
||||
(unresolved)
|
||||
(frozen)
|
||||
(closed)
|
||||
(canceled)
|
||||
(graded)
|
||||
(settled))
|
||||
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
/* static */ share_type bet_object::get_approximate_matching_amount(share_type bet_amount, bet_multiplier_type backer_multiplier, bet_type back_or_lay, bool round_up /* = false */)
|
||||
{
|
||||
|
|
@ -86,5 +116,326 @@ share_type betting_market_position_object::reduce()
|
|||
return immediate_winnings;
|
||||
}
|
||||
|
||||
// betting market object implementation
|
||||
namespace
|
||||
{
|
||||
// Events -- most events happen when the witnesses publish an update operation with a new
|
||||
// status, so if they publish an event with the status set to `frozen`, we'll generate a `frozen_event`
|
||||
struct unresolved_event
|
||||
{
|
||||
database& db;
|
||||
unresolved_event(database& db) : db(db) {}
|
||||
};
|
||||
struct frozen_event
|
||||
{
|
||||
database& db;
|
||||
frozen_event(database& db) : db(db) {}
|
||||
};
|
||||
struct closed_event
|
||||
{
|
||||
database& db;
|
||||
closed_event(database& db) : db(db) {}
|
||||
};
|
||||
struct graded_event
|
||||
{
|
||||
database& db;
|
||||
betting_market_resolution_type new_grading;
|
||||
graded_event(database& db, betting_market_resolution_type new_grading) : db(db), new_grading(new_grading) {}
|
||||
};
|
||||
struct settled_event
|
||||
{
|
||||
database& db;
|
||||
settled_event(database& db) : db(db) {}
|
||||
};
|
||||
struct canceled_event
|
||||
{
|
||||
database& db;
|
||||
canceled_event(database& db) : db(db) {}
|
||||
};
|
||||
|
||||
// Events
|
||||
struct betting_market_state_machine_ : public msm::front::state_machine_def<betting_market_state_machine_>
|
||||
{
|
||||
// disable a few state machine features we don't use for performance
|
||||
typedef int no_exception_thrown;
|
||||
typedef int no_message_queue;
|
||||
|
||||
// States
|
||||
struct unresolved : public msm::front::state<>{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> unresolved", ("id", fsm.betting_market_obj->id));
|
||||
}
|
||||
};
|
||||
struct frozen : public msm::front::state<>{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> frozen", ("id", fsm.betting_market_obj->id));
|
||||
}
|
||||
};
|
||||
struct closed : public msm::front::state<>{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> closed", ("id", fsm.betting_market_obj->id));
|
||||
}
|
||||
};
|
||||
struct graded : public msm::front::state<>{
|
||||
void on_entry(const graded_event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> graded", ("id", fsm.betting_market_obj->id));
|
||||
fsm.betting_market_obj->resolution = event.new_grading;
|
||||
}
|
||||
};
|
||||
struct settled : public msm::front::state<>{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> settled", ("id", fsm.betting_market_obj->id));
|
||||
}
|
||||
};
|
||||
struct canceled : public msm::front::state<>{
|
||||
void on_entry(const canceled_event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> canceled", ("id", fsm.betting_market_obj->id));
|
||||
fsm.betting_market_obj->resolution = betting_market_resolution_type::cancel;
|
||||
}
|
||||
};
|
||||
|
||||
typedef unresolved initial_state;
|
||||
typedef betting_market_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
|
||||
// Transition table for betting market
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < unresolved, frozen_event, frozen >,
|
||||
_row < unresolved, closed_event, closed >,
|
||||
_row < unresolved, canceled_event, canceled >,
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < frozen, unresolved_event, unresolved >,
|
||||
_row < frozen, closed_event, closed >,
|
||||
_row < frozen, canceled_event, canceled >,
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < closed, graded_event, graded >,
|
||||
_row < closed, canceled_event, canceled >,
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < graded, settled_event, settled >,
|
||||
_row < graded, canceled_event, canceled >
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
> {};
|
||||
|
||||
template <class Fsm,class Event>
|
||||
void no_transition(Event const& e, Fsm& ,int state)
|
||||
{
|
||||
FC_THROW_EXCEPTION(graphene::chain::no_transition, "No transition");
|
||||
}
|
||||
|
||||
betting_market_object* betting_market_obj;
|
||||
betting_market_state_machine_(betting_market_object* betting_market_obj) : betting_market_obj(betting_market_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<betting_market_state_machine_> betting_market_state_machine;
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
class betting_market_object::impl {
|
||||
public:
|
||||
betting_market_state_machine state_machine;
|
||||
|
||||
impl(betting_market_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
betting_market_object::betting_market_object() :
|
||||
my(new impl(this))
|
||||
{
|
||||
}
|
||||
|
||||
betting_market_object::betting_market_object(const betting_market_object& rhs) :
|
||||
graphene::db::abstract_object<betting_market_object>(rhs),
|
||||
group_id(rhs.group_id),
|
||||
description(rhs.description),
|
||||
payout_condition(rhs.payout_condition),
|
||||
my(new impl(this))
|
||||
{
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.betting_market_obj = this;
|
||||
}
|
||||
|
||||
betting_market_object& betting_market_object::operator=(const betting_market_object& rhs)
|
||||
{
|
||||
//graphene::db::abstract_object<betting_market_object>::operator=(rhs);
|
||||
id = rhs.id;
|
||||
group_id = rhs.group_id;
|
||||
description = rhs.description;
|
||||
payout_condition = rhs.payout_condition;
|
||||
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.betting_market_obj = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
betting_market_object::~betting_market_object()
|
||||
{
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
bool verify_betting_market_status_constants()
|
||||
{
|
||||
unsigned error_count = 0;
|
||||
typedef msm::back::generate_state_set<betting_market_state_machine::stt>::type all_states;
|
||||
static char const* filled_state_names[mpl::size<all_states>::value];
|
||||
mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >
|
||||
(msm::back::fill_state_names<betting_market_state_machine::stt>(filled_state_names));
|
||||
for (unsigned i = 0; i < mpl::size<all_states>::value; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
|
||||
// at least contain the string we're looking for
|
||||
const char* fc_reflected_value_name = fc::reflector<betting_market_state>::to_string((betting_market_state)i);
|
||||
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
|
||||
fc_elog(fc::logger::get("default"),
|
||||
"Error, state string mismatch between fc and boost::msm for int value ${int_value}: boost::msm -> ${boost_string}, fc::reflect -> ${fc_string}",
|
||||
("int_value", i)("boost_string", filled_state_names[i])("fc_string", fc_reflected_value_name));
|
||||
}
|
||||
catch (const fc::bad_cast_exception&)
|
||||
{
|
||||
fc_elog(fc::logger::get("default"),
|
||||
"Error, no reflection for value ${int_value} in enum betting_market_status",
|
||||
("int_value", i));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
dlog("Done checking constants");
|
||||
|
||||
return error_count == 0;
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
betting_market_status betting_market_object::get_status() const
|
||||
{
|
||||
static bool state_constants_are_correct = verify_betting_market_status_constants();
|
||||
(void)&state_constants_are_correct;
|
||||
betting_market_state state = (betting_market_state)my->state_machine.current_state()[0];
|
||||
|
||||
edump((state));
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case betting_market_state::unresolved:
|
||||
return betting_market_status::unresolved;
|
||||
case betting_market_state::frozen:
|
||||
return betting_market_status::frozen;
|
||||
case betting_market_state::closed:
|
||||
return betting_market_status::unresolved;
|
||||
case betting_market_state::canceled:
|
||||
return betting_market_status::canceled;
|
||||
case betting_market_state::graded:
|
||||
return betting_market_status::graded;
|
||||
case betting_market_state::settled:
|
||||
return betting_market_status::settled;
|
||||
default:
|
||||
FC_THROW("Unexpected betting market state");
|
||||
};
|
||||
}
|
||||
|
||||
void betting_market_object::cancel_all_unmatched_bets(database& db) const
|
||||
{
|
||||
const auto& bet_odds_idx = db.get_index_type<bet_object_index>().indices().get<by_odds>();
|
||||
|
||||
// first, cancel all bets on the active books
|
||||
auto book_itr = bet_odds_idx.lower_bound(std::make_tuple(id));
|
||||
auto book_end = bet_odds_idx.upper_bound(std::make_tuple(id));
|
||||
while (book_itr != book_end)
|
||||
{
|
||||
auto old_book_itr = book_itr;
|
||||
++book_itr;
|
||||
db.cancel_bet(*old_book_itr, true);
|
||||
}
|
||||
|
||||
// then, cancel any delayed bets on that market. We don't have an index for
|
||||
// that, so walk through all delayed bets
|
||||
book_itr = bet_odds_idx.begin();
|
||||
while (book_itr != bet_odds_idx.end() &&
|
||||
book_itr->end_of_delay)
|
||||
{
|
||||
auto old_book_itr = book_itr;
|
||||
++book_itr;
|
||||
if (old_book_itr->betting_market_id == id)
|
||||
db.cancel_bet(*old_book_itr, true);
|
||||
}
|
||||
}
|
||||
|
||||
void betting_market_object::pack_impl(std::ostream& stream) const
|
||||
{
|
||||
boost::archive::binary_oarchive oa(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
oa << my->state_machine;
|
||||
}
|
||||
|
||||
void betting_market_object::unpack_impl(std::istream& stream)
|
||||
{
|
||||
boost::archive::binary_iarchive ia(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
ia >> my->state_machine;
|
||||
}
|
||||
|
||||
void betting_market_object::on_unresolved_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(unresolved_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_frozen_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(frozen_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_closed_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(closed_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_graded_event(database& db, betting_market_resolution_type new_grading)
|
||||
{
|
||||
my->state_machine.process_event(graded_event(db, new_grading));
|
||||
}
|
||||
|
||||
void betting_market_object::on_settled_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(settled_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_canceled_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(canceled_event(db));
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc {
|
||||
// Manually reflect betting_market_object to variant to properly reflect "state"
|
||||
void to_variant(const graphene::chain::betting_market_object& event_obj, fc::variant& v)
|
||||
{
|
||||
fc::mutable_variant_object o;
|
||||
o("id", event_obj.id)
|
||||
("group_id", event_obj.group_id)
|
||||
("description", event_obj.description)
|
||||
("payout_condition", event_obj.payout_condition)
|
||||
("resolution", event_obj.resolution)
|
||||
("status", event_obj.get_status());
|
||||
|
||||
v = o;
|
||||
}
|
||||
|
||||
// Manually reflect betting_market_object to variant to properly reflect "state"
|
||||
void from_variant(const fc::variant& v, graphene::chain::betting_market_object& event_obj)
|
||||
{
|
||||
event_obj.id = v["id"].as<graphene::chain::betting_market_id_type>();
|
||||
event_obj.group_id = v["name"].as<graphene::chain::betting_market_group_id_type>();
|
||||
event_obj.description = v["description"].as<graphene::chain::internationalized_string_type>();
|
||||
event_obj.payout_condition = v["payout_condition"].as<graphene::chain::internationalized_string_type>();
|
||||
event_obj.resolution = v["resolution"].as<fc::optional<graphene::chain::betting_market_resolution_type>>();
|
||||
graphene::chain::betting_market_status status = v["status"].as<graphene::chain::betting_market_status>();
|
||||
const_cast<int*>(event_obj.my->state_machine.current_state())[0] = (int)status;
|
||||
}
|
||||
} //end namespace fc
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#define DEFAULT_LOGGER "betting"
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
|
|
@ -5,6 +6,8 @@
|
|||
#include <graphene/chain/event_object.hpp>
|
||||
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <boost/range/combine.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -50,30 +53,36 @@ void database::cancel_all_unmatched_bets_on_betting_market(const betting_market_
|
|||
}
|
||||
}
|
||||
|
||||
void database::cancel_all_betting_markets_for_event(const event_object& event_obj)
|
||||
{
|
||||
auto& betting_market_group_index = get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
auto& betting_market_index = get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
|
||||
//for each betting market group of event
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj.id)))
|
||||
resolve_betting_market_group(betting_market_group, {});
|
||||
}
|
||||
|
||||
void database::validate_betting_market_group_resolutions(const betting_market_group_object& betting_market_group,
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions)
|
||||
{
|
||||
auto& betting_market_index = get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
auto betting_market_itr = betting_market_index.lower_bound(betting_market_group.id);
|
||||
while (betting_market_itr != betting_market_index.end() && betting_market_itr->group_id == betting_market_group.id)
|
||||
{
|
||||
const betting_market_object& betting_market = *betting_market_itr;
|
||||
// every betting market in the group tied with resolution
|
||||
//idump((betting_market.id)(resolutions));
|
||||
assert(resolutions.count(betting_market.id));
|
||||
++betting_market_itr;
|
||||
}
|
||||
auto& betting_market_index = get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
auto betting_markets_in_group = boost::make_iterator_range(betting_market_index.equal_range(betting_market_group.id));
|
||||
|
||||
// we must have one resolution for each betting market
|
||||
FC_ASSERT(resolutions.size() == boost::size(betting_markets_in_group),
|
||||
"You must publish resolutions for all ${size} markets in the group, you published ${published}", ("size", boost::size(betting_markets_in_group))("published", resolutions.size()));
|
||||
|
||||
// both are sorted by id, we can walk through both and verify that they match
|
||||
unsigned number_of_wins = 0;
|
||||
unsigned number_of_cancels = 0;
|
||||
for (const auto& zipped : boost::combine(resolutions, betting_markets_in_group))
|
||||
{
|
||||
const auto& resolution = boost::get<0>(zipped);
|
||||
const auto& betting_market = boost::get<1>(zipped);
|
||||
FC_ASSERT(resolution.first == betting_market.id, "Missing resolution for betting market ${id}", ("id", betting_market.id));
|
||||
if (resolution.second == betting_market_resolution_type::cancel)
|
||||
++number_of_cancels;
|
||||
else if (resolution.second == betting_market_resolution_type::win)
|
||||
++number_of_wins;
|
||||
else
|
||||
FC_ASSERT(resolution.second == betting_market_resolution_type::not_win);
|
||||
}
|
||||
|
||||
if (number_of_cancels != 0)
|
||||
FC_ASSERT(number_of_cancels == resolutions.size(), "You must cancel all betting markets or none of the betting markets in the group");
|
||||
else
|
||||
FC_ASSERT(number_of_wins == 1, "There must be exactly one winning market");
|
||||
}
|
||||
|
||||
void database::cancel_all_unmatched_bets_on_betting_market_group(const betting_market_group_object& betting_market_group)
|
||||
|
|
@ -90,11 +99,40 @@ void database::cancel_all_unmatched_bets_on_betting_market_group(const betting_m
|
|||
}
|
||||
|
||||
void database::resolve_betting_market_group(const betting_market_group_object& betting_market_group,
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions,
|
||||
bool do_not_remove)
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions)
|
||||
{
|
||||
bool cancel = resolutions.size() == 0;
|
||||
auto& betting_market_index = get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
auto betting_markets_in_group = boost::make_iterator_range(betting_market_index.equal_range(betting_market_group.id));
|
||||
|
||||
bool group_was_canceled = resolutions.begin()->second == betting_market_resolution_type::cancel;
|
||||
|
||||
if (group_was_canceled)
|
||||
modify(betting_market_group, [group_was_canceled,this](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_canceled_event(*this, false); // this cancels the betting markets
|
||||
});
|
||||
else {
|
||||
// TODO: this should be pushed into the bmg's on_graded_event
|
||||
|
||||
// both are sorted by id, we can walk through both and verify that they match
|
||||
for (const auto& zipped : boost::combine(resolutions, betting_markets_in_group))
|
||||
{
|
||||
const auto& resolution = boost::get<0>(zipped);
|
||||
const auto& betting_market = boost::get<1>(zipped);
|
||||
|
||||
modify(betting_market, [this,&resolution](betting_market_object& betting_market_obj) {
|
||||
betting_market_obj.on_graded_event(*this, resolution.second);
|
||||
});
|
||||
}
|
||||
|
||||
modify(betting_market_group, [group_was_canceled,this](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_graded_event(*this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::settle_betting_market_group(const betting_market_group_object& betting_market_group)
|
||||
{
|
||||
ilog("Settling betting market group ${id}", ("id", betting_market_group.id));
|
||||
// we pay the rake fee to the dividend distribution account for the core asset, go ahead
|
||||
// and look up that account now
|
||||
fc::optional<account_id_type> rake_account_id;
|
||||
|
|
@ -105,6 +143,10 @@ void database::resolve_betting_market_group(const betting_market_group_object& b
|
|||
rake_account_id = core_asset_dividend_data_obj.dividend_distribution_account;
|
||||
}
|
||||
|
||||
// collect the resolutions of all markets in the BMG: they were previously published and
|
||||
// stored in the individual betting markets
|
||||
std::map<betting_market_id_type, betting_market_resolution_type> resolutions_by_market_id;
|
||||
|
||||
// collecting bettors and their positions
|
||||
std::map<account_id_type, std::vector<const betting_market_position_object*> > bettor_positions_map;
|
||||
|
||||
|
|
@ -116,11 +158,13 @@ void database::resolve_betting_market_group(const betting_market_group_object& b
|
|||
while (betting_market_itr != betting_market_index.end() && betting_market_itr->group_id == betting_market_group.id)
|
||||
{
|
||||
const betting_market_object& betting_market = *betting_market_itr;
|
||||
FC_ASSERT(betting_market_itr->resolution, "Unexpected error settling betting market ${market_id}: no published resolution",
|
||||
("market_id", betting_market_itr->id));
|
||||
resolutions_by_market_id.emplace(betting_market.id, *betting_market_itr->resolution);
|
||||
|
||||
++betting_market_itr;
|
||||
cancel_all_unmatched_bets_on_betting_market(betting_market);
|
||||
|
||||
// [ROL] why tuple
|
||||
//auto position_itr = position_index.lower_bound(std::make_tuple(betting_market.id));
|
||||
auto position_itr = position_index.lower_bound(betting_market.id);
|
||||
|
||||
while (position_itr != position_index.end() && position_itr->betting_market_id == betting_market.id)
|
||||
|
|
@ -144,14 +188,24 @@ void database::resolve_betting_market_group(const betting_market_group_object& b
|
|||
for (const betting_market_position_object* position : bettor_positions)
|
||||
{
|
||||
betting_market_resolution_type resolution;
|
||||
if (cancel)
|
||||
resolution = betting_market_resolution_type::cancel;
|
||||
else
|
||||
try
|
||||
{
|
||||
// checked in evaluator, should never happen, see above
|
||||
assert(resolutions.count(position->betting_market_id));
|
||||
resolution = resolutions.at(position->betting_market_id);
|
||||
resolution = resolutions_by_market_id.at(position->betting_market_id);
|
||||
}
|
||||
catch (std::out_of_range&)
|
||||
{
|
||||
FC_THROW_EXCEPTION(fc::key_not_found_exception, "Unexpected betting market ID, shouldn't happen");
|
||||
}
|
||||
|
||||
///if (cancel)
|
||||
/// resolution = betting_market_resolution_type::cancel;
|
||||
///else
|
||||
///{
|
||||
/// // checked in evaluator, should never happen, see above
|
||||
/// assert(resolutions.count(position->betting_market_id));
|
||||
/// resolution = resolutions.at(position->betting_market_id);
|
||||
///}
|
||||
|
||||
|
||||
switch (resolution)
|
||||
{
|
||||
|
|
@ -194,29 +248,41 @@ void database::resolve_betting_market_group(const betting_market_group_object& b
|
|||
|
||||
push_applied_operation(betting_market_group_resolved_operation(bettor_id,
|
||||
betting_market_group.id,
|
||||
resolutions,
|
||||
resolutions_by_market_id,
|
||||
payout_amounts,
|
||||
rake_amount));
|
||||
}
|
||||
|
||||
betting_market_itr = betting_market_index.lower_bound(betting_market_group.id);
|
||||
while (betting_market_itr != betting_market_index.end() && betting_market_itr->group_id == betting_market_group.id)
|
||||
{
|
||||
const betting_market_object& betting_market = *betting_market_itr;
|
||||
++betting_market_itr;
|
||||
if (!do_not_remove)
|
||||
remove(betting_market);
|
||||
}
|
||||
if (!do_not_remove)
|
||||
remove(betting_market_group);
|
||||
}
|
||||
// At this point, the betting market group will either be in the "graded" or "canceled" state,
|
||||
// if it was graded, mark it as settled. if it's canceled, let it remain canceled.
|
||||
|
||||
#if 0
|
||||
void database::get_required_deposit_for_bet(const betting_market_object& betting_market,
|
||||
betting_market_resolution_type resolution)
|
||||
{
|
||||
bool was_canceled = betting_market_group.get_status() == betting_market_group_status::canceled;
|
||||
|
||||
if (!was_canceled)
|
||||
modify(betting_market_group, [&](betting_market_group_object& group) {
|
||||
group.on_settled_event(*this);
|
||||
});
|
||||
|
||||
betting_market_itr = betting_market_index.lower_bound(betting_market_group.id);
|
||||
while (betting_market_itr != betting_market_index.end() && betting_market_itr->group_id == betting_market_group.id) {
|
||||
const betting_market_object& betting_market = *betting_market_itr;
|
||||
|
||||
++betting_market_itr;
|
||||
dlog("removing betting market ${id}", ("id", betting_market.id));
|
||||
remove(betting_market);
|
||||
}
|
||||
|
||||
const event_object& event = betting_market_group.event_id(*this);
|
||||
|
||||
dlog("removing betting market group ${id}", ("id", betting_market_group.id));
|
||||
remove(betting_market_group);
|
||||
|
||||
if (event.get_status() == event_status::canceled ||
|
||||
event.get_status() == event_status::settled) {
|
||||
dlog("removing event ${id}", ("id", event.id));
|
||||
remove(event);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
share_type adjust_betting_position(database& db, account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, share_type bet_amount, share_type matched_amount)
|
||||
{ try {
|
||||
|
|
|
|||
|
|
@ -551,6 +551,7 @@ void database::_apply_block( const signed_block& next_block )
|
|||
update_expired_feeds();
|
||||
update_withdraw_permissions();
|
||||
update_tournaments();
|
||||
update_betting_markets(next_block.timestamp);
|
||||
|
||||
// n.b., update_maintenance_flag() happens this late
|
||||
// because get_slot_time() / get_slot_at_time() is needed above
|
||||
|
|
@ -568,7 +569,6 @@ void database::_apply_block( const signed_block& next_block )
|
|||
_applied_ops.clear();
|
||||
|
||||
notify_changed_objects();
|
||||
|
||||
} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) }
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
|
|
@ -222,8 +223,7 @@ void database::place_delayed_bets()
|
|||
// Our current understanding is that the witnesses will typically cancel all unmatched
|
||||
// bets on frozen markets to avoid this.
|
||||
const betting_market_object& betting_market = bet_to_place.betting_market_id(*this);
|
||||
const betting_market_group_object& betting_market_group = betting_market.group_id(*this);
|
||||
if (!betting_market_group.frozen)
|
||||
if (betting_market.get_status() == betting_market_status::unresolved)
|
||||
{
|
||||
modify(bet_to_place, [](bet_object& bet_obj) {
|
||||
// clear the end_of_delay, which will re-sort the bet into its place in the book
|
||||
|
|
@ -655,4 +655,28 @@ void database::update_tournaments()
|
|||
initiate_next_games(*this);
|
||||
}
|
||||
|
||||
void process_settled_betting_markets(database& db, fc::time_point_sec current_block_time)
|
||||
{
|
||||
// after a betting market is graded, it goes through a delay period in which it
|
||||
// can be flagged for re-grading. If it isn't flagged during this interval,
|
||||
// it is automatically settled (paid). Process these now.
|
||||
const auto& betting_market_group_index = db.get_index_type<betting_market_group_object_index>().indices().get<by_settling_time>();
|
||||
|
||||
// this index will be sorted with all bmgs with no settling time set first, followed by
|
||||
// ones with the settling time set by increasing time. Start at the first bmg with a time set
|
||||
auto betting_market_group_iter = betting_market_group_index.upper_bound(fc::optional<fc::time_point_sec>());
|
||||
while (betting_market_group_iter != betting_market_group_index.end() &&
|
||||
*betting_market_group_iter->settling_time <= current_block_time)
|
||||
{
|
||||
auto next_iter = std::next(betting_market_group_iter);
|
||||
db.settle_betting_market_group(*betting_market_group_iter);
|
||||
betting_market_group_iter = next_iter;
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_betting_markets(fc::time_point_sec current_block_time)
|
||||
{
|
||||
process_settled_betting_markets(*this, current_block_time);
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ object_id_type event_create_evaluator::do_apply(const event_create_operation& op
|
|||
const event_object& new_event =
|
||||
d.create<event_object>( [&]( event_object& event_obj ) {
|
||||
event_obj.name = op.name;
|
||||
event_obj.status = event_status::upcoming;
|
||||
event_obj.season = op.season;
|
||||
event_obj.start_time = op.start_time;
|
||||
event_obj.event_group_id = event_group_id;
|
||||
|
|
@ -74,28 +73,19 @@ object_id_type event_create_evaluator::do_apply(const event_create_operation& op
|
|||
void_result event_update_evaluator::do_evaluate(const event_update_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
FC_ASSERT(op.new_event_group_id.valid() ||
|
||||
op.new_name.valid() ||
|
||||
op.new_season.valid() ||
|
||||
op.new_start_time.valid() ||
|
||||
op.is_live_market.valid(), "nothing to change");
|
||||
FC_ASSERT(op.new_event_group_id || op.new_name || op.new_season ||
|
||||
op.new_start_time || op.new_status, "nothing to change");
|
||||
|
||||
if (op.new_event_group_id.valid())
|
||||
if (op.new_event_group_id)
|
||||
{
|
||||
object_id_type resolved_event_group_id = *op.new_event_group_id;
|
||||
if (is_relative(*op.new_event_group_id))
|
||||
resolved_event_group_id = get_relative_id(*op.new_event_group_id);
|
||||
object_id_type resolved_event_group_id = *op.new_event_group_id;
|
||||
if (is_relative(*op.new_event_group_id))
|
||||
resolved_event_group_id = get_relative_id(*op.new_event_group_id);
|
||||
|
||||
FC_ASSERT(resolved_event_group_id.space() == event_group_id_type::space_id &&
|
||||
resolved_event_group_id.type() == event_group_id_type::type_id,
|
||||
"event_group_id must refer to a event_group_id_type");
|
||||
event_group_id = resolved_event_group_id;
|
||||
}
|
||||
|
||||
if (op.is_live_market.valid())
|
||||
{
|
||||
const auto& _event_object = &op.event_id(db());
|
||||
FC_ASSERT(_event_object->is_live_market != *op.is_live_market, "is_live_market would not change the state of the event");
|
||||
FC_ASSERT(resolved_event_group_id.space() == event_group_id_type::space_id &&
|
||||
resolved_event_group_id.type() == event_group_id_type::type_id,
|
||||
"event_group_id must refer to a event_group_id_type");
|
||||
event_group_id = resolved_event_group_id;
|
||||
}
|
||||
|
||||
return void_result();
|
||||
|
|
@ -103,25 +93,21 @@ void_result event_update_evaluator::do_evaluate(const event_update_operation& op
|
|||
|
||||
void_result event_update_evaluator::do_apply(const event_update_operation& op)
|
||||
{ try {
|
||||
database& _db = db();
|
||||
_db.modify(
|
||||
_db.get(op.event_id),
|
||||
[&]( event_object& eo )
|
||||
{
|
||||
if (eo.status > event_status::STATUS_COUNT)
|
||||
eo.status = event_status::upcoming;
|
||||
if( op.new_name.valid() )
|
||||
eo.name = *op.new_name;
|
||||
if( op.new_season.valid() )
|
||||
eo.season = *op.new_season;
|
||||
if( op.new_start_time.valid() )
|
||||
eo.start_time = *op.new_start_time;
|
||||
if( op.new_event_group_id.valid() )
|
||||
eo.event_group_id = event_group_id;
|
||||
if( op.is_live_market.valid() )
|
||||
eo.is_live_market = *op.is_live_market;
|
||||
});
|
||||
return void_result();
|
||||
database& _db = db();
|
||||
_db.modify(_db.get(op.event_id),
|
||||
[&](event_object& eo) {
|
||||
if( op.new_name )
|
||||
eo.name = *op.new_name;
|
||||
if( op.new_season )
|
||||
eo.season = *op.new_season;
|
||||
if( op.new_start_time )
|
||||
eo.start_time = *op.new_start_time;
|
||||
if( op.new_event_group_id )
|
||||
eo.event_group_id = event_group_id;
|
||||
if( op.new_status )
|
||||
eo.dispatch_new_status(_db, *op.new_status);
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result event_update_status_evaluator::do_evaluate(const event_update_status_operation& op)
|
||||
|
|
@ -138,14 +124,13 @@ void_result event_update_status_evaluator::do_evaluate(const event_update_status
|
|||
void_result event_update_status_evaluator::do_apply(const event_update_status_operation& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
//if event is canceled, first cancel all associated betting markets
|
||||
if (op.status == event_status::canceled)
|
||||
d.cancel_all_betting_markets_for_event(*_event_to_update);
|
||||
//update event
|
||||
d.modify( *_event_to_update, [&]( event_object& event_obj) {
|
||||
|
||||
d.modify( *_event_to_update, [&](event_object& event_obj) {
|
||||
if (_event_to_update->get_status() != op.status)
|
||||
event_obj.dispatch_new_status(d, op.status);
|
||||
event_obj.scores = op.scores;
|
||||
event_obj.status = op.status;
|
||||
});
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
|
|
|||
547
libraries/chain/event_object.cpp
Normal file
547
libraries/chain/event_object.cpp
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
#define DEFAULT_LOGGER "betting"
|
||||
#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
|
||||
#define BOOST_MPL_LIMIT_VECTOR_SIZE 30
|
||||
#define BOOST_MPL_LIMIT_MAP_SIZE 30
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/archive/binary_oarchive.hpp>
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/msm/back/tools.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
enum class event_state {
|
||||
upcoming,
|
||||
frozen_upcoming,
|
||||
in_progress,
|
||||
frozen_in_progress,
|
||||
finished,
|
||||
canceled,
|
||||
settled
|
||||
};
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::event_state,
|
||||
(upcoming)
|
||||
(frozen_upcoming)
|
||||
(in_progress)
|
||||
(frozen_in_progress)
|
||||
(finished)
|
||||
(canceled)
|
||||
(settled))
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Events -- most events happen when the witnesses publish an event_update operation with a new
|
||||
// status, so if they publish an event with the status set to `frozen`, we'll generate a `frozen_event`
|
||||
struct upcoming_event
|
||||
{
|
||||
database& db;
|
||||
upcoming_event(database& db) : db(db) {}
|
||||
};
|
||||
struct in_progress_event
|
||||
{
|
||||
database& db;
|
||||
in_progress_event(database& db) : db(db) {}
|
||||
};
|
||||
struct frozen_event
|
||||
{
|
||||
database& db;
|
||||
frozen_event(database& db) : db(db) {}
|
||||
};
|
||||
struct finished_event
|
||||
{
|
||||
database& db;
|
||||
finished_event(database& db) : db(db) {}
|
||||
};
|
||||
struct canceled_event
|
||||
{
|
||||
database& db;
|
||||
canceled_event(database& db) : db(db) {}
|
||||
};
|
||||
|
||||
// event triggered when a betting market group in this event is resolved,
|
||||
// when we get this, check and see if all betting market groups are now
|
||||
// canceled/settled; if so, transition the event to canceled/settled,
|
||||
// otherwise remain in the current state
|
||||
struct betting_market_group_resolved_event
|
||||
{
|
||||
database& db;
|
||||
betting_market_group_id_type resolved_group;
|
||||
bool was_canceled;
|
||||
betting_market_group_resolved_event(database& db, betting_market_group_id_type resolved_group, bool was_canceled) : db(db), resolved_group(resolved_group), was_canceled(was_canceled) {}
|
||||
};
|
||||
|
||||
// event triggered when a betting market group is closed. When we get this,
|
||||
// if all child betting market groups are closed, transition to finished
|
||||
struct betting_market_group_closed_event
|
||||
{
|
||||
database& db;
|
||||
betting_market_group_id_type closed_group;
|
||||
betting_market_group_closed_event(database& db, betting_market_group_id_type closed_group) : db(db), closed_group(closed_group) {}
|
||||
};
|
||||
|
||||
// Events
|
||||
struct event_state_machine_ : public msm::front::state_machine_def<event_state_machine_>
|
||||
|
||||
{
|
||||
// disable a few state machine features we don't use for performance
|
||||
typedef int no_exception_thrown;
|
||||
typedef int no_message_queue;
|
||||
|
||||
// States
|
||||
struct upcoming : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
void on_entry(const upcoming_event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> upcoming", ("id", fsm.event_obj->id));
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(fsm.event_obj->id)))
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_upcoming_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't freeze the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
};
|
||||
struct in_progress : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
void on_entry(const in_progress_event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> in_progress", ("id", fsm.event_obj->id));
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(fsm.event_obj->id)))
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_in_play_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't freeze the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
};
|
||||
struct frozen_upcoming : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> frozen_upcoming", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct frozen_in_progress : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> frozen_in_progress", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct finished : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> finished", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct settled : public msm::front::state<>{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> settled", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct canceled : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> canceled", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
|
||||
// actions
|
||||
void record_whether_group_settled_or_canceled(const betting_market_group_resolved_event& event) {
|
||||
if (!event.was_canceled)
|
||||
event_obj->at_least_one_betting_market_group_settled = true;
|
||||
}
|
||||
|
||||
void freeze_betting_market_groups(const frozen_event& event) {
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
{
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_frozen_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't freeze the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void close_all_betting_market_groups(const finished_event& event) {
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
{
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_closed_event(event.db, true);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't close the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cancel_all_betting_market_groups(const canceled_event& event) {
|
||||
auto& betting_market_group_index = event.db.template get_index_type<betting_market_group_object_index>().indices().template get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_canceled_event(event.db, true);
|
||||
});
|
||||
}
|
||||
|
||||
// Guards
|
||||
bool all_betting_market_groups_are_closed(const betting_market_group_closed_event& event)
|
||||
{
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
if (betting_market_group.id != event.closed_group)
|
||||
{
|
||||
betting_market_group_status status = betting_market_group.get_status();
|
||||
if (status != betting_market_group_status::closed &&
|
||||
status != betting_market_group_status::graded &&
|
||||
status != betting_market_group_status::re_grading &&
|
||||
status != betting_market_group_status::settled &&
|
||||
status != betting_market_group_status::canceled)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool all_betting_market_groups_are_canceled(const betting_market_group_resolved_event& event)
|
||||
{
|
||||
// if the bmg that just resolved was settled, obviously all didn't cancel
|
||||
if (!event.was_canceled)
|
||||
return false;
|
||||
// if a previously-resolved group was settled, all didn't cancel
|
||||
if (event_obj->at_least_one_betting_market_group_settled)
|
||||
return false;
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
if (betting_market_group.id != event.resolved_group)
|
||||
if (betting_market_group.get_status() != betting_market_group_status::canceled)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool all_betting_market_groups_are_resolved(const betting_market_group_resolved_event& event)
|
||||
{
|
||||
if (!event.was_canceled)
|
||||
event_obj->at_least_one_betting_market_group_settled = true;
|
||||
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id))) {
|
||||
if (betting_market_group.id != event.resolved_group) {
|
||||
betting_market_group_status status = betting_market_group.get_status();
|
||||
if (status != betting_market_group_status::canceled && status != betting_market_group_status::settled)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef upcoming initial_state;
|
||||
typedef event_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
// Transition table for tournament
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
_row < upcoming, in_progress_event, in_progress >,
|
||||
a_row< upcoming, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< upcoming, frozen_event, frozen_upcoming, &x::freeze_betting_market_groups >,
|
||||
a_row< upcoming, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< upcoming, betting_market_group_resolved_event,upcoming, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< upcoming, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
_row < frozen_upcoming, upcoming_event, upcoming >,
|
||||
_row < frozen_upcoming, in_progress_event, in_progress >,
|
||||
a_row< frozen_upcoming, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< frozen_upcoming, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< frozen_upcoming, betting_market_group_resolved_event,frozen_upcoming, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< frozen_upcoming, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
a_row< in_progress, frozen_event, frozen_in_progress, &x::freeze_betting_market_groups >,
|
||||
a_row< in_progress, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< in_progress, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< in_progress, betting_market_group_resolved_event,in_progress, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< in_progress, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
_row < frozen_in_progress, in_progress_event, in_progress >,
|
||||
a_row< frozen_in_progress, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< frozen_in_progress, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< frozen_in_progress, betting_market_group_resolved_event,frozen_in_progress, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< frozen_in_progress, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
a_row< finished, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
g_row< finished, betting_market_group_resolved_event,settled, &x::all_betting_market_groups_are_resolved >,
|
||||
g_row< finished, betting_market_group_resolved_event,canceled, &x::all_betting_market_groups_are_canceled >
|
||||
> {};
|
||||
|
||||
template <class Fsm,class Event>
|
||||
void no_transition(Event const& e, Fsm& ,int state)
|
||||
{
|
||||
FC_THROW_EXCEPTION(graphene::chain::no_transition, "No transition");
|
||||
}
|
||||
|
||||
event_object* event_obj;
|
||||
event_state_machine_(event_object* event_obj) : event_obj(event_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<event_state_machine_> event_state_machine;
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
class event_object::impl {
|
||||
public:
|
||||
event_state_machine state_machine;
|
||||
|
||||
impl(event_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
event_object::event_object() :
|
||||
at_least_one_betting_market_group_settled(false),
|
||||
my(new impl(this))
|
||||
{
|
||||
}
|
||||
|
||||
event_object::event_object(const event_object& rhs) :
|
||||
graphene::db::abstract_object<event_object>(rhs),
|
||||
name(rhs.name),
|
||||
season(rhs.season),
|
||||
start_time(rhs.start_time),
|
||||
event_group_id(rhs.event_group_id),
|
||||
at_least_one_betting_market_group_settled(rhs.at_least_one_betting_market_group_settled),
|
||||
scores(rhs.scores),
|
||||
my(new impl(this))
|
||||
{
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.event_obj = this;
|
||||
}
|
||||
|
||||
event_object& event_object::operator=(const event_object& rhs)
|
||||
{
|
||||
//graphene::db::abstract_object<event_object>::operator=(rhs);
|
||||
id = rhs.id;
|
||||
name = rhs.name;
|
||||
season = rhs.season;
|
||||
start_time = rhs.start_time;
|
||||
event_group_id = rhs.event_group_id;
|
||||
at_least_one_betting_market_group_settled = rhs.at_least_one_betting_market_group_settled;
|
||||
scores = rhs.scores;
|
||||
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.event_obj = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
event_object::~event_object()
|
||||
{
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool verify_event_status_constants()
|
||||
{
|
||||
unsigned error_count = 0;
|
||||
typedef msm::back::generate_state_set<event_state_machine::stt>::type all_states;
|
||||
static char const* filled_state_names[mpl::size<all_states>::value];
|
||||
mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >
|
||||
(msm::back::fill_state_names<event_state_machine::stt>(filled_state_names));
|
||||
for (unsigned i = 0; i < mpl::size<all_states>::value; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
|
||||
// at least contain the string we're looking for
|
||||
const char* fc_reflected_value_name = fc::reflector<event_state>::to_string((event_state)i);
|
||||
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
|
||||
fc_elog(fc::logger::get("default"),
|
||||
"Error, state string mismatch between fc and boost::msm for int value ${int_value}: boost::msm -> ${boost_string}, fc::reflect -> ${fc_string}",
|
||||
("int_value", i)("boost_string", filled_state_names[i])("fc_string", fc_reflected_value_name));
|
||||
}
|
||||
catch (const fc::bad_cast_exception&)
|
||||
{
|
||||
fc_elog(fc::logger::get("default"),
|
||||
"Error, no reflection for value ${int_value} in enum event_status",
|
||||
("int_value", i));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
dlog("Done checking constants");
|
||||
|
||||
return error_count == 0;
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
event_status event_object::get_status() const
|
||||
{
|
||||
static bool state_constants_are_correct = verify_event_status_constants();
|
||||
(void)&state_constants_are_correct;
|
||||
event_state state = (event_state)my->state_machine.current_state()[0];
|
||||
|
||||
ddump((state));
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case event_state::upcoming:
|
||||
return event_status::upcoming;
|
||||
case event_state::frozen_upcoming:
|
||||
case event_state::frozen_in_progress:
|
||||
return event_status::frozen;
|
||||
case event_state::in_progress:
|
||||
return event_status::in_progress;
|
||||
case event_state::finished:
|
||||
return event_status::finished;
|
||||
case event_state::canceled:
|
||||
return event_status::canceled;
|
||||
case event_state::settled:
|
||||
return event_status::settled;
|
||||
default:
|
||||
FC_THROW("Unexpected event state");
|
||||
};
|
||||
}
|
||||
|
||||
void event_object::pack_impl(std::ostream& stream) const
|
||||
{
|
||||
boost::archive::binary_oarchive oa(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
oa << my->state_machine;
|
||||
}
|
||||
|
||||
void event_object::unpack_impl(std::istream& stream)
|
||||
{
|
||||
boost::archive::binary_iarchive ia(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
ia >> my->state_machine;
|
||||
}
|
||||
|
||||
void event_object::on_upcoming_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(upcoming_event(db));
|
||||
}
|
||||
|
||||
void event_object::on_in_progress_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(in_progress_event(db));
|
||||
}
|
||||
|
||||
void event_object::on_frozen_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(frozen_event(db));
|
||||
}
|
||||
|
||||
void event_object::on_finished_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(finished_event(db));
|
||||
}
|
||||
|
||||
void event_object::on_canceled_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(canceled_event(db));
|
||||
}
|
||||
|
||||
void event_object::on_betting_market_group_resolved(database& db, betting_market_group_id_type resolved_group, bool was_canceled)
|
||||
{
|
||||
my->state_machine.process_event(betting_market_group_resolved_event(db, resolved_group, was_canceled));
|
||||
}
|
||||
|
||||
void event_object::on_betting_market_group_closed(database& db, betting_market_group_id_type closed_group)
|
||||
{
|
||||
my->state_machine.process_event(betting_market_group_closed_event(db, closed_group));
|
||||
}
|
||||
|
||||
// These are the only statuses that can be explicitly set by witness operations. The missing
|
||||
// status, 'settled', is automatically set when all of the betting market groups have
|
||||
// settled/canceled
|
||||
void event_object::dispatch_new_status(database& db, event_status new_status)
|
||||
{
|
||||
switch (new_status) {
|
||||
case event_status::upcoming: // by witnesses to unfreeze a frozen event
|
||||
on_upcoming_event(db);
|
||||
break;
|
||||
case event_status::in_progress: // by witnesses when the event starts
|
||||
on_in_progress_event(db);
|
||||
break;
|
||||
case event_status::frozen: // by witnesses when the event needs to be frozen
|
||||
on_frozen_event(db);
|
||||
break;
|
||||
case event_status::finished: // by witnesses when the event is complete
|
||||
on_finished_event(db);
|
||||
break;
|
||||
case event_status::canceled: // by witnesses to cancel the event
|
||||
on_canceled_event(db);
|
||||
break;
|
||||
default:
|
||||
FC_THROW("Status ${new_status} cannot be explicitly set", ("new_status", new_status));
|
||||
}
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc {
|
||||
// Manually reflect event_object to variant to properly reflect "state"
|
||||
void to_variant(const graphene::chain::event_object& event_obj, fc::variant& v)
|
||||
{
|
||||
fc::mutable_variant_object o;
|
||||
o("id", event_obj.id)
|
||||
("name", event_obj.name)
|
||||
("season", event_obj.season)
|
||||
("start_time", event_obj.start_time)
|
||||
("event_group_id", event_obj.event_group_id)
|
||||
("scores", event_obj.scores)
|
||||
("status", event_obj.get_status());
|
||||
|
||||
v = o;
|
||||
}
|
||||
|
||||
// Manually reflect event_object to variant to properly reflect "state"
|
||||
void from_variant(const fc::variant& v, graphene::chain::event_object& event_obj)
|
||||
{
|
||||
event_obj.id = v["id"].as<graphene::chain::event_id_type>();
|
||||
event_obj.name = v["name"].as<graphene::chain::internationalized_string_type>();
|
||||
event_obj.season = v["season"].as<graphene::chain::internationalized_string_type>();
|
||||
event_obj.start_time = v["start_time"].as<optional<time_point_sec> >();
|
||||
event_obj.event_group_id = v["event_group_id"].as<graphene::chain::event_group_id_type>();
|
||||
event_obj.scores = v["scores"].as<std::vector<std::string>>();
|
||||
graphene::chain::event_status status = v["status"].as<graphene::chain::event_status>();
|
||||
const_cast<int*>(event_obj.my->state_machine.current_state())[0] = (int)status;
|
||||
}
|
||||
} //end namespace fc
|
||||
|
||||
|
||||
|
|
@ -31,10 +31,24 @@
|
|||
#include <boost/multi_index/composite_key.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class betting_market_object;
|
||||
class betting_market_group_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::betting_market_object& betting_market_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::betting_market_object& betting_market_obj);
|
||||
void to_variant(const graphene::chain::betting_market_group_object& betting_market_group_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::betting_market_group_object& betting_market_group_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
FC_DECLARE_EXCEPTION(no_transition, 100000, "Invalid state transition");
|
||||
class database;
|
||||
|
||||
struct by_event_id;
|
||||
struct by_settling_time;
|
||||
struct by_betting_market_group_id;
|
||||
|
||||
class betting_market_rules_object : public graphene::db::abstract_object< betting_market_rules_object >
|
||||
|
|
@ -54,6 +68,11 @@ class betting_market_group_object : public graphene::db::abstract_object< bettin
|
|||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = betting_market_group_object_type;
|
||||
|
||||
betting_market_group_object();
|
||||
betting_market_group_object(const betting_market_group_object& rhs);
|
||||
~betting_market_group_object();
|
||||
betting_market_group_object& operator=(const betting_market_group_object& rhs);
|
||||
|
||||
internationalized_string_type description;
|
||||
|
||||
event_id_type event_id;
|
||||
|
|
@ -64,9 +83,51 @@ class betting_market_group_object : public graphene::db::abstract_object< bettin
|
|||
|
||||
share_type total_matched_bets_amount;
|
||||
|
||||
bool frozen;
|
||||
bool never_in_play;
|
||||
|
||||
bool delay_bets;
|
||||
uint32_t delay_before_settling;
|
||||
|
||||
fc::optional<fc::time_point_sec> settling_time; // the time the payout will occur (set after grading)
|
||||
|
||||
bool bets_are_allowed() const {
|
||||
return get_status() == betting_market_group_status::upcoming ||
|
||||
get_status() == betting_market_group_status::in_play;
|
||||
}
|
||||
|
||||
bool bets_are_delayed() const {
|
||||
return get_status() == betting_market_group_status::in_play;
|
||||
}
|
||||
|
||||
betting_market_group_status get_status() const;
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const betting_market_group_object& betting_market_group_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, betting_market_group_object& betting_market_group_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::betting_market_group_object& betting_market_group_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::betting_market_group_object& betting_market_group_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
void on_upcoming_event(database& db);
|
||||
void on_in_play_event(database& db);
|
||||
void on_frozen_event(database& db);
|
||||
void on_closed_event(database& db, bool closed_by_event);
|
||||
void on_graded_event(database& db);
|
||||
void on_re_grading_event(database& db);
|
||||
void on_settled_event(database& db);
|
||||
void on_canceled_event(database& db, bool canceled_by_event);
|
||||
void dispatch_new_status(database& db, betting_market_group_status new_status);
|
||||
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
class betting_market_object : public graphene::db::abstract_object< betting_market_object >
|
||||
|
|
@ -75,11 +136,49 @@ class betting_market_object : public graphene::db::abstract_object< betting_mark
|
|||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = betting_market_object_type;
|
||||
|
||||
betting_market_object();
|
||||
betting_market_object(const betting_market_object& rhs);
|
||||
~betting_market_object();
|
||||
betting_market_object& operator=(const betting_market_object& rhs);
|
||||
|
||||
betting_market_group_id_type group_id;
|
||||
|
||||
internationalized_string_type description;
|
||||
|
||||
internationalized_string_type payout_condition;
|
||||
|
||||
// once the market is graded, this holds the proposed grading
|
||||
// after settling/canceling, this is the actual grading
|
||||
fc::optional<betting_market_resolution_type> resolution;
|
||||
|
||||
betting_market_status get_status() const;
|
||||
|
||||
void cancel_all_unmatched_bets(database& db) const;
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const betting_market_object& betting_market_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, betting_market_object& betting_market_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::betting_market_object& betting_market_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::betting_market_object& betting_market_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
void on_unresolved_event(database& db);
|
||||
void on_frozen_event(database& db);
|
||||
void on_closed_event(database& db);
|
||||
void on_graded_event(database& db, betting_market_resolution_type new_grading);
|
||||
void on_settled_event(database& db);
|
||||
void on_canceled_event(database& db);
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
class bet_object : public graphene::db::abstract_object< bet_object >
|
||||
|
|
@ -147,7 +246,8 @@ typedef multi_index_container<
|
|||
betting_market_group_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_event_id>, member<betting_market_group_object, event_id_type, &betting_market_group_object::event_id> >
|
||||
ordered_non_unique< tag<by_event_id>, member<betting_market_group_object, event_id_type, &betting_market_group_object::event_id> >,
|
||||
ordered_non_unique< tag<by_settling_time>, member<betting_market_group_object, fc::optional<fc::time_point_sec>, &betting_market_group_object::settling_time> >
|
||||
> > betting_market_group_object_multi_index_type;
|
||||
typedef generic_index<betting_market_group_object, betting_market_group_object_multi_index_type> betting_market_group_object_index;
|
||||
|
||||
|
|
@ -513,11 +613,101 @@ typedef multi_index_container<
|
|||
> > betting_market_position_multi_index_type;
|
||||
|
||||
typedef generic_index<betting_market_position_object, betting_market_position_multi_index_type> betting_market_position_index;
|
||||
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const betting_market_object& betting_market_obj )
|
||||
{
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<betting_market_object> >(s, betting_market_obj);
|
||||
fc::raw::pack(s, betting_market_obj.id);
|
||||
fc::raw::pack(s, betting_market_obj.group_id);
|
||||
fc::raw::pack(s, betting_market_obj.description);
|
||||
fc::raw::pack(s, betting_market_obj.payout_condition);
|
||||
fc::raw::pack(s, betting_market_obj.resolution);
|
||||
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
betting_market_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, betting_market_object& betting_market_obj )
|
||||
{
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<betting_market_object> >(s, betting_market_obj);
|
||||
fc::raw::unpack(s, betting_market_obj.id);
|
||||
fc::raw::unpack(s, betting_market_obj.group_id);
|
||||
fc::raw::unpack(s, betting_market_obj.description);
|
||||
fc::raw::unpack(s, betting_market_obj.payout_condition);
|
||||
fc::raw::unpack(s, betting_market_obj.resolution);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
betting_market_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const betting_market_group_object& betting_market_group_obj )
|
||||
{
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<betting_market_group_object> >(s, betting_market_group_obj);
|
||||
fc::raw::pack(s, betting_market_group_obj.id);
|
||||
fc::raw::pack(s, betting_market_group_obj.description);
|
||||
fc::raw::pack(s, betting_market_group_obj.event_id);
|
||||
fc::raw::pack(s, betting_market_group_obj.rules_id);
|
||||
fc::raw::pack(s, betting_market_group_obj.asset_id);
|
||||
fc::raw::pack(s, betting_market_group_obj.total_matched_bets_amount);
|
||||
fc::raw::pack(s, betting_market_group_obj.never_in_play);
|
||||
fc::raw::pack(s, betting_market_group_obj.delay_before_settling);
|
||||
fc::raw::pack(s, betting_market_group_obj.settling_time);
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
betting_market_group_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, betting_market_group_object& betting_market_group_obj )
|
||||
{
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<betting_market_group_object> >(s, betting_market_group_obj);
|
||||
fc::raw::unpack(s, betting_market_group_obj.id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.description);
|
||||
fc::raw::unpack(s, betting_market_group_obj.event_id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.rules_id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.asset_id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.total_matched_bets_amount);
|
||||
fc::raw::unpack(s, betting_market_group_obj.never_in_play);
|
||||
fc::raw::unpack(s, betting_market_group_obj.delay_before_settling);
|
||||
fc::raw::unpack(s, betting_market_group_obj.settling_time);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
betting_market_group_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_rules_object, (graphene::db::object), (name)(description) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_group_object, (graphene::db::object), (description)(event_id)(rules_id)(asset_id)(frozen)(delay_bets)(total_matched_bets_amount) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_object, (graphene::db::object), (group_id)(description)(payout_condition) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_group_object, (graphene::db::object), (description) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_object, (graphene::db::object), (group_id) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::bet_object, (graphene::db::object), (bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(back_or_lay)(end_of_delay) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_position_object, (graphene::db::object), (bettor_id)(betting_market_id)(pay_if_payout_condition)(pay_if_not_payout_condition)(pay_if_canceled)(pay_if_not_canceled)(fees_collected) )
|
||||
|
|
|
|||
|
|
@ -391,12 +391,11 @@ namespace graphene { namespace chain {
|
|||
void cancel_bet(const bet_object& bet, bool create_virtual_op = true);
|
||||
void cancel_all_unmatched_bets_on_betting_market(const betting_market_object& betting_market);
|
||||
void cancel_all_unmatched_bets_on_betting_market_group(const betting_market_group_object& betting_market_group);
|
||||
void cancel_all_betting_markets_for_event(const event_object&);
|
||||
void validate_betting_market_group_resolutions(const betting_market_group_object& betting_market_group,
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions);
|
||||
void resolve_betting_market_group(const betting_market_group_object& betting_market_group,
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions,
|
||||
bool do_not_remove = false);
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions);
|
||||
void settle_betting_market_group(const betting_market_group_object& betting_market_group);
|
||||
/**
|
||||
* @brief Process a new bet
|
||||
* @param new_bet_object The new bet to process
|
||||
|
|
@ -481,6 +480,7 @@ namespace graphene { namespace chain {
|
|||
void update_maintenance_flag( bool new_maintenance_flag );
|
||||
void update_withdraw_permissions();
|
||||
void update_tournaments();
|
||||
void update_betting_markets(fc::time_point_sec current_block_time);
|
||||
bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true );
|
||||
|
||||
///Steps performed only at maintenance intervals
|
||||
|
|
|
|||
|
|
@ -26,6 +26,16 @@
|
|||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/protocol/event.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class event_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::event_object& event_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::event_object& event_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -37,6 +47,11 @@ class event_object : public graphene::db::abstract_object< event_object >
|
|||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = event_object_type;
|
||||
|
||||
event_object();
|
||||
event_object(const event_object& rhs);
|
||||
~event_object();
|
||||
event_object& operator=(const event_object& rhs);
|
||||
|
||||
internationalized_string_type name;
|
||||
|
||||
internationalized_string_type season;
|
||||
|
|
@ -45,10 +60,38 @@ class event_object : public graphene::db::abstract_object< event_object >
|
|||
|
||||
event_group_id_type event_group_id;
|
||||
|
||||
bool is_live_market;
|
||||
bool at_least_one_betting_market_group_settled;
|
||||
|
||||
event_status status;
|
||||
event_status get_status() const;
|
||||
vector<string> scores;
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const event_object& event_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, event_object& event_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::event_object& event_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::event_object& event_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
void on_upcoming_event(database& db);
|
||||
void on_in_progress_event(database& db);
|
||||
void on_frozen_event(database& db);
|
||||
void on_finished_event(database& db);
|
||||
void on_canceled_event(database& db);
|
||||
void on_settled_event(database& db);
|
||||
void on_betting_market_group_resolved(database& db, betting_market_group_id_type resolved_group, bool was_canceled);
|
||||
void on_betting_market_group_closed(database& db, betting_market_group_id_type closed_group);
|
||||
void dispatch_new_status(database& db, event_status new_status);
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
struct by_event_group_id;
|
||||
|
|
@ -59,6 +102,52 @@ typedef multi_index_container<
|
|||
ordered_non_unique< tag<by_event_group_id>, member< event_object, event_group_id_type, &event_object::event_group_id > > > > event_object_multi_index_type;
|
||||
|
||||
typedef generic_index<event_object, event_object_multi_index_type> event_object_index;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::event_object, (graphene::db::object), (name)(season)(start_time)(event_group_id)(is_live_market)(status)(scores) )
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const event_object& event_obj )
|
||||
{
|
||||
fc_elog(fc::logger::get("event"), "In event_obj to_raw");
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<event_object> >(s, event_obj);
|
||||
fc::raw::pack(s, event_obj.id);
|
||||
fc::raw::pack(s, event_obj.name);
|
||||
fc::raw::pack(s, event_obj.season);
|
||||
fc::raw::pack(s, event_obj.start_time);
|
||||
fc::raw::pack(s, event_obj.event_group_id);
|
||||
fc::raw::pack(s, event_obj.at_least_one_betting_market_group_settled);
|
||||
fc::raw::pack(s, event_obj.scores);
|
||||
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
event_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, event_object& event_obj )
|
||||
{
|
||||
fc_elog(fc::logger::get("event"), "In event_obj from_raw");
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<event_object> >(s, event_obj);
|
||||
fc::raw::unpack(s, event_obj.id);
|
||||
fc::raw::unpack(s, event_obj.name);
|
||||
fc::raw::unpack(s, event_obj.season);
|
||||
fc::raw::unpack(s, event_obj.start_time);
|
||||
fc::raw::unpack(s, event_obj.event_group_id);
|
||||
fc::raw::unpack(s, event_obj.at_least_one_betting_market_group_settled);
|
||||
fc::raw::unpack(s, event_obj.scores);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
event_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
} } // graphene::chain
|
||||
FC_REFLECT(graphene::chain::event_object, (name))
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,35 @@ struct betting_market_rules_update_operation : public base_operation
|
|||
void validate()const;
|
||||
};
|
||||
|
||||
enum class betting_market_status
|
||||
{
|
||||
unresolved, /// no grading has been published for this betting market
|
||||
frozen, /// bets are suspended, no bets allowed
|
||||
graded, /// grading of win or not_win has been published
|
||||
canceled, /// the betting market is canceled, no further bets are allowed
|
||||
settled, /// the betting market has been paid out
|
||||
BETTING_MARKET_STATUS_COUNT
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The status of a betting market group. This controls the behavior of the betting
|
||||
* markets in the group.
|
||||
*/
|
||||
enum class betting_market_group_status
|
||||
{
|
||||
upcoming, /// betting markets are accepting bets, will never go "in_play"
|
||||
in_play, /// betting markets are delaying bets
|
||||
closed, /// betting markets are no longer accepting bets
|
||||
graded, /// witnesses have published win/not win for the betting markets
|
||||
re_grading, /// initial win/not win grading has been challenged
|
||||
settled, /// paid out
|
||||
frozen, /// betting markets are not accepting bets
|
||||
canceled, /// canceled
|
||||
BETTING_MARKET_GROUP_STATUS_COUNT
|
||||
};
|
||||
|
||||
|
||||
struct betting_market_group_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
|
|
@ -97,6 +126,20 @@ struct betting_market_group_create_operation : public base_operation
|
|||
*/
|
||||
asset_id_type asset_id;
|
||||
|
||||
/**
|
||||
* If true, this market will never go "in-play"
|
||||
*/
|
||||
bool never_in_play;
|
||||
|
||||
/**
|
||||
* After a grading has been published, the blockchain will wait this many
|
||||
* seconds before settling (paying the winners).
|
||||
* If the published grading is flagged (challenged) during this period,
|
||||
* settling will be delayed indefinitely until the betting market
|
||||
* group is re-graded (not implemented)
|
||||
*/
|
||||
uint32_t delay_before_settling;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
|
|
@ -114,9 +157,7 @@ struct betting_market_group_update_operation : public base_operation
|
|||
|
||||
optional<object_id_type> new_rules_id;
|
||||
|
||||
optional<bool> freeze;
|
||||
|
||||
optional<bool> delay_bets;
|
||||
optional<betting_market_group_status> status;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
|
|
@ -384,13 +425,33 @@ FC_REFLECT( graphene::chain::betting_market_rules_update_operation::fee_paramete
|
|||
FC_REFLECT( graphene::chain::betting_market_rules_update_operation,
|
||||
(fee)(new_name)(new_description)(extensions)(betting_market_rules_id) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::betting_market_status,
|
||||
(unresolved)
|
||||
(frozen)
|
||||
(graded)
|
||||
(canceled)
|
||||
(settled)
|
||||
(BETTING_MARKET_STATUS_COUNT) )
|
||||
FC_REFLECT_ENUM( graphene::chain::betting_market_group_status,
|
||||
(upcoming)
|
||||
(in_play)
|
||||
(closed)
|
||||
(graded)
|
||||
(re_grading)
|
||||
(settled)
|
||||
(frozen)
|
||||
(canceled)
|
||||
(BETTING_MARKET_GROUP_STATUS_COUNT) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_create_operation,
|
||||
(fee)(description)(event_id)(rules_id)(asset_id)(extensions) )
|
||||
(fee)(description)(event_id)(rules_id)(asset_id)
|
||||
(never_in_play)(delay_before_settling)
|
||||
(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_update_operation,
|
||||
(fee)(betting_market_group_id)(new_description)(new_rules_id)(freeze)(delay_bets)(extensions) )
|
||||
(fee)(betting_market_group_id)(new_description)(new_rules_id)(status)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_create_operation,
|
||||
|
|
|
|||
|
|
@ -54,6 +54,26 @@ struct event_create_operation : public base_operation
|
|||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* The status of an event. This is used to display in the UI, and setting
|
||||
* the event's status to certain values will propagate down to the
|
||||
* betting market groups in the event:
|
||||
* - when set to `in_progress`, all betting market groups are set to `in_play`
|
||||
* - when set to `completed`, all betting market groups are set to `closed`
|
||||
* - when set to `frozen`, all betting market groups are set to `frozen`
|
||||
* - when set to `canceled`, all betting market groups are set to `canceled`
|
||||
*/
|
||||
enum class event_status
|
||||
{
|
||||
upcoming, /// Event has not started yet, betting is allowed
|
||||
in_progress, /// Event is in progress, if "in-play" betting is enabled, bets will be delayed
|
||||
frozen, /// Betting is temporarily disabled
|
||||
finished, /// Event has finished, no more betting allowed
|
||||
canceled, /// Event has been canceled, all betting markets have been canceled
|
||||
settled, /// All betting markets have been paid out
|
||||
STATUS_COUNT
|
||||
};
|
||||
|
||||
struct event_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
|
|
@ -69,7 +89,7 @@ struct event_update_operation : public base_operation
|
|||
|
||||
optional<time_point_sec> new_start_time;
|
||||
|
||||
optional<bool> is_live_market;
|
||||
optional<event_status> new_status;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
|
|
@ -77,20 +97,6 @@ struct event_update_operation : public base_operation
|
|||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* The status of an event; this is only used for display, the blockchain does
|
||||
* not care about the event's status
|
||||
*/
|
||||
enum class event_status
|
||||
{
|
||||
upcoming,
|
||||
in_progress,
|
||||
frozen,
|
||||
completed,
|
||||
canceled,
|
||||
STATUS_COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
* The current (or final) score of an event.
|
||||
* This is only used for display to the user, witnesses must resolve each
|
||||
|
|
@ -132,9 +138,9 @@ FC_REFLECT( graphene::chain::event_create_operation,
|
|||
|
||||
FC_REFLECT( graphene::chain::event_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_update_operation,
|
||||
(fee)(event_id)(new_event_group_id)(new_name)(new_season)(new_start_time)(is_live_market)(extensions) )
|
||||
(fee)(event_id)(new_event_group_id)(new_name)(new_season)(new_start_time)(new_status)(extensions) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::event_status, (upcoming)(in_progress)(frozen)(completed)(canceled)(STATUS_COUNT) )
|
||||
FC_REFLECT_ENUM( graphene::chain::event_status, (upcoming)(in_progress)(frozen)(finished)(canceled)(settled)(STATUS_COUNT) )
|
||||
FC_REFLECT( graphene::chain::event_update_status_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_update_status_operation,
|
||||
(fee)(event_id)(status)(scores)(extensions) )
|
||||
|
|
|
|||
|
|
@ -96,6 +96,111 @@ void persistent_bet_object_helper::object_modified(const object& after)
|
|||
}
|
||||
|
||||
//////////// end bet_object ///////////////////
|
||||
class persistent_betting_market_object_helper : public secondary_index
|
||||
{
|
||||
public:
|
||||
virtual ~persistent_betting_market_object_helper() {}
|
||||
|
||||
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;
|
||||
void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; }
|
||||
private:
|
||||
bookie_plugin* _bookie_plugin;
|
||||
};
|
||||
|
||||
void persistent_betting_market_object_helper::object_inserted(const object& obj)
|
||||
{
|
||||
const betting_market_object& betting_market_obj = *boost::polymorphic_downcast<const betting_market_object*>(&obj);
|
||||
_bookie_plugin->database().create<persistent_betting_market_object>([&](persistent_betting_market_object& saved_betting_market_obj) {
|
||||
saved_betting_market_obj.ephemeral_betting_market_object = betting_market_obj;
|
||||
});
|
||||
}
|
||||
void persistent_betting_market_object_helper::object_modified(const object& after)
|
||||
{
|
||||
database& db = _bookie_plugin->database();
|
||||
auto& persistent_betting_markets_by_betting_market_id = db.get_index_type<persistent_betting_market_index>().indices().get<by_betting_market_id>();
|
||||
const betting_market_object& betting_market_obj = *boost::polymorphic_downcast<const betting_market_object*>(&after);
|
||||
auto iter = persistent_betting_markets_by_betting_market_id.find(betting_market_obj.id);
|
||||
assert (iter != persistent_betting_markets_by_betting_market_id.end());
|
||||
if (iter != persistent_betting_markets_by_betting_market_id.end())
|
||||
db.modify(*iter, [&](persistent_betting_market_object& saved_betting_market_obj) {
|
||||
saved_betting_market_obj.ephemeral_betting_market_object = betting_market_obj;
|
||||
});
|
||||
}
|
||||
|
||||
//////////// end betting_market_object ///////////////////
|
||||
class persistent_betting_market_group_object_helper : public secondary_index
|
||||
{
|
||||
public:
|
||||
virtual ~persistent_betting_market_group_object_helper() {}
|
||||
|
||||
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;
|
||||
void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; }
|
||||
private:
|
||||
bookie_plugin* _bookie_plugin;
|
||||
};
|
||||
|
||||
void persistent_betting_market_group_object_helper::object_inserted(const object& obj)
|
||||
{
|
||||
const betting_market_group_object& betting_market_group_obj = *boost::polymorphic_downcast<const betting_market_group_object*>(&obj);
|
||||
_bookie_plugin->database().create<persistent_betting_market_group_object>([&](persistent_betting_market_group_object& saved_betting_market_group_obj) {
|
||||
saved_betting_market_group_obj.ephemeral_betting_market_group_object = betting_market_group_obj;
|
||||
});
|
||||
}
|
||||
void persistent_betting_market_group_object_helper::object_modified(const object& after)
|
||||
{
|
||||
database& db = _bookie_plugin->database();
|
||||
auto& persistent_betting_market_groups_by_betting_market_group_id = db.get_index_type<persistent_betting_market_group_index>().indices().get<by_betting_market_group_id>();
|
||||
const betting_market_group_object& betting_market_group_obj = *boost::polymorphic_downcast<const betting_market_group_object*>(&after);
|
||||
auto iter = persistent_betting_market_groups_by_betting_market_group_id.find(betting_market_group_obj.id);
|
||||
assert (iter != persistent_betting_market_groups_by_betting_market_group_id.end());
|
||||
if (iter != persistent_betting_market_groups_by_betting_market_group_id.end())
|
||||
db.modify(*iter, [&](persistent_betting_market_group_object& saved_betting_market_group_obj) {
|
||||
saved_betting_market_group_obj.ephemeral_betting_market_group_object = betting_market_group_obj;
|
||||
});
|
||||
}
|
||||
|
||||
//////////// end betting_market_group_object ///////////////////
|
||||
class persistent_event_object_helper : public secondary_index
|
||||
{
|
||||
public:
|
||||
virtual ~persistent_event_object_helper() {}
|
||||
|
||||
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;
|
||||
void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; }
|
||||
private:
|
||||
bookie_plugin* _bookie_plugin;
|
||||
};
|
||||
|
||||
void persistent_event_object_helper::object_inserted(const object& obj)
|
||||
{
|
||||
const event_object& event_obj = *boost::polymorphic_downcast<const event_object*>(&obj);
|
||||
_bookie_plugin->database().create<persistent_event_object>([&](persistent_event_object& saved_event_obj) {
|
||||
saved_event_obj.ephemeral_event_object = event_obj;
|
||||
});
|
||||
}
|
||||
void persistent_event_object_helper::object_modified(const object& after)
|
||||
{
|
||||
database& db = _bookie_plugin->database();
|
||||
auto& persistent_events_by_event_id = db.get_index_type<persistent_event_index>().indices().get<by_event_id>();
|
||||
const event_object& event_obj = *boost::polymorphic_downcast<const event_object*>(&after);
|
||||
auto iter = persistent_events_by_event_id.find(event_obj.id);
|
||||
assert (iter != persistent_events_by_event_id.end());
|
||||
if (iter != persistent_events_by_event_id.end())
|
||||
db.modify(*iter, [&](persistent_event_object& saved_event_obj) {
|
||||
saved_event_obj.ephemeral_event_object = event_obj;
|
||||
});
|
||||
}
|
||||
|
||||
//////////// end event_object ///////////////////
|
||||
class bookie_plugin_impl
|
||||
{
|
||||
public:
|
||||
|
|
@ -154,122 +259,14 @@ bookie_plugin_impl::~bookie_plugin_impl()
|
|||
|
||||
void bookie_plugin_impl::on_objects_new(const vector<object_id_type>& new_object_ids)
|
||||
{
|
||||
//idump((new_object_ids));
|
||||
graphene::chain::database& db = database();
|
||||
auto& event_id_index = db.get_index_type<persistent_event_index>().indices().get<by_event_id>();
|
||||
auto& betting_market_group_id_index = db.get_index_type<persistent_betting_market_group_index>().indices().get<by_betting_market_group_id>();
|
||||
auto& betting_market_id_index = db.get_index_type<persistent_betting_market_index>().indices().get<by_betting_market_id>();
|
||||
|
||||
for (const object_id_type& new_object_id : new_object_ids)
|
||||
{
|
||||
if (new_object_id.space() == event_id_type::space_id &&
|
||||
new_object_id.type() == event_id_type::type_id)
|
||||
{
|
||||
event_id_type new_event_id = new_object_id;
|
||||
ilog("Creating new persistent event object ${id}", ("id", new_event_id));
|
||||
db.create<persistent_event_object>([&](persistent_event_object& saved_event_obj) {
|
||||
saved_event_obj.ephemeral_event_object = new_event_id(db);
|
||||
});
|
||||
}
|
||||
else if (new_object_id.space() == betting_market_group_object::space_id &&
|
||||
new_object_id.type() == betting_market_group_object::type_id)
|
||||
{
|
||||
betting_market_group_id_type new_betting_market_group_id = new_object_id;
|
||||
ilog("Creating new persistent betting_market_group object ${id}", ("id", new_betting_market_group_id));
|
||||
db.create<persistent_betting_market_group_object>([&](persistent_betting_market_group_object& saved_betting_market_group_obj) {
|
||||
saved_betting_market_group_obj.ephemeral_betting_market_group_object = new_betting_market_group_id(db);
|
||||
});
|
||||
}
|
||||
else if (new_object_id.space() == betting_market_object::space_id &&
|
||||
new_object_id.type() == betting_market_object::type_id)
|
||||
{
|
||||
betting_market_id_type new_betting_market_id = new_object_id;
|
||||
ilog("Creating new persistent betting_market object ${id}", ("id", new_betting_market_id));
|
||||
db.create<persistent_betting_market_object>([&](persistent_betting_market_object& saved_betting_market_obj) {
|
||||
saved_betting_market_obj.ephemeral_betting_market_object = new_betting_market_id(db);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bookie_plugin_impl::on_objects_removed(const vector<object_id_type>& removed_object_ids)
|
||||
{
|
||||
//idump((removed_object_ids));
|
||||
}
|
||||
|
||||
void bookie_plugin_impl::on_objects_changed(const vector<object_id_type>& changed_object_ids)
|
||||
{
|
||||
//idump((changed_object_ids));
|
||||
graphene::chain::database& db = database();
|
||||
auto& event_id_index = db.get_index_type<persistent_event_index>().indices().get<by_event_id>();
|
||||
auto& betting_market_group_id_index = db.get_index_type<persistent_betting_market_group_index>().indices().get<by_betting_market_group_id>();
|
||||
auto& betting_market_id_index = db.get_index_type<persistent_betting_market_index>().indices().get<by_betting_market_id>();
|
||||
|
||||
for (const object_id_type& changed_object_id : changed_object_ids)
|
||||
{
|
||||
if (changed_object_id.space() == event_id_type::space_id &&
|
||||
changed_object_id.type() == event_id_type::type_id)
|
||||
{
|
||||
event_id_type changed_event_id = changed_object_id;
|
||||
const persistent_event_object* old_event_obj = nullptr;
|
||||
|
||||
auto persistent_event_iter = event_id_index.find(changed_event_id);
|
||||
if (persistent_event_iter != event_id_index.end())
|
||||
old_event_obj = &*persistent_event_iter;
|
||||
|
||||
if (old_event_obj)
|
||||
{
|
||||
ilog("Modifying persistent event object ${id}", ("id", changed_event_id));
|
||||
db.modify(*old_event_obj, [&](persistent_event_object& saved_event_obj) {
|
||||
saved_event_obj.ephemeral_event_object = changed_event_id(db);
|
||||
});
|
||||
}
|
||||
else
|
||||
elog("Received change notification on event ${event_id} that we didn't know about", ("event_id", changed_event_id));
|
||||
}
|
||||
else if (changed_object_id.space() == betting_market_group_object::space_id &&
|
||||
changed_object_id.type() == betting_market_group_object::type_id)
|
||||
{
|
||||
betting_market_group_id_type changed_betting_market_group_id = changed_object_id;
|
||||
const persistent_betting_market_group_object* old_betting_market_group_obj = nullptr;
|
||||
|
||||
auto persistent_betting_market_group_iter = betting_market_group_id_index.find(changed_betting_market_group_id);
|
||||
if (persistent_betting_market_group_iter != betting_market_group_id_index.end())
|
||||
old_betting_market_group_obj = &*persistent_betting_market_group_iter;
|
||||
|
||||
if (old_betting_market_group_obj)
|
||||
{
|
||||
ilog("Modifying persistent betting_market_group object ${id}", ("id", changed_betting_market_group_id));
|
||||
db.modify(*old_betting_market_group_obj, [&](persistent_betting_market_group_object& saved_betting_market_group_obj) {
|
||||
saved_betting_market_group_obj.ephemeral_betting_market_group_object = changed_betting_market_group_id(db);
|
||||
});
|
||||
}
|
||||
else
|
||||
elog("Received change notification on betting market group ${betting_market_group_id} that we didn't know about",
|
||||
("betting_market_group_id", changed_betting_market_group_id));
|
||||
}
|
||||
else if (changed_object_id.space() == betting_market_object::space_id &&
|
||||
changed_object_id.type() == betting_market_object::type_id)
|
||||
{
|
||||
betting_market_id_type changed_betting_market_id = changed_object_id;
|
||||
const persistent_betting_market_object* old_betting_market_obj = nullptr;
|
||||
|
||||
auto persistent_betting_market_iter = betting_market_id_index.find(changed_betting_market_id);
|
||||
if (persistent_betting_market_iter != betting_market_id_index.end())
|
||||
old_betting_market_obj = &*persistent_betting_market_iter;
|
||||
|
||||
if (old_betting_market_obj)
|
||||
{
|
||||
ilog("Modifying persistent betting_market object ${id}", ("id", changed_betting_market_id));
|
||||
db.modify(*old_betting_market_obj, [&](persistent_betting_market_object& saved_betting_market_obj) {
|
||||
saved_betting_market_obj.ephemeral_betting_market_object = changed_betting_market_id(db);
|
||||
});
|
||||
}
|
||||
else
|
||||
elog("Received change notification on betting market ${betting_market_id} that we didn't know about",
|
||||
("betting_market_id", changed_betting_market_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_operation_history_object_stored(operation_history_id_type id)
|
||||
|
|
@ -287,7 +284,8 @@ bool is_operation_history_object_stored(operation_history_id_type id)
|
|||
}
|
||||
|
||||
void bookie_plugin_impl::on_block_applied( const signed_block& )
|
||||
{
|
||||
{ try {
|
||||
|
||||
graphene::chain::database& db = database();
|
||||
const vector<optional<operation_history_object> >& hist = db.get_applied_operations();
|
||||
for( const optional<operation_history_object>& o_op : hist )
|
||||
|
|
@ -315,11 +313,28 @@ void bookie_plugin_impl::on_block_applied( const signed_block& )
|
|||
});
|
||||
const bet_object& bet_obj = bet_iter->ephemeral_bet_object;
|
||||
|
||||
const betting_market_object& betting_market = bet_obj.betting_market_id(db); // TODO: this needs to look at the persistent version
|
||||
const betting_market_group_object& betting_market_group = betting_market.group_id(db); // TODO: as does this
|
||||
db.modify( betting_market_group, [&]( betting_market_group_object& obj ){
|
||||
obj.total_matched_bets_amount += amount_bet.amount;
|
||||
});
|
||||
auto& persistent_betting_market_idx = db.get_index_type<persistent_betting_market_index>().indices().get<by_betting_market_id>();
|
||||
auto persistent_betting_market_object_iter = persistent_betting_market_idx.find(bet_obj.betting_market_id);
|
||||
FC_ASSERT(persistent_betting_market_object_iter != persistent_betting_market_idx.end());
|
||||
const betting_market_object& betting_market = persistent_betting_market_object_iter->ephemeral_betting_market_object;
|
||||
|
||||
auto& persistent_betting_market_group_idx = db.get_index_type<persistent_betting_market_group_index>().indices().get<by_betting_market_group_id>();
|
||||
auto persistent_betting_market_group_object_iter = persistent_betting_market_group_idx.find(betting_market.group_id);
|
||||
FC_ASSERT(persistent_betting_market_group_object_iter != persistent_betting_market_group_idx.end());
|
||||
const betting_market_group_object& betting_market_group = persistent_betting_market_group_object_iter->ephemeral_betting_market_group_object;
|
||||
|
||||
// if the object is still in the main database, keep the running total there
|
||||
// otherwise, add it directly to the persistent version
|
||||
auto& betting_market_group_idx = db.get_index_type<betting_market_group_object_index>().indices().get<by_id>();
|
||||
auto betting_market_group_iter = betting_market_group_idx.find(betting_market_group.id);
|
||||
if (betting_market_group_iter != betting_market_group_idx.end())
|
||||
db.modify( *betting_market_group_iter, [&]( betting_market_group_object& obj ){
|
||||
obj.total_matched_bets_amount += amount_bet.amount;
|
||||
});
|
||||
else
|
||||
db.modify( *persistent_betting_market_group_object_iter, [&]( persistent_betting_market_group_object& obj ){
|
||||
obj.ephemeral_betting_market_group_object.total_matched_bets_amount += amount_bet.amount;
|
||||
});
|
||||
}
|
||||
}
|
||||
else if( op.op.which() == operation::tag<event_create_operation>::value )
|
||||
|
|
@ -382,7 +397,7 @@ void bookie_plugin_impl::on_block_applied( const signed_block& )
|
|||
}
|
||||
|
||||
}
|
||||
}
|
||||
} FC_RETHROW_EXCEPTIONS( warn, "" ) }
|
||||
|
||||
void bookie_plugin_impl::fill_localized_event_strings()
|
||||
{
|
||||
|
|
@ -470,6 +485,21 @@ void bookie_plugin::plugin_initialize(const boost::program_options::variables_ma
|
|||
detail::persistent_bet_object_helper* persistent_bet_object_helper_index = nonconst_bet_object_idx.add_secondary_index<detail::persistent_bet_object_helper>();
|
||||
persistent_bet_object_helper_index->set_plugin_instance(this);
|
||||
|
||||
const primary_index<betting_market_object_index>& betting_market_object_idx = database().get_index_type<primary_index<betting_market_object_index> >();
|
||||
primary_index<betting_market_object_index>& nonconst_betting_market_object_idx = const_cast<primary_index<betting_market_object_index>&>(betting_market_object_idx);
|
||||
detail::persistent_betting_market_object_helper* persistent_betting_market_object_helper_index = nonconst_betting_market_object_idx.add_secondary_index<detail::persistent_betting_market_object_helper>();
|
||||
persistent_betting_market_object_helper_index->set_plugin_instance(this);
|
||||
|
||||
const primary_index<betting_market_group_object_index>& betting_market_group_object_idx = database().get_index_type<primary_index<betting_market_group_object_index> >();
|
||||
primary_index<betting_market_group_object_index>& nonconst_betting_market_group_object_idx = const_cast<primary_index<betting_market_group_object_index>&>(betting_market_group_object_idx);
|
||||
detail::persistent_betting_market_group_object_helper* persistent_betting_market_group_object_helper_index = nonconst_betting_market_group_object_idx.add_secondary_index<detail::persistent_betting_market_group_object_helper>();
|
||||
persistent_betting_market_group_object_helper_index->set_plugin_instance(this);
|
||||
|
||||
const primary_index<event_object_index>& event_object_idx = database().get_index_type<primary_index<event_object_index> >();
|
||||
primary_index<event_object_index>& nonconst_event_object_idx = const_cast<primary_index<event_object_index>&>(event_object_idx);
|
||||
detail::persistent_event_object_helper* persistent_event_object_helper_index = nonconst_event_object_idx.add_secondary_index<detail::persistent_event_object_helper>();
|
||||
persistent_event_object_helper_index->set_plugin_instance(this);
|
||||
|
||||
ilog("bookie plugin: plugin_startup() end");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1637,7 +1637,7 @@ class wallet_api
|
|||
fc::optional<object_id_type> event_group_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
fc::optional<internationalized_string_type> season,
|
||||
fc::optional<bool> is_live_market,
|
||||
fc::optional<event_status> status,
|
||||
fc::optional<time_point_sec> start_time,
|
||||
bool broadcast = false);
|
||||
|
||||
|
|
@ -1671,7 +1671,7 @@ class wallet_api
|
|||
betting_market_group_id_type betting_market_group_id,
|
||||
fc::optional<internationalized_string_type> description,
|
||||
fc::optional<object_id_type> rules_id,
|
||||
fc::optional<bool> freeze,
|
||||
fc::optional<betting_market_group_status> status,
|
||||
bool broadcast = false);
|
||||
|
||||
signed_transaction propose_create_betting_market(
|
||||
|
|
|
|||
|
|
@ -5238,7 +5238,7 @@ signed_transaction wallet_api::propose_update_event(
|
|||
fc::optional<object_id_type> event_group_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
fc::optional<internationalized_string_type> season,
|
||||
fc::optional<bool> is_live_market,
|
||||
fc::optional<event_status> status,
|
||||
fc::optional<time_point_sec> start_time,
|
||||
bool broadcast /*= false*/)
|
||||
{
|
||||
|
|
@ -5251,7 +5251,7 @@ signed_transaction wallet_api::propose_update_event(
|
|||
event_update_op.new_start_time = start_time;
|
||||
event_update_op.new_name = name;
|
||||
event_update_op.new_season = season;
|
||||
event_update_op.is_live_market = is_live_market;
|
||||
event_update_op.new_status = status;
|
||||
|
||||
proposal_create_operation prop_op;
|
||||
prop_op.expiration_time = expiration_time;
|
||||
|
|
@ -5367,7 +5367,7 @@ signed_transaction wallet_api::propose_update_betting_market_group(
|
|||
betting_market_group_id_type betting_market_group_id,
|
||||
fc::optional<internationalized_string_type> description,
|
||||
fc::optional<object_id_type> rules_id,
|
||||
fc::optional<bool> freeze,
|
||||
fc::optional<betting_market_group_status> status,
|
||||
bool broadcast /*= false*/)
|
||||
{
|
||||
FC_ASSERT( !is_locked() );
|
||||
|
|
@ -5377,7 +5377,7 @@ signed_transaction wallet_api::propose_update_betting_market_group(
|
|||
betting_market_group_update_op.betting_market_group_id = betting_market_group_id;
|
||||
betting_market_group_update_op.new_description = description;
|
||||
betting_market_group_update_op.new_rules_id = rules_id;
|
||||
betting_market_group_update_op.freeze = freeze;
|
||||
betting_market_group_update_op.status = status;
|
||||
|
||||
proposal_create_operation prop_op;
|
||||
prop_op.expiration_time = expiration_time;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1150,6 +1150,19 @@ void database_fixture::process_operation_by_witnesses(operation op)
|
|||
}
|
||||
}
|
||||
|
||||
void database_fixture::force_operation_by_witnesses(operation op)
|
||||
{
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
signed_transaction trx;
|
||||
trx.operations = {op};
|
||||
for( auto& op : trx.operations )
|
||||
db.current_fee_schedule().set_fee(op);
|
||||
trx.validate();
|
||||
trx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
sign(trx, init_account_priv_key);
|
||||
PUSH_TX(db, trx);
|
||||
}
|
||||
|
||||
void database_fixture::set_is_proposed_trx(operation op)
|
||||
{
|
||||
const flat_set<witness_id_type>& active_witnesses = db.get_global_properties().active_witnesses;
|
||||
|
|
@ -1306,20 +1319,25 @@ const event_object& database_fixture::create_event(internationalized_string_type
|
|||
return *event_index.rbegin();
|
||||
} FC_CAPTURE_AND_RETHROW( (event_group_id) ) }
|
||||
|
||||
void database_fixture::update_event(event_id_type event_id,
|
||||
fc::optional<object_id_type> event_group_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
fc::optional<internationalized_string_type> season,
|
||||
fc::optional<bool> is_live_market)
|
||||
void database_fixture::update_event_impl(event_id_type event_id,
|
||||
fc::optional<object_id_type> event_group_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
fc::optional<internationalized_string_type> season,
|
||||
fc::optional<event_status> status,
|
||||
bool force)
|
||||
{ try {
|
||||
event_update_operation event_update_op;
|
||||
event_update_op.event_id = event_id;
|
||||
event_update_op.new_event_group_id = event_group_id;
|
||||
event_update_op.new_name = name;
|
||||
event_update_op.new_season = season;
|
||||
event_update_op.is_live_market = is_live_market;
|
||||
process_operation_by_witnesses(event_update_op);
|
||||
} FC_CAPTURE_AND_RETHROW( (name)(season) ) }
|
||||
event_update_op.new_status = status;
|
||||
|
||||
if (force)
|
||||
force_operation_by_witnesses(event_update_op);
|
||||
else
|
||||
process_operation_by_witnesses(event_update_op);
|
||||
} FC_CAPTURE_AND_RETHROW( (event_id) ) }
|
||||
|
||||
const betting_market_rules_object& database_fixture::create_betting_market_rules(internationalized_string_type name, internationalized_string_type description)
|
||||
{ try {
|
||||
|
|
@ -1342,33 +1360,44 @@ void database_fixture::update_betting_market_rules(betting_market_rules_id_type
|
|||
process_operation_by_witnesses(betting_market_rules_update_op);
|
||||
} FC_CAPTURE_AND_RETHROW( (name)(description) ) }
|
||||
|
||||
const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description, event_id_type event_id, betting_market_rules_id_type rules_id, asset_id_type asset_id)
|
||||
const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description,
|
||||
event_id_type event_id,
|
||||
betting_market_rules_id_type rules_id,
|
||||
asset_id_type asset_id,
|
||||
bool never_in_play,
|
||||
uint32_t delay_before_settling)
|
||||
{ try {
|
||||
betting_market_group_create_operation betting_market_group_create_op;
|
||||
betting_market_group_create_op.description = description;
|
||||
betting_market_group_create_op.event_id = event_id;
|
||||
betting_market_group_create_op.rules_id = rules_id;
|
||||
betting_market_group_create_op.asset_id = asset_id;
|
||||
betting_market_group_create_op.never_in_play = never_in_play;
|
||||
betting_market_group_create_op.delay_before_settling = delay_before_settling;
|
||||
|
||||
process_operation_by_witnesses(betting_market_group_create_op);
|
||||
const auto& betting_market_group_index = db.get_index_type<betting_market_group_object_index>().indices().get<by_id>();
|
||||
return *betting_market_group_index.rbegin();
|
||||
} FC_CAPTURE_AND_RETHROW( (event_id) ) }
|
||||
|
||||
|
||||
void database_fixture::update_betting_market_group(betting_market_group_id_type betting_market_group_id,
|
||||
fc::optional<internationalized_string_type> description,
|
||||
fc::optional<object_id_type> rules_id,
|
||||
fc::optional<bool> freeze,
|
||||
fc::optional<bool> delay_bets)
|
||||
void database_fixture::update_betting_market_group_impl(betting_market_group_id_type betting_market_group_id,
|
||||
fc::optional<internationalized_string_type> description,
|
||||
fc::optional<object_id_type> rules_id,
|
||||
fc::optional<betting_market_group_status> status,
|
||||
bool force)
|
||||
{ try {
|
||||
betting_market_group_update_operation betting_market_group_update_op;
|
||||
betting_market_group_update_op.betting_market_group_id = betting_market_group_id;
|
||||
betting_market_group_update_op.new_description = description;
|
||||
betting_market_group_update_op.new_rules_id = rules_id;
|
||||
betting_market_group_update_op.freeze = freeze;
|
||||
betting_market_group_update_op.delay_bets = delay_bets;
|
||||
process_operation_by_witnesses(betting_market_group_update_op);
|
||||
} FC_CAPTURE_AND_RETHROW( (betting_market_group_id)(description)(rules_id)(freeze)(delay_bets)) }
|
||||
betting_market_group_update_op.status = status;
|
||||
|
||||
if (force)
|
||||
force_operation_by_witnesses(betting_market_group_update_op);
|
||||
else
|
||||
process_operation_by_witnesses(betting_market_group_update_op);
|
||||
} FC_CAPTURE_AND_RETHROW( (betting_market_group_id)(description)(rules_id)(status)) }
|
||||
|
||||
|
||||
const betting_market_object& database_fixture::create_betting_market(betting_market_group_id_type group_id, internationalized_string_type payout_condition)
|
||||
|
|
@ -1412,6 +1441,7 @@ void database_fixture::update_betting_market(betting_market_id_type betting_mark
|
|||
return ptx.operation_results.front().get<object_id_type>().as<bet_id_type>();
|
||||
} FC_CAPTURE_AND_RETHROW( (bettor_id)(back_or_lay)(amount_to_bet) ) }
|
||||
|
||||
|
||||
void database_fixture::resolve_betting_market_group(betting_market_group_id_type betting_market_group_id,
|
||||
std::map<betting_market_id_type, betting_market_resolution_type> resolutions)
|
||||
{ try {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include <graphene/chain/operation_history_object.hpp>
|
||||
|
||||
#include <boost/parameter.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace graphene::db;
|
||||
|
|
@ -144,6 +146,18 @@ extern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP;
|
|||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace keywords {
|
||||
BOOST_PARAMETER_NAME(event_id)
|
||||
BOOST_PARAMETER_NAME(event_group_id)
|
||||
BOOST_PARAMETER_NAME(name)
|
||||
BOOST_PARAMETER_NAME(season)
|
||||
BOOST_PARAMETER_NAME(status)
|
||||
BOOST_PARAMETER_NAME(force)
|
||||
BOOST_PARAMETER_NAME(betting_market_group_id)
|
||||
BOOST_PARAMETER_NAME(description)
|
||||
BOOST_PARAMETER_NAME(rules_id)
|
||||
}
|
||||
|
||||
struct database_fixture {
|
||||
// the reason we use an app is to exercise the indexes of built-in
|
||||
// plugins
|
||||
|
|
@ -285,6 +299,7 @@ struct database_fixture {
|
|||
asset_id_type dividend_payout_asset_type) const;
|
||||
vector< operation_history_object > get_operation_history( account_id_type account_id )const;
|
||||
void process_operation_by_witnesses(operation op);
|
||||
void force_operation_by_witnesses(operation op);
|
||||
void set_is_proposed_trx(operation op);
|
||||
const sport_object& create_sport(internationalized_string_type name);
|
||||
void update_sport(sport_id_type sport_id, internationalized_string_type name);
|
||||
|
|
@ -297,21 +312,47 @@ struct database_fixture {
|
|||
fc::optional<internationalized_string_type> name,
|
||||
bool dont_set_is_proposed_trx = false);
|
||||
const event_object& create_event(internationalized_string_type name, internationalized_string_type season, event_group_id_type event_group_id);
|
||||
void update_event(event_id_type event_id,
|
||||
fc::optional<object_id_type> event_group_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
fc::optional<internationalized_string_type> season,
|
||||
fc::optional<bool> is_live_market);
|
||||
void update_event_impl(event_id_type event_id,
|
||||
fc::optional<object_id_type> event_group_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
fc::optional<internationalized_string_type> season,
|
||||
fc::optional<event_status> status,
|
||||
bool force);
|
||||
BOOST_PARAMETER_MEMBER_FUNCTION((void), update_event, keywords::tag,
|
||||
(required (event_id, (event_id_type)))
|
||||
(optional (event_group_id, (fc::optional<object_id_type>), fc::optional<object_id_type>())
|
||||
(name, (fc::optional<internationalized_string_type>), fc::optional<internationalized_string_type>())
|
||||
(season, (fc::optional<internationalized_string_type>), fc::optional<internationalized_string_type>())
|
||||
(status, (fc::optional<event_status>), fc::optional<event_status>())
|
||||
(force, (bool), false)))
|
||||
{
|
||||
update_event_impl(event_id, event_group_id, name, season, status, force);
|
||||
}
|
||||
|
||||
const betting_market_rules_object& create_betting_market_rules(internationalized_string_type name, internationalized_string_type description);
|
||||
void update_betting_market_rules(betting_market_rules_id_type rules_id,
|
||||
fc::optional<internationalized_string_type> name,
|
||||
fc::optional<internationalized_string_type> description);
|
||||
const betting_market_group_object& create_betting_market_group(internationalized_string_type description, event_id_type event_id, betting_market_rules_id_type rules_id, asset_id_type asset_id);
|
||||
void update_betting_market_group(betting_market_group_id_type betting_market_group_id,
|
||||
fc::optional<internationalized_string_type> description,
|
||||
fc::optional<object_id_type> rules_id,
|
||||
fc::optional<bool> freeze,
|
||||
fc::optional<bool> delay_bets);
|
||||
const betting_market_group_object& create_betting_market_group(internationalized_string_type description,
|
||||
event_id_type event_id,
|
||||
betting_market_rules_id_type rules_id,
|
||||
asset_id_type asset_id,
|
||||
bool never_in_play,
|
||||
uint32_t delay_before_settling);
|
||||
void update_betting_market_group_impl(betting_market_group_id_type betting_market_group_id,
|
||||
fc::optional<internationalized_string_type> description,
|
||||
fc::optional<object_id_type> rules_id,
|
||||
fc::optional<betting_market_group_status> status,
|
||||
bool force);
|
||||
BOOST_PARAMETER_MEMBER_FUNCTION((void), update_betting_market_group, keywords::tag,
|
||||
(required (betting_market_group_id, (betting_market_group_id_type)))
|
||||
(optional (description, (fc::optional<internationalized_string_type>), fc::optional<internationalized_string_type>())
|
||||
(rules_id, (fc::optional<object_id_type>), fc::optional<object_id_type>())
|
||||
(status, (fc::optional<betting_market_group_status>), fc::optional<betting_market_group_status>())
|
||||
(force, (bool), false)))
|
||||
{
|
||||
update_betting_market_group_impl(betting_market_group_id, description, rules_id, status, force);
|
||||
}
|
||||
|
||||
const betting_market_object& create_betting_market(betting_market_group_id_type group_id, internationalized_string_type payout_condition);
|
||||
void update_betting_market(betting_market_id_type betting_market_id,
|
||||
|
|
|
|||
Loading…
Reference in a new issue