Merge branch 'affiliate_rewards_rebased' into beatrice
This commit is contained in:
commit
2e38cd4f04
38 changed files with 2560 additions and 479 deletions
|
|
@ -13,7 +13,7 @@ add_library( graphene_app
|
|||
|
||||
# need to link graphene_debug_witness because plugins aren't sufficiently isolated #246
|
||||
#target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness )
|
||||
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie )
|
||||
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie )
|
||||
target_include_directories( graphene_app
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )
|
||||
|
|
|
|||
|
|
@ -117,6 +117,12 @@ namespace graphene { namespace app {
|
|||
if( _app.get_plugin( "bookie" ) )
|
||||
_bookie_api = std::make_shared<graphene::bookie::bookie_api>(std::ref(_app));
|
||||
}
|
||||
else if( api_name == "affiliate_stats_api" )
|
||||
{
|
||||
// can only enable this API if the plugin was loaded
|
||||
if( _app.get_plugin( "affiliate_stats" ) )
|
||||
_affiliate_stats_api = std::make_shared<graphene::affiliate_stats::affiliate_stats_api>(std::ref(_app));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -281,6 +287,12 @@ namespace graphene { namespace app {
|
|||
return *_bookie_api;
|
||||
}
|
||||
|
||||
fc::api<graphene::affiliate_stats::affiliate_stats_api> login_api::affiliate_stats() const
|
||||
{
|
||||
FC_ASSERT(_affiliate_stats_api);
|
||||
return *_affiliate_stats_api;
|
||||
}
|
||||
|
||||
#if 0
|
||||
vector<account_id_type> get_relevant_accounts( const object* obj )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -493,6 +493,7 @@ namespace detail {
|
|||
wild_access.allowed_apis.push_back( "history_api" );
|
||||
wild_access.allowed_apis.push_back( "crypto_api" );
|
||||
wild_access.allowed_apis.push_back( "bookie_api" );
|
||||
wild_access.allowed_apis.push_back( "affiliate_stats_api" );
|
||||
_apiaccess.permission_map["*"] = wild_access;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -275,6 +275,11 @@ struct get_impacted_account_visitor
|
|||
{
|
||||
_impacted.insert( op.payout_account_id );
|
||||
}
|
||||
void operator()( const affiliate_payout_operation& op )
|
||||
{
|
||||
_impacted.insert( op.affiliate );
|
||||
}
|
||||
void operator()( const affiliate_referral_payout_operation& op ) { }
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <graphene/accounts_list/accounts_list_plugin.hpp>
|
||||
|
||||
#include <graphene/debug_witness/debug_api.hpp>
|
||||
#include <graphene/affiliate_stats/affiliate_stats_api.hpp>
|
||||
#include <graphene/bookie/bookie_api.hpp>
|
||||
|
||||
#include <graphene/net/node.hpp>
|
||||
|
|
@ -362,6 +363,8 @@ namespace graphene { namespace app {
|
|||
fc::api<graphene::debug_witness::debug_api> debug()const;
|
||||
/// @brief Retrieve the bookie API (if available)
|
||||
fc::api<graphene::bookie::bookie_api> bookie()const;
|
||||
/// @brief Retrieve the affiliate_stats API (if available)
|
||||
fc::api<graphene::affiliate_stats::affiliate_stats_api> affiliate_stats()const;
|
||||
|
||||
/// @brief Called to enable an API, not reflected.
|
||||
void enable_api( const string& api_name );
|
||||
|
|
@ -377,6 +380,7 @@ namespace graphene { namespace app {
|
|||
optional< fc::api<asset_api> > _asset_api;
|
||||
optional< fc::api<graphene::debug_witness::debug_api> > _debug_api;
|
||||
optional< fc::api<graphene::bookie::bookie_api> > _bookie_api;
|
||||
optional< fc::api<graphene::affiliate_stats::affiliate_stats_api> > _affiliate_stats_api;
|
||||
};
|
||||
|
||||
}} // graphene::app
|
||||
|
|
@ -446,4 +450,5 @@ FC_API(graphene::app::login_api,
|
|||
(asset)
|
||||
(debug)
|
||||
(bookie)
|
||||
(affiliate_stats)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ add_library( graphene_chain
|
|||
betting_market_object.cpp
|
||||
betting_market_group_object.cpp
|
||||
|
||||
affiliate_payout.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp"
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
|
|||
FC_ASSERT( !op.extensions.value.active_special_authority.valid() );
|
||||
FC_ASSERT( !op.extensions.value.buyback_options.valid() );
|
||||
}
|
||||
if( d.head_block_time() < HARDFORK_999_TIME )
|
||||
FC_ASSERT( !op.extensions.value.affiliate_distributions.valid(), "Affiliate reward distributions not allowed yet" );
|
||||
|
||||
FC_ASSERT( fee_paying_account->is_lifetime_member(), "Only Lifetime members may register an account." );
|
||||
FC_ASSERT( op.referrer(d).is_member(d.head_block_time()), "The referrer must be either a lifetime or annual subscriber." );
|
||||
|
|
@ -186,6 +188,7 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
|
|||
obj.allowed_assets = o.extensions.value.buyback_options->markets;
|
||||
obj.allowed_assets->emplace( o.extensions.value.buyback_options->asset_to_buy );
|
||||
}
|
||||
obj.affiliate_distributions = o.extensions.value.affiliate_distributions;
|
||||
});
|
||||
|
||||
if( has_small_percent )
|
||||
|
|
|
|||
95
libraries/chain/affiliate_payout.cpp
Normal file
95
libraries/chain/affiliate_payout.cpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/affiliate_payout.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
#include <fc/uint128.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
share_type affiliate_payout_helper::payout( account_id_type player, share_type amount )
|
||||
{
|
||||
return payout( player(_db), amount );
|
||||
}
|
||||
|
||||
share_type affiliate_payout_helper::payout( const account_object& player, share_type amount )
|
||||
{
|
||||
if( !player.affiliate_distributions.valid() )
|
||||
return 0;
|
||||
const auto& dist = player.affiliate_distributions->_dists.find( tag );
|
||||
if( dist == player.affiliate_distributions->_dists.end() || dist->second._dist.empty() )
|
||||
return 0;
|
||||
|
||||
amount = amount.value / 5; // 20% fixed
|
||||
if( amount <= 0 )
|
||||
return 0;
|
||||
|
||||
uint16_t remaining = GRAPHENE_100_PERCENT;
|
||||
share_type paid = 0;
|
||||
share_type to_pay = amount;
|
||||
for( const auto& entry : dist->second._dist )
|
||||
{
|
||||
const account_id_type affiliate = entry.first;
|
||||
const uint16_t share = entry.second;
|
||||
fc::uint128_t payout = to_pay.value;
|
||||
if( share != remaining )
|
||||
{
|
||||
FC_ASSERT( share < remaining );
|
||||
payout *= share;
|
||||
payout /= remaining;
|
||||
//ilog("Paying ${p} of ${P} for ${s} of ${r}", ("p",payout.to_uint64())("P",to_pay.value)("s",share)("r",remaining) );
|
||||
remaining -= share;
|
||||
}
|
||||
FC_ASSERT( payout.to_uint64() <= to_pay );
|
||||
if( payout > 0 )
|
||||
{
|
||||
if ( accumulator.find(affiliate) == accumulator.end() )
|
||||
accumulator[affiliate] = payout.to_uint64();
|
||||
else
|
||||
accumulator[affiliate] += payout.to_uint64();
|
||||
to_pay -= payout.to_uint64();
|
||||
paid += payout.to_uint64();
|
||||
}
|
||||
}
|
||||
FC_ASSERT( to_pay == 0 );
|
||||
FC_ASSERT( paid == amount );
|
||||
|
||||
_db.push_applied_operation( affiliate_referral_payout_operation( player.id, asset( amount, payout_asset ) ) );
|
||||
|
||||
return paid;
|
||||
}
|
||||
|
||||
void affiliate_payout_helper::commit()
|
||||
{
|
||||
for( const auto& entry : accumulator )
|
||||
{
|
||||
asset payout = asset( entry.second, payout_asset );
|
||||
_db.adjust_balance( entry.first, payout );
|
||||
_db.push_applied_operation( affiliate_payout_operation( entry.first, tag, payout ) );
|
||||
}
|
||||
accumulator.clear();
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/affiliate_payout.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
|
|
@ -167,6 +168,8 @@ void database::settle_betting_market_group(const betting_market_group_object& be
|
|||
rake_account_id = core_asset_dividend_data_obj.dividend_distribution_account;
|
||||
}
|
||||
|
||||
affiliate_payout_helper payout_helper( *this, betting_market_group );
|
||||
|
||||
// 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;
|
||||
|
|
@ -261,8 +264,12 @@ void database::settle_betting_market_group(const betting_market_group_object& be
|
|||
if (net_profits.value > 0 && rake_account_id)
|
||||
{
|
||||
rake_amount = ((fc::uint128_t(net_profits.value) * rake_fee_percentage + GRAPHENE_100_PERCENT - 1) / GRAPHENE_100_PERCENT).to_uint64();
|
||||
share_type affiliates_share;
|
||||
if (rake_amount.value)
|
||||
adjust_balance(*rake_account_id, asset(rake_amount, betting_market_group.asset_id));
|
||||
affiliates_share = payout_helper.payout( bettor_id, rake_amount );
|
||||
FC_ASSERT( rake_amount.value >= affiliates_share.value );
|
||||
if (rake_amount.value > affiliates_share.value)
|
||||
adjust_balance(*rake_account_id, asset(rake_amount - affiliates_share, betting_market_group.asset_id));
|
||||
}
|
||||
|
||||
// pay winning - rake
|
||||
|
|
@ -300,6 +307,8 @@ void database::settle_betting_market_group(const betting_market_group_object& be
|
|||
|
||||
fc_dlog(fc::logger::get("betting"), "removing betting market group ${id}", ("id", betting_market_group.id));
|
||||
remove(betting_market_group);
|
||||
|
||||
payout_helper.commit();
|
||||
}
|
||||
|
||||
void database::remove_completed_events()
|
||||
|
|
|
|||
|
|
@ -658,13 +658,10 @@ operation_result database::apply_operation(transaction_evaluation_state& eval_st
|
|||
{ try {
|
||||
int i_which = op.which();
|
||||
uint64_t u_which = uint64_t( i_which );
|
||||
if( i_which < 0 )
|
||||
assert( "Negative operation tag" && false );
|
||||
if( u_which >= _operation_evaluators.size() )
|
||||
assert( "No registered evaluator for this operation" && false );
|
||||
FC_ASSERT( i_which >= 0, "Negative operation tag in operation ${op}", ("op",op) );
|
||||
FC_ASSERT( u_which < _operation_evaluators.size(), "No registered evaluator for operation ${op}", ("op",op) );
|
||||
unique_ptr<op_evaluator>& eval = _operation_evaluators[ u_which ];
|
||||
if( !eval )
|
||||
assert( "No registered evaluator for this operation" && false );
|
||||
FC_ASSERT( eval, "No registered evaluator for operation ${op}", ("op",op) );
|
||||
auto op_id = push_applied_operation( op );
|
||||
auto result = eval->evaluate( eval_state, op, true );
|
||||
set_applied_operation_result( op_id, result );
|
||||
|
|
|
|||
|
|
@ -262,6 +262,11 @@ struct get_impacted_account_visitor
|
|||
{
|
||||
_impacted.insert( op.payout_account_id );
|
||||
}
|
||||
void operator()( const affiliate_payout_operation& op )
|
||||
{
|
||||
_impacted.insert( op.affiliate );
|
||||
}
|
||||
void operator()( const affiliate_referral_payout_operation& op ) { }
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
4
libraries/chain/hardfork.d/999.hf
Normal file
4
libraries/chain/hardfork.d/999.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// Placeholder HF for affiliate reward system
|
||||
#ifndef HARDFORK_999_TIME
|
||||
#define HARDFORK_999_TIME (fc::time_point_sec( 1600000000 ))
|
||||
#endif
|
||||
|
|
@ -227,6 +227,8 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
optional< flat_set<asset_id_type> > allowed_assets;
|
||||
|
||||
optional< affiliate_reward_distributions > affiliate_distributions;
|
||||
|
||||
bool has_special_authority()const
|
||||
{
|
||||
return (owner_special_authority.which() != special_authority::tag< no_special_authority >::value)
|
||||
|
|
@ -446,7 +448,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_object,
|
|||
(cashback_vb)
|
||||
(owner_special_authority)(active_special_authority)
|
||||
(top_n_control_flags)
|
||||
(allowed_assets)
|
||||
(allowed_assets)(affiliate_distributions)
|
||||
)
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::account_balance_object,
|
||||
|
|
|
|||
90
libraries/chain/include/graphene/chain/affiliate_payout.hpp
Normal file
90
libraries/chain/include/graphene/chain/affiliate_payout.hpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/protocol/affiliate.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class database;
|
||||
|
||||
namespace impl {
|
||||
class game_type_visitor {
|
||||
public:
|
||||
typedef app_tag result_type;
|
||||
|
||||
inline app_tag operator()( const rock_paper_scissors_game_options& o )const { return rps; }
|
||||
};
|
||||
}
|
||||
|
||||
template<typename GAME>
|
||||
app_tag get_tag_for_game( const GAME& game );
|
||||
|
||||
template<>
|
||||
inline app_tag get_tag_for_game( const betting_market_group_object& game )
|
||||
{
|
||||
return bookie;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline app_tag get_tag_for_game( const tournament_object& game )
|
||||
{
|
||||
return game.options.game_options.visit( impl::game_type_visitor() );
|
||||
}
|
||||
|
||||
template<typename GAME>
|
||||
asset_id_type get_asset_for_game( const GAME& game );
|
||||
|
||||
template<>
|
||||
inline asset_id_type get_asset_for_game( const betting_market_group_object& game )
|
||||
{
|
||||
return game.asset_id;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline asset_id_type get_asset_for_game( const tournament_object& game )
|
||||
{
|
||||
return game.options.buy_in.asset_id;
|
||||
}
|
||||
|
||||
class affiliate_payout_helper {
|
||||
public:
|
||||
template<typename GAME>
|
||||
affiliate_payout_helper( database& db, const GAME& game )
|
||||
: _db(db), tag( get_tag_for_game( game ) ), payout_asset( get_asset_for_game( game ) ) {}
|
||||
|
||||
share_type payout( account_id_type player, share_type amount );
|
||||
share_type payout( const account_object& player, share_type amount );
|
||||
void commit();
|
||||
|
||||
private:
|
||||
database& _db;
|
||||
app_tag tag;
|
||||
asset_id_type payout_asset;
|
||||
std::map<account_id_type, share_type> accumulator;
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -151,7 +151,7 @@
|
|||
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4
|
||||
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3
|
||||
|
||||
#define GRAPHENE_CURRENT_DB_VERSION "PPY1.11"
|
||||
#define GRAPHENE_CURRENT_DB_VERSION "PPY_999"
|
||||
|
||||
#define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT)
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,21 @@ namespace graphene { namespace chain {
|
|||
void validate()const;
|
||||
};
|
||||
|
||||
enum app_tag {
|
||||
bookie = 0,
|
||||
rps = 1
|
||||
};
|
||||
struct affiliate_reward_distribution
|
||||
{
|
||||
fc::flat_map<account_id_type,uint16_t> _dist;
|
||||
void validate()const;
|
||||
};
|
||||
struct affiliate_reward_distributions
|
||||
{
|
||||
fc::flat_map<app_tag,affiliate_reward_distribution> _dists;
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup operations
|
||||
*/
|
||||
|
|
@ -71,6 +86,7 @@ namespace graphene { namespace chain {
|
|||
optional< special_authority > owner_special_authority;
|
||||
optional< special_authority > active_special_authority;
|
||||
optional< buyback_account_options > buyback_options;
|
||||
optional< affiliate_reward_distributions > affiliate_distributions;
|
||||
};
|
||||
|
||||
struct fee_parameters_type
|
||||
|
|
@ -268,6 +284,10 @@ FC_REFLECT(graphene::chain::account_options, (memo_key)(voting_account)(num_witn
|
|||
FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing,
|
||||
(no_listing)(white_listed)(black_listed)(white_and_black_listed))
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::app_tag, (bookie)(rps) )
|
||||
FC_REFLECT( graphene::chain::affiliate_reward_distribution, (_dist) );
|
||||
FC_REFLECT( graphene::chain::affiliate_reward_distributions, (_dists) );
|
||||
|
||||
FC_REFLECT(graphene::chain::account_create_operation::ext, (null_ext)(owner_special_authority)(active_special_authority)(buyback_options) )
|
||||
FC_REFLECT( graphene::chain::account_create_operation,
|
||||
(fee)(registrar)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
#include <graphene/chain/protocol/account.hpp>
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/protocol/memo.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
/**
|
||||
* Virtual op generated when an affiliate receives payout.
|
||||
*/
|
||||
|
||||
struct affiliate_payout_operation : public base_operation
|
||||
{
|
||||
affiliate_payout_operation(){}
|
||||
affiliate_payout_operation( account_id_type a, app_tag t, const asset& amount )
|
||||
: affiliate(a), tag(t), payout(amount) {}
|
||||
|
||||
struct fee_parameters_type { };
|
||||
asset fee;
|
||||
|
||||
// Account of the receiving affiliate
|
||||
account_id_type affiliate;
|
||||
// App-tag for which the payout was generated
|
||||
app_tag tag;
|
||||
// Payout amount
|
||||
asset payout;
|
||||
|
||||
account_id_type fee_payer()const { return affiliate; }
|
||||
void validate()const {
|
||||
FC_ASSERT( false, "Virtual operation" );
|
||||
}
|
||||
|
||||
share_type calculate_fee(const fee_parameters_type& params)const
|
||||
{ return 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Virtual op generated when a player generates an affiliate payout
|
||||
*/
|
||||
struct affiliate_referral_payout_operation : public base_operation
|
||||
{
|
||||
affiliate_referral_payout_operation(){}
|
||||
affiliate_referral_payout_operation( account_id_type p, const asset& amount )
|
||||
: player(p), payout(amount) {}
|
||||
|
||||
struct fee_parameters_type { };
|
||||
asset fee;
|
||||
|
||||
// Account of the winning player
|
||||
account_id_type player;
|
||||
// Payout amount
|
||||
asset payout;
|
||||
|
||||
account_id_type fee_payer()const { return player; }
|
||||
void validate()const {
|
||||
FC_ASSERT( false, "virtual operation" );
|
||||
}
|
||||
|
||||
share_type calculate_fee(const fee_parameters_type& params)const
|
||||
{ return 0; }
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::affiliate_payout_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::affiliate_referral_payout_operation::fee_parameters_type, )
|
||||
|
||||
FC_REFLECT( graphene::chain::affiliate_payout_operation, (fee)(affiliate)(tag)(payout) )
|
||||
FC_REFLECT( graphene::chain::affiliate_referral_payout_operation, (fee)(player)(payout) )
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/protocol/account.hpp>
|
||||
#include <graphene/chain/protocol/affiliate.hpp>
|
||||
#include <graphene/chain/protocol/assert.hpp>
|
||||
#include <graphene/chain/protocol/asset_ops.hpp>
|
||||
#include <graphene/chain/protocol/balance.hpp>
|
||||
|
|
@ -124,7 +125,9 @@ namespace graphene { namespace chain {
|
|||
bet_canceled_operation, // VIRTUAL
|
||||
betting_market_group_update_operation,
|
||||
betting_market_update_operation,
|
||||
event_update_status_operation
|
||||
event_update_status_operation,
|
||||
affiliate_payout_operation, // VIRTUAL
|
||||
affiliate_referral_payout_operation // VIRTUAL
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -25,8 +25,11 @@
|
|||
#include <graphene/chain/proposal_evaluator.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/protocol/account.hpp>
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
|
|
@ -52,8 +55,20 @@ struct proposal_operation_hardfork_visitor
|
|||
"Parameter extensions are not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const sport_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "sport_create_operation not allowed yet!" );
|
||||
void operator()(const graphene::chain::tournament_payout_operation &o) const {
|
||||
// TODO: move check into tournament_payout_operation::validate after HARDFORK_999_TIME
|
||||
FC_ASSERT( block_time < HARDFORK_999_TIME, "Not allowed!" );
|
||||
}
|
||||
|
||||
void operator()(const graphene::chain::asset_settle_cancel_operation &o) const {
|
||||
// TODO: move check into asset_settle_cancel_operation::validate after HARDFORK_999_TIME
|
||||
FC_ASSERT( block_time < HARDFORK_999_TIME, "Not allowed!" );
|
||||
}
|
||||
|
||||
void operator()(const graphene::chain::account_create_operation &aco) const {
|
||||
// TODO: remove after HARDFORK_999_TIME
|
||||
if (block_time < HARDFORK_999_TIME)
|
||||
FC_ASSERT( !aco.extensions.value.affiliate_distributions.valid(), "Affiliate reward distributions not allowed yet" );
|
||||
}
|
||||
|
||||
void operator()(const sport_update_operation &v) const {
|
||||
|
|
|
|||
|
|
@ -182,6 +182,27 @@ void account_options::validate() const
|
|||
"May not specify fewer witnesses or committee members than the number voted for.");
|
||||
}
|
||||
|
||||
void affiliate_reward_distribution::validate() const
|
||||
{
|
||||
// sum of weights must equal 100%
|
||||
uint32_t sum = 0;
|
||||
for( const auto& share : _dist )
|
||||
{
|
||||
FC_ASSERT( share.second > 0, "Must leave out affilates who receive 0%!" );
|
||||
FC_ASSERT( share.second <= GRAPHENE_100_PERCENT, "Can't pay out more than 100% per affiliate!" );
|
||||
sum += share.second;
|
||||
FC_ASSERT( sum <= GRAPHENE_100_PERCENT, "Can't pay out more than 100% total!" );
|
||||
}
|
||||
FC_ASSERT( sum == GRAPHENE_100_PERCENT, "Total affiliate distributions must cover 100%!" );
|
||||
}
|
||||
|
||||
void affiliate_reward_distributions::validate() const
|
||||
{
|
||||
FC_ASSERT( !_dists.empty(), "Empty affiliate reward distributions not allowed!" );
|
||||
for( const auto& dist: _dists )
|
||||
dist.second.validate();
|
||||
}
|
||||
|
||||
share_type account_create_operation::calculate_fee( const fee_parameters_type& k )const
|
||||
{
|
||||
auto core_fee_required = k.basic_fee;
|
||||
|
|
@ -226,6 +247,8 @@ void account_create_operation::validate()const
|
|||
FC_ASSERT( m != extensions.value.buyback_options->asset_to_buy );
|
||||
}
|
||||
}
|
||||
if( extensions.value.affiliate_distributions.valid() )
|
||||
extensions.value.affiliate_distributions->validate();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/affiliate_payout.hpp>
|
||||
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
|
|
@ -311,10 +312,8 @@ namespace graphene { namespace chain {
|
|||
const account_id_type& winner = *event.match.match_winners.begin();
|
||||
uint16_t rake_fee_percentage = event.db.get_global_properties().parameters.rake_fee_percentage;
|
||||
|
||||
// check whether the core asset pays dividends. If so, we transfer the rake fee
|
||||
// to the core asset's dividend account
|
||||
const asset_object& core_asset_obj = asset_id_type()(event.db);
|
||||
optional<asset_dividend_data_id_type> dividend_id = core_asset_obj.dividend_data_id;
|
||||
const asset_object & asset_obj = asset_id_type(0)(event.db);
|
||||
optional<asset_dividend_data_id_type> dividend_id = asset_obj.dividend_data_id;
|
||||
|
||||
share_type rake_amount = 0;
|
||||
if (dividend_id)
|
||||
|
|
@ -335,7 +334,15 @@ namespace graphene { namespace chain {
|
|||
event.db.push_applied_operation(op);
|
||||
}
|
||||
|
||||
if (dividend_id && rake_amount.value)
|
||||
if (rake_amount.value)
|
||||
{
|
||||
affiliate_payout_helper payout_helper( event.db, tournament_obj );
|
||||
rake_amount -= payout_helper.payout( winner, rake_amount );
|
||||
payout_helper.commit();
|
||||
FC_ASSERT( rake_amount.value >= 0 );
|
||||
}
|
||||
|
||||
if (rake_amount.value)
|
||||
{
|
||||
// Adjusting balance of dividend_distribution_account
|
||||
const asset_dividend_data_id_type& asset_dividend_data_id_= *dividend_id;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
add_subdirectory( witness )
|
||||
add_subdirectory( account_history )
|
||||
add_subdirectory( accounts_list )
|
||||
add_subdirectory( affiliate_stats )
|
||||
add_subdirectory( market_history )
|
||||
add_subdirectory( delayed_node )
|
||||
add_subdirectory( bookie )
|
||||
|
|
|
|||
|
|
@ -81,6 +81,17 @@ class account_history_plugin : public graphene::app::plugin
|
|||
std::unique_ptr<detail::account_history_plugin_impl> my;
|
||||
};
|
||||
|
||||
class affiliate_reward_index : public secondary_index
|
||||
{
|
||||
public:
|
||||
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{};
|
||||
|
||||
map<account_id_type, set<operation_history_id_type> > _history_by_account;
|
||||
};
|
||||
|
||||
} } //graphene::account_history
|
||||
|
||||
/*struct by_id;
|
||||
|
|
|
|||
24
libraries/plugins/affiliate_stats/CMakeLists.txt
Normal file
24
libraries/plugins/affiliate_stats/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
file(GLOB HEADERS "include/graphene/affiliate_stats/*.hpp")
|
||||
|
||||
add_library( graphene_affiliate_stats
|
||||
affiliate_stats_api.cpp
|
||||
affiliate_stats_plugin.cpp
|
||||
)
|
||||
|
||||
target_link_libraries( graphene_affiliate_stats graphene_chain graphene_app )
|
||||
target_include_directories( graphene_affiliate_stats
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
|
||||
if(MSVC)
|
||||
set_source_files_properties( affiliate_stats_plugin.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
|
||||
endif(MSVC)
|
||||
|
||||
install( TARGETS
|
||||
graphene_affiliate_stats
|
||||
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/affiliate_stats" )
|
||||
|
||||
136
libraries/plugins/affiliate_stats/affiliate_stats_api.cpp
Normal file
136
libraries/plugins/affiliate_stats/affiliate_stats_api.cpp
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <fc/optional.hpp>
|
||||
#include <fc/variant_object.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
#include <graphene/app/application.hpp>
|
||||
|
||||
#include <graphene/chain/block_database.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
||||
#include <graphene/affiliate_stats/affiliate_stats_api.hpp>
|
||||
|
||||
#include <graphene/account_history/account_history_plugin.hpp>
|
||||
|
||||
namespace graphene { namespace affiliate_stats {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class affiliate_stats_api_impl
|
||||
{
|
||||
public:
|
||||
affiliate_stats_api_impl(graphene::app::application& _app);
|
||||
|
||||
std::vector<top_referred_account> list_top_referred_accounts( asset_id_type asset, uint16_t limit )const
|
||||
{
|
||||
std::vector<top_referred_account> result;
|
||||
result.reserve( limit );
|
||||
auto& idx = app.chain_database()->get_index_type<referral_reward_index>().indices().get<by_asset>();
|
||||
auto itr = idx.lower_bound( boost::make_tuple( asset, share_type(GRAPHENE_MAX_SHARE_SUPPLY) ) );
|
||||
while( itr != idx.end() && itr->get_asset_id() == asset && limit-- > 0 )
|
||||
result.push_back( *itr++ );
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<top_app> list_top_rewards_per_app( asset_id_type asset, uint16_t limit )const
|
||||
{
|
||||
std::vector<top_app> result;
|
||||
result.reserve( limit );
|
||||
auto& idx = app.chain_database()->get_index_type<app_reward_index>().indices().get<by_asset>();
|
||||
auto itr = idx.lower_bound( boost::make_tuple( asset, share_type(GRAPHENE_MAX_SHARE_SUPPLY) ) );
|
||||
while( itr != idx.end() && itr->get_asset_id() == asset && limit-- > 0 )
|
||||
result.push_back( *itr++ );
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<referral_payment> list_historic_referral_rewards( account_id_type affiliate, operation_history_id_type start, uint16_t limit )const
|
||||
{
|
||||
shared_ptr<const affiliate_stats_plugin> plugin = app.get_plugin<const affiliate_stats_plugin>( "affiliate_stats" );
|
||||
|
||||
std::vector<referral_payment> result;
|
||||
const auto& list = plugin->get_reward_history( affiliate );
|
||||
result.reserve( limit );
|
||||
auto inner = list.lower_bound( start );
|
||||
while( inner != list.end() && result.size() < limit )
|
||||
result.push_back( referral_payment( (*inner++)(*app.chain_database()) ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
graphene::app::application& app;
|
||||
};
|
||||
|
||||
affiliate_stats_api_impl::affiliate_stats_api_impl(graphene::app::application& _app)
|
||||
: app(_app) {}
|
||||
|
||||
} // detail
|
||||
|
||||
top_referred_account::top_referred_account() {}
|
||||
|
||||
top_referred_account::top_referred_account( const referral_reward_object& rro )
|
||||
: referral( rro.referral ), total_payout( rro.total_payout ) {}
|
||||
|
||||
top_app::top_app() {}
|
||||
|
||||
top_app::top_app( const app_reward_object& aro )
|
||||
: app( aro.app ), total_payout( aro.total_payout ) {}
|
||||
|
||||
|
||||
affiliate_stats_api::affiliate_stats_api(graphene::app::application& app)
|
||||
: my(std::make_shared<detail::affiliate_stats_api_impl>(app)) {}
|
||||
|
||||
std::vector<top_referred_account> affiliate_stats_api::list_top_referred_accounts( asset_id_type asset, uint16_t limit )const
|
||||
{
|
||||
FC_ASSERT( limit <= 100 );
|
||||
return my->list_top_referred_accounts( asset, limit );
|
||||
}
|
||||
|
||||
std::vector<top_app> affiliate_stats_api::list_top_rewards_per_app( asset_id_type asset, uint16_t limit )const
|
||||
{
|
||||
FC_ASSERT( limit <= 100 );
|
||||
return my->list_top_rewards_per_app( asset, limit );
|
||||
}
|
||||
|
||||
std::vector<referral_payment> affiliate_stats_api::list_historic_referral_rewards( account_id_type affiliate, operation_history_id_type start, uint16_t limit )const
|
||||
{
|
||||
FC_ASSERT( limit <= 100 );
|
||||
return my->list_historic_referral_rewards( affiliate, start, limit );
|
||||
}
|
||||
|
||||
|
||||
referral_payment::referral_payment() {}
|
||||
|
||||
referral_payment::referral_payment( const operation_history_object& oho )
|
||||
: id(oho.id), block_num(oho.block_num), tag(oho.op.get<affiliate_payout_operation>().tag),
|
||||
payout(oho.op.get<affiliate_payout_operation>().payout) {}
|
||||
|
||||
} } // graphene::affiliate_stats
|
||||
|
||||
|
||||
215
libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp
Normal file
215
libraries/plugins/affiliate_stats/affiliate_stats_plugin.cpp
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <graphene/affiliate_stats/affiliate_stats_plugin.hpp>
|
||||
#include <graphene/affiliate_stats/affiliate_stats_objects.hpp>
|
||||
|
||||
#include <graphene/app/impacted.hpp>
|
||||
|
||||
#include <graphene/chain/account_evaluator.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/config.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/operation_history_object.hpp>
|
||||
#include <graphene/chain/transaction_evaluation_state.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
||||
namespace graphene { namespace affiliate_stats {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class affiliate_reward_index : public graphene::db::index_observer
|
||||
{
|
||||
public:
|
||||
affiliate_reward_index( graphene::chain::database& _db ) : db(_db) {}
|
||||
virtual void on_add( const graphene::db::object& obj ) override;
|
||||
virtual void on_remove( const graphene::db::object& obj ) override;
|
||||
virtual void on_modify( const graphene::db::object& before ) override{};
|
||||
|
||||
std::map<graphene::chain::account_id_type, std::set<graphene::chain::operation_history_id_type> > _history_by_account;
|
||||
private:
|
||||
graphene::chain::database& db;
|
||||
};
|
||||
|
||||
class affiliate_stats_plugin_impl
|
||||
{
|
||||
public:
|
||||
affiliate_stats_plugin_impl(affiliate_stats_plugin& _plugin)
|
||||
: _self( _plugin ) { }
|
||||
virtual ~affiliate_stats_plugin_impl();
|
||||
|
||||
/** this method is called as a callback after a block is applied
|
||||
* and will process/index all operations that were applied in the block.
|
||||
*/
|
||||
void update_affiliate_stats( const signed_block& b );
|
||||
|
||||
graphene::chain::database& database()
|
||||
{
|
||||
return _self.database();
|
||||
}
|
||||
|
||||
const std::set<graphene::chain::operation_history_id_type>& get_reward_history( account_id_type& affiliate )const;
|
||||
|
||||
typedef void result_type;
|
||||
template<typename Operation>
|
||||
void operator()( const Operation& op ) {}
|
||||
|
||||
shared_ptr<affiliate_reward_index> _fr_index;
|
||||
affiliate_stats_plugin& _self;
|
||||
app_reward_index* _ar_index;
|
||||
referral_reward_index* _rr_index;
|
||||
private:
|
||||
};
|
||||
|
||||
affiliate_stats_plugin_impl::~affiliate_stats_plugin_impl() {}
|
||||
|
||||
template<>
|
||||
void affiliate_stats_plugin_impl::operator()( const affiliate_payout_operation& op )
|
||||
{
|
||||
auto& by_app = _ar_index->indices().get<by_app_asset>();
|
||||
auto itr = by_app.find( boost::make_tuple( op.tag, op.payout.asset_id ) );
|
||||
if( itr == by_app.end() )
|
||||
{
|
||||
database().create<app_reward_object>( [&op]( app_reward_object& aro ) {
|
||||
aro.app = op.tag;
|
||||
aro.total_payout = op.payout;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
database().modify( *itr, [&op]( app_reward_object& aro ) {
|
||||
aro.total_payout += op.payout;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void affiliate_stats_plugin_impl::operator()( const affiliate_referral_payout_operation& op )
|
||||
{
|
||||
auto& by_referral = _rr_index->indices().get<by_referral_asset>();
|
||||
auto itr = by_referral.find( boost::make_tuple( op.player, op.payout.asset_id ) );
|
||||
if( itr == by_referral.end() )
|
||||
{
|
||||
database().create<referral_reward_object>( [&op]( referral_reward_object& rro ) {
|
||||
rro.referral = op.player;
|
||||
rro.total_payout = op.payout;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
database().modify( *itr, [&op]( referral_reward_object& rro ) {
|
||||
rro.total_payout += op.payout;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void affiliate_stats_plugin_impl::update_affiliate_stats( const signed_block& b )
|
||||
{
|
||||
vector<optional< operation_history_object > >& hist = database().get_applied_operations();
|
||||
for( optional< operation_history_object >& o_op : hist )
|
||||
{
|
||||
if( !o_op.valid() )
|
||||
continue;
|
||||
|
||||
o_op->op.visit( *this );
|
||||
}
|
||||
}
|
||||
|
||||
static const std::set<graphene::chain::operation_history_id_type> EMPTY;
|
||||
const std::set<graphene::chain::operation_history_id_type>& affiliate_stats_plugin_impl::get_reward_history( account_id_type& affiliate )const
|
||||
{
|
||||
auto itr = _fr_index->_history_by_account.find( affiliate );
|
||||
if( itr == _fr_index->_history_by_account.end() )
|
||||
return EMPTY;
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
|
||||
static optional<std::pair<account_id_type, operation_history_id_type>> get_account( const database& db, const object& obj )
|
||||
{
|
||||
FC_ASSERT( dynamic_cast<const account_transaction_history_object*>(&obj) );
|
||||
const account_transaction_history_object& ath = static_cast<const account_transaction_history_object&>(obj);
|
||||
const operation_history_object& oho = db.get<operation_history_object>( ath.operation_id );
|
||||
if( oho.op.which() == operation::tag<affiliate_payout_operation>::value )
|
||||
return std::make_pair( ath.account, ath.operation_id );
|
||||
return optional<std::pair<account_id_type, operation_history_id_type>>();
|
||||
}
|
||||
|
||||
void affiliate_reward_index::on_add( const object& obj )
|
||||
{
|
||||
optional<std::pair<account_id_type, operation_history_id_type>> acct_ath = get_account( db, obj );
|
||||
if( !acct_ath.valid() ) return;
|
||||
_history_by_account[acct_ath->first].insert( acct_ath->second );
|
||||
}
|
||||
|
||||
void affiliate_reward_index::on_remove( const object& obj )
|
||||
{
|
||||
optional<std::pair<account_id_type, operation_history_id_type>> acct_ath = get_account( db, obj );
|
||||
if( !acct_ath.valid() ) return;
|
||||
_history_by_account[acct_ath->first].erase( acct_ath->second );
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
affiliate_stats_plugin::affiliate_stats_plugin()
|
||||
: my( new detail::affiliate_stats_plugin_impl(*this) ) {}
|
||||
|
||||
affiliate_stats_plugin::~affiliate_stats_plugin() {}
|
||||
|
||||
std::string affiliate_stats_plugin::plugin_name()const
|
||||
{
|
||||
return "affiliate_stats";
|
||||
}
|
||||
|
||||
void affiliate_stats_plugin::plugin_set_program_options(
|
||||
boost::program_options::options_description& cli,
|
||||
boost::program_options::options_description& cfg
|
||||
)
|
||||
{
|
||||
cli.add_options()
|
||||
;
|
||||
cfg.add(cli);
|
||||
}
|
||||
|
||||
void affiliate_stats_plugin::plugin_initialize(const boost::program_options::variables_map& options)
|
||||
{
|
||||
database().applied_block.connect( [this]( const signed_block& b){ my->update_affiliate_stats(b); } );
|
||||
|
||||
my->_ar_index = database().add_index< primary_index< app_reward_index > >();
|
||||
my->_rr_index = database().add_index< primary_index< referral_reward_index > >();
|
||||
my->_fr_index = shared_ptr<detail::affiliate_reward_index>( new detail::affiliate_reward_index( database() ) );
|
||||
const_cast<primary_index<account_transaction_history_index>&>(database().get_index_type<primary_index<account_transaction_history_index>>()).add_observer( my->_fr_index );
|
||||
}
|
||||
|
||||
void affiliate_stats_plugin::plugin_startup() {}
|
||||
|
||||
const std::set<graphene::chain::operation_history_id_type>& affiliate_stats_plugin::get_reward_history( account_id_type& affiliate )const
|
||||
{
|
||||
return my->get_reward_history( affiliate );
|
||||
}
|
||||
|
||||
} } // graphene::affiliate_stats
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fc/api.hpp>
|
||||
#include <fc/variant_object.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/operation_history_object.hpp>
|
||||
|
||||
#include <graphene/affiliate_stats/affiliate_stats_objects.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
|
||||
namespace graphene { namespace app {
|
||||
class application;
|
||||
} }
|
||||
|
||||
namespace graphene { namespace affiliate_stats {
|
||||
|
||||
namespace detail {
|
||||
class affiliate_stats_api_impl;
|
||||
}
|
||||
|
||||
class referral_payment {
|
||||
public:
|
||||
referral_payment();
|
||||
referral_payment( const operation_history_object& oho );
|
||||
operation_history_id_type id;
|
||||
uint32_t block_num;
|
||||
app_tag tag;
|
||||
asset payout;
|
||||
};
|
||||
|
||||
class top_referred_account {
|
||||
public:
|
||||
top_referred_account();
|
||||
top_referred_account( const referral_reward_object& rro );
|
||||
|
||||
account_id_type referral;
|
||||
asset total_payout;
|
||||
};
|
||||
|
||||
class top_app {
|
||||
public:
|
||||
top_app();
|
||||
top_app( const app_reward_object& rro );
|
||||
|
||||
app_tag app;
|
||||
asset total_payout;
|
||||
};
|
||||
|
||||
class affiliate_stats_api
|
||||
{
|
||||
public:
|
||||
affiliate_stats_api(graphene::app::application& app);
|
||||
|
||||
std::vector<referral_payment> list_historic_referral_rewards( account_id_type affiliate, operation_history_id_type start, uint16_t limit = 100 )const;
|
||||
// get_pending_referral_reward() - not implemented because we have continuous payouts
|
||||
// get_previous_referral_reward() - not implemented because we have continuous payouts
|
||||
std::vector<top_referred_account> list_top_referred_accounts( asset_id_type asset, uint16_t limit = 100 )const;
|
||||
std::vector<top_app> list_top_rewards_per_app( asset_id_type asset, uint16_t limit = 100 )const;
|
||||
|
||||
std::shared_ptr<detail::affiliate_stats_api_impl> my;
|
||||
};
|
||||
|
||||
} } // graphene::affiliate_stats
|
||||
|
||||
FC_REFLECT(graphene::affiliate_stats::referral_payment, (id)(block_num)(tag)(payout) )
|
||||
FC_REFLECT(graphene::affiliate_stats::top_referred_account, (referral)(total_payout) )
|
||||
FC_REFLECT(graphene::affiliate_stats::top_app, (app)(total_payout) )
|
||||
|
||||
FC_API(graphene::affiliate_stats::affiliate_stats_api,
|
||||
(list_historic_referral_rewards)
|
||||
(list_top_referred_accounts)
|
||||
(list_top_rewards_per_app)
|
||||
)
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/affiliate_stats/affiliate_stats_plugin.hpp>
|
||||
|
||||
namespace graphene { namespace affiliate_stats {
|
||||
using namespace chain;
|
||||
|
||||
enum stats_object_type
|
||||
{
|
||||
app_reward_object_type,
|
||||
referral_reward_object_type,
|
||||
STATS_OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
class app_reward_object : public graphene::db::abstract_object<app_reward_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = AFFILIATE_STATS_SPACE_ID;
|
||||
static const uint8_t type_id = app_reward_object_type;
|
||||
|
||||
app_tag app;
|
||||
asset total_payout;
|
||||
|
||||
inline share_type get_amount()const { return total_payout.amount; }
|
||||
inline asset_id_type get_asset_id()const { return total_payout.asset_id; }
|
||||
};
|
||||
|
||||
typedef object_id<AFFILIATE_STATS_SPACE_ID, app_reward_object_type, app_reward_object> app_reward_id_type;
|
||||
|
||||
struct by_asset;
|
||||
struct by_app_asset;
|
||||
typedef multi_index_container<
|
||||
app_reward_object,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
|
||||
ordered_non_unique<tag<by_asset>,
|
||||
composite_key<
|
||||
app_reward_object,
|
||||
const_mem_fun<app_reward_object, asset_id_type, &app_reward_object::get_asset_id>,
|
||||
const_mem_fun<app_reward_object, share_type, &app_reward_object::get_amount> >,
|
||||
composite_key_compare<
|
||||
std::less<asset_id_type>,
|
||||
std::greater<share_type> >
|
||||
>,
|
||||
ordered_unique<tag<by_app_asset>,
|
||||
composite_key<
|
||||
app_reward_object,
|
||||
member<app_reward_object, app_tag, &app_reward_object::app>,
|
||||
const_mem_fun<app_reward_object, asset_id_type, &app_reward_object::get_asset_id> >
|
||||
> > > app_reward_multi_index_type;
|
||||
typedef generic_index<app_reward_object, app_reward_multi_index_type> app_reward_index;
|
||||
|
||||
class referral_reward_object : public graphene::db::abstract_object<referral_reward_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = AFFILIATE_STATS_SPACE_ID;
|
||||
static const uint8_t type_id = referral_reward_object_type;
|
||||
|
||||
account_id_type referral;
|
||||
asset total_payout;
|
||||
|
||||
inline share_type get_amount()const { return total_payout.amount; }
|
||||
inline asset_id_type get_asset_id()const { return total_payout.asset_id; }
|
||||
};
|
||||
|
||||
typedef object_id<AFFILIATE_STATS_SPACE_ID, referral_reward_object_type, referral_reward_object> referral_reward_id_type;
|
||||
|
||||
struct by_referral_asset;
|
||||
typedef multi_index_container<
|
||||
referral_reward_object,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
|
||||
ordered_non_unique<tag<by_asset>,
|
||||
composite_key<
|
||||
referral_reward_object,
|
||||
const_mem_fun<referral_reward_object, asset_id_type, &referral_reward_object::get_asset_id>,
|
||||
const_mem_fun<referral_reward_object, share_type, &referral_reward_object::get_amount> >,
|
||||
composite_key_compare<
|
||||
std::less<asset_id_type>,
|
||||
std::greater<share_type> >
|
||||
>,
|
||||
ordered_unique<tag<by_referral_asset>,
|
||||
composite_key<
|
||||
referral_reward_object,
|
||||
member<referral_reward_object, account_id_type, &referral_reward_object::referral>,
|
||||
const_mem_fun<referral_reward_object, asset_id_type, &referral_reward_object::get_asset_id> >
|
||||
> > > referral_reward_multi_index_type;
|
||||
typedef generic_index<referral_reward_object, referral_reward_multi_index_type> referral_reward_index;
|
||||
|
||||
} } //graphene::affiliate_stats
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::affiliate_stats::app_reward_object, (graphene::db::object), (app)(total_payout) )
|
||||
FC_REFLECT_DERIVED( graphene::affiliate_stats::referral_reward_object, (graphene::db::object), (referral)(total_payout) )
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/app/plugin.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
#include <fc/thread/future.hpp>
|
||||
|
||||
namespace graphene { namespace affiliate_stats {
|
||||
using namespace chain;
|
||||
|
||||
//
|
||||
// Plugins should #define their SPACE_ID's so plugins with
|
||||
// conflicting SPACE_ID assignments can be compiled into the
|
||||
// same binary (by simply re-assigning some of the conflicting #defined
|
||||
// SPACE_ID's in a build script).
|
||||
//
|
||||
// Assignment of SPACE_ID's cannot be done at run-time because
|
||||
// various template automagic depends on them being known at compile
|
||||
// time.
|
||||
//
|
||||
#ifndef AFFILIATE_STATS_SPACE_ID
|
||||
#define AFFILIATE_STATS_SPACE_ID 7
|
||||
#endif
|
||||
|
||||
namespace detail
|
||||
{
|
||||
class affiliate_stats_plugin_impl;
|
||||
}
|
||||
|
||||
class affiliate_stats_plugin : public graphene::app::plugin
|
||||
{
|
||||
public:
|
||||
affiliate_stats_plugin();
|
||||
virtual ~affiliate_stats_plugin();
|
||||
|
||||
std::string plugin_name()const override;
|
||||
virtual void plugin_set_program_options(
|
||||
boost::program_options::options_description& cli,
|
||||
boost::program_options::options_description& cfg) override;
|
||||
virtual void plugin_initialize(const boost::program_options::variables_map& options) override;
|
||||
virtual void plugin_startup() override;
|
||||
|
||||
const std::set<graphene::chain::operation_history_id_type>& get_reward_history( account_id_type& affiliate )const;
|
||||
|
||||
friend class detail::affiliate_stats_plugin_impl;
|
||||
std::unique_ptr<detail::affiliate_stats_plugin_impl> my;
|
||||
};
|
||||
|
||||
} } //graphene::affiliate_stats
|
||||
|
|
@ -11,7 +11,7 @@ endif()
|
|||
|
||||
# We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246
|
||||
target_link_libraries( witness_node
|
||||
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full graphene_snapshot fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
|
||||
PRIVATE graphene_app graphene_account_history graphene_affiliate_stats graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
|
||||
# also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins
|
||||
|
||||
install( TARGETS
|
||||
|
|
|
|||
|
|
@ -29,9 +29,10 @@
|
|||
#include <graphene/market_history/market_history_plugin.hpp>
|
||||
//#include <graphene/generate_genesis/generate_genesis_plugin.hpp>
|
||||
//#include <graphene/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.hpp>
|
||||
#include <graphene/affiliate_stats/affiliate_stats_plugin.hpp>
|
||||
#include <graphene/bookie/bookie_plugin.hpp>
|
||||
#include <graphene/utilities/git_revision.hpp>
|
||||
#include <graphene/snapshot/snapshot.hpp>
|
||||
//#include <graphene/snapshot/snapshot.hpp>
|
||||
|
||||
#include <fc/exception/exception.hpp>
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
|
@ -84,8 +85,9 @@ int main(int argc, char** argv) {
|
|||
//auto generate_genesis_plug = node->register_plugin<generate_genesis_plugin::generate_genesis_plugin>();
|
||||
//auto generate_uia_sharedrop_genesis_plug = node->register_plugin<generate_uia_sharedrop_genesis::generate_uia_sharedrop_genesis_plugin>();
|
||||
auto list_plug = node->register_plugin<accounts_list::accounts_list_plugin>();
|
||||
auto affiliate_stats_plug = node->register_plugin<affiliate_stats::affiliate_stats_plugin>();
|
||||
auto bookie_plug = node->register_plugin<bookie::bookie_plugin>();
|
||||
auto snapshot_plug = node->register_plugin<snapshot_plugin::snapshot_plugin>();
|
||||
// auto snapshot_plug = node->register_plugin<snapshot_plugin::snapshot_plugin>();
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
#include <boost/scoped_ptr.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include "../common/database_fixture.hpp"
|
||||
#include "../common/betting_test_markets.hpp"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <fc/crypto/openssl.hpp>
|
||||
|
|
@ -41,14 +41,9 @@
|
|||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
#include <graphene/chain/sport_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/event_group_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
|
||||
#include <graphene/bookie/bookie_api.hpp>
|
||||
//#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
struct enable_betting_logging_config {
|
||||
enable_betting_logging_config()
|
||||
|
|
@ -1382,40 +1377,6 @@ BOOST_AUTO_TEST_CASE( cancel_one_event_in_group )
|
|||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
// set up a fixture that places a series of two matched bets, we'll use this fixture to verify
|
||||
// the result in all three possible outcomes
|
||||
struct simple_bet_test_fixture : database_fixture {
|
||||
betting_market_id_type capitals_win_betting_market_id;
|
||||
betting_market_id_type blackhawks_win_betting_market_id;
|
||||
betting_market_group_id_type moneyline_betting_markets_id;
|
||||
|
||||
simple_bet_test_fixture()
|
||||
{
|
||||
ACTORS( (alice)(bob) );
|
||||
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
|
||||
|
||||
// give alice and bob 10k each
|
||||
transfer(account_id_type(), alice_id, asset(10000));
|
||||
transfer(account_id_type(), bob_id, asset(10000));
|
||||
|
||||
// place bets at 10:1
|
||||
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
|
||||
// reverse positions at 1:1
|
||||
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
|
||||
capitals_win_betting_market_id = capitals_win_market.id;
|
||||
blackhawks_win_betting_market_id = blackhawks_win_market.id;
|
||||
moneyline_betting_markets_id = moneyline_betting_markets.id;
|
||||
|
||||
// close betting to prepare for the next operation which will be grading or cancel
|
||||
update_betting_market_group(moneyline_betting_markets.id, _status = betting_market_group_status::closed);
|
||||
generate_blocks(1);
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( simple_bet_tests, simple_bet_test_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( win )
|
||||
|
|
|
|||
173
tests/common/betting_test_markets.hpp
Normal file
173
tests/common/betting_test_markets.hpp
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "database_fixture.hpp"
|
||||
|
||||
#include <graphene/chain/sport_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/event_group_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
|
||||
#define CREATE_ICE_HOCKEY_BETTING_MARKET(never_in_play, delay_before_settling) \
|
||||
create_sport({{"en", "Ice Hockey"}, {"zh_Hans", "冰球"}, {"ja", "アイスホッケー"}}); \
|
||||
generate_blocks(1); \
|
||||
const sport_object& ice_hockey = *db.get_index_type<sport_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_event_group({{"en", "NHL"}, {"zh_Hans", "國家冰球聯盟"}, {"ja", "ナショナルホッケーリーグ"}}, ice_hockey.id); \
|
||||
generate_blocks(1); \
|
||||
const event_group_object& nhl = *db.get_index_type<event_group_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_event({{"en", "Washington Capitals/Chicago Blackhawks"}, {"zh_Hans", "華盛頓首都隊/芝加哥黑鷹"}, {"ja", "ワシントン・キャピタルズ/シカゴ・ブラックホークス"}}, {{"en", "2016-17"}}, nhl.id); \
|
||||
generate_blocks(1); \
|
||||
const event_object& capitals_vs_blackhawks = *db.get_index_type<event_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market_rules({{"en", "NHL Rules v1.0"}}, {{"en", "The winner will be the team with the most points at the end of the game. The team with fewer points will not be the winner."}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_rules_object& betting_market_rules = *db.get_index_type<betting_market_rules_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market_group({{"en", "Moneyline"}}, capitals_vs_blackhawks.id, betting_market_rules.id, asset_id_type(), never_in_play, delay_before_settling); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_group_object& moneyline_betting_markets = *db.get_index_type<betting_market_group_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(moneyline_betting_markets.id, {{"en", "Washington Capitals win"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& capitals_win_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(moneyline_betting_markets.id, {{"en", "Chicago Blackhawks win"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& blackhawks_win_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
(void)capitals_win_market; (void)blackhawks_win_market;
|
||||
|
||||
// create the basic betting market, plus groups for the first, second, and third period results
|
||||
#define CREATE_EXTENDED_ICE_HOCKEY_BETTING_MARKET(never_in_play, delay_before_settling) \
|
||||
CREATE_ICE_HOCKEY_BETTING_MARKET(never_in_play, delay_before_settling) \
|
||||
create_betting_market_group({{"en", "First Period Result"}}, capitals_vs_blackhawks.id, betting_market_rules.id, asset_id_type(), never_in_play, delay_before_settling); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_group_object& first_period_result_betting_markets = *db.get_index_type<betting_market_group_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(first_period_result_betting_markets.id, {{"en", "Washington Capitals win"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& first_period_capitals_win_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(first_period_result_betting_markets.id, {{"en", "Chicago Blackhawks win"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& first_period_blackhawks_win_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
(void)first_period_capitals_win_market; (void)first_period_blackhawks_win_market; \
|
||||
\
|
||||
create_betting_market_group({{"en", "Second Period Result"}}, capitals_vs_blackhawks.id, betting_market_rules.id, asset_id_type(), never_in_play, delay_before_settling); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_group_object& second_period_result_betting_markets = *db.get_index_type<betting_market_group_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(second_period_result_betting_markets.id, {{"en", "Washington Capitals win"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& second_period_capitals_win_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(second_period_result_betting_markets.id, {{"en", "Chicago Blackhawks win"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& second_period_blackhawks_win_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
(void)second_period_capitals_win_market; (void)second_period_blackhawks_win_market; \
|
||||
\
|
||||
create_betting_market_group({{"en", "Third Period Result"}}, capitals_vs_blackhawks.id, betting_market_rules.id, asset_id_type(), never_in_play, delay_before_settling); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_group_object& third_period_result_betting_markets = *db.get_index_type<betting_market_group_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(third_period_result_betting_markets.id, {{"en", "Washington Capitals win"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& third_period_capitals_win_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(third_period_result_betting_markets.id, {{"en", "Chicago Blackhawks win"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& third_period_blackhawks_win_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
(void)third_period_capitals_win_market; (void)third_period_blackhawks_win_market;
|
||||
|
||||
#define CREATE_TENNIS_BETTING_MARKET() \
|
||||
create_betting_market_rules({{"en", "Tennis Rules v1.0"}}, {{"en", "The winner is the player who wins the last ball in the match."}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_rules_object& tennis_rules = *db.get_index_type<betting_market_rules_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_sport({{"en", "Tennis"}}); \
|
||||
generate_blocks(1); \
|
||||
const sport_object& tennis = *db.get_index_type<sport_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_event_group({{"en", "Wimbledon"}}, tennis.id); \
|
||||
generate_blocks(1); \
|
||||
const event_group_object& wimbledon = *db.get_index_type<event_group_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_event({{"en", "R. Federer/T. Berdych"}}, {{"en", "2017"}}, wimbledon.id); \
|
||||
generate_blocks(1); \
|
||||
const event_object& berdych_vs_federer = *db.get_index_type<event_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_event({{"en", "M. Cilic/S. Querrye"}}, {{"en", "2017"}}, wimbledon.id); \
|
||||
generate_blocks(1); \
|
||||
const event_object& cilic_vs_querrey = *db.get_index_type<event_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market_group({{"en", "Moneyline 1st sf"}}, berdych_vs_federer.id, tennis_rules.id, asset_id_type(), false, 0); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_group_object& moneyline_berdych_vs_federer = *db.get_index_type<betting_market_group_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market_group({{"en", "Moneyline 2nd sf"}}, cilic_vs_querrey.id, tennis_rules.id, asset_id_type(), false, 0); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_group_object& moneyline_cilic_vs_querrey = *db.get_index_type<betting_market_group_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(moneyline_berdych_vs_federer.id, {{"en", "T. Berdych defeats R. Federer"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& berdych_wins_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(moneyline_berdych_vs_federer.id, {{"en", "R. Federer defeats T. Berdych"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& federer_wins_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(moneyline_cilic_vs_querrey.id, {{"en", "M. Cilic defeats S. Querrey"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& cilic_wins_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(moneyline_cilic_vs_querrey.id, {{"en", "S. Querrey defeats M. Cilic"}});\
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& querrey_wins_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_event({{"en", "R. Federer/M. Cilic"}}, {{"en", "2017"}}, wimbledon.id); \
|
||||
generate_blocks(1); \
|
||||
const event_object& cilic_vs_federer = *db.get_index_type<event_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market_group({{"en", "Moneyline final"}}, cilic_vs_federer.id, tennis_rules.id, asset_id_type(), false, 0); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_group_object& moneyline_cilic_vs_federer = *db.get_index_type<betting_market_group_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(moneyline_cilic_vs_federer.id, {{"en", "R. Federer defeats M. Cilic"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& federer_wins_final_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market(moneyline_cilic_vs_federer.id, {{"en", "M. Cilic defeats R. Federer"}}); \
|
||||
generate_blocks(1); \
|
||||
const betting_market_object& cilic_wins_final_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin(); \
|
||||
(void)federer_wins_market;(void)cilic_wins_market;(void)federer_wins_final_market; (void)cilic_wins_final_market; (void)berdych_wins_market; (void)querrey_wins_market;
|
||||
|
||||
// set up a fixture that places a series of two matched bets, we'll use this fixture to verify
|
||||
// the result in all three possible outcomes
|
||||
struct simple_bet_test_fixture : database_fixture {
|
||||
betting_market_id_type capitals_win_betting_market_id;
|
||||
betting_market_id_type blackhawks_win_betting_market_id;
|
||||
betting_market_group_id_type moneyline_betting_markets_id;
|
||||
|
||||
simple_bet_test_fixture()
|
||||
{
|
||||
ACTORS( (alice)(bob) );
|
||||
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
|
||||
|
||||
// give alice and bob 10k each
|
||||
transfer(account_id_type(), alice_id, asset(10000));
|
||||
transfer(account_id_type(), bob_id, asset(10000));
|
||||
|
||||
// place bets at 10:1
|
||||
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
|
||||
// reverse positions at 1:1
|
||||
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
|
||||
capitals_win_betting_market_id = capitals_win_market.id;
|
||||
blackhawks_win_betting_market_id = blackhawks_win_market.id;
|
||||
moneyline_betting_markets_id = moneyline_betting_markets.id;
|
||||
|
||||
// close betting to prepare for the next operation which will be grading or cancel
|
||||
update_betting_market_group(moneyline_betting_markets.id, graphene::chain::keywords::_status = betting_market_group_status::closed);
|
||||
generate_blocks(1);
|
||||
}
|
||||
};
|
||||
|
|
@ -28,6 +28,7 @@
|
|||
#include <graphene/market_history/market_history_plugin.hpp>
|
||||
#include <graphene/bookie/bookie_plugin.hpp>
|
||||
#include <graphene/bookie/bookie_api.hpp>
|
||||
#include <graphene/affiliate_stats/affiliate_stats_plugin.hpp>
|
||||
|
||||
#include <graphene/db/simple_index.hpp>
|
||||
|
||||
|
|
@ -83,6 +84,7 @@ database_fixture::database_fixture()
|
|||
auto ahplugin = app.register_plugin<graphene::account_history::account_history_plugin>();
|
||||
auto mhplugin = app.register_plugin<graphene::market_history::market_history_plugin>();
|
||||
auto bookieplugin = app.register_plugin<graphene::bookie::bookie_plugin>();
|
||||
auto affiliateplugin = app.register_plugin<graphene::affiliate_stats::affiliate_stats_plugin>();
|
||||
init_account_pub_key = init_account_priv_key.get_public_key();
|
||||
|
||||
boost::program_options::variables_map options;
|
||||
|
|
@ -114,10 +116,13 @@ database_fixture::database_fixture()
|
|||
mhplugin->plugin_initialize(options);
|
||||
bookieplugin->plugin_set_app(&app);
|
||||
bookieplugin->plugin_initialize(options);
|
||||
affiliateplugin->plugin_set_app(&app);
|
||||
affiliateplugin->plugin_initialize(options);
|
||||
|
||||
ahplugin->plugin_startup();
|
||||
mhplugin->plugin_startup();
|
||||
bookieplugin->plugin_startup();
|
||||
affiliateplugin->plugin_startup();
|
||||
|
||||
generate_block();
|
||||
|
||||
|
|
@ -451,7 +456,7 @@ const asset_object& database_fixture::get_asset( const string& symbol )const
|
|||
{
|
||||
const auto& idx = db.get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
const auto itr = idx.find(symbol);
|
||||
assert( itr != idx.end() );
|
||||
FC_ASSERT( itr != idx.end() );
|
||||
return *itr;
|
||||
}
|
||||
|
||||
|
|
@ -459,7 +464,7 @@ const account_object& database_fixture::get_account( const string& name )const
|
|||
{
|
||||
const auto& idx = db.get_index_type<account_index>().indices().get<by_name>();
|
||||
const auto itr = idx.find(name);
|
||||
assert( itr != idx.end() );
|
||||
FC_ASSERT( itr != idx.end() );
|
||||
return *itr;
|
||||
}
|
||||
|
||||
|
|
|
|||
433
tests/common/tournament_helper.cpp
Normal file
433
tests/common/tournament_helper.cpp
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "tournament_helper.hpp"
|
||||
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
|
||||
#include <fc/crypto/rand.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
|
||||
tournaments_helper::tournaments_helper(database_fixture& df) : df(df)
|
||||
{
|
||||
assets.insert(asset_id_type());
|
||||
current_asset_idx = 0;
|
||||
optional<account_id_type> dividend_account = get_asset_dividend_account(asset_id_type());
|
||||
if (dividend_account.valid())
|
||||
players.insert(*dividend_account);
|
||||
}
|
||||
|
||||
const std::set<tournament_id_type>& tournaments_helper::list_tournaments()const
|
||||
{
|
||||
return tournaments;
|
||||
}
|
||||
|
||||
const std::map<account_id_type, std::map<asset_id_type, share_type>> tournaments_helper::list_players_balances()const
|
||||
{
|
||||
std::map<account_id_type, std::map<asset_id_type, share_type>> result;
|
||||
for (account_id_type player_id: players)
|
||||
{
|
||||
for( asset_id_type asset_id: assets)
|
||||
{
|
||||
asset a = df.db.get_balance(player_id, asset_id);
|
||||
result[player_id][a.asset_id] = a.amount;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::map<account_id_type, std::map<asset_id_type, share_type>> tournaments_helper::get_players_fees()const
|
||||
{
|
||||
return players_fees;
|
||||
}
|
||||
|
||||
void tournaments_helper::reset_players_fees()
|
||||
{
|
||||
for (account_id_type player_id: players)
|
||||
{
|
||||
for( asset_id_type asset_id: assets)
|
||||
{
|
||||
players_fees[player_id][asset_id] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tournaments_helper::create_asset(const account_id_type& issuer_account_id,
|
||||
const string& symbol,
|
||||
uint8_t precision,
|
||||
asset_options& common,
|
||||
const fc::ecc::private_key& sig_priv_key)
|
||||
{
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
signed_transaction tx;
|
||||
asset_create_operation op;
|
||||
op.issuer = issuer_account_id;
|
||||
op.symbol = symbol;
|
||||
op.precision = precision;
|
||||
op.common_options = common;
|
||||
|
||||
tx.operations = {op};
|
||||
for( auto& op : tx.operations )
|
||||
db.current_fee_schedule().set_fee(op);
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, sig_priv_key);
|
||||
PUSH_TX(db, tx);
|
||||
|
||||
assets.insert(asset_id_type(++current_asset_idx));
|
||||
}
|
||||
|
||||
void tournaments_helper::update_dividend_asset(const asset_id_type asset_to_update_id,
|
||||
dividend_asset_options new_options,
|
||||
const fc::ecc::private_key& sig_priv_key)
|
||||
{
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
signed_transaction tx;
|
||||
asset_update_dividend_operation update_op;
|
||||
|
||||
update_op.issuer = asset_to_update_id(db).issuer;
|
||||
update_op.asset_to_update = asset_to_update_id;
|
||||
update_op.new_options = new_options;
|
||||
|
||||
tx.operations = {update_op};
|
||||
for( auto& op : tx.operations )
|
||||
db.current_fee_schedule().set_fee(op);
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, sig_priv_key);
|
||||
PUSH_TX(db, tx);
|
||||
|
||||
optional<account_id_type> dividend_account = get_asset_dividend_account(asset_to_update_id);
|
||||
if (dividend_account.valid())
|
||||
players.insert(*dividend_account);
|
||||
}
|
||||
|
||||
optional<account_id_type> tournaments_helper::get_asset_dividend_account(const asset_id_type& asset_id)const
|
||||
{
|
||||
graphene::chain::database& db = df.db;
|
||||
optional<account_id_type> result;
|
||||
const asset_object& asset_obj = asset_id_type()(db);
|
||||
|
||||
if (asset_obj.dividend_data_id.valid())
|
||||
{
|
||||
const asset_dividend_data_object& dividend_data = (*asset_obj.dividend_data_id)(db);
|
||||
result = dividend_data.dividend_distribution_account;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const tournament_id_type tournaments_helper::create_tournament (const account_id_type& creator,
|
||||
const fc::ecc::private_key& sig_priv_key,
|
||||
asset buy_in,
|
||||
uint32_t number_of_players,
|
||||
uint32_t time_per_commit_move,
|
||||
uint32_t time_per_reveal_move,
|
||||
uint32_t number_of_wins,
|
||||
int32_t registration_deadline,
|
||||
uint32_t start_delay,
|
||||
uint32_t round_delay,
|
||||
bool insurance_enabled,
|
||||
uint32_t number_of_gestures,
|
||||
uint32_t start_time,
|
||||
fc::optional<flat_set<account_id_type> > whitelist
|
||||
)
|
||||
{
|
||||
if (current_tournament_idx.valid())
|
||||
current_tournament_idx = *current_tournament_idx + 1;
|
||||
else
|
||||
current_tournament_idx = 0;
|
||||
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
signed_transaction trx;
|
||||
tournament_options options;
|
||||
tournament_create_operation op;
|
||||
rock_paper_scissors_game_options& game_options = options.game_options.get<rock_paper_scissors_game_options>();
|
||||
|
||||
game_options.number_of_gestures = number_of_gestures;
|
||||
game_options.time_per_commit_move = time_per_commit_move;
|
||||
game_options.time_per_reveal_move = time_per_reveal_move;
|
||||
game_options.insurance_enabled = insurance_enabled;
|
||||
|
||||
options.registration_deadline = db.head_block_time() + fc::seconds(registration_deadline + *current_tournament_idx);
|
||||
options.buy_in = buy_in;
|
||||
options.number_of_players = number_of_players;
|
||||
if (start_delay)
|
||||
options.start_delay = start_delay;
|
||||
if (start_time)
|
||||
options.start_time = db.head_block_time() + fc::seconds(start_time);
|
||||
options.round_delay = round_delay;
|
||||
options.number_of_wins = number_of_wins;
|
||||
if (whitelist.valid())
|
||||
options.whitelist = *whitelist;
|
||||
|
||||
op.creator = creator;
|
||||
op.options = options;
|
||||
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));
|
||||
df.sign(trx, sig_priv_key);
|
||||
PUSH_TX(db, trx);
|
||||
|
||||
tournament_id_type tournament_id = tournament_id_type(*current_tournament_idx);
|
||||
tournaments.insert(tournament_id);
|
||||
return tournament_id;
|
||||
}
|
||||
|
||||
void tournaments_helper::join_tournament(const tournament_id_type & tournament_id,
|
||||
const account_id_type& player_id,
|
||||
const account_id_type& payer_id,
|
||||
const fc::ecc::private_key& sig_priv_key,
|
||||
asset buy_in
|
||||
)
|
||||
{
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
signed_transaction tx;
|
||||
tournament_join_operation op;
|
||||
|
||||
op.payer_account_id = payer_id;
|
||||
op.buy_in = buy_in;
|
||||
op.player_account_id = player_id;
|
||||
op.tournament_id = tournament_id;
|
||||
tx.operations = {op};
|
||||
for( auto& op : tx.operations )
|
||||
db.current_fee_schedule().set_fee(op);
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, sig_priv_key);
|
||||
PUSH_TX(db, tx);
|
||||
|
||||
players.insert(player_id);
|
||||
players_keys[player_id] = sig_priv_key;
|
||||
}
|
||||
|
||||
void tournaments_helper::leave_tournament(const tournament_id_type & tournament_id,
|
||||
const account_id_type& player_id,
|
||||
const account_id_type& canceling_account_id,
|
||||
const fc::ecc::private_key& sig_priv_key
|
||||
)
|
||||
{
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
signed_transaction tx;
|
||||
tournament_leave_operation op;
|
||||
|
||||
op.canceling_account_id = canceling_account_id;
|
||||
op.player_account_id = player_id;
|
||||
op.tournament_id = tournament_id;
|
||||
tx.operations = {op};
|
||||
for( auto& op : tx.operations )
|
||||
db.current_fee_schedule().set_fee(op);
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, sig_priv_key);
|
||||
PUSH_TX(db, tx);
|
||||
|
||||
//players.erase(player_id);
|
||||
}
|
||||
|
||||
// stolen from cli_wallet
|
||||
void tournaments_helper::rps_throw(const game_id_type& game_id,
|
||||
const account_id_type& player_id,
|
||||
rock_paper_scissors_gesture gesture,
|
||||
const fc::ecc::private_key& sig_priv_key
|
||||
)
|
||||
{
|
||||
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
|
||||
// check whether the gesture is appropriate for the game we're playing
|
||||
game_object game_obj = game_id(db);
|
||||
match_object match_obj = game_obj.match_id(db);
|
||||
tournament_object tournament_obj = match_obj.tournament_id(db);
|
||||
rock_paper_scissors_game_options game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
|
||||
FC_ASSERT( (int)gesture < game_options.number_of_gestures );
|
||||
|
||||
account_object player_account_obj = player_id(db);
|
||||
|
||||
// construct the complete throw, the commit, and reveal
|
||||
rock_paper_scissors_throw full_throw;
|
||||
fc::rand_bytes((char*)&full_throw.nonce1, sizeof(full_throw.nonce1));
|
||||
fc::rand_bytes((char*)&full_throw.nonce2, sizeof(full_throw.nonce2));
|
||||
full_throw.gesture = gesture;
|
||||
|
||||
rock_paper_scissors_throw_commit commit_throw;
|
||||
commit_throw.nonce1 = full_throw.nonce1;
|
||||
std::vector<char> full_throw_packed(fc::raw::pack(full_throw));
|
||||
commit_throw.throw_hash = fc::sha256::hash(full_throw_packed.data(), full_throw_packed.size());
|
||||
|
||||
rock_paper_scissors_throw_reveal reveal_throw;
|
||||
reveal_throw.nonce2 = full_throw.nonce2;
|
||||
reveal_throw.gesture = full_throw.gesture;
|
||||
|
||||
// store off the reveal for applying after both players commit
|
||||
committed_game_moves[commit_throw] = reveal_throw;
|
||||
latest_committs[player_account_obj.id] = commit_throw;
|
||||
|
||||
signed_transaction tx;
|
||||
game_move_operation move_operation;
|
||||
move_operation.game_id = game_id;
|
||||
move_operation.player_account_id = player_account_obj.id;
|
||||
move_operation.move = commit_throw;
|
||||
tx.operations = {move_operation};
|
||||
for( operation& op : tx.operations )
|
||||
{
|
||||
asset f = db.current_fee_schedule().set_fee(op);
|
||||
players_fees[player_id][f.asset_id] -= f.amount;
|
||||
}
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, sig_priv_key);
|
||||
if (/*match_obj.match_winners.empty() &&*/ game_obj.get_state() == game_state::expecting_commit_moves) // checking again
|
||||
PUSH_TX(db, tx);
|
||||
}
|
||||
|
||||
void tournaments_helper::rps_reveal( const game_id_type& game_id,
|
||||
const account_id_type& player_id,
|
||||
rock_paper_scissors_gesture gesture,
|
||||
const fc::ecc::private_key& sig_priv_key )
|
||||
{
|
||||
graphene::chain::database& db = df.db;
|
||||
|
||||
FC_ASSERT( latest_committs.find(player_id) != latest_committs.end() );
|
||||
const auto& reveal = committed_game_moves.find( latest_committs[player_id] );
|
||||
FC_ASSERT( reveal != committed_game_moves.end() );
|
||||
FC_ASSERT( reveal->second.gesture == gesture );
|
||||
|
||||
game_move_operation move_operation;
|
||||
move_operation.game_id = game_id;
|
||||
move_operation.player_account_id = player_id;
|
||||
move_operation.move = reveal->second;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( move_operation );
|
||||
const asset f = db.current_fee_schedule().set_fee( tx.operations[0] );
|
||||
players_fees[player_id][f.asset_id] -= f.amount;
|
||||
tx.validate();
|
||||
test::set_expiration( db, tx );
|
||||
df.sign( tx, sig_priv_key );
|
||||
PUSH_TX(db, tx);
|
||||
}
|
||||
|
||||
// spaghetti programming
|
||||
// walking through all tournaments, matches and games and throwing random moves
|
||||
// optionaly skip generting randomly selected moves
|
||||
// every_move_is >= 0 : every game is tie
|
||||
void tournaments_helper::play_games(unsigned skip_some_commits, unsigned skip_some_reveals, int every_move_is)
|
||||
{
|
||||
//try
|
||||
//{
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
|
||||
for(const auto& tournament_id: tournaments)
|
||||
{
|
||||
const tournament_object& tournament = tournament_id(db);
|
||||
const tournament_details_object& tournament_details = tournament.tournament_details_id(db);
|
||||
rock_paper_scissors_game_options game_options = tournament.options.game_options.get<rock_paper_scissors_game_options>();
|
||||
for(const auto& match_id: tournament_details.matches)
|
||||
{
|
||||
const match_object& match = match_id(db);
|
||||
for(const auto& game_id: match.games )
|
||||
{
|
||||
const game_object& game = game_id(db);
|
||||
const rock_paper_scissors_game_details& rps_details = game.game_details.get<rock_paper_scissors_game_details>();
|
||||
if (game.get_state() == game_state::expecting_commit_moves)
|
||||
{
|
||||
for(const auto& player_id: game.players)
|
||||
{
|
||||
if ( players_keys.find(player_id) != players_keys.end())
|
||||
{
|
||||
if (!skip_some_commits || player_id.instance.value % skip_some_commits != game_id.instance.value % skip_some_commits)
|
||||
{
|
||||
auto iter = std::find(game.players.begin(), game.players.end(), player_id);
|
||||
unsigned player_index = std::distance(game.players.begin(), iter);
|
||||
if (!rps_details.commit_moves.at(player_index))
|
||||
rps_throw(game_id, player_id,
|
||||
(rock_paper_scissors_gesture) (every_move_is >= 0 ? every_move_is : (std::rand() % game_options.number_of_gestures)), players_keys[player_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (game.get_state() == game_state::expecting_reveal_moves)
|
||||
{
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{
|
||||
if (rps_details.commit_moves.at(i) &&
|
||||
!rps_details.reveal_moves.at(i))
|
||||
{
|
||||
const account_id_type& player_id = game.players[i];
|
||||
if (players_keys.find(player_id) != players_keys.end())
|
||||
{
|
||||
{
|
||||
|
||||
auto iter = committed_game_moves.find(*rps_details.commit_moves.at(i));
|
||||
if (iter != committed_game_moves.end())
|
||||
{
|
||||
if (!skip_some_reveals || player_id.instance.value % skip_some_reveals != game_id.instance.value % skip_some_reveals)
|
||||
{
|
||||
const rock_paper_scissors_throw_reveal& reveal = iter->second;
|
||||
|
||||
game_move_operation move_operation;
|
||||
move_operation.game_id = game.id;
|
||||
move_operation.player_account_id = player_id;
|
||||
move_operation.move = reveal;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations = {move_operation};
|
||||
for( auto& op : tx.operations )
|
||||
{
|
||||
asset f = db.current_fee_schedule().set_fee(op);
|
||||
players_fees[player_id][f.asset_id] -= f.amount;
|
||||
}
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, players_keys[player_id]);
|
||||
if (game.get_state() == game_state::expecting_reveal_moves) // check again
|
||||
PUSH_TX(db, tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
//catch (fc::exception& e)
|
||||
//{
|
||||
// edump((e.to_detail_string()));
|
||||
// throw;
|
||||
//}
|
||||
}
|
||||
123
tests/common/tournament_helper.hpp
Normal file
123
tests/common/tournament_helper.hpp
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
using namespace graphene::chain;
|
||||
|
||||
// class performing operations necessary for creating tournaments,
|
||||
// having players join the tournaments and playing tournaments to completion.
|
||||
class tournaments_helper
|
||||
{
|
||||
public:
|
||||
|
||||
tournaments_helper(database_fixture& df);
|
||||
|
||||
const std::set<tournament_id_type>& list_tournaments()const;
|
||||
|
||||
const std::map<account_id_type, std::map<asset_id_type, share_type>> list_players_balances()const;
|
||||
|
||||
const std::map<account_id_type, std::map<asset_id_type, share_type>> get_players_fees()const;
|
||||
|
||||
void reset_players_fees();
|
||||
|
||||
void create_asset(const account_id_type& issuer_account_id,
|
||||
const string& symbol,
|
||||
uint8_t precision,
|
||||
asset_options& common,
|
||||
const fc::ecc::private_key& sig_priv_key);
|
||||
|
||||
void update_dividend_asset(const asset_id_type asset_to_update_id,
|
||||
dividend_asset_options new_options,
|
||||
const fc::ecc::private_key& sig_priv_key);
|
||||
|
||||
optional<account_id_type> get_asset_dividend_account( const asset_id_type& asset_id = asset_id_type() )const;
|
||||
|
||||
const tournament_id_type create_tournament( const account_id_type& creator,
|
||||
const fc::ecc::private_key& sig_priv_key,
|
||||
asset buy_in,
|
||||
uint32_t number_of_players = 2,
|
||||
uint32_t time_per_commit_move = 3,
|
||||
uint32_t time_per_reveal_move = 1,
|
||||
uint32_t number_of_wins = 3,
|
||||
int32_t registration_deadline = 3600,
|
||||
uint32_t start_delay = 3,
|
||||
uint32_t round_delay = 3,
|
||||
bool insurance_enabled = false,
|
||||
uint32_t number_of_gestures = 3,
|
||||
uint32_t start_time = 0,
|
||||
fc::optional<flat_set<account_id_type> > whitelist = fc::optional<flat_set<account_id_type> >()
|
||||
);
|
||||
|
||||
void join_tournament(const tournament_id_type & tournament_id,
|
||||
const account_id_type& player_id,
|
||||
const account_id_type& payer_id,
|
||||
const fc::ecc::private_key& sig_priv_key,
|
||||
asset buy_in
|
||||
);
|
||||
|
||||
void leave_tournament(const tournament_id_type & tournament_id,
|
||||
const account_id_type& player_id,
|
||||
const account_id_type& canceling_account_id,
|
||||
const fc::ecc::private_key& sig_priv_key
|
||||
);
|
||||
|
||||
// stolen from cli_wallet
|
||||
void rps_throw(const game_id_type& game_id,
|
||||
const account_id_type& player_id,
|
||||
rock_paper_scissors_gesture gesture,
|
||||
const fc::ecc::private_key& sig_priv_key
|
||||
);
|
||||
|
||||
void rps_reveal( const game_id_type& game_id,
|
||||
const account_id_type& player_id,
|
||||
rock_paper_scissors_gesture gesture,
|
||||
const fc::ecc::private_key& sig_priv_key );
|
||||
|
||||
// spaghetti programming
|
||||
// walking through all tournaments, matches and games and throwing random moves
|
||||
// optionaly skip generting randomly selected moves
|
||||
// every_move_is >= 0 : every game is tie
|
||||
void play_games(unsigned skip_some_commits = 0, unsigned skip_some_reveals = 0, int every_move_is = -1);
|
||||
|
||||
private:
|
||||
database_fixture& df;
|
||||
// index of last created tournament
|
||||
fc::optional<uint64_t> current_tournament_idx;
|
||||
// index of last asset
|
||||
uint64_t current_asset_idx;
|
||||
// assets : core and maybe others
|
||||
std::set<asset_id_type> assets;
|
||||
// tournaments to be played
|
||||
std::set<tournament_id_type> tournaments;
|
||||
// all players registered in tournaments
|
||||
std::set<account_id_type> players;
|
||||
// players' private keys
|
||||
std::map<account_id_type, fc::ecc::private_key> players_keys;
|
||||
// total charges for moves made by every player
|
||||
std::map<account_id_type, std::map<asset_id_type, share_type>> players_fees;
|
||||
// store of commits and reveals
|
||||
std::map<rock_paper_scissors_throw_commit, rock_paper_scissors_throw_reveal> committed_game_moves;
|
||||
// store of latest commits, for use by rps_reveal
|
||||
std::map<account_id_type, rock_paper_scissors_throw_commit> latest_committs;
|
||||
};
|
||||
732
tests/tests/affiliate_tests.cpp
Normal file
732
tests/tests/affiliate_tests.cpp
Normal file
|
|
@ -0,0 +1,732 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/betting_test_markets.hpp"
|
||||
#include "../common/tournament_helper.hpp"
|
||||
|
||||
#include <graphene/chain/affiliate_payout.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
|
||||
#include <graphene/affiliate_stats/affiliate_stats_api.hpp>
|
||||
#include <graphene/affiliate_stats/affiliate_stats_objects.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::db;
|
||||
|
||||
class affiliate_test_helper
|
||||
{
|
||||
public:
|
||||
affiliate_test_helper( database_fixture& f ) : fixture(f), accounts(&fixture.db.get_index_type<account_index>())
|
||||
{
|
||||
fixture.generate_blocks( HARDFORK_999_TIME );
|
||||
fixture.generate_block();
|
||||
|
||||
test::set_expiration( fixture.db, fixture.trx );
|
||||
fixture.trx.clear();
|
||||
|
||||
alice_id = find_account( "alice" );
|
||||
if( alice_id == account_id_type() )
|
||||
{
|
||||
ACTOR(alice);
|
||||
this->alice_id = alice_id;
|
||||
}
|
||||
|
||||
ann_id = find_account( "ann" );
|
||||
if( ann_id == account_id_type() )
|
||||
{
|
||||
ACTOR(ann);
|
||||
this->ann_id = ann_id;
|
||||
}
|
||||
|
||||
audrey_id = find_account( "audrey" );
|
||||
if( audrey_id == account_id_type() )
|
||||
{
|
||||
ACTOR(audrey);
|
||||
this->audrey_id = audrey_id;
|
||||
}
|
||||
|
||||
paula_id = find_account("paula");
|
||||
if( paula_id == account_id_type() )
|
||||
{
|
||||
// Paula: 100% to Alice for Bookie / nothing for RPS
|
||||
account_create_operation aco = fixture.make_account( "paula", paula_private_key.get_public_key() );
|
||||
aco.extensions.value.affiliate_distributions = affiliate_reward_distributions();
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist[alice_id] = GRAPHENE_100_PERCENT;
|
||||
fixture.trx.operations.push_back( aco );
|
||||
processed_transaction op_result = fixture.db.push_transaction( fixture.trx, ~0 );
|
||||
paula_id = op_result.operation_results[0].get<object_id_type>();
|
||||
fixture.trx.clear();
|
||||
fixture.fund( paula_id(fixture.db), asset(20000000) );
|
||||
}
|
||||
|
||||
penny_id = find_account("penny");
|
||||
if( penny_id == account_id_type() )
|
||||
{
|
||||
// Penny: For Bookie 60% to Alice + 40% to Ann / For RPS 20% to Alice, 30% to Ann, 50% to Audrey
|
||||
account_create_operation aco = fixture.make_account( "penny", penny_private_key.get_public_key() );
|
||||
aco.extensions.value.affiliate_distributions = affiliate_reward_distributions();
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist[alice_id] = GRAPHENE_100_PERCENT * 3 / 5;
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist[ann_id] = GRAPHENE_100_PERCENT
|
||||
- aco.extensions.value.affiliate_distributions->_dists[bookie]._dist[alice_id];
|
||||
aco.extensions.value.affiliate_distributions->_dists[rps]._dist[alice_id] = GRAPHENE_100_PERCENT / 5;
|
||||
aco.extensions.value.affiliate_distributions->_dists[rps]._dist[audrey_id] = GRAPHENE_100_PERCENT / 2;
|
||||
aco.extensions.value.affiliate_distributions->_dists[rps]._dist[ann_id] = GRAPHENE_100_PERCENT
|
||||
- aco.extensions.value.affiliate_distributions->_dists[rps]._dist[alice_id]
|
||||
- aco.extensions.value.affiliate_distributions->_dists[rps]._dist[audrey_id];
|
||||
fixture.trx.operations.push_back( aco );
|
||||
processed_transaction op_result = fixture.db.push_transaction( fixture.trx, ~0 );
|
||||
penny_id = op_result.operation_results[0].get<object_id_type>();
|
||||
fixture.trx.clear();
|
||||
fixture.fund( penny_id(fixture.db), asset(30000000) );
|
||||
}
|
||||
|
||||
petra_id = find_account("petra");
|
||||
if( petra_id == account_id_type() )
|
||||
{
|
||||
// Petra: nothing for Bookie / For RPS 10% to Ann, 90% to Audrey
|
||||
account_create_operation aco = fixture.make_account( "petra", petra_private_key.get_public_key() );
|
||||
aco.extensions.value.affiliate_distributions = affiliate_reward_distributions();
|
||||
aco.extensions.value.affiliate_distributions->_dists[rps]._dist[ann_id] = GRAPHENE_100_PERCENT / 10;
|
||||
aco.extensions.value.affiliate_distributions->_dists[rps]._dist[audrey_id] = GRAPHENE_100_PERCENT
|
||||
- aco.extensions.value.affiliate_distributions->_dists[rps]._dist[ann_id];
|
||||
fixture.trx.operations.push_back( aco );
|
||||
processed_transaction op_result = fixture.db.push_transaction( fixture.trx, ~0 );
|
||||
petra_id = op_result.operation_results[0].get<object_id_type>();
|
||||
fixture.trx.clear();
|
||||
fixture.fund( petra_id(fixture.db), asset(40000000) );
|
||||
}
|
||||
|
||||
update_balances();
|
||||
}
|
||||
|
||||
void update_balances()
|
||||
{
|
||||
alice_ppy = fixture.get_balance( alice_id, asset_id_type() );
|
||||
ann_ppy = fixture.get_balance( ann_id, asset_id_type() );
|
||||
audrey_ppy = fixture.get_balance( audrey_id, asset_id_type() );
|
||||
paula_ppy = fixture.get_balance( paula_id, asset_id_type() );
|
||||
penny_ppy = fixture.get_balance( penny_id, asset_id_type() );
|
||||
petra_ppy = fixture.get_balance( petra_id, asset_id_type() );
|
||||
}
|
||||
|
||||
database_fixture& fixture;
|
||||
|
||||
account_id_type alice_id;
|
||||
account_id_type ann_id;
|
||||
account_id_type audrey_id;
|
||||
account_id_type paula_id;
|
||||
account_id_type penny_id;
|
||||
account_id_type petra_id;
|
||||
|
||||
int64_t alice_ppy;
|
||||
int64_t ann_ppy;
|
||||
int64_t audrey_ppy;
|
||||
int64_t paula_ppy;
|
||||
int64_t penny_ppy;
|
||||
int64_t petra_ppy;
|
||||
|
||||
private:
|
||||
const account_index* accounts;
|
||||
account_id_type find_account( const string& name )
|
||||
{
|
||||
auto itr = accounts->indices().get<by_name>().find( name );
|
||||
if( itr == accounts->indices().get<by_name>().end() ) return account_id_type();
|
||||
return itr->id;
|
||||
}
|
||||
|
||||
static fc::ecc::private_key generate_private_key( const string& seed )
|
||||
{
|
||||
return database_fixture::generate_private_key( seed );
|
||||
}
|
||||
|
||||
const account_object& create_account( const string& name, const fc::ecc::public_key& key )
|
||||
{
|
||||
return fixture.create_account( name, key );
|
||||
}
|
||||
|
||||
public:
|
||||
const fc::ecc::private_key alice_private_key = generate_private_key( "alice" );
|
||||
const fc::ecc::private_key ann_private_key = generate_private_key( "ann" );
|
||||
const fc::ecc::private_key audrey_private_key = generate_private_key( "audrey" );
|
||||
const fc::ecc::private_key paula_private_key = generate_private_key( "paula" );
|
||||
const fc::ecc::private_key penny_private_key = generate_private_key( "penny" );
|
||||
const fc::ecc::private_key petra_private_key = generate_private_key( "petra" );
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( affiliate_tests, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( account_test )
|
||||
{
|
||||
ACTORS( (alice)(ann)(audrey) );
|
||||
fund( alice );
|
||||
|
||||
const fc::ecc::private_key paula_private_key = generate_private_key( "paula" );
|
||||
|
||||
account_create_operation aco = make_account( "paula", paula_private_key.get_public_key() );
|
||||
aco.extensions.value.affiliate_distributions = affiliate_reward_distributions();
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist[alice_id] = GRAPHENE_100_PERCENT;
|
||||
test::set_expiration( db, trx );
|
||||
trx.clear();
|
||||
trx.operations.push_back( aco );
|
||||
// not allowed before hardfork
|
||||
GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, ~0), fc::assert_exception );
|
||||
|
||||
proposal_create_operation pco;
|
||||
pco.fee_paying_account = alice_id;
|
||||
aco.registrar = ann_id;
|
||||
pco.proposed_ops.emplace_back( aco );
|
||||
aco.registrar = account_id_type();
|
||||
pco.expiration_time = db.head_block_time() + fc::days(1);
|
||||
trx.clear();
|
||||
trx.operations.push_back( pco );
|
||||
// not allowed before hardfork
|
||||
GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, ~0), fc::assert_exception );
|
||||
|
||||
generate_blocks( HARDFORK_999_TIME );
|
||||
generate_block();
|
||||
|
||||
// Proposal is now allowed
|
||||
pco.expiration_time = db.head_block_time() + fc::days(1);
|
||||
trx.clear();
|
||||
trx.operations.push_back( pco );
|
||||
test::set_expiration( db, trx );
|
||||
db.push_transaction(trx, ~0);
|
||||
|
||||
// Must contain at least one app-tag
|
||||
aco.extensions.value.affiliate_distributions->_dists.clear();
|
||||
trx.clear();
|
||||
trx.operations.push_back( aco );
|
||||
GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, ~0), fc::assert_exception );
|
||||
|
||||
// Distribution for app-tag must be non-empty
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist.clear();
|
||||
trx.clear();
|
||||
trx.operations.push_back( aco );
|
||||
GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, ~0), fc::assert_exception );
|
||||
|
||||
// If more than one app-tag given, neither can be empty
|
||||
aco.extensions.value.affiliate_distributions->_dists[rps]._dist[alice_id] = GRAPHENE_100_PERCENT;
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist.clear();
|
||||
trx.clear();
|
||||
trx.operations.push_back( aco );
|
||||
GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, ~0), fc::assert_exception );
|
||||
|
||||
// Sum of percentage must be 100%
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist[ann_id] = 1;
|
||||
trx.clear();
|
||||
trx.operations.push_back( aco );
|
||||
GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, ~0), fc::assert_exception );
|
||||
|
||||
// Individual percentages cannot exceed 100%
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist[ann_id] = -1;
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist[audrey_id] = 1 + GRAPHENE_100_PERCENT;
|
||||
trx.clear();
|
||||
trx.operations.push_back( aco );
|
||||
GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, ~0), fc::assert_exception );
|
||||
|
||||
// Sum of percentage must be 100%
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist[ann_id] = GRAPHENE_100_PERCENT - 10;
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist[audrey_id] = 9;
|
||||
trx.clear();
|
||||
trx.operations.push_back( aco );
|
||||
GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, ~0), fc::assert_exception );
|
||||
|
||||
// Ok
|
||||
aco.extensions.value.affiliate_distributions->_dists[bookie]._dist[audrey_id] = 10;
|
||||
trx.clear();
|
||||
trx.operations.push_back( aco );
|
||||
db.push_transaction(trx, ~0);
|
||||
|
||||
const auto& paula = get_account( aco.name );
|
||||
BOOST_CHECK( paula.affiliate_distributions.valid() );
|
||||
BOOST_CHECK_EQUAL( 2, paula.affiliate_distributions->_dists.size() );
|
||||
|
||||
const auto& app_rps = paula.affiliate_distributions->_dists.find( rps );
|
||||
BOOST_CHECK( app_rps != paula.affiliate_distributions->_dists.end() );
|
||||
BOOST_CHECK_EQUAL( 1, app_rps->second._dist.size() );
|
||||
const auto& share_alice = app_rps->second._dist.find( alice_id );
|
||||
BOOST_CHECK( share_alice != app_rps->second._dist.end() );
|
||||
BOOST_CHECK_EQUAL( GRAPHENE_100_PERCENT, share_alice->second );
|
||||
|
||||
const auto& app_bookie = paula.affiliate_distributions->_dists.find( bookie );
|
||||
BOOST_CHECK( app_bookie != paula.affiliate_distributions->_dists.end() );
|
||||
BOOST_CHECK_EQUAL( 2, app_bookie->second._dist.size() );
|
||||
const auto& share_ann = app_bookie->second._dist.find( ann_id );
|
||||
BOOST_CHECK( share_ann != app_bookie->second._dist.end() );
|
||||
BOOST_CHECK_EQUAL( GRAPHENE_100_PERCENT - 10, share_ann->second );
|
||||
const auto& share_audrey = app_bookie->second._dist.find( audrey_id );
|
||||
BOOST_CHECK( share_audrey != app_bookie->second._dist.end() );
|
||||
BOOST_CHECK_EQUAL( 10, share_audrey->second );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( affiliate_payout_helper_test )
|
||||
{
|
||||
ACTORS( (irene) );
|
||||
|
||||
const asset_id_type btc_id = create_user_issued_asset( "BTC", irene, 0 ).id;
|
||||
issue_uia( irene, asset( 100000, btc_id ) );
|
||||
|
||||
affiliate_test_helper ath( *this );
|
||||
|
||||
int64_t alice_btc = 0;
|
||||
int64_t ann_btc = 0;
|
||||
int64_t audrey_btc = 0;
|
||||
|
||||
{
|
||||
const tournament_object& game = db.create<tournament_object>( []( tournament_object& t ) {
|
||||
t.options.game_options = rock_paper_scissors_game_options();
|
||||
t.options.buy_in = asset( 10 );
|
||||
});
|
||||
affiliate_payout_helper helper = affiliate_payout_helper( db, game );
|
||||
// Alice has no distribution set
|
||||
BOOST_CHECK_EQUAL( 0, helper.payout( ath.alice_id, 1000 ).value );
|
||||
// Paula has nothing for Bookie
|
||||
BOOST_CHECK_EQUAL( 0, helper.payout( ath.paula_id, 1000 ).value );
|
||||
// 20% of 4 gets rounded down to 0
|
||||
BOOST_CHECK_EQUAL( 0, helper.payout( ath.penny_id, 4 ).value );
|
||||
|
||||
// 20% of 5 = 1 is paid to Audrey
|
||||
BOOST_CHECK_EQUAL( 1, helper.payout( ath.penny_id, 5 ).value );
|
||||
ath.audrey_ppy++;
|
||||
|
||||
// 20% of 50 = 10: 2 to Alice, 3 to Ann, 5 to Audrey
|
||||
BOOST_CHECK_EQUAL( 10, helper.payout( ath.penny_id, 50 ).value );
|
||||
ath.alice_ppy += 2;
|
||||
ath.ann_ppy += 3;
|
||||
ath.audrey_ppy += 5;
|
||||
|
||||
// 20% of 59 = 11: 1 to Ann, 10 to Audrey
|
||||
BOOST_CHECK_EQUAL( 11, helper.payout( ath.petra_id, 59 ).value );
|
||||
ath.audrey_ppy += 10;
|
||||
ath.ann_ppy += 1;
|
||||
|
||||
helper.commit();
|
||||
|
||||
BOOST_CHECK_EQUAL( ath.alice_ppy, get_balance( ath.alice_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.ann_ppy, get_balance( ath.ann_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.audrey_ppy, get_balance( ath.audrey_id, asset_id_type() ) );
|
||||
}
|
||||
|
||||
{
|
||||
const tournament_object& game = db.create<tournament_object>( [btc_id]( tournament_object& t ) {
|
||||
t.options.game_options = rock_paper_scissors_game_options();
|
||||
t.options.buy_in = asset( 10, btc_id );
|
||||
});
|
||||
affiliate_payout_helper helper = affiliate_payout_helper( db, game );
|
||||
// 20% of 60 = 12: 2 to Alice, 3 to Ann, 7 to Audrey
|
||||
BOOST_CHECK_EQUAL( 12, helper.payout( ath.penny_id, 60 ).value );
|
||||
alice_btc += 2;
|
||||
ann_btc += 3;
|
||||
audrey_btc += 7;
|
||||
helper.commit();
|
||||
BOOST_CHECK_EQUAL( alice_btc, get_balance( ath.alice_id, btc_id ) );
|
||||
BOOST_CHECK_EQUAL( ann_btc, get_balance( ath.ann_id, btc_id ) );
|
||||
BOOST_CHECK_EQUAL( audrey_btc, get_balance( ath.audrey_id, btc_id ) );
|
||||
}
|
||||
|
||||
{
|
||||
const betting_market_group_object& game = db.create<betting_market_group_object>( []( betting_market_group_object& b ) {
|
||||
b.asset_id = asset_id_type();
|
||||
} );
|
||||
affiliate_payout_helper helper = affiliate_payout_helper( db, game );
|
||||
// Alice has no distribution set
|
||||
BOOST_CHECK_EQUAL( 0, helper.payout( ath.alice_id, 1000 ).value );
|
||||
// Petra has nothing for Bookie
|
||||
BOOST_CHECK_EQUAL( 0, helper.payout( ath.petra_id, 1000 ).value );
|
||||
// 20% of 4 gets rounded down to 0
|
||||
BOOST_CHECK_EQUAL( 0, helper.payout( ath.penny_id, 4 ).value );
|
||||
|
||||
// 20% of 5 = 1 is paid to Ann
|
||||
BOOST_CHECK_EQUAL( 1, helper.payout( ath.penny_id, 5 ).value );
|
||||
ath.ann_ppy++;
|
||||
|
||||
// 20% of 40 = 8: 8 to Alice
|
||||
BOOST_CHECK_EQUAL( 8, helper.payout( ath.paula_id, 40 ).value );
|
||||
ath.alice_ppy += 8;
|
||||
|
||||
// intermediate commit should clear internal accumulator
|
||||
helper.commit();
|
||||
|
||||
// 20% of 59 = 11: 6 to Alice, 5 to Ann
|
||||
BOOST_CHECK_EQUAL( 11, helper.payout( ath.penny_id, 59 ).value );
|
||||
ath.alice_ppy += 6;
|
||||
ath.ann_ppy += 5;
|
||||
|
||||
helper.commit();
|
||||
|
||||
BOOST_CHECK_EQUAL( ath.alice_ppy, get_balance( ath.alice_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.ann_ppy, get_balance( ath.ann_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.audrey_ppy, get_balance( ath.audrey_id, asset_id_type() ) );
|
||||
}
|
||||
|
||||
{
|
||||
const betting_market_group_object& game = db.create<betting_market_group_object>( [btc_id]( betting_market_group_object& b ) {
|
||||
b.asset_id = btc_id;
|
||||
} );
|
||||
affiliate_payout_helper helper = affiliate_payout_helper( db, game );
|
||||
// 20% of 60 = 12: 7 to Alice, 5 to Ann
|
||||
BOOST_CHECK_EQUAL( 12, helper.payout( ath.penny_id, 60 ).value );
|
||||
alice_btc += 7;
|
||||
ann_btc += 5;
|
||||
helper.commit();
|
||||
BOOST_CHECK_EQUAL( alice_btc, get_balance( ath.alice_id, btc_id ) );
|
||||
BOOST_CHECK_EQUAL( ann_btc, get_balance( ath.ann_id, btc_id ) );
|
||||
BOOST_CHECK_EQUAL( audrey_btc, get_balance( ath.audrey_id, btc_id ) );
|
||||
}
|
||||
|
||||
{
|
||||
// Fix total supply
|
||||
auto& index = db.get_index_type<account_balance_index>().indices().get<by_account_asset>();
|
||||
auto itr = index.find( boost::make_tuple( account_id_type(), asset_id_type() ) );
|
||||
BOOST_CHECK( itr != index.end() );
|
||||
db.modify( *itr, [&ath]( account_balance_object& bal ) {
|
||||
bal.balance -= ath.alice_ppy + ath.ann_ppy + ath.audrey_ppy;
|
||||
});
|
||||
|
||||
itr = index.find( boost::make_tuple( irene_id, btc_id ) );
|
||||
BOOST_CHECK( itr != index.end() );
|
||||
db.modify( *itr, [alice_btc,ann_btc,audrey_btc]( account_balance_object& bal ) {
|
||||
bal.balance -= alice_btc + ann_btc + audrey_btc;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( rps_tournament_payout_test )
|
||||
{ try {
|
||||
ACTORS( (martha) );
|
||||
|
||||
affiliate_test_helper ath( *this );
|
||||
|
||||
fund( martha_id(db), asset(1000000000) );
|
||||
|
||||
upgrade_to_lifetime_member( martha_id(db) );
|
||||
|
||||
tournaments_helper helper(*this);
|
||||
account_id_type dividend_id = *helper.get_asset_dividend_account();
|
||||
int64_t div_ppy = get_balance( dividend_id, asset_id_type() );
|
||||
const asset buy_in = asset(12000);
|
||||
tournament_id_type tournament_id = helper.create_tournament( martha_id, martha_private_key, buy_in, 3, 3, 1, 1 );
|
||||
BOOST_REQUIRE(tournament_id == tournament_id_type());
|
||||
|
||||
helper.join_tournament( tournament_id, ath.paula_id, ath.paula_id, ath.paula_private_key, buy_in);
|
||||
helper.join_tournament( tournament_id, ath.penny_id, ath.penny_id, ath.penny_private_key, buy_in);
|
||||
helper.join_tournament( tournament_id, ath.petra_id, ath.petra_id, ath.petra_private_key, buy_in);
|
||||
|
||||
generate_block();
|
||||
const tournament_object& tournament = db.get<tournament_object>( tournament_id );
|
||||
|
||||
BOOST_CHECK_EQUAL( ath.paula_ppy - 12000, get_balance( ath.paula_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.penny_ppy - 12000, get_balance( ath.penny_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.petra_ppy - 12000, get_balance( ath.petra_id, asset_id_type() ) );
|
||||
|
||||
const tournament_details_object& tournament_details = tournament.tournament_details_id(db);
|
||||
BOOST_CHECK_EQUAL( 3, tournament_details.matches.size() );
|
||||
|
||||
{ // 3 players, match 1 is a bye for penny
|
||||
const match_object& match = tournament_details.matches[0](db);
|
||||
BOOST_CHECK_EQUAL( int64_t(match_state::match_complete), int64_t(match.get_state()) );
|
||||
BOOST_CHECK_EQUAL( 1, match.players.size() );
|
||||
BOOST_CHECK_EQUAL( object_id_type(ath.penny_id).instance(), object_id_type(match.players[0]).instance() );
|
||||
}
|
||||
{ // match 2 is paula vs. petra: paula wins
|
||||
const match_object& match = tournament_details.matches[1](db);
|
||||
BOOST_CHECK_EQUAL( int64_t(match_state::match_in_progress), int64_t(match.get_state()) );
|
||||
BOOST_CHECK_EQUAL( 2, match.players.size() );
|
||||
BOOST_CHECK_EQUAL( object_id_type(ath.paula_id).instance(), object_id_type(match.players[0]).instance() );
|
||||
BOOST_CHECK_EQUAL( object_id_type(ath.petra_id).instance(), object_id_type(match.players[1]).instance() );
|
||||
BOOST_CHECK_EQUAL( 1, match.games.size() );
|
||||
|
||||
const game_object& game = match.games[0](db);
|
||||
BOOST_CHECK_EQUAL( int64_t(game_state::expecting_commit_moves), int64_t(game.get_state()) );
|
||||
helper.rps_throw( game.id, ath.paula_id, rock_paper_scissors_gesture::paper, ath.paula_private_key );
|
||||
helper.rps_throw( game.id, ath.petra_id, rock_paper_scissors_gesture::rock, ath.petra_private_key );
|
||||
|
||||
BOOST_CHECK_EQUAL( int64_t(game_state::expecting_reveal_moves), int64_t(game.get_state()) );
|
||||
helper.rps_reveal( game.id, ath.paula_id, rock_paper_scissors_gesture::paper, ath.paula_private_key );
|
||||
helper.rps_reveal( game.id, ath.petra_id, rock_paper_scissors_gesture::rock, ath.petra_private_key );
|
||||
|
||||
BOOST_CHECK_EQUAL( int64_t(game_state::game_complete), int64_t(game.get_state()) );
|
||||
BOOST_CHECK_EQUAL( 1, match.games.size() );
|
||||
BOOST_CHECK_EQUAL( int64_t(match_state::match_complete), int64_t(match.get_state()) );
|
||||
}
|
||||
{ // match 3 is penny vs. paula: penny wins
|
||||
const match_object& match = tournament_details.matches[2](db);
|
||||
BOOST_CHECK_EQUAL( int64_t(match_state::match_in_progress), int64_t(match.get_state()) );
|
||||
BOOST_CHECK_EQUAL( 2, match.players.size() );
|
||||
BOOST_CHECK_EQUAL( object_id_type(ath.penny_id).instance(), object_id_type(match.players[0]).instance() );
|
||||
BOOST_CHECK_EQUAL( object_id_type(ath.paula_id).instance(), object_id_type(match.players[1]).instance() );
|
||||
BOOST_CHECK_EQUAL( 1, match.games.size() );
|
||||
|
||||
const game_object& game = match.games[0](db);
|
||||
BOOST_CHECK_EQUAL( int64_t(game_state::expecting_commit_moves), int64_t(game.get_state()) );
|
||||
helper.rps_throw( game.id, ath.paula_id, rock_paper_scissors_gesture::paper, ath.paula_private_key );
|
||||
helper.rps_throw( game.id, ath.penny_id, rock_paper_scissors_gesture::scissors, ath.penny_private_key );
|
||||
|
||||
BOOST_CHECK_EQUAL( int64_t(game_state::expecting_reveal_moves), int64_t(game.get_state()) );
|
||||
helper.rps_reveal( game.id, ath.paula_id, rock_paper_scissors_gesture::paper, ath.paula_private_key );
|
||||
helper.rps_reveal( game.id, ath.penny_id, rock_paper_scissors_gesture::scissors, ath.penny_private_key );
|
||||
|
||||
BOOST_CHECK_EQUAL( int64_t(game_state::game_complete), int64_t(game.get_state()) );
|
||||
BOOST_CHECK_EQUAL( int64_t(match_state::match_complete), int64_t(match.get_state()) );
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL( 3*GRAPHENE_1_PERCENT, db.get_global_properties().parameters.rake_fee_percentage );
|
||||
// Penny wins net 3*buy_in minus rake = 36000 - 1080 = 34920
|
||||
BOOST_CHECK_EQUAL( ath.paula_ppy - 12000, get_balance( ath.paula_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.penny_ppy - 12000 + 34920, get_balance( ath.penny_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.petra_ppy - 12000, get_balance( ath.petra_id, asset_id_type() ) );
|
||||
|
||||
// Dividend account receives 80% of rake = 864
|
||||
BOOST_CHECK_EQUAL( div_ppy + 864, get_balance( dividend_id, asset_id_type() ) );
|
||||
|
||||
// 20% of rake = 216 is paid to Penny's affiliates: 43 to Alice, 64 to Ann, 109 to Audrey
|
||||
BOOST_CHECK_EQUAL( ath.alice_ppy + 43, get_balance( ath.alice_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.ann_ppy + 64, get_balance( ath.ann_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.audrey_ppy + 109, get_balance( ath.audrey_id, asset_id_type() ) );
|
||||
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( bookie_payout_test )
|
||||
{ try {
|
||||
ACTORS( (irene) );
|
||||
|
||||
const asset_id_type btc_id = create_user_issued_asset( "BTC", irene, 0 ).id;
|
||||
|
||||
affiliate_test_helper ath( *this );
|
||||
|
||||
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
|
||||
|
||||
// place bets at 10:1
|
||||
place_bet(ath.paula_id, capitals_win_market.id, bet_type::back, asset(10000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
place_bet(ath.penny_id, capitals_win_market.id, bet_type::lay, asset(100000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
|
||||
// reverse positions at 1:1
|
||||
place_bet(ath.paula_id, capitals_win_market.id, bet_type::lay, asset(110000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
place_bet(ath.penny_id, capitals_win_market.id, bet_type::back, asset(110000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
|
||||
update_betting_market_group(moneyline_betting_markets.id, graphene::chain::keywords::_status = betting_market_group_status::closed);
|
||||
resolve_betting_market_group(moneyline_betting_markets.id,
|
||||
{{capitals_win_market.id, betting_market_resolution_type::win},
|
||||
{blackhawks_win_market.id, betting_market_resolution_type::not_win}});
|
||||
generate_block();
|
||||
|
||||
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage();
|
||||
BOOST_CHECK_EQUAL( 3 * GRAPHENE_1_PERCENT, rake_fee_percentage );
|
||||
uint32_t rake_value;
|
||||
// rake_value = (-10000 + 110000 - 110000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
|
||||
// paula starts with 20000000, pays 10000 (bet), wins 110000, then pays 110000 (bet), wins 0
|
||||
BOOST_CHECK_EQUAL( get_balance( ath.paula_id, asset_id_type() ), ath.paula_ppy - 10000 + 110000 - 110000 + 0 );
|
||||
// no wins -> no affiliate payouts
|
||||
|
||||
rake_value = (-100000 - 110000 + 220000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
|
||||
// penny starts with 30000000, pays 100000 (bet), wins 0, then pays 110000 (bet), wins 220000
|
||||
BOOST_CHECK_EQUAL( get_balance( ath.penny_id, asset_id_type() ), ath.penny_ppy - 100000 + 0 - 110000 + 220000 - rake_value );
|
||||
// penny wins 10000 net, rake is 3% of that (=300)
|
||||
// Affiliate pay is 20% of rake (=60). Of this, 60%=36 go to Alice, 40%=24 go to Ann
|
||||
BOOST_CHECK_EQUAL( ath.alice_ppy + 36, get_balance( ath.alice_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.ann_ppy + 24, get_balance( ath.ann_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.audrey_ppy + 0, get_balance( ath.audrey_id, asset_id_type() ) );
|
||||
|
||||
{
|
||||
test::set_expiration( db, trx );
|
||||
issue_uia( ath.paula_id, asset( 1000000, btc_id ) );
|
||||
issue_uia( ath.petra_id, asset( 1000000, btc_id ) );
|
||||
|
||||
create_event({{"en", "Washington Capitals/Chicago Blackhawks"}, {"zh_Hans", "華盛頓首都隊/芝加哥黑鷹"}, {"ja", "ワシントン・キャピタルズ/シカゴ・ブラックホークス"}}, {{"en", "2016-17"}}, nhl.id); \
|
||||
generate_blocks(1); \
|
||||
const event_object& capitals_vs_blackhawks2 = *db.get_index_type<event_object_index>().indices().get<by_id>().rbegin(); \
|
||||
create_betting_market_group({{"en", "Moneyline"}}, capitals_vs_blackhawks2.id, betting_market_rules.id, btc_id, false, 0);
|
||||
generate_blocks(1);
|
||||
const betting_market_group_object& moneyline_betting_markets2 = *db.get_index_type<betting_market_group_object_index>().indices().get<by_id>().rbegin();
|
||||
create_betting_market(moneyline_betting_markets2.id, {{"en", "Washington Capitals win"}});
|
||||
generate_blocks(1);
|
||||
const betting_market_object& capitals_win_market2 = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin();
|
||||
create_betting_market(moneyline_betting_markets2.id, {{"en", "Chicago Blackhawks win"}});
|
||||
generate_blocks(1);
|
||||
const betting_market_object& blackhawks_win_market2 = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin();
|
||||
|
||||
// place bets at 10:1
|
||||
place_bet(ath.paula_id, capitals_win_market2.id, bet_type::back, asset(10000, btc_id), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
place_bet(ath.petra_id, capitals_win_market2.id, bet_type::lay, asset(100000, btc_id), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
|
||||
// reverse positions at 1:1
|
||||
place_bet(ath.paula_id, capitals_win_market2.id, bet_type::lay, asset(110000, btc_id), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
place_bet(ath.petra_id, capitals_win_market2.id, bet_type::back, asset(110000, btc_id), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
|
||||
update_betting_market_group(moneyline_betting_markets2.id, graphene::chain::keywords::_status = betting_market_group_status::closed);
|
||||
resolve_betting_market_group(moneyline_betting_markets2.id,
|
||||
{{capitals_win_market2.id, betting_market_resolution_type::not_win},
|
||||
{blackhawks_win_market2.id, betting_market_resolution_type::win}});
|
||||
generate_block();
|
||||
|
||||
rake_value = (-10000 + 0 - 110000 + 220000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
|
||||
// paula starts with 1000000, pays 10000 (bet), wins 0, then pays 110000 (bet), wins 220000
|
||||
BOOST_CHECK_EQUAL( get_balance( ath.paula_id, btc_id ), 1000000 - 10000 + 0 - 110000 + 220000 - rake_value );
|
||||
// net win 100000, 3% rake = 3000, 20% of that is 600, 100% of that goes to Alice
|
||||
BOOST_CHECK_EQUAL( 600, get_balance( ath.alice_id, btc_id ) );
|
||||
BOOST_CHECK_EQUAL( 0, get_balance( ath.ann_id, btc_id ) );
|
||||
BOOST_CHECK_EQUAL( 0, get_balance( ath.audrey_id, btc_id ) );
|
||||
|
||||
// rake_value = (-100000 + 110000 - 110000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
|
||||
rake_value = 0;
|
||||
// petra starts with 1000000, pays 100000 (bet), wins 110000, then pays 110000 (bet), wins 0
|
||||
BOOST_CHECK_EQUAL( get_balance( ath.petra_id, btc_id ), 1000000 - 100000 + 110000 - 110000 + 0 - rake_value );
|
||||
// petra wins nothing -> no payout
|
||||
}
|
||||
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( statistics_test )
|
||||
{ try {
|
||||
|
||||
INVOKE(rps_tournament_payout_test);
|
||||
|
||||
affiliate_test_helper ath( *this );
|
||||
|
||||
transfer( ath.audrey_id, ath.alice_id, asset( 10 ), asset(0) );
|
||||
transfer( ath.audrey_id, ath.ann_id, asset( 10 ), asset(0) );
|
||||
|
||||
INVOKE(bookie_payout_test);
|
||||
|
||||
const asset_id_type btc_id = get_asset( "BTC" ).id;
|
||||
|
||||
transfer( ath.alice_id, ath.ann_id, asset( 100, btc_id ), asset(0) );
|
||||
transfer( ath.alice_id, ath.audrey_id, asset( 100, btc_id ), asset(0) );
|
||||
|
||||
generate_block();
|
||||
|
||||
{
|
||||
const auto& idx = db.get_index_type<graphene::affiliate_stats::referral_reward_index>().indices().get<graphene::affiliate_stats::by_asset>();
|
||||
BOOST_CHECK_EQUAL( 2, idx.size() ); // penny 216+60 CORE, paula 600 BTC
|
||||
}
|
||||
|
||||
{
|
||||
const auto& idx = db.get_index_type<graphene::affiliate_stats::app_reward_index>().indices().get<graphene::affiliate_stats::by_asset>();
|
||||
BOOST_CHECK_EQUAL( 3, idx.size() ); // rps 216 CORE, bookie 60 CORE + 600 BTC
|
||||
}
|
||||
|
||||
graphene::affiliate_stats::affiliate_stats_api stats( app );
|
||||
|
||||
{
|
||||
std::vector<graphene::affiliate_stats::referral_payment> refs = stats.list_historic_referral_rewards( ath.alice_id, operation_history_id_type() );
|
||||
BOOST_CHECK_EQUAL( 3, refs.size() );
|
||||
BOOST_REQUIRE_LE( 1, refs.size() );
|
||||
BOOST_CHECK_EQUAL( app_tag::rps, refs[0].tag );
|
||||
BOOST_CHECK_EQUAL( 43, refs[0].payout.amount.value );
|
||||
BOOST_CHECK_EQUAL( 0, refs[0].payout.asset_id.instance.value );
|
||||
BOOST_REQUIRE_LE( 2, refs.size() );
|
||||
BOOST_CHECK_EQUAL( app_tag::bookie, refs[1].tag );
|
||||
BOOST_CHECK_EQUAL( 36, refs[1].payout.amount.value );
|
||||
BOOST_CHECK_EQUAL( 0, refs[1].payout.asset_id.instance.value );
|
||||
BOOST_REQUIRE_LE( 3, refs.size() );
|
||||
BOOST_CHECK_EQUAL( app_tag::bookie, refs[2].tag );
|
||||
BOOST_CHECK_EQUAL( 600, refs[2].payout.amount.value );
|
||||
BOOST_CHECK_EQUAL( btc_id.instance.value, refs[2].payout.asset_id.instance.value );
|
||||
|
||||
BOOST_CHECK_EQUAL( 3, stats.list_historic_referral_rewards( ath.alice_id, refs[0].id ).size() );
|
||||
BOOST_CHECK_EQUAL( 2, stats.list_historic_referral_rewards( ath.alice_id, refs[1].id ).size() );
|
||||
BOOST_CHECK_EQUAL( 1, stats.list_historic_referral_rewards( ath.alice_id, refs[2].id ).size() );
|
||||
|
||||
refs = stats.list_historic_referral_rewards( ath.ann_id, operation_history_id_type() );
|
||||
BOOST_CHECK_EQUAL( 2, refs.size() );
|
||||
BOOST_REQUIRE_LE( 1, refs.size() );
|
||||
BOOST_CHECK_EQUAL( app_tag::rps, refs[0].tag );
|
||||
BOOST_CHECK_EQUAL( 64, refs[0].payout.amount.value );
|
||||
BOOST_CHECK_EQUAL( 0, refs[0].payout.asset_id.instance.value );
|
||||
BOOST_REQUIRE_LE( 2, refs.size() );
|
||||
BOOST_CHECK_EQUAL( app_tag::bookie, refs[1].tag );
|
||||
BOOST_CHECK_EQUAL( 24, refs[1].payout.amount.value );
|
||||
BOOST_CHECK_EQUAL( 0, refs[1].payout.asset_id.instance.value );
|
||||
|
||||
BOOST_CHECK_EQUAL( 2, stats.list_historic_referral_rewards( ath.ann_id, refs[0].id ).size() );
|
||||
BOOST_CHECK_EQUAL( 1, stats.list_historic_referral_rewards( ath.ann_id, refs[1].id ).size() );
|
||||
|
||||
refs = stats.list_historic_referral_rewards( ath.audrey_id, operation_history_id_type() );
|
||||
BOOST_CHECK_EQUAL( 1, refs.size() );
|
||||
BOOST_REQUIRE_LE( 1, refs.size() );
|
||||
BOOST_CHECK_EQUAL( app_tag::rps, refs[0].tag );
|
||||
BOOST_CHECK_EQUAL( 109, refs[0].payout.amount.value );
|
||||
BOOST_CHECK_EQUAL( 0, refs[0].payout.asset_id.instance.value );
|
||||
|
||||
BOOST_CHECK_EQUAL( 0, stats.list_historic_referral_rewards( ath.alice_id, operation_history_id_type(9990) ).size() );
|
||||
BOOST_CHECK_EQUAL( 1, stats.list_historic_referral_rewards( ath.alice_id, operation_history_id_type(), 1 ).size() );
|
||||
BOOST_CHECK_EQUAL( 0, stats.list_historic_referral_rewards( ath.paula_id, operation_history_id_type() ).size() );
|
||||
BOOST_CHECK_EQUAL( 0, stats.list_historic_referral_rewards( ath.penny_id, operation_history_id_type() ).size() );
|
||||
BOOST_CHECK_EQUAL( 0, stats.list_historic_referral_rewards( ath.petra_id, operation_history_id_type() ).size() );
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<graphene::affiliate_stats::top_referred_account> refs = stats.list_top_referred_accounts( asset_id_type() );
|
||||
BOOST_CHECK_EQUAL( 1, refs.size() );
|
||||
BOOST_REQUIRE_LE( 1, refs.size() );
|
||||
BOOST_CHECK_EQUAL( ath.penny_id.instance.value, refs[0].referral.instance.value );
|
||||
BOOST_CHECK_EQUAL( 276, refs[0].total_payout.amount.value );
|
||||
BOOST_CHECK_EQUAL( 0, refs[0].total_payout.asset_id.instance.value );
|
||||
|
||||
refs = stats.list_top_referred_accounts( btc_id );
|
||||
BOOST_CHECK_EQUAL( 1, refs.size() );
|
||||
BOOST_REQUIRE_LE( 1, refs.size() );
|
||||
BOOST_CHECK_EQUAL( ath.paula_id.instance.value, refs[0].referral.instance.value );
|
||||
BOOST_CHECK_EQUAL( 600, refs[0].total_payout.amount.value );
|
||||
BOOST_CHECK_EQUAL( btc_id.instance.value, refs[0].total_payout.asset_id.instance.value );
|
||||
|
||||
BOOST_CHECK_EQUAL( 1, stats.list_top_referred_accounts( btc_id, 1 ).size() );
|
||||
BOOST_CHECK_EQUAL( 0, stats.list_top_referred_accounts( btc_id, 0 ).size() );
|
||||
BOOST_CHECK_EQUAL( 0, stats.list_top_referred_accounts( asset_id_type(9999) ).size() );
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<graphene::affiliate_stats::top_app> top = stats.list_top_rewards_per_app( asset_id_type() );
|
||||
BOOST_CHECK_EQUAL( 2, top.size() );
|
||||
BOOST_REQUIRE_LE( 1, top.size() );
|
||||
BOOST_CHECK_EQUAL( app_tag::rps, top[0].app );
|
||||
BOOST_CHECK_EQUAL( 216, top[0].total_payout.amount.value );
|
||||
BOOST_CHECK_EQUAL( 0, top[0].total_payout.asset_id.instance.value );
|
||||
BOOST_REQUIRE_LE( 2, top.size() );
|
||||
BOOST_CHECK_EQUAL( app_tag::bookie, top[1].app );
|
||||
BOOST_CHECK_EQUAL( 60, top[1].total_payout.amount.value );
|
||||
BOOST_CHECK_EQUAL( 0, top[1].total_payout.asset_id.instance.value );
|
||||
|
||||
top = stats.list_top_rewards_per_app( btc_id );
|
||||
BOOST_CHECK_EQUAL( 1, top.size() );
|
||||
BOOST_REQUIRE_LE( 1, top.size() );
|
||||
BOOST_CHECK_EQUAL( app_tag::bookie, top[0].app );
|
||||
BOOST_CHECK_EQUAL( 600, top[0].total_payout.amount.value );
|
||||
BOOST_CHECK_EQUAL( btc_id.instance.value, top[0].total_payout.asset_id.instance.value );
|
||||
|
||||
BOOST_CHECK_EQUAL( 1, stats.list_top_rewards_per_app( asset_id_type(), 1 ).size() );
|
||||
BOOST_CHECK_EQUAL( 0, stats.list_top_referred_accounts( btc_id, 0 ).size() );
|
||||
BOOST_CHECK_EQUAL( 0, stats.list_top_rewards_per_app( asset_id_type(9999) ).size() );
|
||||
}
|
||||
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include "../common/database_fixture.hpp"
|
||||
#include "../common/tournament_helper.hpp"
|
||||
#include <graphene/utilities/tempdir.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
|
|
@ -44,421 +44,6 @@ using namespace graphene::chain;
|
|||
|
||||
BOOST_AUTO_TEST_SUITE(tournament_tests)
|
||||
|
||||
// class performing operations necessary for creating tournaments,
|
||||
// having players join the tournaments and playing tournaments to completion.
|
||||
class tournaments_helper
|
||||
{
|
||||
public:
|
||||
|
||||
tournaments_helper(database_fixture& df) : df(df)
|
||||
{
|
||||
assets.insert(asset_id_type());
|
||||
current_asset_idx = 0;
|
||||
optional<account_id_type> dividend_account = get_asset_dividend_account(asset_id_type());
|
||||
if (dividend_account.valid())
|
||||
players.insert(*dividend_account);
|
||||
}
|
||||
|
||||
const std::set<tournament_id_type>& list_tournaments()
|
||||
{
|
||||
return tournaments;
|
||||
}
|
||||
|
||||
std::map<account_id_type, std::map<asset_id_type, share_type>> list_players_balances()
|
||||
{
|
||||
std::map<account_id_type, std::map<asset_id_type, share_type>> result;
|
||||
for (account_id_type player_id: players)
|
||||
{
|
||||
for( asset_id_type asset_id: assets)
|
||||
{
|
||||
asset a = df.db.get_balance(player_id, asset_id);
|
||||
result[player_id][a.asset_id] = a.amount;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<account_id_type, std::map<asset_id_type, share_type>> get_players_fees()
|
||||
{
|
||||
return players_fees;
|
||||
}
|
||||
|
||||
void reset_players_fees()
|
||||
{
|
||||
for (account_id_type player_id: players)
|
||||
{
|
||||
for( asset_id_type asset_id: assets)
|
||||
{
|
||||
players_fees[player_id][asset_id] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void create_asset(const account_id_type& issuer_account_id,
|
||||
const string& symbol,
|
||||
uint8_t precision,
|
||||
asset_options& common,
|
||||
const fc::ecc::private_key& sig_priv_key)
|
||||
{
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
signed_transaction tx;
|
||||
asset_create_operation op;
|
||||
op.issuer = issuer_account_id;
|
||||
op.symbol = symbol;
|
||||
op.precision = precision;
|
||||
op.common_options = common;
|
||||
|
||||
tx.operations = {op};
|
||||
for( auto& op : tx.operations )
|
||||
db.current_fee_schedule().set_fee(op);
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, sig_priv_key);
|
||||
PUSH_TX(db, tx);
|
||||
|
||||
assets.insert(asset_id_type(++current_asset_idx));
|
||||
}
|
||||
|
||||
void update_dividend_asset(const asset_id_type asset_to_update_id,
|
||||
dividend_asset_options new_options,
|
||||
const fc::ecc::private_key& sig_priv_key)
|
||||
{
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
signed_transaction tx;
|
||||
asset_update_dividend_operation update_op;
|
||||
|
||||
update_op.issuer = asset_to_update_id(db).issuer;
|
||||
update_op.asset_to_update = asset_to_update_id;
|
||||
update_op.new_options = new_options;
|
||||
|
||||
tx.operations = {update_op};
|
||||
for( auto& op : tx.operations )
|
||||
db.current_fee_schedule().set_fee(op);
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, sig_priv_key);
|
||||
PUSH_TX(db, tx);
|
||||
|
||||
optional<account_id_type> dividend_account = get_asset_dividend_account(asset_to_update_id);
|
||||
if (dividend_account.valid())
|
||||
players.insert(*dividend_account);
|
||||
}
|
||||
|
||||
optional<account_id_type> get_asset_dividend_account(const asset_id_type& asset_id)
|
||||
{
|
||||
graphene::chain::database& db = df.db;
|
||||
optional<account_id_type> result;
|
||||
const asset_object& asset_obj = asset_id(db);
|
||||
|
||||
if (asset_obj.dividend_data_id.valid())
|
||||
{
|
||||
const asset_dividend_data_object& dividend_data = (*asset_obj.dividend_data_id)(db);
|
||||
result = dividend_data.dividend_distribution_account;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const tournament_id_type create_tournament (const account_id_type& creator,
|
||||
const fc::ecc::private_key& sig_priv_key,
|
||||
asset buy_in,
|
||||
uint32_t number_of_players = 2,
|
||||
uint32_t time_per_commit_move = 3,
|
||||
uint32_t time_per_reveal_move = 1,
|
||||
uint32_t number_of_wins = 3,
|
||||
int32_t registration_deadline = 3600,
|
||||
uint32_t start_delay = 3,
|
||||
uint32_t round_delay = 3,
|
||||
bool insurance_enabled = false,
|
||||
uint32_t number_of_gestures = 3,
|
||||
uint32_t start_time = 0,
|
||||
fc::optional<flat_set<account_id_type> > whitelist = fc::optional<flat_set<account_id_type> >()
|
||||
)
|
||||
{
|
||||
if (current_tournament_idx.valid())
|
||||
current_tournament_idx = *current_tournament_idx + 1;
|
||||
else
|
||||
current_tournament_idx = 0;
|
||||
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
signed_transaction trx;
|
||||
tournament_options options;
|
||||
tournament_create_operation op;
|
||||
rock_paper_scissors_game_options& game_options = options.game_options.get<rock_paper_scissors_game_options>();
|
||||
|
||||
game_options.number_of_gestures = number_of_gestures;
|
||||
game_options.time_per_commit_move = time_per_commit_move;
|
||||
game_options.time_per_reveal_move = time_per_reveal_move;
|
||||
game_options.insurance_enabled = insurance_enabled;
|
||||
|
||||
options.registration_deadline = db.head_block_time() + fc::seconds(registration_deadline + *current_tournament_idx);
|
||||
options.buy_in = buy_in;
|
||||
options.number_of_players = number_of_players;
|
||||
if (start_delay)
|
||||
options.start_delay = start_delay;
|
||||
if (start_time)
|
||||
options.start_time = db.head_block_time() + fc::seconds(start_time);
|
||||
options.round_delay = round_delay;
|
||||
options.number_of_wins = number_of_wins;
|
||||
if (whitelist.valid())
|
||||
options.whitelist = *whitelist;
|
||||
|
||||
op.creator = creator;
|
||||
op.options = options;
|
||||
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));
|
||||
df.sign(trx, sig_priv_key);
|
||||
PUSH_TX(db, trx);
|
||||
|
||||
tournament_id_type tournament_id = tournament_id_type(*current_tournament_idx);
|
||||
tournaments.insert(tournament_id);
|
||||
return tournament_id;
|
||||
}
|
||||
|
||||
void join_tournament(const tournament_id_type & tournament_id,
|
||||
const account_id_type& player_id,
|
||||
const account_id_type& payer_id,
|
||||
const fc::ecc::private_key& sig_priv_key,
|
||||
asset buy_in
|
||||
)
|
||||
{
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
signed_transaction tx;
|
||||
tournament_join_operation op;
|
||||
|
||||
op.payer_account_id = payer_id;
|
||||
op.buy_in = buy_in;
|
||||
op.player_account_id = player_id;
|
||||
op.tournament_id = tournament_id;
|
||||
tx.operations = {op};
|
||||
for( auto& op : tx.operations )
|
||||
db.current_fee_schedule().set_fee(op);
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, sig_priv_key);
|
||||
PUSH_TX(db, tx);
|
||||
|
||||
players.insert(player_id);
|
||||
players_keys[player_id] = sig_priv_key;
|
||||
}
|
||||
|
||||
void leave_tournament(const tournament_id_type & tournament_id,
|
||||
const account_id_type& player_id,
|
||||
const account_id_type& canceling_account_id,
|
||||
const fc::ecc::private_key& sig_priv_key
|
||||
)
|
||||
{
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
signed_transaction tx;
|
||||
tournament_leave_operation op;
|
||||
|
||||
op.canceling_account_id = canceling_account_id;
|
||||
op.player_account_id = player_id;
|
||||
op.tournament_id = tournament_id;
|
||||
tx.operations = {op};
|
||||
for( auto& op : tx.operations )
|
||||
db.current_fee_schedule().set_fee(op);
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, sig_priv_key);
|
||||
PUSH_TX(db, tx);
|
||||
|
||||
//players.erase(player_id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// stolen from cli_wallet
|
||||
void rps_throw(const game_id_type& game_id,
|
||||
const account_id_type& player_id,
|
||||
rock_paper_scissors_gesture gesture,
|
||||
const fc::ecc::private_key& sig_priv_key
|
||||
)
|
||||
{
|
||||
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
|
||||
// check whether the gesture is appropriate for the game we're playing
|
||||
game_object game_obj = game_id(db);
|
||||
match_object match_obj = game_obj.match_id(db);
|
||||
tournament_object tournament_obj = match_obj.tournament_id(db);
|
||||
rock_paper_scissors_game_options game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
|
||||
assert((int)gesture < game_options.number_of_gestures);
|
||||
|
||||
account_object player_account_obj = player_id(db);
|
||||
|
||||
// construct the complete throw, the commit, and reveal
|
||||
rock_paper_scissors_throw full_throw;
|
||||
rand_bytes((char*)&full_throw.nonce1, sizeof(full_throw.nonce1));
|
||||
rand_bytes((char*)&full_throw.nonce2, sizeof(full_throw.nonce2));
|
||||
full_throw.gesture = gesture;
|
||||
|
||||
rock_paper_scissors_throw_commit commit_throw;
|
||||
commit_throw.nonce1 = full_throw.nonce1;
|
||||
std::vector<char> full_throw_packed(fc::raw::pack(full_throw));
|
||||
commit_throw.throw_hash = fc::sha256::hash(full_throw_packed.data(), full_throw_packed.size());
|
||||
|
||||
rock_paper_scissors_throw_reveal reveal_throw;
|
||||
reveal_throw.nonce2 = full_throw.nonce2;
|
||||
reveal_throw.gesture = full_throw.gesture;
|
||||
|
||||
// store off the reveal for applying after both players commit
|
||||
committed_game_moves[commit_throw] = reveal_throw;
|
||||
|
||||
signed_transaction tx;
|
||||
game_move_operation move_operation;
|
||||
move_operation.game_id = game_id;
|
||||
move_operation.player_account_id = player_account_obj.id;
|
||||
move_operation.move = commit_throw;
|
||||
tx.operations = {move_operation};
|
||||
for( operation& op : tx.operations )
|
||||
{
|
||||
asset f = db.current_fee_schedule().set_fee(op);
|
||||
players_fees[player_id][f.asset_id] -= f.amount;
|
||||
}
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, sig_priv_key);
|
||||
if (/*match_obj.match_winners.empty() &&*/ game_obj.get_state() == game_state::expecting_commit_moves) // checking again
|
||||
PUSH_TX(db, tx);
|
||||
}
|
||||
|
||||
// spaghetti programming
|
||||
// walking through all tournaments, matches and games and throwing random moves
|
||||
// optionaly skip generting randomly selected moves
|
||||
// every_move_is >= 0 : every game is tie
|
||||
void play_games(unsigned skip_some_commits = 0, unsigned skip_some_reveals = 0, int every_move_is = -1)
|
||||
{
|
||||
//try
|
||||
//{
|
||||
graphene::chain::database& db = df.db;
|
||||
const chain_parameters& params = db.get_global_properties().parameters;
|
||||
|
||||
for(const auto& tournament_id: tournaments)
|
||||
{
|
||||
const tournament_object& tournament = tournament_id(db);
|
||||
const tournament_details_object& tournament_details = tournament.tournament_details_id(db);
|
||||
rock_paper_scissors_game_options game_options = tournament.options.game_options.get<rock_paper_scissors_game_options>();
|
||||
for(const auto& match_id: tournament_details.matches)
|
||||
{
|
||||
const match_object& match = match_id(db);
|
||||
for(const auto& game_id: match.games )
|
||||
{
|
||||
const game_object& game = game_id(db);
|
||||
const rock_paper_scissors_game_details& rps_details = game.game_details.get<rock_paper_scissors_game_details>();
|
||||
if (game.get_state() == game_state::expecting_commit_moves)
|
||||
{
|
||||
for(const auto& player_id: game.players)
|
||||
{
|
||||
if ( players_keys.find(player_id) != players_keys.end())
|
||||
{
|
||||
if (!skip_some_commits || player_id.instance.value % skip_some_commits != game_id.instance.value % skip_some_commits)
|
||||
{
|
||||
auto iter = std::find(game.players.begin(), game.players.end(), player_id);
|
||||
unsigned player_index = std::distance(game.players.begin(), iter);
|
||||
if (!rps_details.commit_moves.at(player_index))
|
||||
rps_throw(game_id, player_id,
|
||||
(rock_paper_scissors_gesture) (every_move_is >= 0 ? every_move_is : (std::rand() % game_options.number_of_gestures)), players_keys[player_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (game.get_state() == game_state::expecting_reveal_moves)
|
||||
{
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{
|
||||
if (rps_details.commit_moves.at(i) &&
|
||||
!rps_details.reveal_moves.at(i))
|
||||
{
|
||||
const account_id_type& player_id = game.players[i];
|
||||
if (players_keys.find(player_id) != players_keys.end())
|
||||
{
|
||||
{
|
||||
|
||||
auto iter = committed_game_moves.find(*rps_details.commit_moves.at(i));
|
||||
if (iter != committed_game_moves.end())
|
||||
{
|
||||
if (!skip_some_reveals || player_id.instance.value % skip_some_reveals != game_id.instance.value % skip_some_reveals)
|
||||
{
|
||||
const rock_paper_scissors_throw_reveal& reveal = iter->second;
|
||||
|
||||
game_move_operation move_operation;
|
||||
move_operation.game_id = game.id;
|
||||
move_operation.player_account_id = player_id;
|
||||
move_operation.move = reveal;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations = {move_operation};
|
||||
for( auto& op : tx.operations )
|
||||
{
|
||||
asset f = db.current_fee_schedule().set_fee(op);
|
||||
players_fees[player_id][f.asset_id] -= f.amount;
|
||||
}
|
||||
tx.validate();
|
||||
tx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3));
|
||||
df.sign(tx, players_keys[player_id]);
|
||||
if (game.get_state() == game_state::expecting_reveal_moves) // check again
|
||||
PUSH_TX(db, tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
//catch (fc::exception& e)
|
||||
//{
|
||||
// edump((e.to_detail_string()));
|
||||
// throw;
|
||||
//}
|
||||
}
|
||||
|
||||
private:
|
||||
database_fixture& df;
|
||||
// index of last created tournament
|
||||
fc::optional<uint64_t> current_tournament_idx;
|
||||
// index of last asset
|
||||
uint64_t current_asset_idx;
|
||||
// assets : core and maybe others
|
||||
std::set<asset_id_type> assets;
|
||||
// tournaments to be played
|
||||
std::set<tournament_id_type> tournaments;
|
||||
// all players registered in tournaments
|
||||
std::set<account_id_type> players;
|
||||
// players' private keys
|
||||
std::map<account_id_type, fc::ecc::private_key> players_keys;
|
||||
// total charges for moves made by every player
|
||||
std::map<account_id_type, std::map<asset_id_type, share_type>> players_fees;
|
||||
// store of commits and reveals
|
||||
std::map<rock_paper_scissors_throw_commit, rock_paper_scissors_throw_reveal> committed_game_moves;
|
||||
|
||||
// taken from rand.cpp
|
||||
void rand_bytes(char* buf, int count)
|
||||
{
|
||||
fc::init_openssl();
|
||||
|
||||
int result = RAND_bytes((unsigned char*)buf, count);
|
||||
if (result != 1)
|
||||
FC_THROW("Error calling OpenSSL's RAND_bytes(): ${code}", ("code", (uint32_t)ERR_get_error()));
|
||||
}
|
||||
};
|
||||
|
||||
/// Expecting assertion
|
||||
|
||||
// creating tournament
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( Registration_deadline_has_already, database_fixture )
|
||||
{
|
||||
try
|
||||
|
|
|
|||
Loading…
Reference in a new issue