From ccd570874fe7321e899fa9102cf1b7082d28a432 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Tue, 21 Mar 2017 12:27:13 -0400 Subject: [PATCH] Updated data structures to take bets with explicit odds --- libraries/chain/betting_market_evaluator.cpp | 2 +- .../graphene/chain/betting_market_object.hpp | 4 +- .../chain/include/graphene/chain/config.hpp | 17 ++++++ .../chain/protocol/betting_market.hpp | 50 +++++++++++++++-- .../include/graphene/chain/protocol/event.hpp | 54 +++++++++++++++++++ libraries/chain/protocol/betting_market.cpp | 5 ++ 6 files changed, 124 insertions(+), 8 deletions(-) diff --git a/libraries/chain/betting_market_evaluator.cpp b/libraries/chain/betting_market_evaluator.cpp index 9f4aafef..5e83adb3 100644 --- a/libraries/chain/betting_market_evaluator.cpp +++ b/libraries/chain/betting_market_evaluator.cpp @@ -107,7 +107,7 @@ object_id_type bet_place_evaluator::do_apply(const bet_place_operation& op) bet_obj.bettor_id = op.bettor_id; bet_obj.betting_market_id = op.betting_market_id; bet_obj.amount_to_bet = op.amount_to_bet; - bet_obj.amount_to_win = op.amount_to_win; + bet_obj.backer_multiplier = op.backer_multiplier; bet_obj.amount_reserved_for_fees = op.amount_reserved_for_fees; bet_obj.back_or_lay = op.back_or_lay; }); diff --git a/libraries/chain/include/graphene/chain/betting_market_object.hpp b/libraries/chain/include/graphene/chain/betting_market_object.hpp index 463b159b..ac080b22 100644 --- a/libraries/chain/include/graphene/chain/betting_market_object.hpp +++ b/libraries/chain/include/graphene/chain/betting_market_object.hpp @@ -70,7 +70,7 @@ class bet_object : public graphene::db::abstract_object< bet_object > share_type amount_to_bet; - share_type amount_to_win; + bet_multiplier_type backer_multiplier; share_type amount_reserved_for_fees; @@ -104,4 +104,4 @@ typedef generic_index bet_object_index; FC_REFLECT_DERIVED( graphene::chain::betting_market_group_object, (graphene::db::object), (event_id)(options) ) FC_REFLECT_DERIVED( graphene::chain::betting_market_object, (graphene::db::object), (group_id)(payout_condition)(asset_id) ) -FC_REFLECT_DERIVED( graphene::chain::bet_object, (graphene::db::object), (bettor_id)(betting_market_id)(amount_to_bet)(amount_to_win)(amount_reserved_for_fees)(back_or_lay) ) +FC_REFLECT_DERIVED( graphene::chain::bet_object, (graphene::db::object), (bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(amount_reserved_for_fees)(back_or_lay) ) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 07dff7ff..f5c4b556 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -169,3 +169,20 @@ ///@} #define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET (asset_id_type(743)) + +/** + * Betting-related constants. + * + * We store bet multipliers as fixed-precision uint32_t. These values are + * the maximum power-of-ten bet we can have on a "symmetric" market: + * (decimal) 1.0001 - 10001 + * (fractional) 1:10000 - 10000:1 + */ +///@{ +/// betting odds (multipliers) are stored as fixed-precision, divide by this to get the actual multiplier +#define GRAPHENE_BETTING_ODDS_PRECISION 10000 +/// the smallest bet multiplier we will accept +#define GRAPHENE_BETTING_MIN_MULTIPLIER 10001 +/// the largest bet multiplier we will accept +#define GRAPHENE_BETTING_MAX_MULTIPLIER 100010000 +///@} diff --git a/libraries/chain/include/graphene/chain/protocol/betting_market.hpp b/libraries/chain/include/graphene/chain/protocol/betting_market.hpp index dd29a2ec..5da23817 100644 --- a/libraries/chain/include/graphene/chain/protocol/betting_market.hpp +++ b/libraries/chain/include/graphene/chain/protocol/betting_market.hpp @@ -29,7 +29,7 @@ namespace graphene { namespace chain { -enum betting_market_type { +enum class betting_market_type { moneyline, spread, over_under, @@ -88,7 +88,31 @@ struct betting_market_create_operation : public base_operation void validate()const; }; -enum bet_type { back_bet, lay_bet }; +enum class betting_market_resolution_type { + win, + not_win, + cancel, + BETTING_MARKET_RESOLUTION_COUNT +}; + +struct betting_market_resolve_operation : public base_operation +{ + struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; + asset fee; + + betting_market_id_type betting_market_id; + + betting_market_resolution_type resolution; + + extensions_type extensions; + + account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; } + void validate()const; +}; + +enum class bet_type { back, lay }; +typedef uint32_t bet_multiplier_type; + struct bet_place_operation : public base_operation { struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; @@ -98,10 +122,19 @@ struct bet_place_operation : public base_operation betting_market_id_type betting_market_id; + /// the bettor's stake share_type amount_to_bet; - share_type amount_to_win; + // decimal odds as seen by the backer, even if this is a lay bet. + // this is a fixed-precision number scaled by GRAPHENE_BETTING_ODDS_PRECISION. + // + // For example, an even 1/1 bet would be decimal odds 2.0, so backer_multiplier + // would be 2 * GRAPHENE_BETTING_ODDS_PRECISION. + bet_multiplier_type backer_multiplier; + // the amount the blockchain reserves to pay the percentage fee on matched bets. + // when this bet is (partially) matched, the blockchain will take (part of) the fee. If this bet is canceled + // the remaining amount will be returned to the bettor share_type amount_reserved_for_fees; bet_type back_or_lay; @@ -131,7 +164,14 @@ FC_REFLECT( graphene::chain::betting_market_create_operation::fee_parameters_typ FC_REFLECT( graphene::chain::betting_market_create_operation, (fee)(group_id)(payout_condition)(asset_id)(extensions) ) -FC_REFLECT_ENUM( graphene::chain::bet_type, (back_bet)(lay_bet) ) +FC_REFLECT_ENUM( graphene::chain::betting_market_resolution_type, (win)(not_win)(cancel)(BETTING_MARKET_RESOLUTION_COUNT) ) + +FC_REFLECT( graphene::chain::betting_market_resolve_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::betting_market_resolve_operation, + (fee)(betting_market_id)(resolution)(extensions) ) + + +FC_REFLECT_ENUM( graphene::chain::bet_type, (back)(lay) ) FC_REFLECT( graphene::chain::bet_place_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::bet_place_operation, - (fee)(bettor_id)(betting_market_id)(amount_to_bet)(amount_to_win)(amount_reserved_for_fees)(back_or_lay)(extensions) ) + (fee)(bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(amount_reserved_for_fees)(back_or_lay)(extensions) ) diff --git a/libraries/chain/include/graphene/chain/protocol/event.hpp b/libraries/chain/include/graphene/chain/protocol/event.hpp index 4c89d8f3..bfe06483 100644 --- a/libraries/chain/include/graphene/chain/protocol/event.hpp +++ b/libraries/chain/include/graphene/chain/protocol/event.hpp @@ -60,8 +60,62 @@ struct event_create_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, + 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 + * betting market explicitly. + * These are free-form strings that we assume will make sense to the user. + * For a game like football, this may be a score like "3". For races, + * it could be a time like "1:53.4". + */ +typedef map scores_map_type; +struct event_update_status_operation : public base_operation +{ + struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; + asset fee; + + /// the id of the event to update + event_id_type event_id; + + /** + * the new status of the event (if the status hasn't changed, the creator + * of this operation must still set `status` to the event's current status) + */ + event_status status; + + /* + * the new scores to be merged with the existing scores (if this operation + * does not provide scores for all competitors, they will keep their + * previous score + */ + scores_map_type scores; + + extensions_type extensions; + + account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; } + void validate()const; +}; + } } FC_REFLECT( graphene::chain::event_create_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::event_create_operation, (fee)(name)(season)(start_time)(event_group_id)(competitors)(extensions) ) +FC_REFLECT_ENUM( graphene::chain::event_status, (upcoming)(in_progress)(completed)(canceled)(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) ) + diff --git a/libraries/chain/protocol/betting_market.cpp b/libraries/chain/protocol/betting_market.cpp index 69e13a40..bd6c4593 100644 --- a/libraries/chain/protocol/betting_market.cpp +++ b/libraries/chain/protocol/betting_market.cpp @@ -35,6 +35,11 @@ void betting_market_create_operation::validate() const FC_ASSERT( fee.amount >= 0 ); } +void betting_market_resolve_operation::validate() const +{ + FC_ASSERT( fee.amount >= 0 ); +} + void bet_place_operation::validate() const { FC_ASSERT( fee.amount >= 0 );