Start implementing bettimg_market_resolve op

This commit is contained in:
Eric Frias 2017-03-23 19:35:10 -04:00
parent fd38d385ca
commit 784b11059f
9 changed files with 126 additions and 12 deletions

View file

@ -209,6 +209,7 @@ struct get_impacted_account_visitor
void operator()( const event_create_operation& op ) {}
void operator()( const betting_market_group_create_operation& op ) {}
void operator()( const betting_market_create_operation& op ) {}
void operator()( const betting_market_resolve_operation& op ) {}
void operator()( const bet_place_operation& op )
{
_impacted.insert( op.bettor_id );

View file

@ -179,4 +179,17 @@ void_result bet_cancel_evaluator::do_apply(const bet_cancel_operation& op)
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result betting_market_resolve_evaluator::do_evaluate(const betting_market_resolve_operation& op)
{ try {
const database& d = db();
_betting_market = &op.betting_market_id(d);
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result betting_market_resolve_evaluator::do_apply(const betting_market_resolve_operation& op)
{ try {
db().resolve_betting_market(*_betting_market, op.resolution);
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
} } // graphene::chain

View file

@ -19,17 +19,50 @@ void database::cancel_bet( const bet_object& bet, bool create_virtual_op )
remove(bet);
}
void database::resolve_betting_market(const betting_market_object& betting_market,
betting_market_resolution_type resolution)
{
// TODO: cancel all unmatched bets on the books for this market
auto& index = get_index_type<betting_market_position_index>().indices().get<by_bettor_betting_market>();
auto position_itr = index.lower_bound(std::make_tuple(betting_market.id));
while (position_itr != index.end() &&
position_itr->betting_market_id == betting_market.id)
{
const betting_market_position_object& position = *position_itr;
++position_itr;
share_type payout_amount = 0;
switch (resolution)
{
case betting_market_resolution_type::win:
payout_amount += position.pay_if_payout_condition;
payout_amount += position.pay_if_not_canceled;
break;
case betting_market_resolution_type::not_win:
payout_amount += position.pay_if_not_payout_condition;
payout_amount += position.pay_if_not_canceled;
break;
case betting_market_resolution_type::cancel:
payout_amount += position.pay_if_canceled;
break;
}
adjust_balance(position.bettor_id, asset(payout_amount, betting_market.asset_id));
//TODO: pay the fees to the correct (dividend-distribution) account
adjust_balance(account_id_type(), asset(position.fees_collected, betting_market.asset_id));
//TODO: generate a virtual op to notify the bettor that they won or lost
remove(position);
}
remove(betting_market);
}
bool maybe_cull_small_bet( database& db, const bet_object& bet_object_to_cull )
{
/**
* There are times when the AMOUNT_FOR_SALE * SALE_PRICE == 0 which means that we
* have hit the limit where the seller is asking for nothing in return. When this
* happens we must refund any balance back to the seller, it is too small to be
* sold at the sale price.
*
* If the order is a taker order (as opposed to a maker order), so the price is
* set by the counterparty, this check is deferred until the order becomes unmatched
* (see #555) -- however, detecting this condition is the responsibility of the caller.
* There are times when this bet can't be matched (for example, it's now laying a 2:1 bet for
* 1 satoshi, so it could only be matched by half a satoshi). Remove these bets from
* the books.
*/
if( bet_object_to_cull.get_matching_amount() == 0 )

View file

@ -218,6 +218,7 @@ void database::initialize_evaluators()
register_evaluator<betting_market_group_create_evaluator>();
register_evaluator<betting_market_create_evaluator>();
register_evaluator<bet_place_evaluator>();
register_evaluator<betting_market_resolve_evaluator>();
}
void database::initialize_indexes()

View file

@ -76,4 +76,15 @@ namespace graphene { namespace chain {
const bet_object* _bet_to_cancel;
};
class betting_market_resolve_evaluator : public evaluator<betting_market_resolve_evaluator>
{
public:
typedef betting_market_resolve_operation operation_type;
void_result do_evaluate( const betting_market_resolve_operation& o );
void_result do_apply( const betting_market_resolve_operation& o );
private:
const betting_market_object* _betting_market;
};
} } // graphene::chain

View file

@ -235,6 +235,7 @@ typedef multi_index_container<
typedef generic_index<bet_object, bet_object_multi_index_type> bet_object_index;
struct by_bettor_betting_market{};
struct by_betting_market_bettor{};
typedef multi_index_container<
betting_market_position_object,
indexed_by<
@ -243,8 +244,13 @@ typedef multi_index_container<
composite_key<
betting_market_position_object,
member<betting_market_position_object, account_id_type, &betting_market_position_object::bettor_id>,
member<betting_market_position_object, betting_market_id_type, &betting_market_position_object::betting_market_id>
> > > > betting_market_position_multi_index_type;
member<betting_market_position_object, betting_market_id_type, &betting_market_position_object::betting_market_id> > >,
ordered_unique< tag<by_betting_market_bettor>,
composite_key<
betting_market_position_object,
member<betting_market_position_object, betting_market_id_type, &betting_market_position_object::betting_market_id>,
member<betting_market_position_object, account_id_type, &betting_market_position_object::bettor_id> > >
> > betting_market_position_multi_index_type;
typedef generic_index<betting_market_position_object, betting_market_position_multi_index_type> betting_market_position_index;
} } // graphene::chain

