2016-09-02 14:53:08 +00:00
|
|
|
#include <graphene/chain/protocol/tournament.hpp>
|
2016-09-02 19:04:17 +00:00
|
|
|
#include <graphene/chain/tournament_object.hpp>
|
2016-09-02 14:53:08 +00:00
|
|
|
#include <graphene/chain/tournament_evaluator.hpp>
|
|
|
|
|
#include <graphene/chain/database.hpp>
|
|
|
|
|
#include <graphene/chain/exceptions.hpp>
|
|
|
|
|
#include <graphene/chain/hardfork.hpp>
|
2016-09-02 19:04:17 +00:00
|
|
|
#include <graphene/chain/is_authorized_asset.hpp>
|
2016-09-02 14:53:08 +00:00
|
|
|
namespace graphene { namespace chain {
|
|
|
|
|
|
|
|
|
|
void_result tournament_create_evaluator::do_evaluate( const tournament_create_operation& op )
|
|
|
|
|
{ try {
|
|
|
|
|
database& d = db();
|
|
|
|
|
FC_ASSERT(op.options.type_of_game == rock_paper_scissors, "Unsupported game type ${type}", ("type", op.options.type_of_game));
|
|
|
|
|
|
|
|
|
|
FC_ASSERT(op.options.registration_deadline >= d.head_block_time(), "Registration deadline has already passed");
|
|
|
|
|
|
|
|
|
|
// TODO: make this committee-set
|
|
|
|
|
const fc::time_point_sec maximum_registration_deadline = d.head_block_time() + fc::days(30);
|
|
|
|
|
FC_ASSERT(op.options.registration_deadline <= maximum_registration_deadline,
|
|
|
|
|
"Registration deadline must be before ${maximum_registration_deadline}",
|
|
|
|
|
("maximum_registration_deadline", maximum_registration_deadline));
|
|
|
|
|
|
|
|
|
|
FC_ASSERT(op.options.number_of_players > 1, "If you're going to play with yourself, do it off-chain");
|
|
|
|
|
// TODO: make this committee-set
|
|
|
|
|
const uint32_t maximum_players_in_tournament = 256;
|
|
|
|
|
FC_ASSERT(op.options.number_of_players <= maximum_players_in_tournament,
|
|
|
|
|
"Tournaments may not have more than ${maximum_players_in_tournament} players",
|
|
|
|
|
("maximum_players_in_tournament", maximum_players_in_tournament));
|
|
|
|
|
|
|
|
|
|
// TODO: make this committee-set
|
|
|
|
|
const uint32_t maximum_tournament_whitelist_length = 1000;
|
2016-09-03 21:51:27 +00:00
|
|
|
FC_ASSERT(op.options.whitelist.size() >= op.options.number_of_players, "Whitelist must allow enough players to fill the tournament");
|
2016-09-02 14:53:08 +00:00
|
|
|
FC_ASSERT(op.options.whitelist.size() < maximum_tournament_whitelist_length,
|
|
|
|
|
"Whitelist must not be longer than ${maximum_tournament_whitelist_length}",
|
|
|
|
|
("maximum_tournament_whitelist_length", maximum_tournament_whitelist_length));
|
|
|
|
|
|
|
|
|
|
if (op.options.start_time)
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT(!op.options.start_delay, "Cannot specify both a fixed start time and a delay");
|
|
|
|
|
FC_ASSERT(*op.options.start_time >= op.options.registration_deadline,
|
|
|
|
|
"Cannot start before registration deadline expires");
|
|
|
|
|
// TODO: make this committee-set
|
|
|
|
|
const uint32_t maximum_start_time_in_future = 60 * 60 * 24 * 7 * 4; // 1 month
|
|
|
|
|
FC_ASSERT((*op.options.start_time - d.head_block_time()).to_seconds() <= maximum_start_time_in_future,
|
|
|
|
|
"Start time is too far in the future");
|
|
|
|
|
}
|
|
|
|
|
else if (op.options.start_delay)
|
|
|
|
|
{
|
|
|
|
|
FC_ASSERT(!op.options.start_time, "Cannot specify both a fixed start time and a delay");
|
|
|
|
|
// TODO: make this committee-set
|
|
|
|
|
const uint32_t maximum_start_delay = 60 * 60 * 24 * 7; // 1 week
|
|
|
|
|
FC_ASSERT(*op.options.start_delay < maximum_start_delay,
|
|
|
|
|
"Start delay is too long");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
FC_THROW("Must specify either a fixed start time or a delay");
|
|
|
|
|
|
|
|
|
|
// TODO: make this committee-set
|
|
|
|
|
const uint32_t maximum_round_delay = 60 * 60; // one hour
|
|
|
|
|
FC_ASSERT(op.options.round_delay < maximum_round_delay,
|
|
|
|
|
"Round delay is too long");
|
|
|
|
|
|
|
|
|
|
// TODO: make this committee-set
|
|
|
|
|
const uint32_t maximum_tournament_number_of_wins = 100;
|
|
|
|
|
FC_ASSERT(op.options.number_of_wins > 0);
|
|
|
|
|
FC_ASSERT(op.options.number_of_wins <= maximum_tournament_number_of_wins,
|
|
|
|
|
"Matches may not require more than ${number_of_wins} wins",
|
|
|
|
|
("number_of_wins", maximum_tournament_number_of_wins));
|
|
|
|
|
|
|
|
|
|
return void_result();
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
|
|
|
|
|
|
|
|
|
object_id_type tournament_create_evaluator::do_apply( const tournament_create_operation& op )
|
|
|
|
|
{ try {
|
2016-09-02 19:04:17 +00:00
|
|
|
const tournament_object& new_tournament =
|
|
|
|
|
db().create<tournament_object>( [&]( tournament_object& t ) {
|
|
|
|
|
t.options = op.options;
|
|
|
|
|
t.creator = op.creator;
|
2016-09-03 21:51:27 +00:00
|
|
|
t.state = tournament_state::accepting_registrations;
|
2016-09-02 19:04:17 +00:00
|
|
|
// t.dynamic_tournament_data_id = dyn_tournament.id;
|
|
|
|
|
});
|
|
|
|
|
return new_tournament.id;
|
2016-09-02 14:53:08 +00:00
|
|
|
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
|
|
|
|
|
|
|
|
|
void_result tournament_join_evaluator::do_evaluate( const tournament_join_operation& op )
|
|
|
|
|
{ try {
|
2016-09-02 19:04:17 +00:00
|
|
|
const database& d = db();
|
2016-09-02 23:25:02 +00:00
|
|
|
_tournament_obj = &op.tournament_id(d);
|
|
|
|
|
_tournament_details_obj = &_tournament_obj->tournament_details_id(d);
|
|
|
|
|
_payer_account = &op.payer_account_id(d);
|
|
|
|
|
//const account_object& player_account = op.player_account_id(d);
|
|
|
|
|
_buy_in_asset_type = &op.buy_in.asset_id(d);
|
2016-09-02 19:04:17 +00:00
|
|
|
|
2016-09-03 21:51:27 +00:00
|
|
|
FC_ASSERT(_tournament_obj->state == tournament_state::accepting_registrations);
|
2016-09-02 23:25:02 +00:00
|
|
|
FC_ASSERT(_tournament_details_obj->registered_players.size() < _tournament_obj->options.number_of_players,
|
|
|
|
|
"Tournament is already full");
|
|
|
|
|
FC_ASSERT(d.head_block_time() <= _tournament_obj->options.registration_deadline,
|
|
|
|
|
"Registration deadline has already passed");
|
|
|
|
|
|
|
|
|
|
FC_ASSERT(_tournament_obj->options.whitelist.empty() ||
|
|
|
|
|
_tournament_obj->options.whitelist.find(op.player_account_id) == _tournament_obj->options.whitelist.end(),
|
|
|
|
|
"Player is not on the whitelist for this tournament");
|
|
|
|
|
|
|
|
|
|
FC_ASSERT(_tournament_details_obj->registered_players.find(op.player_account_id) == _tournament_details_obj->registered_players.end(),
|
2016-09-02 19:04:17 +00:00
|
|
|
"Player is already registered for this tournament");
|
2016-09-02 23:25:02 +00:00
|
|
|
FC_ASSERT(op.buy_in == _tournament_obj->options.buy_in, "Buy-in is incorrect");
|
2016-09-02 19:04:17 +00:00
|
|
|
|
2016-09-02 23:25:02 +00:00
|
|
|
GRAPHENE_ASSERT(!_buy_in_asset_type->is_transfer_restricted(),
|
2016-09-02 19:04:17 +00:00
|
|
|
transfer_restricted_transfer_asset,
|
|
|
|
|
"Asset {asset} has transfer_restricted flag enabled",
|
|
|
|
|
("asset", op.buy_in.asset_id));
|
|
|
|
|
|
2016-09-02 23:25:02 +00:00
|
|
|
GRAPHENE_ASSERT(is_authorized_asset(d, *_payer_account, *_buy_in_asset_type),
|
2016-09-02 19:04:17 +00:00
|
|
|
transfer_from_account_not_whitelisted,
|
|
|
|
|
"payer account ${payer} is not whitelisted for asset ${asset}",
|
|
|
|
|
("payer", op.payer_account_id)
|
|
|
|
|
("asset", op.buy_in.asset_id));
|
|
|
|
|
|
2016-09-02 23:25:02 +00:00
|
|
|
bool sufficient_balance = d.get_balance(*_payer_account, *_buy_in_asset_type).amount >= op.buy_in.amount;
|
2016-09-02 19:04:17 +00:00
|
|
|
FC_ASSERT(sufficient_balance,
|
|
|
|
|
"Insufficient Balance: paying account '${payer}' has insufficient balance to pay buy-in of ${buy_in} (balance is ${balance})",
|
2016-09-02 23:25:02 +00:00
|
|
|
("payer", _payer_account->name)
|
2016-09-02 19:04:17 +00:00
|
|
|
("buy_in", d.to_pretty_string(op.buy_in))
|
2016-09-02 23:25:02 +00:00
|
|
|
("balance",d.to_pretty_string(d.get_balance(*_payer_account, *_buy_in_asset_type))));
|
2016-09-02 14:53:08 +00:00
|
|
|
return void_result();
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
|
|
|
|
|
2016-09-02 19:04:17 +00:00
|
|
|
void_result tournament_join_evaluator::do_apply( const tournament_join_operation& op )
|
2016-09-02 14:53:08 +00:00
|
|
|
{ try {
|
2016-09-03 21:51:27 +00:00
|
|
|
bool registration_complete = _tournament_details_obj->registered_players.size() + 1 == _tournament_obj->options.number_of_players;
|
|
|
|
|
if (registration_complete)
|
|
|
|
|
fc_ilog(fc::logger::get("tournament"),
|
|
|
|
|
"Tournament ${id} now has enough players registered to begin",
|
|
|
|
|
("id", _tournament_obj->id));
|
2016-09-02 23:25:02 +00:00
|
|
|
|
2016-09-02 19:04:17 +00:00
|
|
|
db().adjust_balance(op.payer_account_id, -op.buy_in);
|
2016-09-02 23:25:02 +00:00
|
|
|
db().modify(*_tournament_details_obj, [&](tournament_details_object& tournament_details_obj){
|
|
|
|
|
tournament_details_obj.payers[op.payer_account_id] += op.buy_in.amount;
|
|
|
|
|
tournament_details_obj.registered_players.insert(op.player_account_id);
|
|
|
|
|
});
|
|
|
|
|
db().modify(*_tournament_obj, [&](tournament_object& tournament_obj){
|
|
|
|
|
++tournament_obj.registered_players;
|
|
|
|
|
tournament_obj.prize_pool += op.buy_in.amount;
|
2016-09-03 21:51:27 +00:00
|
|
|
if (registration_complete)
|
2016-09-02 23:25:02 +00:00
|
|
|
{
|
|
|
|
|
if (tournament_obj.options.start_time)
|
|
|
|
|
tournament_obj.start_time = tournament_obj.options.start_time;
|
|
|
|
|
else
|
|
|
|
|
tournament_obj.start_time = db().head_block_time() + fc::seconds(*tournament_obj.options.start_delay);
|
2016-09-03 21:51:27 +00:00
|
|
|
// even if the start time is now, mark it as awaiting; we will promote it to in_progress
|
|
|
|
|
// in update_tournaments() called at the end of the block
|
|
|
|
|
tournament_obj.state = tournament_state::awaiting_start;
|
2016-09-02 23:25:02 +00:00
|
|
|
}
|
2016-09-02 19:04:17 +00:00
|
|
|
});
|
|
|
|
|
return void_result();
|
2016-09-02 14:53:08 +00:00
|
|
|
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
|
|
|
|
|
|
|
|
|
} }
|
|
|
|
|
|
|
|
|
|
|