View file

@ -365,6 +365,8 @@ namespace graphene { namespace chain {
/// @{ @group Betting Market Helpers
void cancel_bet(const bet_object& bet, bool create_virtual_op = true);
void resolve_betting_market(const betting_market_object& betting_market,
betting_market_resolution_type resolution);
/**
* @brief Process a new bet
* @param new_bet_object The new bet to process

View file

@ -104,6 +104,7 @@ namespace graphene { namespace chain {
betting_market_group_create_operation,
betting_market_create_operation,
bet_place_operation,
betting_market_resolve_operation,
bet_matched_operation, // VIRTUAL
bet_cancel_operation,
bet_canceled_operation // VIRTUAL

View file

@ -1809,7 +1809,7 @@ BOOST_AUTO_TEST_CASE( peerplays_sport_create_test )
const betting_market_object& market = *db.get_index_type<betting_market_object_index>().indices().begin();
{
// have bob lay a bet at 1:1 odds
// have bob lay a bet for 1M (+20k fees) at 1:1 odds
signed_transaction tx;
bet_place_operation bet_op;
bet_op.bettor_id = bob_id;
@ -1826,7 +1826,7 @@ BOOST_AUTO_TEST_CASE( peerplays_sport_create_test )
}
{
// have alice back a matching bet at 1:1 odds
// have alice back a matching bet at 1:1 odds (also costing 1.02M)
signed_transaction tx;
bet_place_operation bet_op;
bet_op.bettor_id = alice_id;
@ -1841,6 +1841,52 @@ BOOST_AUTO_TEST_CASE( peerplays_sport_create_test )
sign(tx, alice_private_key);
db.push_transaction(tx);
}
// caps win
{
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = (*active_witnesses.begin())(db).witness_account;
betting_market_resolve_operation betting_market_resolve_op;
betting_market_resolve_op.betting_market_id = market.id;
betting_market_resolve_op.resolution = betting_market_resolution_type::win;
proposal_op.proposed_ops.emplace_back(betting_market_resolve_op);
proposal_op.expiration_time = db.head_block_time() + fc::days(1);
signed_transaction tx;
tx.operations.push_back(proposal_op);
set_expiration(db, tx);
sign(tx, init_account_priv_key);
db.push_transaction(tx);
}
BOOST_REQUIRE_EQUAL(db.get_index_type<proposal_index>().indices().size(), 1);
{
const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();
for (const witness_id_type& witness_id : active_witnesses)
{
BOOST_TEST_MESSAGE("Approving market resolve witness " << fc::variant(witness_id).as<std::string>());
const witness_object& witness = witness_id(db);
const account_object& witness_account = witness.witness_account(db);
proposal_update_operation pup;
pup.proposal = prop.id;
pup.fee_paying_account = witness_account.id;
pup.active_approvals_to_add.insert(witness_account.id);
signed_transaction tx;
tx.operations.push_back( pup );
set_expiration( db, tx );
sign(tx, init_account_priv_key);
db.push_transaction(tx, ~0);
if (db.get_index_type<proposal_index>().indices().size() == 0)
break;
}
}
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
}
}