Merge branch 'rock-paper-scissors' of https://bitbucket.org/peerplaysblockchain/peerplays-graphene into betting-merge
This commit is contained in:
commit
d3482f3a08
82 changed files with 226632 additions and 122 deletions
4
.gitmodules
vendored
4
.gitmodules
vendored
|
|
@ -3,6 +3,6 @@
|
|||
url = https://github.com/bitshares/bitshares-core.wiki.git
|
||||
ignore = dirty
|
||||
[submodule "libraries/fc"]
|
||||
path = libraries/fc
|
||||
url = git@bitbucket.org:peerplaysblockchain/peerplays-fc.git
|
||||
path = libraries/fc
|
||||
url = https://bitbucket.org/peerplaysblockchain/peerplays-fc
|
||||
ignore = dirty
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ endif()
|
|||
list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules" )
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS "ON")
|
||||
set(GRAPHENE_EGENESIS_JSON "${CMAKE_CURRENT_SOURCE_DIR}/genesis.json" )
|
||||
set(GRAPHENE_EGENESIS_JSON "${CMAKE_CURRENT_SOURCE_DIR}/genesis.json" CACHE PATH "location of the genesis.json to embed in the executable" )
|
||||
|
||||
#set (ENABLE_INSTALLER 1)
|
||||
#set (USE_PCH 1)
|
||||
|
|
|
|||
580
genesis.json
580
genesis.json
File diff suppressed because one or more lines are too long
216158
genesis/genesis.json
Normal file
216158
genesis/genesis.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -12,7 +12,8 @@ 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_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 )
|
||||
target_include_directories( graphene_app
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include <graphene/chain/transaction_object.hpp>
|
||||
#include <graphene/chain/withdraw_permission_object.hpp>
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
|
@ -268,6 +269,7 @@ namespace graphene { namespace app {
|
|||
return *_debug_api;
|
||||
}
|
||||
|
||||
#if 0
|
||||
vector<account_id_type> get_relevant_accounts( const object* obj )
|
||||
{
|
||||
vector<account_id_type> result;
|
||||
|
|
@ -361,6 +363,13 @@ namespace graphene { namespace app {
|
|||
const auto& aobj = dynamic_cast<const bet_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
result.push_back( aobj->bettor_id );
|
||||
} case tournament_object_type:{
|
||||
const tournament_object* tournament_obj = dynamic_cast<const tournament_object*>(obj);
|
||||
assert(tournament_obj);
|
||||
const tournament_details_object& details = tournament_obj->tournament_details_id(*_app.chain_database());
|
||||
flat_set<account_id_type> impacted = details.registered_players;
|
||||
impacted.insert(tournament_obj->creator);
|
||||
std::copy(impacted.begin(), impacted.end(), std::back_inserter(result));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -428,6 +437,7 @@ namespace graphene { namespace app {
|
|||
}
|
||||
return result;
|
||||
} // end get_relevant_accounts( obj )
|
||||
#endif
|
||||
|
||||
vector<order_history_object> history_api::get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const
|
||||
{
|
||||
|
|
@ -546,6 +556,13 @@ namespace graphene { namespace app {
|
|||
return result;
|
||||
}
|
||||
|
||||
vector<account_balance_object> history_api::list_core_accounts()const
|
||||
{
|
||||
auto list = _app.get_plugin<accounts_list_plugin>( "accounts_list" );
|
||||
FC_ASSERT( list );
|
||||
return list->list_accounts();
|
||||
}
|
||||
|
||||
flat_set<uint32_t> history_api::get_market_history_buckets()const
|
||||
{
|
||||
auto hist = _app.get_plugin<market_history_plugin>( "market_history" );
|
||||
|
|
|
|||
|
|
@ -178,7 +178,8 @@ namespace detail {
|
|||
"seed05.bts-nodes.net:1776", // Thom (USA)
|
||||
"seed06.bts-nodes.net:1776", // Thom (USA)
|
||||
"seed07.bts-nodes.net:1776", // Thom (Singapore)
|
||||
"seeds.bitshares.eu:1776" // pc (http://seeds.quisquis.de/bitshares.html)
|
||||
"seeds.bitshares.eu:1776", // pc (http://seeds.quisquis.de/bitshares.html)
|
||||
"peerplays.blocktrades.info:2776"
|
||||
};
|
||||
for( const string& endpoint_string : seeds )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <graphene/app/database_api.hpp>
|
||||
#include <graphene/chain/get_config.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
|
||||
#include <fc/bloom_filter.hpp>
|
||||
|
|
@ -151,6 +152,13 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
// Blinded balances
|
||||
vector<blinded_balance_object> get_blinded_balances( const flat_set<commitment_type>& commitments )const;
|
||||
|
||||
// Tournaments
|
||||
vector<tournament_object> get_tournaments_in_state(tournament_state state, uint32_t limit) const;
|
||||
vector<tournament_object> get_tournaments(tournament_id_type stop, unsigned limit, tournament_id_type start);
|
||||
vector<tournament_object> get_tournaments_by_state(tournament_id_type stop, unsigned limit, tournament_id_type start, tournament_state state);
|
||||
vector<tournament_id_type> get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
|
||||
|
||||
|
||||
//private:
|
||||
template<typename T>
|
||||
void subscribe_to_item( const T& i )const
|
||||
|
|
@ -1884,6 +1892,99 @@ vector<blinded_balance_object> database_api_impl::get_blinded_balances( const fl
|
|||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Tournament methods //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
vector<tournament_object> database_api::get_tournaments_in_state(tournament_state state, uint32_t limit) const
|
||||
{
|
||||
return my->get_tournaments_in_state(state, limit);
|
||||
}
|
||||
|
||||
vector<tournament_object> database_api_impl::get_tournaments_in_state(tournament_state state, uint32_t limit) const
|
||||
{
|
||||
vector<tournament_object> result;
|
||||
const auto& registration_deadline_index = _db.get_index_type<tournament_index>().indices().get<by_registration_deadline>();
|
||||
const auto range = registration_deadline_index.equal_range(boost::make_tuple(state));
|
||||
for (const tournament_object& tournament_obj : boost::make_iterator_range(range.first, range.second))
|
||||
{
|
||||
result.emplace_back(tournament_obj);
|
||||
subscribe_to_item( tournament_obj.id );
|
||||
|
||||
if (result.size() >= limit)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<tournament_object> database_api::get_tournaments(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start)
|
||||
{
|
||||
return my->get_tournaments(stop, limit, start);
|
||||
}
|
||||
|
||||
vector<tournament_object> database_api_impl::get_tournaments(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start)
|
||||
{
|
||||
vector<tournament_object> result;
|
||||
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
|
||||
for (auto elem: tournament_idx) {
|
||||
if( result.size() >= limit ) break;
|
||||
if( ( (elem.get_id().instance.value <= start.instance.value) || start == tournament_id_type()) &&
|
||||
( (elem.get_id().instance.value >= stop.instance.value) || stop == tournament_id_type()))
|
||||
result.push_back( elem );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
vector<tournament_object> database_api::get_tournaments_by_state(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start,
|
||||
tournament_state state)
|
||||
{
|
||||
return my->get_tournaments_by_state(stop, limit, start, state);
|
||||
}
|
||||
|
||||
vector<tournament_object> database_api_impl::get_tournaments_by_state(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start,
|
||||
tournament_state state)
|
||||
{
|
||||
vector<tournament_object> result;
|
||||
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
|
||||
for (auto elem: tournament_idx) {
|
||||
if( result.size() >= limit ) break;
|
||||
if( ( (elem.get_id().instance.value <= start.instance.value) || start == tournament_id_type()) &&
|
||||
( (elem.get_id().instance.value >= stop.instance.value) || stop == tournament_id_type()) &&
|
||||
elem.get_state() == state )
|
||||
result.push_back( elem );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<tournament_id_type> database_api::get_registered_tournaments(account_id_type account_filter, uint32_t limit) const
|
||||
{
|
||||
return my->get_registered_tournaments(account_filter, limit);
|
||||
}
|
||||
|
||||
vector<tournament_id_type> database_api_impl::get_registered_tournaments(account_id_type account_filter, uint32_t limit) const
|
||||
{
|
||||
const auto& tournament_details_idx = _db.get_index_type<tournament_details_index>();
|
||||
const auto& tournament_details_primary_idx = dynamic_cast<const primary_index<tournament_details_index>&>(tournament_details_idx);
|
||||
const auto& players_idx = tournament_details_primary_idx.get_secondary_index<graphene::chain::tournament_players_index>();
|
||||
|
||||
vector<tournament_id_type> tournament_ids = players_idx.get_registered_tournaments_for_account(account_filter);
|
||||
if (tournament_ids.size() >= limit)
|
||||
tournament_ids.resize(limit);
|
||||
return tournament_ids;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Private methods //
|
||||
|
|
@ -1978,6 +2079,12 @@ void database_api_impl::handle_object_changed(bool force_notify, bool full_objec
|
|||
if( _market_subscriptions.size() )
|
||||
{
|
||||
market_queue_type broadcast_queue;
|
||||
/// pushing the future back / popping the prior future if it is complete.
|
||||
/// if a connection hangs then this could get backed up and result in
|
||||
/// a failure to exit cleanly.
|
||||
//fc::async([capture_this,this,updates,market_broadcast_queue](){
|
||||
//if( _subscribe_callback )
|
||||
// _subscribe_callback( updates );
|
||||
|
||||
for(auto id : ids)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -238,6 +238,31 @@ struct get_impacted_account_visitor
|
|||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
|
||||
void operator()( const tournament_create_operation& op )
|
||||
{
|
||||
_impacted.insert( op.creator );
|
||||
_impacted.insert( op.options.whitelist.begin(), op.options.whitelist.end() );
|
||||
}
|
||||
void operator()( const tournament_join_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer_account_id );
|
||||
_impacted.insert( op.player_account_id );
|
||||
}
|
||||
void operator()( const tournament_leave_operation& op )
|
||||
{
|
||||
//if account canceling registration is not the player, it must be the payer
|
||||
if (op.canceling_account_id != op.player_account_id)
|
||||
_impacted.erase( op.canceling_account_id );
|
||||
_impacted.erase( op.player_account_id );
|
||||
}
|
||||
void operator()( const game_move_operation& op )
|
||||
{
|
||||
_impacted.insert( op.player_account_id );
|
||||
}
|
||||
void operator()( const tournament_payout_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payout_account_id );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <graphene/chain/protocol/confidential.hpp>
|
||||
|
||||
#include <graphene/market_history/market_history_plugin.hpp>
|
||||
#include <graphene/accounts_list/accounts_list_plugin.hpp>
|
||||
|
||||
#include <graphene/debug_witness/debug_api.hpp>
|
||||
|
||||
|
|
@ -49,6 +50,7 @@
|
|||
namespace graphene { namespace app {
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::market_history;
|
||||
using namespace graphene::accounts_list;
|
||||
using namespace fc::ecc;
|
||||
using namespace std;
|
||||
|
||||
|
|
@ -141,6 +143,7 @@ namespace graphene { namespace app {
|
|||
vector<order_history_object> get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const;
|
||||
vector<bucket_object> get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds,
|
||||
fc::time_point_sec start, fc::time_point_sec end )const;
|
||||
vector<account_balance_object> list_core_accounts()const;
|
||||
flat_set<uint32_t> get_market_history_buckets()const;
|
||||
private:
|
||||
application& _app;
|
||||
|
|
@ -393,6 +396,7 @@ FC_API(graphene::app::history_api,
|
|||
(get_fill_order_history)
|
||||
(get_market_history)
|
||||
(get_market_history_buckets)
|
||||
(list_core_accounts)
|
||||
)
|
||||
FC_API(graphene::app::block_api,
|
||||
(get_blocks)
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
|
||||
#include <graphene/market_history/market_history_plugin.hpp>
|
||||
|
||||
|
|
@ -606,6 +607,28 @@ class database_api
|
|||
*/
|
||||
vector<blinded_balance_object> get_blinded_balances( const flat_set<commitment_type>& commitments )const;
|
||||
|
||||
/////////////////
|
||||
// Tournaments //
|
||||
/////////////////
|
||||
/**
|
||||
* @return the list of tournaments in the given state
|
||||
*/
|
||||
vector<tournament_object> get_tournaments_in_state(tournament_state state, uint32_t limit) const;
|
||||
|
||||
vector<tournament_object> get_tournaments(tournament_id_type stop = tournament_id_type(),
|
||||
unsigned limit = 100,
|
||||
tournament_id_type start = tournament_id_type());
|
||||
|
||||
vector<tournament_object> get_tournaments_by_state(tournament_id_type stop = tournament_id_type(),
|
||||
unsigned limit = 100,
|
||||
tournament_id_type start = tournament_id_type(),
|
||||
tournament_state state = tournament_state::accepting_registrations);
|
||||
|
||||
/**
|
||||
* @return the list of tournaments that a given account is registered to play in
|
||||
*/
|
||||
vector<tournament_id_type> get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr< database_api_impl > my;
|
||||
};
|
||||
|
|
@ -717,4 +740,10 @@ FC_API(graphene::app::database_api,
|
|||
|
||||
// Blinded balances
|
||||
(get_blinded_balances)
|
||||
|
||||
// Tournaments
|
||||
(get_tournaments_in_state)
|
||||
(get_tournaments_by_state)
|
||||
(get_tournaments )
|
||||
(get_registered_tournaments)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ add_library( graphene_chain
|
|||
protocol/fee_schedule.cpp
|
||||
protocol/confidential.cpp
|
||||
protocol/vote.cpp
|
||||
protocol/tournament.cpp
|
||||
|
||||
genesis_state.cpp
|
||||
get_config.cpp
|
||||
|
|
@ -75,6 +76,10 @@ add_library( graphene_chain
|
|||
proposal_evaluator.cpp
|
||||
market_evaluator.cpp
|
||||
vesting_balance_evaluator.cpp
|
||||
tournament_evaluator.cpp
|
||||
tournament_object.cpp
|
||||
match_object.cpp
|
||||
game_object.cpp
|
||||
withdraw_permission_evaluator.cpp
|
||||
worker_evaluator.cpp
|
||||
confidential_evaluator.cpp
|
||||
|
|
|
|||
|
|
@ -397,6 +397,22 @@ signed_block database::_generate_block(
|
|||
pending_block.transaction_merkle_root = pending_block.calculate_merkle_root();
|
||||
pending_block.witness = witness_id;
|
||||
|
||||
// Genesis witnesses start with a default initial secret
|
||||
if( witness_obj.next_secret_hash == secret_hash_type::hash( secret_hash_type() ) )
|
||||
pending_block.previous_secret = secret_hash_type();
|
||||
else
|
||||
{
|
||||
secret_hash_type::encoder last_enc;
|
||||
fc::raw::pack( last_enc, block_signing_private_key );
|
||||
fc::raw::pack( last_enc, witness_obj.previous_secret );
|
||||
pending_block.previous_secret = last_enc.result();
|
||||
}
|
||||
|
||||
secret_hash_type::encoder next_enc;
|
||||
fc::raw::pack( next_enc, block_signing_private_key );
|
||||
fc::raw::pack( next_enc, pending_block.previous_secret );
|
||||
pending_block.next_secret_hash = secret_hash_type::hash(next_enc.result());
|
||||
|
||||
if( !(skip & skip_witness_signature) )
|
||||
pending_block.sign( block_signing_private_key );
|
||||
|
||||
|
|
@ -513,6 +529,8 @@ void database::_apply_block( const signed_block& next_block )
|
|||
++_current_trx_in_block;
|
||||
}
|
||||
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
|
||||
update_witness_schedule(next_block);
|
||||
update_global_dynamic_data(next_block);
|
||||
update_signing_witness(signing_witness, next_block);
|
||||
update_last_irreversible_block();
|
||||
|
|
@ -527,6 +545,7 @@ void database::_apply_block( const signed_block& next_block )
|
|||
clear_expired_orders();
|
||||
update_expired_feeds();
|
||||
update_withdraw_permissions();
|
||||
update_tournaments();
|
||||
|
||||
// n.b., update_maintenance_flag() happens this late
|
||||
// because get_slot_time() / get_slot_at_time() is needed above
|
||||
|
|
@ -534,7 +553,8 @@ void database::_apply_block( const signed_block& next_block )
|
|||
// update_global_dynamic_data() as perhaps these methods only need
|
||||
// to be called for header validation?
|
||||
update_maintenance_flag( maint_needed );
|
||||
update_witness_schedule();
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
|
||||
update_witness_schedule();
|
||||
if( !_node_property_object.debug_updates.empty() )
|
||||
apply_debug_updates();
|
||||
|
||||
|
|
@ -543,6 +563,7 @@ void database::_apply_block( const signed_block& next_block )
|
|||
_applied_ops.clear();
|
||||
|
||||
notify_changed_objects();
|
||||
|
||||
} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) }
|
||||
|
||||
|
||||
|
|
@ -650,6 +671,9 @@ const witness_object& database::validate_block_header( uint32_t skip, const sign
|
|||
FC_ASSERT( head_block_id() == next_block.previous, "", ("head_block_id",head_block_id())("next.prev",next_block.previous) );
|
||||
FC_ASSERT( head_block_time() < next_block.timestamp, "", ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num()) );
|
||||
const witness_object& witness = next_block.witness(*this);
|
||||
//DLN: TODO: Temporarily commented out to test shuffle vs RNG scheduling algorithm for witnesses, this was causing shuffle agorithm to fail during create_witness test. This should be re-enabled for RNG, and maybe for shuffle too, don't really know for sure.
|
||||
// FC_ASSERT( secret_hash_type::hash( next_block.previous_secret ) == witness.next_secret_hash, "",
|
||||
// ("previous_secret", next_block.previous_secret)("next_secret_hash", witness.next_secret_hash)("null_secret_hash", secret_hash_type::hash( secret_hash_type())));
|
||||
|
||||
if( !(skip&skip_witness_signature) )
|
||||
FC_ASSERT( next_block.validate_signee( witness.signing_key ) );
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@
|
|||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/witness_schedule_object.hpp>
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
|
||||
|
||||
#include <graphene/chain/sport_object.hpp>
|
||||
|
|
@ -72,6 +75,7 @@
|
|||
#include <graphene/chain/event_group_evaluator.hpp>
|
||||
#include <graphene/chain/event_evaluator.hpp>
|
||||
#include <graphene/chain/betting_market_evaluator.hpp>
|
||||
#include <graphene/chain/tournament_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
|
|
@ -218,6 +222,10 @@ void database::initialize_evaluators()
|
|||
register_evaluator<betting_market_create_evaluator>();
|
||||
register_evaluator<bet_place_evaluator>();
|
||||
register_evaluator<betting_market_group_resolve_evaluator>();
|
||||
register_evaluator<tournament_create_evaluator>();
|
||||
register_evaluator<tournament_join_evaluator>();
|
||||
register_evaluator<game_move_evaluator>();
|
||||
register_evaluator<tournament_leave_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
@ -254,6 +262,12 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<betting_market_object_index > >();
|
||||
add_index< primary_index<bet_object_index > >();
|
||||
|
||||
add_index< primary_index<tournament_index> >();
|
||||
auto tournament_details_idx = add_index< primary_index<tournament_details_index> >();
|
||||
tournament_details_idx->add_secondary_index<tournament_players_index>();
|
||||
add_index< primary_index<match_index> >();
|
||||
add_index< primary_index<game_index> >();
|
||||
|
||||
//Implementation object indexes
|
||||
add_index< primary_index<transaction_index > >();
|
||||
add_index< primary_index<account_balance_index > >();
|
||||
|
|
@ -372,6 +386,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
}).get_id() == GRAPHENE_PROXY_TO_SELF_ACCOUNT);
|
||||
FC_ASSERT(create<account_object>([this](account_object& a) {
|
||||
a.name = "default-dividend-distribution";
|
||||
//a.name = "test-dividend-distribution";
|
||||
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
|
||||
a.owner.weight_threshold = 1;
|
||||
a.active.weight_threshold = 1;
|
||||
|
|
@ -380,7 +395,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
a.network_fee_percentage = 0;
|
||||
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT;
|
||||
}).get_id() == GRAPHENE_RAKE_FEE_ACCOUNT_ID);
|
||||
|
||||
//}).get_id() == TOURNAMENT_RAKE_FEE_ACCOUNT_ID);
|
||||
// Create more special accounts
|
||||
while( true )
|
||||
{
|
||||
|
|
@ -414,6 +429,9 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
a.options.next_payout_time = genesis_state.initial_timestamp + fc::hours(1);
|
||||
a.options.payout_interval = 7*24*60*60;
|
||||
a.dividend_distribution_account = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
|
||||
//a.options.next_payout_time = genesis_state.initial_timestamp + fc::days(1);
|
||||
//a.options.payout_interval = 30*24*60*60;
|
||||
//a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
|
||||
});
|
||||
|
||||
const asset_object& core_asset =
|
||||
|
|
@ -434,6 +452,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
assert( asset_id_type(core_asset.id) == asset().asset_id );
|
||||
assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) );
|
||||
|
||||
#ifdef _DEFAULT_DIVIDEND_ASSET
|
||||
// Create default dividend asset
|
||||
const asset_dynamic_data_object& dyn_asset1 =
|
||||
create<asset_dynamic_data_object>([&](asset_dynamic_data_object& a) {
|
||||
|
|
@ -446,17 +465,20 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
a.options.next_payout_time = genesis_state.initial_timestamp + fc::hours(1);
|
||||
a.options.payout_interval = 7*24*60*60;
|
||||
a.dividend_distribution_account = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
|
||||
//a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
|
||||
});
|
||||
|
||||
const asset_object& default_asset =
|
||||
create<asset_object>( [&]( asset_object& a ) {
|
||||
a.symbol = "DEF";
|
||||
//a.symbol = "DEFAULT";
|
||||
a.options.max_market_fee =
|
||||
a.options.max_supply = genesis_state.max_core_supply;
|
||||
a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
|
||||
a.options.flags = 0;
|
||||
a.options.issuer_permissions = 79;
|
||||
a.issuer = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
|
||||
//a.issuer = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
|
||||
a.options.core_exchange_rate.base.amount = 1;
|
||||
a.options.core_exchange_rate.base.asset_id = asset_id_type(0);
|
||||
a.options.core_exchange_rate.quote.amount = 1;
|
||||
|
|
@ -465,6 +487,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
a.dividend_data_id = div_asset1.id;
|
||||
});
|
||||
assert( default_asset.id == asset_id_type(1) );
|
||||
#endif
|
||||
|
||||
// Create more special assets
|
||||
while( true )
|
||||
|
|
@ -523,6 +546,19 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
} );
|
||||
create<block_summary_object>([&](block_summary_object&) {});
|
||||
|
||||
// Create initial accounts from graphene-based chains
|
||||
// graphene accounts can refer to other accounts in their authorities, so
|
||||
// we first create all accounts with dummy authorities, then go back and
|
||||
// set up the authorities once the accounts all have ids assigned.
|
||||
for( const auto& account : genesis_state.initial_bts_accounts )
|
||||
{
|
||||
account_create_operation cop;
|
||||
cop.name = account.name;
|
||||
cop.registrar = GRAPHENE_TEMP_ACCOUNT;
|
||||
cop.owner = authority(1, GRAPHENE_TEMP_ACCOUNT, 1);
|
||||
account_id_type account_id(apply_operation(genesis_eval_state, cop).get<object_id_type>());
|
||||
}
|
||||
|
||||
// Create initial accounts
|
||||
for( const auto& account : genesis_state.initial_accounts )
|
||||
{
|
||||
|
|
@ -561,6 +597,32 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
return itr->get_id();
|
||||
};
|
||||
|
||||
for( const auto& account : genesis_state.initial_bts_accounts )
|
||||
{
|
||||
account_update_operation op;
|
||||
op.account = get_account_id(account.name);
|
||||
|
||||
authority owner_authority;
|
||||
owner_authority.weight_threshold = account.owner_authority.weight_threshold;
|
||||
for (const auto& value : account.owner_authority.account_auths)
|
||||
owner_authority.account_auths.insert(std::make_pair(get_account_id(value.first), value.second));
|
||||
owner_authority.key_auths = account.owner_authority.key_auths;
|
||||
owner_authority.address_auths = account.owner_authority.address_auths;
|
||||
|
||||
op.owner = std::move(owner_authority);
|
||||
|
||||
authority active_authority;
|
||||
active_authority.weight_threshold = account.active_authority.weight_threshold;
|
||||
for (const auto& value : account.active_authority.account_auths)
|
||||
active_authority.account_auths.insert(std::make_pair(get_account_id(value.first), value.second));
|
||||
active_authority.key_auths = account.active_authority.key_auths;
|
||||
active_authority.address_auths = account.active_authority.address_auths;
|
||||
|
||||
op.active = std::move(active_authority);
|
||||
|
||||
apply_operation(genesis_eval_state, op);
|
||||
}
|
||||
|
||||
// Helper function to get asset ID by symbol
|
||||
const auto& assets_by_symbol = get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
const auto get_asset_id = [&assets_by_symbol](const string& symbol) {
|
||||
|
|
@ -646,6 +708,46 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
});
|
||||
}
|
||||
|
||||
// Create balances for all bts accounts
|
||||
for( const auto& account : genesis_state.initial_bts_accounts ) {
|
||||
if (account.core_balance != share_type()) {
|
||||
total_supplies[asset_id_type()] += account.core_balance;
|
||||
|
||||
create<account_balance_object>([&](account_balance_object& b) {
|
||||
b.owner = get_account_id(account.name);
|
||||
b.balance = account.core_balance;
|
||||
});
|
||||
}
|
||||
|
||||
// create any vesting balances for this account
|
||||
if (account.vesting_balances)
|
||||
for (const auto& vesting_balance : *account.vesting_balances) {
|
||||
create<vesting_balance_object>([&](vesting_balance_object& vbo) {
|
||||
vbo.owner = get_account_id(account.name);
|
||||
vbo.balance = asset(vesting_balance.amount, get_asset_id(vesting_balance.asset_symbol));
|
||||
if (vesting_balance.policy_type == "linear") {
|
||||
auto initial_linear_vesting_policy = vesting_balance.policy.as<genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy>();
|
||||
linear_vesting_policy new_vesting_policy;
|
||||
new_vesting_policy.begin_timestamp = initial_linear_vesting_policy.begin_timestamp;
|
||||
new_vesting_policy.vesting_cliff_seconds = initial_linear_vesting_policy.vesting_cliff_seconds;
|
||||
new_vesting_policy.vesting_duration_seconds = initial_linear_vesting_policy.vesting_duration_seconds;
|
||||
new_vesting_policy.begin_balance = initial_linear_vesting_policy.begin_balance;
|
||||
vbo.policy = new_vesting_policy;
|
||||
} else if (vesting_balance.policy_type == "cdd") {
|
||||
auto initial_cdd_vesting_policy = vesting_balance.policy.as<genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy>();
|
||||
cdd_vesting_policy new_vesting_policy;
|
||||
new_vesting_policy.vesting_seconds = initial_cdd_vesting_policy.vesting_seconds;
|
||||
new_vesting_policy.coin_seconds_earned = initial_cdd_vesting_policy.coin_seconds_earned;
|
||||
new_vesting_policy.start_claim = initial_cdd_vesting_policy.start_claim;
|
||||
new_vesting_policy.coin_seconds_earned_last_update = initial_cdd_vesting_policy.coin_seconds_earned_last_update;
|
||||
vbo.policy = new_vesting_policy;
|
||||
}
|
||||
total_supplies[get_asset_id(vesting_balance.asset_symbol)] += vesting_balance.amount;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create initial balances
|
||||
share_type total_allocation;
|
||||
for( const auto& handout : genesis_state.initial_balances )
|
||||
|
|
@ -669,7 +771,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
|
||||
linear_vesting_policy policy;
|
||||
policy.begin_timestamp = vest.begin_timestamp;
|
||||
policy.vesting_cliff_seconds = 0;
|
||||
policy.vesting_cliff_seconds = vest.vesting_cliff_seconds ? *vest.vesting_cliff_seconds : 0;
|
||||
policy.vesting_duration_seconds = vest.vesting_duration_seconds;
|
||||
policy.begin_balance = vest.begin_balance;
|
||||
|
||||
|
|
@ -687,8 +789,11 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
{
|
||||
total_supplies[ asset_id_type(0) ] = GRAPHENE_MAX_SHARE_SUPPLY;
|
||||
}
|
||||
#ifdef _DEFAULT_DIVIDEND_ASSET
|
||||
total_debts[ asset_id_type(1) ] =
|
||||
total_supplies[ asset_id_type(1) ] = 0;
|
||||
// it is workaround, should be clarified
|
||||
total_debts[ asset_id_type() ] = total_supplies[ asset_id_type() ];
|
||||
|
||||
const auto& idx = get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
auto it = idx.begin();
|
||||
|
|
@ -740,6 +845,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
std::for_each(genesis_state.initial_witness_candidates.begin(), genesis_state.initial_witness_candidates.end(),
|
||||
[&](const genesis_state_type::initial_witness_type& witness) {
|
||||
witness_create_operation op;
|
||||
op.initial_secret = secret_hash_type::hash(secret_hash_type());
|
||||
op.witness_account = get_account_id(witness.owner_name);
|
||||
op.block_signing_key = witness.block_signing_key;
|
||||
apply_operation(genesis_eval_state, op);
|
||||
|
|
@ -776,17 +882,47 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
}
|
||||
});
|
||||
|
||||
// Initialize witness schedule
|
||||
#ifndef NDEBUG
|
||||
const witness_schedule_object& wso =
|
||||
#endif
|
||||
create<witness_schedule_object>([&](witness_schedule_object& _wso)
|
||||
{
|
||||
// for scheduled
|
||||
memset(_wso.rng_seed.begin(), 0, _wso.rng_seed.size());
|
||||
|
||||
witness_scheduler_rng rng(_wso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
||||
|
||||
auto init_witnesses = get_global_properties().active_witnesses;
|
||||
|
||||
_wso.scheduler = witness_scheduler();
|
||||
_wso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1);
|
||||
_wso.scheduler.update(init_witnesses);
|
||||
|
||||
for( size_t i=0; i<init_witnesses.size(); ++i )
|
||||
_wso.scheduler.produce_schedule(rng);
|
||||
|
||||
_wso.last_scheduling_block = 0;
|
||||
|
||||
_wso.recent_slots_filled = fc::uint128::max_value();
|
||||
|
||||
// for shuffled
|
||||
for( const witness_id_type& wid : get_global_properties().active_witnesses )
|
||||
_wso.current_shuffled_witnesses.push_back( wid );
|
||||
});
|
||||
assert( wso.id == witness_schedule_id_type() );
|
||||
|
||||
// Enable fees
|
||||
modify(get_global_properties(), [&genesis_state](global_property_object& p) {
|
||||
p.parameters.current_fees = genesis_state.initial_parameters.current_fees;
|
||||
});
|
||||
|
||||
// Create witness scheduler
|
||||
create<witness_schedule_object>([&]( witness_schedule_object& wso )
|
||||
{
|
||||
for( const witness_id_type& wid : get_global_properties().active_witnesses )
|
||||
wso.current_shuffled_witnesses.push_back( wid );
|
||||
});
|
||||
//create<witness_schedule_object>([&]( witness_schedule_object& wso )
|
||||
//{
|
||||
// for( const witness_id_type& wid : get_global_properties().active_witnesses )
|
||||
// wso.current_shuffled_witnesses.push_back( wid );
|
||||
//});
|
||||
|
||||
// Create FBA counters
|
||||
create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include <graphene/chain/vesting_balance_object.hpp>
|
||||
#include <graphene/chain/vote_count.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/witness_schedule_object.hpp>
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
|
||||
#define USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX // vesting_balance_object by_asset_balance index needed
|
||||
|
|
@ -242,6 +243,11 @@ void database::update_active_witnesses()
|
|||
});
|
||||
});
|
||||
|
||||
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
modify(wso, [&](witness_schedule_object& _wso)
|
||||
{
|
||||
_wso.scheduler.update(gpo.active_witnesses);
|
||||
});
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::update_active_committee_members()
|
||||
|
|
@ -934,7 +940,6 @@ void schedule_pending_dividend_balances(database& db,
|
|||
auto itr = vesting_amounts.find(holder_balance_object.owner);
|
||||
if (itr != vesting_amounts.end())
|
||||
holder_balance += itr->second;
|
||||
//if (holder_balance.value)
|
||||
|
||||
fc::uint128_t amount_to_credit(delta_balance.value);
|
||||
amount_to_credit *= holder_balance.value;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@
|
|||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
database::database()
|
||||
database::database() :
|
||||
_random_number_generator(fc::ripemd160().data())
|
||||
{
|
||||
initialize_indexes();
|
||||
initialize_evaluators();
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
#include <graphene/chain/transaction_object.hpp>
|
||||
#include <graphene/chain/withdraw_permission_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
|
|
@ -42,32 +44,51 @@ namespace graphene { namespace chain {
|
|||
|
||||
void database::update_global_dynamic_data( const signed_block& b )
|
||||
{
|
||||
const dynamic_global_property_object& _dgp =
|
||||
dynamic_global_property_id_type(0)(*this);
|
||||
const dynamic_global_property_object& _dgp = dynamic_global_property_id_type(0)(*this);
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
|
||||
uint32_t missed_blocks = get_slot_at_time( b.timestamp );
|
||||
|
||||
//#define DIRTY_TRICK // problem with missed_blocks can occur when "maintenance_interval" set to few minutes
|
||||
#ifdef DIRTY_TRICK
|
||||
if (missed_blocks != 0) {
|
||||
#else
|
||||
assert( missed_blocks != 0 );
|
||||
missed_blocks--;
|
||||
for( uint32_t i = 0; i < missed_blocks; ++i ) {
|
||||
const auto& witness_missed = get_scheduled_witness( i+1 )(*this);
|
||||
if( witness_missed.id != b.witness ) {
|
||||
/*
|
||||
const auto& witness_account = witness_missed.witness_account(*this);
|
||||
if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) )
|
||||
wlog( "Witness ${name} missed block ${n} around ${t}", ("name",witness_account.name)("n",b.block_num())("t",b.timestamp) );
|
||||
*/
|
||||
#endif
|
||||
// bad if-condition, this code needs to execute for both shuffled and rng algorithms
|
||||
// if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
|
||||
// {
|
||||
missed_blocks--;
|
||||
for( uint32_t i = 0; i < missed_blocks; ++i ) {
|
||||
const auto& witness_missed = get_scheduled_witness( i+1 )(*this);
|
||||
if( witness_missed.id != b.witness ) {
|
||||
/*
|
||||
const auto& witness_account = witness_missed.witness_account(*this);
|
||||
if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) )
|
||||
wlog( "Witness ${name} missed block ${n} around ${t}", ("name",witness_account.name)("n",b.block_num())("t",b.timestamp) );
|
||||
*/
|
||||
|
||||
modify( witness_missed, [&]( witness_object& w ) {
|
||||
w.total_missed++;
|
||||
});
|
||||
}
|
||||
modify( witness_missed, [&]( witness_object& w ) {
|
||||
w.total_missed++;
|
||||
});
|
||||
}
|
||||
}
|
||||
// }
|
||||
#ifdef DIRTY_TRICK
|
||||
}
|
||||
|
||||
#endif
|
||||
// dynamic global properties updating
|
||||
modify( _dgp, [&]( dynamic_global_property_object& dgp ){
|
||||
secret_hash_type::encoder enc;
|
||||
fc::raw::pack( enc, dgp.random );
|
||||
fc::raw::pack( enc, b.previous_secret );
|
||||
dgp.random = enc.result();
|
||||
|
||||
_random_number_generator = fc::hash_ctr_rng<secret_hash_type, 20>(dgp.random.data());
|
||||
|
||||
if( BOOST_UNLIKELY( b.block_num() == 1 ) )
|
||||
dgp.recently_missed_count = 0;
|
||||
else if( _checkpoints.size() && _checkpoints.rbegin()->first >= b.block_num() )
|
||||
else if( _checkpoints.size() && _checkpoints.rbegin()->first >= b.block_num() )
|
||||
dgp.recently_missed_count = 0;
|
||||
else if( missed_blocks )
|
||||
dgp.recently_missed_count += GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT*missed_blocks;
|
||||
|
|
@ -118,6 +139,8 @@ void database::update_signing_witness(const witness_object& signing_witness, con
|
|||
{
|
||||
_wit.last_aslot = new_block_aslot;
|
||||
_wit.last_confirmed_block_num = new_block.block_num();
|
||||
_wit.previous_secret = new_block.previous_secret;
|
||||
_wit.next_secret_hash = new_block.next_secret_hash;
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
@ -155,7 +178,6 @@ void database::update_last_irreversible_block()
|
|||
} );
|
||||
}
|
||||
}
|
||||
|
||||
void database::clear_expired_transactions()
|
||||
{ try {
|
||||
//Look for expired transactions in the deduplication list, and remove them.
|
||||
|
|
@ -474,4 +496,116 @@ void database::update_withdraw_permissions()
|
|||
remove(*permit_index.begin());
|
||||
}
|
||||
|
||||
uint64_t database::get_random_bits( uint64_t bound )
|
||||
{
|
||||
return _random_number_generator(bound);
|
||||
}
|
||||
|
||||
void process_finished_games(database& db)
|
||||
{
|
||||
//auto& games_index = db.get_index_type<game_index>().indices().get<by_id>();
|
||||
}
|
||||
|
||||
void process_finished_matches(database& db)
|
||||
{
|
||||
}
|
||||
|
||||
void process_in_progress_tournaments(database& db)
|
||||
{
|
||||
auto& start_time_index = db.get_index_type<tournament_index>().indices().get<by_start_time>();
|
||||
auto start_iter = start_time_index.lower_bound(boost::make_tuple(tournament_state::in_progress));
|
||||
while (start_iter != start_time_index.end() &&
|
||||
start_iter->get_state() == tournament_state::in_progress)
|
||||
{
|
||||
auto next_iter = std::next(start_iter);
|
||||
start_iter->check_for_new_matches_to_start(db);
|
||||
start_iter = next_iter;
|
||||
}
|
||||
}
|
||||
|
||||
void cancel_expired_tournaments(database& db)
|
||||
{
|
||||
// First, cancel any tournaments that didn't get enough players
|
||||
auto& registration_deadline_index = db.get_index_type<tournament_index>().indices().get<by_registration_deadline>();
|
||||
// this index is sorted on state and deadline, so the tournaments awaiting registrations with the earliest
|
||||
// deadlines will be at the beginning
|
||||
while (!registration_deadline_index.empty() &&
|
||||
registration_deadline_index.begin()->get_state() == tournament_state::accepting_registrations &&
|
||||
registration_deadline_index.begin()->options.registration_deadline <= db.head_block_time())
|
||||
{
|
||||
const tournament_object& tournament_obj = *registration_deadline_index.begin();
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"Canceling tournament ${id} because its deadline expired",
|
||||
("id", tournament_obj.id));
|
||||
// cancel this tournament
|
||||
db.modify(tournament_obj, [&](tournament_object& t) {
|
||||
t.on_registration_deadline_passed(db);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void start_fully_registered_tournaments(database& db)
|
||||
{
|
||||
// Next, start any tournaments that have enough players and whose start time just arrived
|
||||
auto& start_time_index = db.get_index_type<tournament_index>().indices().get<by_start_time>();
|
||||
while (1)
|
||||
{
|
||||
// find the first tournament waiting to start; if its start time has arrived, start it
|
||||
auto start_iter = start_time_index.lower_bound(boost::make_tuple(tournament_state::awaiting_start));
|
||||
if (start_iter != start_time_index.end() &&
|
||||
start_iter->get_state() == tournament_state::awaiting_start &&
|
||||
*start_iter->start_time <= db.head_block_time())
|
||||
{
|
||||
db.modify(*start_iter, [&](tournament_object& t) {
|
||||
t.on_start_time_arrived(db);
|
||||
});
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void initiate_next_round_of_matches(database& db)
|
||||
{
|
||||
}
|
||||
|
||||
void initiate_next_games(database& db)
|
||||
{
|
||||
// Next, trigger timeouts on any games which have been waiting too long for commit or
|
||||
// reveal moves
|
||||
auto& next_timeout_index = db.get_index_type<game_index>().indices().get<by_next_timeout>();
|
||||
while (1)
|
||||
{
|
||||
// empty time_points are sorted to the beginning, so upper_bound takes us to the first
|
||||
// non-empty time_point
|
||||
auto start_iter = next_timeout_index.upper_bound(boost::make_tuple(optional<time_point_sec>()));
|
||||
if (start_iter != next_timeout_index.end() &&
|
||||
*start_iter->next_timeout <= db.head_block_time())
|
||||
{
|
||||
db.modify(*start_iter, [&](game_object& game) {
|
||||
game.on_timeout(db);
|
||||
});
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_tournaments()
|
||||
{
|
||||
// Process as follows:
|
||||
// - Process games
|
||||
// - Process matches
|
||||
// - Process tournaments
|
||||
// - Process matches
|
||||
// - Process games
|
||||
process_finished_games(*this);
|
||||
process_finished_matches(*this);
|
||||
cancel_expired_tournaments(*this);
|
||||
start_fully_registered_tournaments(*this);
|
||||
process_in_progress_tournaments(*this);
|
||||
initiate_next_round_of_matches(*this);
|
||||
initiate_next_games(*this);
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -33,10 +33,43 @@ using boost::container::flat_set;
|
|||
|
||||
witness_id_type database::get_scheduled_witness( uint32_t slot_num )const
|
||||
{
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
uint64_t current_aslot = dpo.current_aslot + slot_num;
|
||||
return wso.current_shuffled_witnesses[ current_aslot % wso.current_shuffled_witnesses.size() ];
|
||||
witness_id_type wid;
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
|
||||
{
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
uint64_t current_aslot = dpo.current_aslot + slot_num;
|
||||
return wso.current_shuffled_witnesses[ current_aslot % wso.current_shuffled_witnesses.size() ];
|
||||
}
|
||||
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM &&
|
||||
slot_num != 0 )
|
||||
{
|
||||
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
// ask the near scheduler who goes in the given slot
|
||||
bool slot_is_near = wso.scheduler.get_slot(slot_num-1, wid);
|
||||
if(! slot_is_near)
|
||||
{
|
||||
// if the near scheduler doesn't know, we have to extend it to
|
||||
// a far scheduler.
|
||||
// n.b. instantiating it is slow, but block gaps long enough to
|
||||
// need it are likely pretty rare.
|
||||
|
||||
witness_scheduler_rng far_rng(wso.rng_seed.begin(), GRAPHENE_FAR_SCHEDULE_CTR_IV);
|
||||
|
||||
far_future_witness_scheduler far_scheduler =
|
||||
far_future_witness_scheduler(wso.scheduler, far_rng);
|
||||
if(!far_scheduler.get_slot(slot_num-1, wid))
|
||||
{
|
||||
// no scheduled witness -- somebody set up us the bomb
|
||||
// n.b. this code path is impossible, the present
|
||||
// implementation of far_future_witness_scheduler
|
||||
// returns true unconditionally
|
||||
assert( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
return wid;
|
||||
}
|
||||
|
||||
fc::time_point_sec database::get_slot_time(uint32_t slot_num)const
|
||||
|
|
@ -72,17 +105,12 @@ fc::time_point_sec database::get_slot_time(uint32_t slot_num)const
|
|||
uint32_t database::get_slot_at_time(fc::time_point_sec when)const
|
||||
{
|
||||
fc::time_point_sec first_slot_time = get_slot_time( 1 );
|
||||
//@ROL std::cout << "@get_slot_at_time " << when.to_iso_string() << " " << first_slot_time.to_iso_string() << "\n";
|
||||
if( when < first_slot_time )
|
||||
return 0;
|
||||
return (when - first_slot_time).to_seconds() / block_interval() + 1;
|
||||
}
|
||||
|
||||
uint32_t database::witness_participation_rate()const
|
||||
{
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
return uint64_t(GRAPHENE_100_PERCENT) * dpo.recent_slots_filled.popcount() / 128;
|
||||
}
|
||||
|
||||
void database::update_witness_schedule()
|
||||
{
|
||||
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
|
|
@ -118,4 +146,100 @@ void database::update_witness_schedule()
|
|||
}
|
||||
}
|
||||
|
||||
vector<witness_id_type> database::get_near_witness_schedule()const
|
||||
{
|
||||
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
|
||||
vector<witness_id_type> result;
|
||||
result.reserve(wso.scheduler.size());
|
||||
uint32_t slot_num = 0;
|
||||
witness_id_type wid;
|
||||
|
||||
while( wso.scheduler.get_slot(slot_num++, wid) )
|
||||
result.emplace_back(wid);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void database::update_witness_schedule(const signed_block& next_block)
|
||||
{
|
||||
auto start = fc::time_point::now();
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
const witness_schedule_object& wso = get(witness_schedule_id_type());
|
||||
uint32_t schedule_needs_filled = gpo.active_witnesses.size();
|
||||
uint32_t schedule_slot = get_slot_at_time(next_block.timestamp);
|
||||
|
||||
// We shouldn't be able to generate _pending_block with timestamp
|
||||
// in the past, and incoming blocks from the network with timestamp
|
||||
// in the past shouldn't be able to make it this far without
|
||||
// triggering FC_ASSERT elsewhere
|
||||
|
||||
assert( schedule_slot > 0 );
|
||||
|
||||
witness_id_type first_witness;
|
||||
bool slot_is_near = wso.scheduler.get_slot( schedule_slot-1, first_witness );
|
||||
|
||||
witness_id_type wit;
|
||||
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
|
||||
assert( dpo.random.data_size() == witness_scheduler_rng::seed_length );
|
||||
assert( witness_scheduler_rng::seed_length == wso.rng_seed.size() );
|
||||
|
||||
modify(wso, [&](witness_schedule_object& _wso)
|
||||
{
|
||||
_wso.slots_since_genesis += schedule_slot;
|
||||
witness_scheduler_rng rng(wso.rng_seed.data, _wso.slots_since_genesis);
|
||||
|
||||
_wso.scheduler._min_token_count = std::max(int(gpo.active_witnesses.size()) / 2, 1);
|
||||
|
||||
if( slot_is_near )
|
||||
{
|
||||
uint32_t drain = schedule_slot;
|
||||
while( drain > 0 )
|
||||
{
|
||||
if( _wso.scheduler.size() == 0 )
|
||||
break;
|
||||
_wso.scheduler.consume_schedule();
|
||||
--drain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_wso.scheduler.reset_schedule( first_witness );
|
||||
}
|
||||
while( !_wso.scheduler.get_slot(schedule_needs_filled, wit) )
|
||||
{
|
||||
if( _wso.scheduler.produce_schedule(rng) & emit_turn )
|
||||
memcpy(_wso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size());
|
||||
}
|
||||
_wso.last_scheduling_block = next_block.block_num();
|
||||
_wso.recent_slots_filled = (
|
||||
(_wso.recent_slots_filled << 1)
|
||||
+ 1) << (schedule_slot - 1);
|
||||
});
|
||||
auto end = fc::time_point::now();
|
||||
static uint64_t total_time = 0;
|
||||
static uint64_t calls = 0;
|
||||
total_time += (end - start).count();
|
||||
if( ++calls % 1000 == 0 )
|
||||
idump( ( double(total_time/1000000.0)/calls) );
|
||||
}
|
||||
|
||||
uint32_t database::witness_participation_rate()const
|
||||
{
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
|
||||
{
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
return uint64_t(GRAPHENE_100_PERCENT) * dpo.recent_slots_filled.popcount() / 128;
|
||||
}
|
||||
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
|
||||
{
|
||||
const witness_schedule_object& wso = get(witness_schedule_id_type());
|
||||
return uint64_t(GRAPHENE_100_PERCENT) * wso.recent_slots_filled.popcount() / 128;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
583
libraries/chain/game_object.cpp
Normal file
583
libraries/chain/game_object.cpp
Normal file
|
|
@ -0,0 +1,583 @@
|
|||
/*
|
||||
* 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 <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/archive/binary_oarchive.hpp>
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/msm/back/tools.hpp>
|
||||
#include <boost/range/algorithm/copy.hpp>
|
||||
|
||||
#include <fc/crypto/hash_ctr_rng.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Events
|
||||
struct initiate_game
|
||||
{
|
||||
database& db;
|
||||
vector<account_id_type> players;
|
||||
initiate_game(database& db, const vector<account_id_type>& players) :
|
||||
db(db), players(players)
|
||||
{}
|
||||
};
|
||||
|
||||
struct game_move
|
||||
{
|
||||
database& db;
|
||||
const game_move_operation& move;
|
||||
game_move(database& db, const game_move_operation& move) :
|
||||
db(db), move(move)
|
||||
{}
|
||||
};
|
||||
|
||||
struct timeout
|
||||
{
|
||||
database& db;
|
||||
timeout(database& db) :
|
||||
db(db)
|
||||
{}
|
||||
};
|
||||
|
||||
struct game_state_machine_ : public msm::front::state_machine_def<game_state_machine_>
|
||||
{
|
||||
// disable a few state machine features we don't use for performance
|
||||
typedef int no_exception_thrown;
|
||||
typedef int no_message_queue;
|
||||
|
||||
// States
|
||||
struct waiting_for_game_to_start : public msm::front::state<> {};
|
||||
struct expecting_commit_moves : public msm::front::state<>
|
||||
{
|
||||
void set_next_timeout(database& db, game_object& game)
|
||||
{
|
||||
const match_object& match_obj = game.match_id(db);
|
||||
const tournament_object& tournament_obj = match_obj.tournament_id(db);
|
||||
const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
|
||||
game.next_timeout = db.head_block_time() + game_options.time_per_commit_move;
|
||||
}
|
||||
void on_entry(const initiate_game& event, game_state_machine_& fsm)
|
||||
{
|
||||
game_object& game = *fsm.game_obj;
|
||||
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"game ${id} is now in progress, expecting commit moves",
|
||||
("id", game.id));
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"game ${id} is associtated with match ${match_id}",
|
||||
("id", game.id)
|
||||
("match_id", game.match_id));
|
||||
set_next_timeout(event.db, game);
|
||||
}
|
||||
void on_entry(const game_move& event, game_state_machine_& fsm)
|
||||
{
|
||||
game_object& game = *fsm.game_obj;
|
||||
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"game ${id} received a commit move, still expecting another commit move",
|
||||
("id", game.id));
|
||||
}
|
||||
};
|
||||
struct expecting_reveal_moves : public msm::front::state<>
|
||||
{
|
||||
void set_next_timeout(database& db, game_object& game)
|
||||
{
|
||||
const match_object& match_obj = game.match_id(db);
|
||||
const tournament_object& tournament_obj = match_obj.tournament_id(db);
|
||||
const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
|
||||
game.next_timeout = db.head_block_time() + game_options.time_per_reveal_move;
|
||||
}
|
||||
void on_entry(const timeout& event, game_state_machine_& fsm)
|
||||
{
|
||||
game_object& game = *fsm.game_obj;
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"game ${id} timed out waiting for commit moves, now expecting reveal move",
|
||||
("id", game.id));
|
||||
set_next_timeout(event.db, game);
|
||||
}
|
||||
void on_entry(const game_move& event, game_state_machine_& fsm)
|
||||
{
|
||||
game_object& game = *fsm.game_obj;
|
||||
|
||||
if (event.move.move.which() == game_specific_moves::tag<rock_paper_scissors_throw_commit>::value)
|
||||
{
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"game ${id} received a commit move, now expecting reveal moves",
|
||||
("id", game.id));
|
||||
set_next_timeout(event.db, game);
|
||||
}
|
||||
else
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"game ${id} received a reveal move, still expecting reveal moves",
|
||||
("id", game.id));
|
||||
}
|
||||
};
|
||||
|
||||
struct game_complete : public msm::front::state<>
|
||||
{
|
||||
void clear_next_timeout(database& db, game_object& game)
|
||||
{
|
||||
//const match_object& match_obj = game.match_id(db);
|
||||
//const tournament_object& tournament_obj = match_obj.tournament_id(db);
|
||||
//const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
|
||||
game.next_timeout = fc::optional<fc::time_point_sec>();
|
||||
}
|
||||
void on_entry(const timeout& event, game_state_machine_& fsm)
|
||||
{
|
||||
game_object& game = *fsm.game_obj;
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"timed out waiting for commits or reveals, game ${id} is complete",
|
||||
("id", game.id));
|
||||
|
||||
game.make_automatic_moves(event.db);
|
||||
game.determine_winner(event.db);
|
||||
clear_next_timeout(event.db, game);
|
||||
}
|
||||
|
||||
void on_entry(const game_move& event, game_state_machine_& fsm)
|
||||
{
|
||||
game_object& game = *fsm.game_obj;
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"received a reveal move, game ${id} is complete",
|
||||
("id", fsm.game_obj->id));
|
||||
|
||||
// if one player didn't commit a move we might need to make their "insurance" move now
|
||||
game.make_automatic_moves(event.db);
|
||||
game.determine_winner(event.db);
|
||||
clear_next_timeout(event.db, game);
|
||||
}
|
||||
};
|
||||
typedef waiting_for_game_to_start initial_state;
|
||||
|
||||
typedef game_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
// Guards
|
||||
bool already_have_other_commit(const game_move& event)
|
||||
{
|
||||
auto iter = std::find(game_obj->players.begin(), game_obj->players.end(),
|
||||
event.move.player_account_id);
|
||||
unsigned player_index = std::distance(game_obj->players.begin(), iter);
|
||||
// hard-coded here for two-player games
|
||||
unsigned other_player_index = player_index == 0 ? 1 : 0;
|
||||
const rock_paper_scissors_game_details& game_details = game_obj->game_details.get<rock_paper_scissors_game_details>();
|
||||
return game_details.commit_moves.at(other_player_index).valid();
|
||||
}
|
||||
|
||||
bool now_have_reveals_for_all_commits(const game_move& event)
|
||||
{
|
||||
auto iter = std::find(game_obj->players.begin(), game_obj->players.end(),
|
||||
event.move.player_account_id);
|
||||
unsigned this_reveal_index = std::distance(game_obj->players.begin(), iter);
|
||||
|
||||
const rock_paper_scissors_game_details& game_details = game_obj->game_details.get<rock_paper_scissors_game_details>();
|
||||
for (unsigned i = 0; i < game_details.commit_moves.size(); ++i)
|
||||
if (game_details.commit_moves[i] && !game_details.reveal_moves[i] && i != this_reveal_index)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool have_at_least_one_commit_move(const timeout& event)
|
||||
{
|
||||
const rock_paper_scissors_game_details& game_details = game_obj->game_details.get<rock_paper_scissors_game_details>();
|
||||
return game_details.commit_moves[0] || game_details.commit_moves[1];
|
||||
}
|
||||
|
||||
void apply_commit_move(const game_move& event)
|
||||
{
|
||||
auto iter = std::find(game_obj->players.begin(), game_obj->players.end(),
|
||||
event.move.player_account_id);
|
||||
unsigned player_index = std::distance(game_obj->players.begin(), iter);
|
||||
|
||||
rock_paper_scissors_game_details& details = game_obj->game_details.get<rock_paper_scissors_game_details>();
|
||||
details.commit_moves[player_index] = event.move.move.get<rock_paper_scissors_throw_commit>();
|
||||
}
|
||||
|
||||
void apply_reveal_move(const game_move& event)
|
||||
{
|
||||
auto iter = std::find(game_obj->players.begin(), game_obj->players.end(),
|
||||
event.move.player_account_id);
|
||||
unsigned player_index = std::distance(game_obj->players.begin(), iter);
|
||||
|
||||
rock_paper_scissors_game_details& details = game_obj->game_details.get<rock_paper_scissors_game_details>();
|
||||
details.reveal_moves[player_index] = event.move.move.get<rock_paper_scissors_throw_reveal>();
|
||||
}
|
||||
|
||||
void start_next_game(const game_complete& event)
|
||||
{
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"In start_next_game action");
|
||||
}
|
||||
|
||||
// Transition table for tournament
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < waiting_for_game_to_start, initiate_game, expecting_commit_moves >,
|
||||
// +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+
|
||||
a_row < expecting_commit_moves, game_move, expecting_commit_moves, &x::apply_commit_move >,
|
||||
row < expecting_commit_moves, game_move, expecting_reveal_moves, &x::apply_commit_move, &x::already_have_other_commit >,
|
||||
_row < expecting_commit_moves, timeout, game_complete >,
|
||||
g_row < expecting_commit_moves, timeout, expecting_reveal_moves, &x::have_at_least_one_commit_move >,
|
||||
// +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < expecting_reveal_moves, timeout, game_complete >,
|
||||
a_row < expecting_reveal_moves, game_move, expecting_reveal_moves, &x::apply_reveal_move >,
|
||||
row < expecting_reveal_moves, game_move, game_complete, &x::apply_reveal_move, &x::now_have_reveals_for_all_commits >
|
||||
// +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+
|
||||
//a_row < game_in_progress, game_complete, game_in_progress, &x::start_next_game >,
|
||||
//g_row < game_in_progress, game_complete, game_complete, &x::was_final_game >
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
> {};
|
||||
|
||||
|
||||
game_object* game_obj;
|
||||
game_state_machine_(game_object* game_obj) : game_obj(game_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<game_state_machine_> game_state_machine;
|
||||
}
|
||||
|
||||
class game_object::impl {
|
||||
public:
|
||||
game_state_machine state_machine;
|
||||
|
||||
impl(game_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
game_object::game_object() :
|
||||
my(new impl(this))
|
||||
{
|
||||
}
|
||||
|
||||
game_object::game_object(const game_object& rhs) :
|
||||
graphene::db::abstract_object<game_object>(rhs),
|
||||
match_id(rhs.match_id),
|
||||
players(rhs.players),
|
||||
winners(rhs.winners),
|
||||
game_details(rhs.game_details),
|
||||
next_timeout(rhs.next_timeout),
|
||||
my(new impl(this))
|
||||
{
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.game_obj = this;
|
||||
}
|
||||
|
||||
game_object& game_object::operator=(const game_object& rhs)
|
||||
{
|
||||
//graphene::db::abstract_object<game_object>::operator=(rhs);
|
||||
id = rhs.id;
|
||||
match_id = rhs.match_id;
|
||||
players = rhs.players;
|
||||
winners = rhs.winners;
|
||||
game_details = rhs.game_details;
|
||||
next_timeout = rhs.next_timeout;
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.game_obj = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
game_object::~game_object()
|
||||
{
|
||||
}
|
||||
|
||||
bool verify_game_state_constants()
|
||||
{
|
||||
unsigned error_count = 0;
|
||||
typedef msm::back::generate_state_set<game_state_machine::stt>::type all_states;
|
||||
static char const* filled_state_names[mpl::size<all_states>::value];
|
||||
mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >
|
||||
(msm::back::fill_state_names<game_state_machine::stt>(filled_state_names));
|
||||
for (unsigned i = 0; i < mpl::size<all_states>::value; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
|
||||
// at least contain the string we're looking for
|
||||
const char* fc_reflected_value_name = fc::reflector<game_state>::to_string((game_state)i);
|
||||
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
|
||||
fc_elog(fc::logger::get("game"),
|
||||
"Error, state string misgame between fc and boost::msm for int value ${int_value}: boost::msm -> ${boost_string}, fc::reflect -> ${fc_string}",
|
||||
("int_value", i)("boost_string", filled_state_names[i])("fc_string", fc_reflected_value_name));
|
||||
}
|
||||
catch (const fc::bad_cast_exception&)
|
||||
{
|
||||
fc_elog(fc::logger::get("game"),
|
||||
"Error, no reflection for value ${int_value} in enum game_state",
|
||||
("int_value", i));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
|
||||
return error_count == 0;
|
||||
}
|
||||
|
||||
game_state game_object::get_state() const
|
||||
{
|
||||
static bool state_constants_are_correct = verify_game_state_constants();
|
||||
(void)&state_constants_are_correct;
|
||||
game_state state = (game_state)my->state_machine.current_state()[0];
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void game_object::evaluate_move_operation(const database& db, const game_move_operation& op) const
|
||||
{
|
||||
//const match_object& match_obj = match_id(db);
|
||||
|
||||
if (game_details.which() == game_specific_details::tag<rock_paper_scissors_game_details>::value)
|
||||
{
|
||||
if (op.move.which() == game_specific_moves::tag<rock_paper_scissors_throw_commit>::value)
|
||||
{
|
||||
// Is this move made by a player in the match
|
||||
auto iter = std::find(players.begin(), players.end(),
|
||||
op.player_account_id);
|
||||
if (iter == players.end())
|
||||
FC_THROW("Player ${account_id} is not a player in game ${game}",
|
||||
("account_id", op.player_account_id)
|
||||
("game", id));
|
||||
unsigned player_index = std::distance(players.begin(), iter);
|
||||
|
||||
//const rock_paper_scissors_throw_commit& commit = op.move.get<rock_paper_scissors_throw_commit>();
|
||||
|
||||
// are we expecting commits?
|
||||
if (get_state() != game_state::expecting_commit_moves)
|
||||
FC_THROW("Game ${game} is not accepting any commit moves", ("game", id));
|
||||
|
||||
// has this player committed already?
|
||||
const rock_paper_scissors_game_details& details = game_details.get<rock_paper_scissors_game_details>();
|
||||
if (details.commit_moves.at(player_index))
|
||||
FC_THROW("Player ${account_id} has already committed their move for game ${game}",
|
||||
("account_id", op.player_account_id)
|
||||
("game", id));
|
||||
// if all the above checks pass, then the move is accepted
|
||||
}
|
||||
else if (op.move.which() == game_specific_moves::tag<rock_paper_scissors_throw_reveal>::value)
|
||||
{
|
||||
// Is this move made by a player in the match
|
||||
auto iter = std::find(players.begin(), players.end(),
|
||||
op.player_account_id);
|
||||
if (iter == players.end())
|
||||
FC_THROW("Player ${account_id} is not a player in game ${game}",
|
||||
("account_id", op.player_account_id)
|
||||
("game", id));
|
||||
unsigned player_index = std::distance(players.begin(), iter);
|
||||
|
||||
// has this player committed already?
|
||||
const rock_paper_scissors_game_details& details = game_details.get<rock_paper_scissors_game_details>();
|
||||
if (!details.commit_moves.at(player_index))
|
||||
FC_THROW("Player ${account_id} cannot reveal a move which they did not commit in game ${game}",
|
||||
("account_id", op.player_account_id)
|
||||
("game", id));
|
||||
|
||||
// are we expecting reveals?
|
||||
if (get_state() != game_state::expecting_reveal_moves)
|
||||
FC_THROW("Game ${game} is not accepting any reveal moves", ("game", id));
|
||||
|
||||
const rock_paper_scissors_throw_commit& commit = *details.commit_moves.at(player_index);
|
||||
const rock_paper_scissors_throw_reveal& reveal = op.move.get<rock_paper_scissors_throw_reveal>();
|
||||
|
||||
// does the reveal match the commit?
|
||||
rock_paper_scissors_throw reconstructed_throw;
|
||||
reconstructed_throw.nonce1 = commit.nonce1;
|
||||
reconstructed_throw.nonce2 = reveal.nonce2;
|
||||
reconstructed_throw.gesture = reveal.gesture;
|
||||
fc::sha256 reconstructed_hash = reconstructed_throw.calculate_hash();
|
||||
|
||||
if (commit.throw_hash != reconstructed_hash)
|
||||
FC_THROW("Reveal does not match commit's hash of ${commit_hash}",
|
||||
("commit_hash", commit.throw_hash));
|
||||
|
||||
// is the throw valid for this game
|
||||
const match_object& match_obj = match_id(db);
|
||||
const tournament_object& tournament_obj = match_obj.tournament_id(db);
|
||||
const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
|
||||
if ((unsigned)reveal.gesture >= game_options.number_of_gestures)
|
||||
FC_THROW("Gesture ${gesture_int} is not valid for this game", ("gesture", (unsigned)reveal.gesture));
|
||||
// if all the above checks pass, then the move is accepted
|
||||
}
|
||||
else
|
||||
FC_THROW("The only valid moves in a rock-paper-scissors game are commit and reveal, not ${type}",
|
||||
("type", op.move.which()));
|
||||
}
|
||||
else
|
||||
FC_THROW("Game of type ${type} not supported", ("type", game_details.which()));
|
||||
}
|
||||
|
||||
void game_object::make_automatic_moves(database& db)
|
||||
{
|
||||
rock_paper_scissors_game_details& rps_game_details = game_details.get<rock_paper_scissors_game_details>();
|
||||
|
||||
unsigned players_without_commit_moves = 0;
|
||||
bool no_player_has_reveal_move = true;
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{
|
||||
if (!rps_game_details.commit_moves[i])
|
||||
++players_without_commit_moves;
|
||||
if (rps_game_details.reveal_moves[i])
|
||||
no_player_has_reveal_move = false;
|
||||
}
|
||||
|
||||
if (players_without_commit_moves || no_player_has_reveal_move)
|
||||
{
|
||||
const match_object& match_obj = match_id(db);
|
||||
const tournament_object& tournament_obj = match_obj.tournament_id(db);
|
||||
const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
|
||||
|
||||
if (game_options.insurance_enabled)
|
||||
{
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{
|
||||
if (!rps_game_details.commit_moves[i] ||
|
||||
no_player_has_reveal_move)
|
||||
{
|
||||
struct rock_paper_scissors_throw_reveal reveal;
|
||||
reveal.nonce2 = 0;
|
||||
reveal.gesture = (rock_paper_scissors_gesture)db.get_random_bits(game_options.number_of_gestures);
|
||||
rps_game_details.reveal_moves[i] = reveal;
|
||||
ilog("Player ${player} failed to commit a move, generating a random move for him: ${gesture}",
|
||||
("player", i)("gesture", reveal.gesture));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void game_object::determine_winner(database& db)
|
||||
{
|
||||
// we now know who played what, figure out if we have a winner
|
||||
const rock_paper_scissors_game_details& rps_game_details = game_details.get<rock_paper_scissors_game_details>();
|
||||
if (rps_game_details.reveal_moves[0] && rps_game_details.reveal_moves[1] &&
|
||||
rps_game_details.reveal_moves[0]->gesture == rps_game_details.reveal_moves[1]->gesture)
|
||||
ilog("The game was a tie, both players threw ${gesture}", ("gesture", rps_game_details.reveal_moves[0]->gesture));
|
||||
else
|
||||
{
|
||||
const match_object& match_obj = match_id(db);
|
||||
const tournament_object& tournament_obj = match_obj.tournament_id(db);
|
||||
const rock_paper_scissors_game_options& game_options = tournament_obj.options.game_options.get<rock_paper_scissors_game_options>();
|
||||
|
||||
if (rps_game_details.reveal_moves[0] && rps_game_details.reveal_moves[1])
|
||||
{
|
||||
unsigned winner = ((((int)rps_game_details.reveal_moves[0]->gesture -
|
||||
(int)rps_game_details.reveal_moves[1]->gesture +
|
||||
game_options.number_of_gestures) % game_options.number_of_gestures) + 1) % 2;
|
||||
ilog("${gesture1} vs ${gesture2}, ${winner} wins",
|
||||
("gesture1", rps_game_details.reveal_moves[1]->gesture)
|
||||
("gesture2", rps_game_details.reveal_moves[0]->gesture)
|
||||
("winner", rps_game_details.reveal_moves[winner]->gesture));
|
||||
winners.insert(players[winner]);
|
||||
}
|
||||
else if (rps_game_details.reveal_moves[0])
|
||||
{
|
||||
ilog("Player 1 didn't commit or reveal their move, player 0 wins");
|
||||
winners.insert(players[0]);
|
||||
}
|
||||
else if (rps_game_details.reveal_moves[1])
|
||||
{
|
||||
ilog("Player 0 didn't commit or reveal their move, player 1 wins");
|
||||
winners.insert(players[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilog("Neither player made a move, both players lose");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const match_object& match_obj = match_id(db);
|
||||
db.modify(match_obj, [&](match_object& match) {
|
||||
match.on_game_complete(db, *this);
|
||||
});
|
||||
}
|
||||
|
||||
void game_object::on_move(database& db, const game_move_operation& op)
|
||||
{
|
||||
my->state_machine.process_event(game_move(db, op));
|
||||
}
|
||||
|
||||
void game_object::on_timeout(database& db)
|
||||
{
|
||||
my->state_machine.process_event(timeout(db));
|
||||
}
|
||||
|
||||
void game_object::start_game(database& db, const std::vector<account_id_type>& players)
|
||||
{
|
||||
my->state_machine.process_event(initiate_game(db, players));
|
||||
}
|
||||
|
||||
void game_object::pack_impl(std::ostream& stream) const
|
||||
{
|
||||
boost::archive::binary_oarchive oa(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
oa << my->state_machine;
|
||||
}
|
||||
|
||||
void game_object::unpack_impl(std::istream& stream)
|
||||
{
|
||||
boost::archive::binary_iarchive ia(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
ia >> my->state_machine;
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc {
|
||||
// Manually reflect game_object to variant to properly reflect "state"
|
||||
void to_variant(const graphene::chain::game_object& game_obj, fc::variant& v)
|
||||
{
|
||||
fc_elog(fc::logger::get("tournament"), "In game_obj to_variant");
|
||||
elog("In game_obj to_variant");
|
||||
fc::mutable_variant_object o;
|
||||
o("id", game_obj.id)
|
||||
("match_id", game_obj.match_id)
|
||||
("players", game_obj.players)
|
||||
("winners", game_obj.winners)
|
||||
("game_details", game_obj.game_details)
|
||||
("next_timeout", game_obj.next_timeout)
|
||||
("state", game_obj.get_state());
|
||||
|
||||
v = o;
|
||||
}
|
||||
|
||||
// Manually reflect game_object to variant to properly reflect "state"
|
||||
void from_variant(const fc::variant& v, graphene::chain::game_object& game_obj)
|
||||
{
|
||||
fc_elog(fc::logger::get("tournament"), "In game_obj from_variant");
|
||||
game_obj.id = v["id"].as<graphene::chain::game_id_type>();
|
||||
game_obj.match_id = v["match_id"].as<graphene::chain::match_id_type>();
|
||||
game_obj.players = v["players"].as<std::vector<graphene::chain::account_id_type> >();
|
||||
game_obj.winners = v["winners"].as<flat_set<graphene::chain::account_id_type> >();
|
||||
game_obj.game_details = v["game_details"].as<graphene::chain::game_specific_details>();
|
||||
game_obj.next_timeout = v["next_timeout"].as<fc::optional<time_point_sec> >();
|
||||
graphene::chain::game_state state = v["state"].as<graphene::chain::game_state>();
|
||||
const_cast<int*>(game_obj.my->state_machine.current_state())[0] = (int)state;
|
||||
}
|
||||
} //end namespace fc
|
||||
|
||||
|
||||
|
|
@ -96,6 +96,7 @@ fc::variant_object get_config()
|
|||
result[ "GRAPHENE_MAX_URL_LENGTH" ] = GRAPHENE_MAX_URL_LENGTH;
|
||||
result[ "GRAPHENE_NEAR_SCHEDULE_CTR_IV" ] = GRAPHENE_NEAR_SCHEDULE_CTR_IV;
|
||||
result[ "GRAPHENE_FAR_SCHEDULE_CTR_IV" ] = GRAPHENE_FAR_SCHEDULE_CTR_IV;
|
||||
result[ "GRAPHENE_RNG_SEED_LENGTH" ] = GRAPHENE_RNG_SEED_LENGTH;
|
||||
result[ "GRAPHENE_CORE_ASSET_CYCLE_RATE" ] = GRAPHENE_CORE_ASSET_CYCLE_RATE;
|
||||
result[ "GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS" ] = GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS;
|
||||
result[ "GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK" ] = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK;
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#define GRAPHENE_SYMBOL "BTS"
|
||||
#define GRAPHENE_ADDRESS_PREFIX "BTS"
|
||||
#define GRAPHENE_SYMBOL "PPY"
|
||||
#define GRAPHENE_ADDRESS_PREFIX "PPY"
|
||||
|
||||
#define GRAPHENE_MIN_ACCOUNT_NAME_LENGTH 1
|
||||
#define GRAPHENE_MAX_ACCOUNT_NAME_LENGTH 63
|
||||
|
|
@ -113,6 +113,9 @@
|
|||
|
||||
#define GRAPHENE_MAX_URL_LENGTH 127
|
||||
|
||||
#define GRAPHENE_WITNESS_SHUFFLED_ALGORITHM 0
|
||||
#define GRAPHENE_WITNESS_SCHEDULED_ALGORITHM 1
|
||||
|
||||
// counter initialization values used to derive near and far future seeds for shuffling witnesses
|
||||
// we use the fractional bits of sqrt(2) in hex
|
||||
#define GRAPHENE_NEAR_SCHEDULE_CTR_IV ( (uint64_t( 0x6a09 ) << 0x30) \
|
||||
|
|
@ -126,6 +129,10 @@
|
|||
| (uint64_t( 0x84ca ) << 0x10) \
|
||||
| (uint64_t( 0xa73b ) ) )
|
||||
|
||||
// counter used to determine bits of entropy
|
||||
// must be less than or equal to secret_hash_type::data_length()
|
||||
#define GRAPHENE_RNG_SEED_LENGTH (160 / 8)
|
||||
|
||||
/**
|
||||
* every second, the fraction of burned core asset which cycles is
|
||||
* GRAPHENE_CORE_ASSET_CYCLE_RATE / (1 << GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS)
|
||||
|
|
@ -166,6 +173,7 @@
|
|||
#define GRAPHENE_PROXY_TO_SELF_ACCOUNT (graphene::chain::account_id_type(5))
|
||||
///
|
||||
#define GRAPHENE_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6))
|
||||
#define TOURNAMENT_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6))
|
||||
/// Sentinel value used in the scheduler.
|
||||
#define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0))
|
||||
///@}
|
||||
|
|
@ -203,3 +211,18 @@
|
|||
{ 1000000, 50000}, /* <= 100: 5.00 */ \
|
||||
{ 10000000, 100000} } /* <= 1000: 10.00 */
|
||||
#define GRAPHENE_DEFAULT_BETTING_PERCENT_FEE (2 * GRAPHENE_1_PERCENT)
|
||||
#define TOURNAMENT_MIN_ROUND_DELAY 0
|
||||
#define TOURNAMENT_MAX_ROUND_DELAY 600
|
||||
#define TOURNAMENT_MIN_TIME_PER_COMMIT_MOVE 0
|
||||
#define TOURNAMENT_MAN_TIME_PER_COMMIT_MOVE 600
|
||||
#define TOURNAMENT_MIN_TIME_PER_REVEAL_MOVE 0
|
||||
#define TOURNAMENT_MAX_TIME_PER_REVEAL_MOVE 600
|
||||
#define TOURNAMENT_DEFAULT_RAKE_FEE_PERCENTAGE (3*GRAPHENE_1_PERCENT)
|
||||
#define TOURNAMENT_MINIMAL_RAKE_FEE_PERCENTAGE (1*GRAPHENE_1_PERCENT)
|
||||
#define TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE (20*GRAPHENE_1_PERCENT)
|
||||
#define TOURNAMENT_MAXIMAL_REGISTRATION_DEADLINE (60*60*24*30) // seconds, 30 days
|
||||
#define TOURNAMENT_MAX_NUMBER_OF_WINS 100
|
||||
#define TOURNAMENT_MAX_PLAYERS_NUMBER 256
|
||||
#define TOURNAMENT_MAX_WHITELIST_LENGTH 1000
|
||||
#define TOURNAMENT_MAX_START_TIME_IN_FUTURE (60*60*24*7*4) // 1 month
|
||||
#define TOURNAMENT_MAX_START_DELAY (60*60*24*7) // 1 week
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
#include <graphene/db/simple_index.hpp>
|
||||
#include <fc/signals.hpp>
|
||||
|
||||
#include <fc/crypto/hash_ctr_rng.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/protocol.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
|
|
@ -244,7 +246,9 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
uint32_t get_slot_at_time(fc::time_point_sec when)const;
|
||||
|
||||
vector<witness_id_type> get_near_witness_schedule()const;
|
||||
void update_witness_schedule();
|
||||
void update_witness_schedule(const signed_block& next_block);
|
||||
|
||||
//////////////////// db_getter.cpp ////////////////////
|
||||
|
||||
|
|
@ -256,6 +260,8 @@ namespace graphene { namespace chain {
|
|||
const node_property_object& get_node_properties()const;
|
||||
const fee_schedule& current_fee_schedule()const;
|
||||
|
||||
uint64_t get_random_bits( uint64_t bound );
|
||||
|
||||
time_point_sec head_block_time()const;
|
||||
uint32_t head_block_num()const;
|
||||
block_id_type head_block_id()const;
|
||||
|
|
@ -461,6 +467,7 @@ namespace graphene { namespace chain {
|
|||
void update_expired_feeds();
|
||||
void update_maintenance_flag( bool new_maintenance_flag );
|
||||
void update_withdraw_permissions();
|
||||
void update_tournaments();
|
||||
bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true );
|
||||
|
||||
///Steps performed only at maintenance intervals
|
||||
|
|
@ -516,6 +523,7 @@ namespace graphene { namespace chain {
|
|||
flat_map<uint32_t,block_id_type> _checkpoints;
|
||||
|
||||
node_property_object _node_property_object;
|
||||
fc::hash_ctr_rng<secret_hash_type, 20> _random_number_generator;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
|
|
|
|||
147
libraries/chain/include/graphene/chain/game_object.hpp
Normal file
147
libraries/chain/include/graphene/chain/game_object.hpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/rock_paper_scissors.hpp>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
#include <graphene/db/flat_index.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class game_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::game_object& game_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::game_object& game_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class database;
|
||||
using namespace graphene::db;
|
||||
|
||||
enum class game_state
|
||||
{
|
||||
game_in_progress,
|
||||
expecting_commit_moves,
|
||||
expecting_reveal_moves,
|
||||
game_complete
|
||||
};
|
||||
|
||||
class game_object : public graphene::db::abstract_object<game_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = game_object_type;
|
||||
|
||||
match_id_type match_id;
|
||||
|
||||
vector<account_id_type> players;
|
||||
|
||||
flat_set<account_id_type> winners;
|
||||
|
||||
game_specific_details game_details;
|
||||
|
||||
fc::optional<time_point_sec> next_timeout;
|
||||
|
||||
game_state get_state() const;
|
||||
|
||||
game_object();
|
||||
game_object(const game_object& rhs);
|
||||
~game_object();
|
||||
game_object& operator=(const game_object& rhs);
|
||||
|
||||
void evaluate_move_operation(const database& db, const game_move_operation& op) const;
|
||||
void make_automatic_moves(database& db);
|
||||
void determine_winner(database& db);
|
||||
|
||||
void on_move(database& db, const game_move_operation& op);
|
||||
void on_timeout(database& db);
|
||||
void start_game(database& db, const std::vector<account_id_type>& players);
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const game_object& game_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, game_object& game_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::game_object& game_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::game_object& game_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
struct by_next_timeout {};
|
||||
typedef multi_index_container<
|
||||
game_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_next_timeout>,
|
||||
composite_key<game_object,
|
||||
member<game_object, optional<time_point_sec>, &game_object::next_timeout>,
|
||||
member<object, object_id_type, &object::id> > > >
|
||||
> game_object_multi_index_type;
|
||||
typedef generic_index<game_object, game_object_multi_index_type> game_index;
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const game_object& game_obj )
|
||||
{
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<game_object> >(s, game_obj);
|
||||
fc::raw::pack(s, game_obj.id);
|
||||
fc::raw::pack(s, game_obj.match_id);
|
||||
fc::raw::pack(s, game_obj.players);
|
||||
fc::raw::pack(s, game_obj.winners);
|
||||
fc::raw::pack(s, game_obj.game_details);
|
||||
fc::raw::pack(s, game_obj.next_timeout);
|
||||
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
game_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, game_object& game_obj )
|
||||
{
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<game_object> >(s, game_obj);
|
||||
fc::raw::unpack(s, game_obj.id);
|
||||
fc::raw::unpack(s, game_obj.match_id);
|
||||
fc::raw::unpack(s, game_obj.players);
|
||||
fc::raw::unpack(s, game_obj.winners);
|
||||
fc::raw::unpack(s, game_obj.game_details);
|
||||
fc::raw::unpack(s, game_obj.next_timeout);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
game_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::game_state,
|
||||
(game_in_progress)
|
||||
(expecting_commit_moves)
|
||||
(expecting_reveal_moves)
|
||||
(game_complete))
|
||||
|
||||
//FC_REFLECT_TYPENAME(graphene::chain::game_object) // manually serialized
|
||||
FC_REFLECT(graphene::chain::game_object, (players))
|
||||
|
||||
|
||||
|
|
@ -52,6 +52,46 @@ struct genesis_state_type {
|
|||
public_key_type active_key;
|
||||
bool is_lifetime_member = false;
|
||||
};
|
||||
struct initial_bts_account_type {
|
||||
struct initial_authority {
|
||||
uint32_t weight_threshold;
|
||||
flat_map<string, weight_type> account_auths; // uses account name instead of account id
|
||||
flat_map<public_key_type, weight_type> key_auths;
|
||||
flat_map<address, weight_type> address_auths;
|
||||
};
|
||||
struct initial_cdd_vesting_policy {
|
||||
uint32_t vesting_seconds;
|
||||
fc::uint128_t coin_seconds_earned;
|
||||
fc::time_point_sec start_claim;
|
||||
fc::time_point_sec coin_seconds_earned_last_update;
|
||||
};
|
||||
struct initial_linear_vesting_policy {
|
||||
fc::time_point_sec begin_timestamp;
|
||||
uint32_t vesting_cliff_seconds;
|
||||
uint32_t vesting_duration_seconds;
|
||||
share_type begin_balance;
|
||||
};
|
||||
struct initial_vesting_balance {
|
||||
string asset_symbol;
|
||||
share_type amount;
|
||||
std::string policy_type; // either "linear" or "cdd"
|
||||
fc::variant policy; // either an initial_cdd_vesting_policy or initial_linear_vesting_policy
|
||||
};
|
||||
initial_bts_account_type(const string& name = string(),
|
||||
const initial_authority& owner_authority = initial_authority(),
|
||||
const initial_authority& active_authority = initial_authority(),
|
||||
const share_type& core_balance = share_type())
|
||||
: name(name),
|
||||
owner_authority(owner_authority),
|
||||
active_authority(active_authority),
|
||||
core_balance(core_balance)
|
||||
{}
|
||||
string name;
|
||||
initial_authority owner_authority;
|
||||
initial_authority active_authority;
|
||||
share_type core_balance;
|
||||
fc::optional<std::vector<initial_vesting_balance> > vesting_balances;
|
||||
};
|
||||
struct initial_asset_type {
|
||||
struct initial_collateral_position {
|
||||
address owner;
|
||||
|
|
@ -81,6 +121,7 @@ struct genesis_state_type {
|
|||
string asset_symbol;
|
||||
share_type amount;
|
||||
time_point_sec begin_timestamp;
|
||||
fc::optional<uint32_t> vesting_cliff_seconds;
|
||||
uint32_t vesting_duration_seconds = 0;
|
||||
share_type begin_balance;
|
||||
};
|
||||
|
|
@ -103,6 +144,7 @@ struct genesis_state_type {
|
|||
share_type max_core_supply = GRAPHENE_MAX_SHARE_SUPPLY;
|
||||
chain_parameters initial_parameters;
|
||||
immutable_chain_parameters immutable_parameters;
|
||||
vector<initial_bts_account_type> initial_bts_accounts;
|
||||
vector<initial_account_type> initial_accounts;
|
||||
vector<initial_asset_type> initial_assets;
|
||||
vector<initial_balance_type> initial_balances;
|
||||
|
|
@ -139,7 +181,7 @@ FC_REFLECT(graphene::chain::genesis_state_type::initial_balance_type,
|
|||
(owner)(asset_symbol)(amount))
|
||||
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_vesting_balance_type,
|
||||
(owner)(asset_symbol)(amount)(begin_timestamp)(vesting_duration_seconds)(begin_balance))
|
||||
(owner)(asset_symbol)(amount)(begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds)(begin_balance))
|
||||
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_witness_type, (owner_name)(block_signing_key))
|
||||
|
||||
|
|
@ -147,8 +189,35 @@ FC_REFLECT(graphene::chain::genesis_state_type::initial_committee_member_type, (
|
|||
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_worker_type, (owner_name)(daily_pay))
|
||||
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority,
|
||||
(weight_threshold)
|
||||
(account_auths)
|
||||
(key_auths)
|
||||
(address_auths))
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy,
|
||||
(vesting_seconds)
|
||||
(coin_seconds_earned)
|
||||
(start_claim)
|
||||
(coin_seconds_earned_last_update))
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy,
|
||||
(begin_timestamp)
|
||||
(vesting_cliff_seconds)
|
||||
(vesting_duration_seconds)
|
||||
(begin_balance))
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance,
|
||||
(asset_symbol)
|
||||
(amount)
|
||||
(policy_type)
|
||||
(policy))
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type,
|
||||
(name)
|
||||
(owner_authority)
|
||||
(active_authority)
|
||||
(core_balance)
|
||||
(vesting_balances))
|
||||
|
||||
FC_REFLECT(graphene::chain::genesis_state_type,
|
||||
(initial_timestamp)(max_core_supply)(initial_parameters)(initial_accounts)(initial_assets)(initial_balances)
|
||||
(initial_timestamp)(max_core_supply)(initial_parameters)(initial_bts_accounts)(initial_accounts)(initial_assets)(initial_balances)
|
||||
(initial_vesting_balances)(initial_active_witnesses)(initial_witness_candidates)
|
||||
(initial_committee_candidates)(initial_worker_candidates)
|
||||
(initial_chain_id)
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ namespace graphene { namespace chain {
|
|||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_dynamic_global_property_object_type;
|
||||
|
||||
secret_hash_type random;
|
||||
uint32_t head_block_number = 0;
|
||||
block_id_type head_block_id;
|
||||
time_point_sec time;
|
||||
|
|
@ -125,6 +126,7 @@ namespace graphene { namespace chain {
|
|||
}}
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::db::object),
|
||||
(random)
|
||||
(head_block_number)
|
||||
(head_block_id)
|
||||
(time)
|
||||
|
|
|
|||
167
libraries/chain/include/graphene/chain/match_object.hpp
Normal file
167
libraries/chain/include/graphene/chain/match_object.hpp
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#include <graphene/chain/rock_paper_scissors.hpp>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
#include <graphene/db/flat_index.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class match_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::match_object& match_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::match_object& match_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class database;
|
||||
using namespace graphene::db;
|
||||
|
||||
enum class match_state
|
||||
{
|
||||
waiting_on_previous_matches,
|
||||
match_in_progress,
|
||||
match_complete
|
||||
};
|
||||
|
||||
class match_object : public graphene::db::abstract_object<match_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = match_object_type;
|
||||
|
||||
tournament_id_type tournament_id;
|
||||
|
||||
/// The players in the match
|
||||
vector<account_id_type> players;
|
||||
|
||||
/// The list of games in the match
|
||||
/// Unlike tournaments where the list of matches is known at the start,
|
||||
/// the list of games will start with one game and grow until we have played
|
||||
/// enough games to declare a winner for the match.
|
||||
vector<game_id_type> games;
|
||||
|
||||
/// A list of the winners of each round of the game. This information is
|
||||
/// also stored in the game object, but is duplicated here to allow displaying
|
||||
/// information about a match without having to request all game objects
|
||||
vector<flat_set<account_id_type> > game_winners;
|
||||
|
||||
/// A count of the number of wins for each player
|
||||
vector<uint32_t> number_of_wins;
|
||||
|
||||
/// the total number of games that ended up in a tie/draw/stalemate
|
||||
uint32_t number_of_ties;
|
||||
|
||||
// If the match is not yet complete, this will be empty
|
||||
// If the match is in the "match_complete" state, it will contain the
|
||||
// list of winners.
|
||||
// For Rock-paper-scissors, there will be one winner, unless there is
|
||||
// a stalemate (in that case, there are no winners)
|
||||
flat_set<account_id_type> match_winners;
|
||||
|
||||
/// the time the match started
|
||||
time_point_sec start_time;
|
||||
|
||||
/// If the match has ended, the time it ended
|
||||
optional<time_point_sec> end_time;
|
||||
|
||||
match_object();
|
||||
match_object(const match_object& rhs);
|
||||
~match_object();
|
||||
match_object& operator=(const match_object& rhs);
|
||||
|
||||
match_state get_state() const;
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const match_object& match_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, match_object& match_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::match_object& match_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::match_object& match_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
void on_initiate_match(database& db);
|
||||
void on_game_complete(database& db, const game_object& game);
|
||||
game_id_type start_next_game(database& db, match_id_type match_id);
|
||||
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
typedef multi_index_container<
|
||||
match_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > > >
|
||||
> match_object_multi_index_type;
|
||||
typedef generic_index<match_object, match_object_multi_index_type> match_index;
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const match_object& match_obj )
|
||||
{
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<match_object> >(s, match_obj);
|
||||
fc::raw::pack(s, match_obj.id);
|
||||
fc::raw::pack(s, match_obj.tournament_id);
|
||||
fc::raw::pack(s, match_obj.players);
|
||||
fc::raw::pack(s, match_obj.games);
|
||||
fc::raw::pack(s, match_obj.game_winners);
|
||||
fc::raw::pack(s, match_obj.number_of_wins);
|
||||
fc::raw::pack(s, match_obj.number_of_ties);
|
||||
fc::raw::pack(s, match_obj.match_winners);
|
||||
fc::raw::pack(s, match_obj.start_time);
|
||||
fc::raw::pack(s, match_obj.end_time);
|
||||
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
match_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, match_object& match_obj )
|
||||
{
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<match_object> >(s, match_obj);
|
||||
fc::raw::unpack(s, match_obj.id);
|
||||
fc::raw::unpack(s, match_obj.tournament_id);
|
||||
fc::raw::unpack(s, match_obj.players);
|
||||
fc::raw::unpack(s, match_obj.games);
|
||||
fc::raw::unpack(s, match_obj.game_winners);
|
||||
fc::raw::unpack(s, match_obj.number_of_wins);
|
||||
fc::raw::unpack(s, match_obj.number_of_ties);
|
||||
fc::raw::unpack(s, match_obj.match_winners);
|
||||
fc::raw::unpack(s, match_obj.start_time);
|
||||
fc::raw::unpack(s, match_obj.end_time);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
match_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::match_state,
|
||||
(waiting_on_previous_matches)
|
||||
(match_in_progress)
|
||||
(match_complete))
|
||||
|
||||
//FC_REFLECT_TYPENAME(graphene::chain::match_object) // manually serialized
|
||||
FC_REFLECT(graphene::chain::match_object, (players))
|
||||
|
||||
|
|
@ -264,6 +264,7 @@ namespace graphene { namespace chain {
|
|||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions))
|
||||
// FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing)
|
||||
FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing,
|
||||
(no_listing)(white_listed)(black_listed)(white_and_black_listed))
|
||||
|
||||
|
|
|
|||
|
|
@ -132,4 +132,5 @@ void add_authority_accounts(
|
|||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::authority, (weight_threshold)(account_auths)(key_auths)(address_auths) )
|
||||
// FC_REFLECT_TYPENAME( graphene::chain::authority::classification )
|
||||
FC_REFLECT_ENUM( graphene::chain::authority::classification, (owner)(active)(key) )
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ namespace graphene { namespace chain {
|
|||
uint32_t block_num()const { return num_from_id(previous) + 1; }
|
||||
fc::time_point_sec timestamp;
|
||||
witness_id_type witness;
|
||||
secret_hash_type next_secret_hash;
|
||||
secret_hash_type previous_secret;
|
||||
checksum_type transaction_merkle_root;
|
||||
extensions_type extensions;
|
||||
|
||||
|
|
@ -57,6 +59,13 @@ namespace graphene { namespace chain {
|
|||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::block_header, (previous)(timestamp)(witness)(transaction_merkle_root)(extensions) )
|
||||
FC_REFLECT( graphene::chain::block_header,
|
||||
(previous)
|
||||
(timestamp)
|
||||
(witness)
|
||||
(next_secret_hash)
|
||||
(previous_secret)
|
||||
(transaction_merkle_root)
|
||||
(extensions) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::signed_block_header, (graphene::chain::block_header), (witness_signature) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::signed_block, (graphene::chain::signed_block_header), (transactions) )
|
||||
|
|
|
|||
|
|
@ -73,7 +73,22 @@ namespace graphene { namespace chain {
|
|||
bet_multiplier_type min_bet_multiplier = GRAPHENE_DEFAULT_MIN_BET_MULTIPLIER;
|
||||
bet_multiplier_type max_bet_multiplier = GRAPHENE_DEFAULT_MAX_BET_MULTIPLIER;
|
||||
flat_map<bet_multiplier_type, bet_multiplier_type> permitted_betting_odds_increments = GRAPHENE_DEFAULT_PERMITTED_BETTING_ODDS_INCREMENTS;
|
||||
|
||||
//uint8_t witness_schedule_algorithm = GRAPHENE_WITNESS_SHUFFLED_ALGORITHM; ///< 0 shuffled, 1 scheduled
|
||||
uint8_t witness_schedule_algorithm = GRAPHENE_WITNESS_SCHEDULED_ALGORITHM; ///< 0 shuffled, 1 scheduled
|
||||
/* rps tournament parameters constraints */
|
||||
uint32_t min_round_delay = TOURNAMENT_MIN_ROUND_DELAY; ///< miniaml delay between games
|
||||
uint32_t max_round_delay = TOURNAMENT_MAX_ROUND_DELAY; ///< maximal delay between games
|
||||
uint32_t min_time_per_commit_move = TOURNAMENT_MIN_TIME_PER_COMMIT_MOVE; ///< minimal time to commit the next move
|
||||
uint32_t max_time_per_commit_move = TOURNAMENT_MAN_TIME_PER_COMMIT_MOVE; ///< maximal time to commit the next move
|
||||
uint32_t min_time_per_reveal_move = TOURNAMENT_MIN_TIME_PER_REVEAL_MOVE; ///< minimal time to reveal move
|
||||
uint32_t max_time_per_reveal_move = TOURNAMENT_MAX_TIME_PER_REVEAL_MOVE; ///< maximal time to reveal move
|
||||
uint16_t rake_fee_percentage = TOURNAMENT_DEFAULT_RAKE_FEE_PERCENTAGE; ///< part of prize paid into the dividend account for the core token holders
|
||||
uint32_t maximum_registration_deadline = TOURNAMENT_MAXIMAL_REGISTRATION_DEADLINE; ///< value registration deadline must be before
|
||||
uint16_t maximum_players_in_tournament = TOURNAMENT_MAX_PLAYERS_NUMBER; ///< maximal count of players in tournament
|
||||
uint16_t maximum_tournament_whitelist_length = TOURNAMENT_MAX_WHITELIST_LENGTH; ///< maximal tournament whitelist length
|
||||
uint32_t maximum_tournament_start_time_in_future = TOURNAMENT_MAX_START_TIME_IN_FUTURE;
|
||||
uint32_t maximum_tournament_start_delay = TOURNAMENT_MAX_START_DELAY;
|
||||
uint16_t maximum_tournament_number_of_wins = TOURNAMENT_MAX_NUMBER_OF_WINS;
|
||||
extensions_type extensions;
|
||||
|
||||
/** defined in fee_schedule.cpp */
|
||||
|
|
@ -115,5 +130,19 @@ FC_REFLECT( graphene::chain::chain_parameters,
|
|||
(max_bet_multiplier)
|
||||
(betting_rake_fee_percentage)
|
||||
(permitted_betting_odds_increments)
|
||||
(witness_schedule_algorithm)
|
||||
(min_round_delay)
|
||||
(max_round_delay)
|
||||
(min_time_per_commit_move)
|
||||
(max_time_per_commit_move)
|
||||
(min_time_per_reveal_move)
|
||||
(max_time_per_reveal_move)
|
||||
(rake_fee_percentage)
|
||||
(maximum_registration_deadline)
|
||||
(maximum_players_in_tournament)
|
||||
(maximum_tournament_whitelist_length)
|
||||
(maximum_tournament_start_time_in_future)
|
||||
(maximum_tournament_start_delay)
|
||||
(maximum_tournament_number_of_wins)
|
||||
(extensions)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include <graphene/chain/protocol/event_group.hpp>
|
||||
#include <graphene/chain/protocol/event.hpp>
|
||||
#include <graphene/chain/protocol/betting_market.hpp>
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -110,7 +111,14 @@ namespace graphene { namespace chain {
|
|||
betting_market_group_freeze_operation,
|
||||
bet_matched_operation, // VIRTUAL
|
||||
bet_cancel_operation,
|
||||
bet_canceled_operation // VIRTUAL
|
||||
bet_canceled_operation, // VIRTUAL
|
||||
tournament_create_operation,
|
||||
tournament_join_operation,
|
||||
game_move_operation,
|
||||
asset_update_dividend_operation,
|
||||
asset_dividend_distribution_operation, // VIRTUAL
|
||||
tournament_payout_operation, // VIRTUAL
|
||||
tournament_leave_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <fc/container/flat.hpp>
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct rock_paper_scissors_game_options
|
||||
{
|
||||
/// If true and a user fails to commit their move before the time_per_commit_move expires,
|
||||
/// the blockchain will randomly choose a move for the user
|
||||
bool insurance_enabled;
|
||||
/// The number of seconds users are given to commit their next move, counted from the beginning
|
||||
/// of the hand (during the game, a hand begins immediately on the block containing the
|
||||
/// second player's reveal or where the time_per_reveal move has expired).
|
||||
/// Note, if these times aren't an even multiple of the block interval, they will be rounded
|
||||
/// up.
|
||||
uint32_t time_per_commit_move;
|
||||
|
||||
/// The number of seconds users are given to reveal their move, counted from the time of the
|
||||
/// block containing the second commit or the where the time_per_commit_move expired
|
||||
uint32_t time_per_reveal_move;
|
||||
|
||||
/// The number of allowed gestures, must be either 3 or 5. If 3, the game is
|
||||
/// standard rock-paper-scissors, if 5, it's
|
||||
/// rock-paper-scissors-lizard-spock.
|
||||
uint8_t number_of_gestures;
|
||||
};
|
||||
|
||||
enum class rock_paper_scissors_gesture
|
||||
{
|
||||
rock,
|
||||
paper,
|
||||
scissors,
|
||||
spock,
|
||||
lizard
|
||||
};
|
||||
|
||||
struct rock_paper_scissors_throw
|
||||
{
|
||||
uint64_t nonce1;
|
||||
uint64_t nonce2;
|
||||
rock_paper_scissors_gesture gesture;
|
||||
fc::sha256 calculate_hash() const;
|
||||
};
|
||||
|
||||
struct rock_paper_scissors_throw_commit
|
||||
{
|
||||
uint64_t nonce1;
|
||||
fc::sha256 throw_hash;
|
||||
bool operator<(const graphene::chain::rock_paper_scissors_throw_commit& rhs) const
|
||||
{
|
||||
return std::tie(nonce1, throw_hash) < std::tie(rhs.nonce1, rhs.throw_hash);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct rock_paper_scissors_throw_reveal
|
||||
{
|
||||
uint64_t nonce2;
|
||||
rock_paper_scissors_gesture gesture;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::rock_paper_scissors_game_options, (insurance_enabled)(time_per_commit_move)(time_per_reveal_move)(number_of_gestures) )
|
||||
|
||||
// FC_REFLECT_TYPENAME( graphene::chain::rock_paper_scissors_gesture)
|
||||
FC_REFLECT_ENUM( graphene::chain::rock_paper_scissors_gesture,
|
||||
(rock)
|
||||
(paper)
|
||||
(scissors)
|
||||
(spock)
|
||||
(lizard))
|
||||
|
||||
FC_REFLECT( graphene::chain::rock_paper_scissors_throw,
|
||||
(nonce1)
|
||||
(nonce2)
|
||||
(gesture) )
|
||||
|
||||
FC_REFLECT( graphene::chain::rock_paper_scissors_throw_commit,
|
||||
(nonce1)
|
||||
(throw_hash) )
|
||||
|
||||
FC_REFLECT( graphene::chain::rock_paper_scissors_throw_reveal,
|
||||
(nonce2)(gesture) )
|
||||
|
||||
279
libraries/chain/include/graphene/chain/protocol/tournament.hpp
Normal file
279
libraries/chain/include/graphene/chain/protocol/tournament.hpp
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <fc/container/flat.hpp>
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/protocol/rock_paper_scissors.hpp>
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
enum class payout_type
|
||||
{
|
||||
prize_award,
|
||||
buyin_refund,
|
||||
rake_fee
|
||||
};
|
||||
|
||||
typedef fc::static_variant<rock_paper_scissors_game_options> game_specific_options;
|
||||
|
||||
/**
|
||||
* @brief Options specified when creating a new tournament
|
||||
*/
|
||||
struct tournament_options
|
||||
{
|
||||
/// If there aren't enough players registered for the tournament before this time,
|
||||
/// the tournament is canceled
|
||||
fc::time_point_sec registration_deadline;
|
||||
|
||||
/// Number of players in the tournament. Must be greater than 1. Currently max is 255, should it be committe-settable?
|
||||
uint32_t number_of_players;
|
||||
|
||||
/// Each player must pay this much to join the tournament. This can be
|
||||
/// in any asset supported by the blockchain. If the tournament is canceled,
|
||||
/// the buy-in will be returned.
|
||||
asset buy_in;
|
||||
|
||||
/// A list of all accounts allowed to register for this tournament. If empty,
|
||||
/// anyone can register for the tournament
|
||||
flat_set<account_id_type> whitelist;
|
||||
|
||||
/// If specified, this is the time the tourament will start (must not be before the registration
|
||||
/// deadline). If this is not specified, the creator must specify `start_delay` instead.
|
||||
optional<fc::time_point_sec> start_time;
|
||||
|
||||
/// If specified, this is the number of seconds after the final player registers before the
|
||||
/// tournament begins. If this is not specified, the creator must specify an absolute `start_time`
|
||||
optional<uint32_t> start_delay;
|
||||
|
||||
/// The delay, in seconds, between the end of the last game in one round of the tournament and the
|
||||
/// start of all the games in the next round
|
||||
uint32_t round_delay;
|
||||
|
||||
/// The winner of a round in the tournament is the first to reach this number of wins
|
||||
uint32_t number_of_wins;
|
||||
|
||||
/// Metadata about this tournament. This can be empty or it can contain any keys the creator desires.
|
||||
/// The GUI will standardize on displaying a few keys, likely:
|
||||
/// "name"
|
||||
/// "description"
|
||||
/// "url"
|
||||
fc::variant_object meta;
|
||||
|
||||
/// Parameters that are specific to the type_of_game in this tournament
|
||||
/// The type stored in this static_variant field determines what type of game is being
|
||||
/// played, so each different supported game must have a unique game_options data type
|
||||
game_specific_options game_options;
|
||||
|
||||
void validate() const;
|
||||
};
|
||||
|
||||
struct tournament_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
share_type fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
uint32_t price_per_kbyte = 10;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
|
||||
/// The account that created the tournament
|
||||
account_id_type creator;
|
||||
|
||||
/// Options for the tournament
|
||||
tournament_options options;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return creator; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const;
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct tournament_join_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
share_type fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
|
||||
/// The account that is paying the buy-in for the tournament, if the tournament is
|
||||
/// canceled, will be refunded the buy-in.
|
||||
account_id_type payer_account_id;
|
||||
|
||||
/// The account that will play in the tournament, will receive any winnings.
|
||||
account_id_type player_account_id;
|
||||
|
||||
/// The tournament `player_account_id` is joining
|
||||
tournament_id_type tournament_id;
|
||||
|
||||
/// The buy-in paid by the `payer_account_id`
|
||||
asset buy_in;
|
||||
|
||||
extensions_type extensions;
|
||||
account_id_type fee_payer()const { return payer_account_id; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const;
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct tournament_leave_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
share_type fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
|
||||
/// The account that is unregistering the player from tournament (must be payer or player)
|
||||
account_id_type canceling_account_id;
|
||||
|
||||
/// The account that would play in the tournament, would receive any winnings.
|
||||
account_id_type player_account_id;
|
||||
|
||||
/// The tournament `player_account_id` is leaving
|
||||
tournament_id_type tournament_id;
|
||||
|
||||
extensions_type extensions;
|
||||
account_id_type fee_payer()const { return canceling_account_id; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const;
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
|
||||
typedef fc::static_variant<rock_paper_scissors_throw_commit, rock_paper_scissors_throw_reveal> game_specific_moves;
|
||||
|
||||
struct game_move_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
share_type fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
asset fee;
|
||||
|
||||
/// the id of the game
|
||||
game_id_type game_id;
|
||||
|
||||
/// The account of the player making this move
|
||||
account_id_type player_account_id;
|
||||
|
||||
/// the move itself
|
||||
game_specific_moves move;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return player_account_id; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const;
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct tournament_payout_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {};
|
||||
|
||||
asset fee;
|
||||
|
||||
/// The account received payout
|
||||
account_id_type payout_account_id;
|
||||
|
||||
/// The tournament generated payout
|
||||
tournament_id_type tournament_id;
|
||||
|
||||
/// The payout amount
|
||||
asset payout_amount;
|
||||
|
||||
payout_type type;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return payout_account_id; }
|
||||
share_type calculate_fee(const fee_parameters_type&)const { return 0; }
|
||||
void validate()const {}
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::payout_type,
|
||||
(prize_award)
|
||||
(buyin_refund)
|
||||
(rake_fee)
|
||||
)
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::game_specific_options )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::game_specific_moves )
|
||||
FC_REFLECT( graphene::chain::tournament_options,
|
||||
(registration_deadline)
|
||||
(number_of_players)
|
||||
(buy_in)
|
||||
(whitelist)
|
||||
(start_time)
|
||||
(start_delay)
|
||||
(round_delay)
|
||||
(number_of_wins)
|
||||
(meta)
|
||||
(game_options))
|
||||
FC_REFLECT( graphene::chain::tournament_create_operation,
|
||||
(fee)
|
||||
(creator)
|
||||
(options)
|
||||
(extensions))
|
||||
FC_REFLECT( graphene::chain::tournament_join_operation,
|
||||
(fee)
|
||||
(payer_account_id)
|
||||
(player_account_id)
|
||||
(tournament_id)
|
||||
(buy_in)
|
||||
(extensions))
|
||||
FC_REFLECT( graphene::chain::tournament_leave_operation,
|
||||
(fee)
|
||||
(canceling_account_id)
|
||||
(player_account_id)
|
||||
(tournament_id)
|
||||
(extensions))
|
||||
FC_REFLECT( graphene::chain::game_move_operation,
|
||||
(fee)
|
||||
(game_id)
|
||||
(player_account_id)
|
||||
(move)
|
||||
(extensions))
|
||||
FC_REFLECT( graphene::chain::tournament_payout_operation,
|
||||
(fee)
|
||||
(payout_account_id)
|
||||
(tournament_id)
|
||||
(payout_amount)
|
||||
(type)
|
||||
(extensions))
|
||||
|
||||
FC_REFLECT( graphene::chain::tournament_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::tournament_join_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::tournament_leave_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::game_move_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::tournament_payout_operation::fee_parameters_type, )
|
||||
|
||||
|
|
@ -141,6 +141,10 @@ namespace graphene { namespace chain {
|
|||
betting_market_group_object_type,
|
||||
betting_market_object_type,
|
||||
bet_object_type,
|
||||
tournament_object_type,
|
||||
tournament_details_object_type,
|
||||
match_object_type,
|
||||
game_object_type,
|
||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
|
|
@ -194,6 +198,10 @@ namespace graphene { namespace chain {
|
|||
class betting_market_group_object;
|
||||
class betting_market_object;
|
||||
class bet_object;
|
||||
class tournament_object;
|
||||
class tournament_details_object;
|
||||
class match_object;
|
||||
class game_object;
|
||||
|
||||
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
|
||||
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
|
||||
|
|
@ -216,6 +224,10 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< protocol_ids, betting_market_group_object_type, betting_market_group_object> betting_market_group_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_object_type, betting_market_object> betting_market_id_type;
|
||||
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
|
||||
typedef object_id< protocol_ids, tournament_object_type, tournament_object> tournament_id_type;
|
||||
typedef object_id< protocol_ids, tournament_details_object_type, tournament_details_object> tournament_details_id_type;
|
||||
typedef object_id< protocol_ids, match_object_type, match_object> match_id_type;
|
||||
typedef object_id< protocol_ids, game_object_type, game_object> game_id_type;
|
||||
|
||||
// implementation types
|
||||
class global_property_object;
|
||||
|
|
@ -235,6 +247,7 @@ namespace graphene { namespace chain {
|
|||
class fba_accumulator_object;
|
||||
class betting_market_position_object;
|
||||
class global_betting_statistics_object;
|
||||
class tournament_details_object;
|
||||
class asset_dividend_data_object;
|
||||
class pending_dividend_payout_balance_for_holder_object;
|
||||
|
||||
|
|
@ -269,6 +282,7 @@ namespace graphene { namespace chain {
|
|||
typedef fc::sha256 digest_type;
|
||||
typedef fc::ecc::compact_signature signature_type;
|
||||
typedef safe<int64_t> share_type;
|
||||
typedef fc::ripemd160 secret_hash_type;
|
||||
typedef uint16_t weight_type;
|
||||
|
||||
struct public_key_type
|
||||
|
|
@ -385,6 +399,10 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(betting_market_group_object_type)
|
||||
(betting_market_object_type)
|
||||
(bet_object_type)
|
||||
(tournament_object_type)
|
||||
(tournament_details_object_type)
|
||||
(match_object_type)
|
||||
(game_object_type)
|
||||
(OBJECT_TYPE_COUNT)
|
||||
)
|
||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||
|
|
@ -435,6 +453,7 @@ FC_REFLECT_TYPENAME( graphene::chain::betting_market_rules_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::betting_market_group_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::betting_market_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::bet_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::tournament_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::global_property_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::dynamic_global_property_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_id_type )
|
||||
|
|
@ -450,6 +469,7 @@ FC_REFLECT_TYPENAME( graphene::chain::buyback_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::betting_market_position_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type )
|
||||
|
||||
FC_REFLECT( graphene::chain::void_t, )
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ namespace graphene { namespace chain {
|
|||
account_id_type witness_account;
|
||||
string url;
|
||||
public_key_type block_signing_key;
|
||||
secret_hash_type initial_secret;
|
||||
|
||||
account_id_type fee_payer()const { return witness_account; }
|
||||
void validate()const;
|
||||
|
|
@ -67,6 +68,8 @@ namespace graphene { namespace chain {
|
|||
optional< string > new_url;
|
||||
/// The new block signing key.
|
||||
optional< public_key_type > new_signing_key;
|
||||
/// The new secreat hash.
|
||||
optional<secret_hash_type> new_initial_secret;
|
||||
|
||||
account_id_type fee_payer()const { return witness_account; }
|
||||
void validate()const;
|
||||
|
|
@ -77,7 +80,7 @@ namespace graphene { namespace chain {
|
|||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::witness_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(url)(block_signing_key) )
|
||||
FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(url)(block_signing_key)(initial_secret) )
|
||||
|
||||
FC_REFLECT( graphene::chain::witness_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::witness_update_operation, (fee)(witness)(witness_account)(new_url)(new_signing_key) )
|
||||
FC_REFLECT( graphene::chain::witness_update_operation, (fee)(witness)(witness_account)(new_url)(new_signing_key)(new_initial_secret) )
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
#include <fc/optional.hpp>
|
||||
#include <fc/static_variant.hpp>
|
||||
#include <fc/array.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
struct rock_paper_scissors_game_details
|
||||
{
|
||||
// note: I wanted to declare these as fixed arrays, but they don't serialize properly
|
||||
//fc::array<fc::optional<rock_paper_scissors_throw_commit>, 2> commit_moves;
|
||||
//fc::array<fc::optional<rock_paper_scissors_throw_reveal>, 2> reveal_moves;
|
||||
std::vector<fc::optional<rock_paper_scissors_throw_commit> > commit_moves;
|
||||
std::vector<fc::optional<rock_paper_scissors_throw_reveal> > reveal_moves;
|
||||
rock_paper_scissors_game_details() :
|
||||
commit_moves(2),
|
||||
reveal_moves(2)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef fc::static_variant<rock_paper_scissors_game_details> game_specific_details;
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::rock_paper_scissors_game_details,
|
||||
(commit_moves)(reveal_moves) )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::game_specific_details )
|
||||
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class tournament_create_evaluator : public evaluator<tournament_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef tournament_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const tournament_create_operation& o );
|
||||
object_id_type do_apply( const tournament_create_operation& o );
|
||||
};
|
||||
|
||||
class tournament_join_evaluator : public evaluator<tournament_join_evaluator>
|
||||
{
|
||||
private:
|
||||
const tournament_object* _tournament_obj = nullptr;
|
||||
const tournament_details_object* _tournament_details_obj = nullptr;
|
||||
const account_object* _payer_account = nullptr;
|
||||
const asset_object* _buy_in_asset_type = nullptr;
|
||||
public:
|
||||
typedef tournament_join_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const tournament_join_operation& o );
|
||||
void_result do_apply( const tournament_join_operation& o );
|
||||
};
|
||||
|
||||
class tournament_leave_evaluator : public evaluator<tournament_leave_evaluator>
|
||||
{
|
||||
private:
|
||||
const tournament_object* _tournament_obj = nullptr;
|
||||
const tournament_details_object* _tournament_details_obj = nullptr;
|
||||
public:
|
||||
typedef tournament_leave_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const tournament_leave_operation& o );
|
||||
void_result do_apply( const tournament_leave_operation& o );
|
||||
};
|
||||
|
||||
class game_move_evaluator : public evaluator<game_move_evaluator>
|
||||
{
|
||||
private:
|
||||
const game_object* _game_obj = nullptr;
|
||||
public:
|
||||
typedef game_move_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const game_move_operation& o );
|
||||
void_result do_apply( const game_move_operation& o );
|
||||
};
|
||||
|
||||
|
||||
} }
|
||||
249
libraries/chain/include/graphene/chain/tournament_object.hpp
Normal file
249
libraries/chain/include/graphene/chain/tournament_object.hpp
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#include <graphene/chain/rock_paper_scissors.hpp>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
#include <graphene/db/flat_index.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class tournament_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::tournament_object& tournament_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::tournament_object& tournament_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class database;
|
||||
using namespace graphene::db;
|
||||
|
||||
/// The tournament object has a lot of details, most of which are only of interest to anyone
|
||||
/// involved in the tournament. The main `tournament_object` contains all of the information
|
||||
/// needed to display an overview of the tournament, this object contains the rest.
|
||||
class tournament_details_object : public graphene::db::abstract_object<tournament_details_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = tournament_details_object_type;
|
||||
|
||||
/// the tournament object for which this is the details
|
||||
tournament_id_type tournament_id;
|
||||
|
||||
/// List of players registered for this tournament
|
||||
flat_set<account_id_type> registered_players;
|
||||
|
||||
/// List of payers who have contributed to the prize pool
|
||||
flat_map<account_id_type, share_type> payers;
|
||||
|
||||
/// List of player payer pairs needed by torunament leave operation
|
||||
flat_map<account_id_type, account_id_type> players_payers;
|
||||
|
||||
/// List of all matches in this tournament. When the tournament starts, all matches
|
||||
/// are created. Matches in the first round will have players, matches in later
|
||||
/// rounds will not be populated.
|
||||
vector<match_id_type> matches;
|
||||
};
|
||||
|
||||
enum class tournament_state
|
||||
{
|
||||
accepting_registrations,
|
||||
awaiting_start,
|
||||
in_progress,
|
||||
registration_period_expired,
|
||||
concluded
|
||||
};
|
||||
|
||||
class tournament_object : public graphene::db::abstract_object<tournament_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = tournament_object_type;
|
||||
|
||||
tournament_object();
|
||||
tournament_object(const tournament_object& rhs);
|
||||
~tournament_object();
|
||||
tournament_object& operator=(const tournament_object& rhs);
|
||||
|
||||
tournament_id_type get_id() const { return id; };
|
||||
/// the account that created this tournament
|
||||
account_id_type creator;
|
||||
|
||||
/// the options set when creating the tournament
|
||||
tournament_options options;
|
||||
|
||||
/// If the tournament has started, the time it started
|
||||
optional<time_point_sec> start_time;
|
||||
/// If the tournament has ended, the time it ended
|
||||
optional<time_point_sec> end_time;
|
||||
|
||||
/// Total prize pool accumulated
|
||||
/// This is the sum of all payers in the details object, and will be
|
||||
/// registered_players.size() * buy_in_amount
|
||||
share_type prize_pool;
|
||||
|
||||
/// The number of players registered for the tournament
|
||||
/// (same as the details object's registered_players.size(), here to avoid
|
||||
/// the GUI having to get the details object)
|
||||
uint32_t registered_players = 0;
|
||||
|
||||
/// The current high-level status of the tournament (whether it is currently running or has been canceled, etc)
|
||||
//tournament_state state;
|
||||
|
||||
/// Detailed information on this tournament
|
||||
tournament_details_id_type tournament_details_id;
|
||||
|
||||
tournament_state get_state() const;
|
||||
|
||||
time_point_sec get_registration_deadline() const { return options.registration_deadline; }
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const tournament_object& tournament_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, tournament_object& tournament_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::tournament_object& tournament_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::tournament_object& tournament_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
/// called by database maintenance code when registration for this contest has expired
|
||||
void on_registration_deadline_passed(database& db);
|
||||
void on_player_registered(database& db, account_id_type payer_id, account_id_type player_id);
|
||||
void on_player_unregistered(database& db, account_id_type player_id);
|
||||
void on_start_time_arrived(database& db);
|
||||
void on_match_completed(database& db, const match_object& match);
|
||||
|
||||
void check_for_new_matches_to_start(database& db) const;
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
struct by_registration_deadline {};
|
||||
struct by_start_time {};
|
||||
typedef multi_index_container<
|
||||
tournament_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_registration_deadline>,
|
||||
composite_key<tournament_object,
|
||||
const_mem_fun<tournament_object, tournament_state, &tournament_object::get_state>,
|
||||
const_mem_fun<tournament_object, time_point_sec, &tournament_object::get_registration_deadline> > >,
|
||||
ordered_non_unique< tag<by_start_time>,
|
||||
composite_key<tournament_object,
|
||||
const_mem_fun<tournament_object, tournament_state, &tournament_object::get_state>,
|
||||
member<tournament_object, optional<time_point_sec>, &tournament_object::start_time> > >
|
||||
>
|
||||
> tournament_object_multi_index_type;
|
||||
typedef generic_index<tournament_object, tournament_object_multi_index_type> tournament_index;
|
||||
|
||||
typedef multi_index_container<
|
||||
tournament_details_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > > >
|
||||
> tournament_details_object_multi_index_type;
|
||||
typedef generic_index<tournament_details_object, tournament_details_object_multi_index_type> tournament_details_index;
|
||||
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const tournament_object& tournament_obj )
|
||||
{
|
||||
fc_elog(fc::logger::get("tournament"), "In tournament_obj to_raw");
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<tournament_object> >(s, tournament_obj);
|
||||
fc::raw::pack(s, tournament_obj.id);
|
||||
fc::raw::pack(s, tournament_obj.creator);
|
||||
fc::raw::pack(s, tournament_obj.options);
|
||||
fc::raw::pack(s, tournament_obj.start_time);
|
||||
fc::raw::pack(s, tournament_obj.end_time);
|
||||
fc::raw::pack(s, tournament_obj.prize_pool);
|
||||
fc::raw::pack(s, tournament_obj.registered_players);
|
||||
fc::raw::pack(s, tournament_obj.tournament_details_id);
|
||||
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
tournament_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc_elog(fc::logger::get("tournament"), "Serialized state ${state} to bytes ${bytes}",
|
||||
("state", tournament_obj.get_state())("bytes", fc::to_hex(stringified_stream.c_str(), stringified_stream.size())));
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, tournament_object& tournament_obj )
|
||||
{
|
||||
fc_elog(fc::logger::get("tournament"), "In tournament_obj from_raw");
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<tournament_object> >(s, tournament_obj);
|
||||
fc::raw::unpack(s, tournament_obj.id);
|
||||
fc::raw::unpack(s, tournament_obj.creator);
|
||||
fc::raw::unpack(s, tournament_obj.options);
|
||||
fc::raw::unpack(s, tournament_obj.start_time);
|
||||
fc::raw::unpack(s, tournament_obj.end_time);
|
||||
fc::raw::unpack(s, tournament_obj.prize_pool);
|
||||
fc::raw::unpack(s, tournament_obj.registered_players);
|
||||
fc::raw::unpack(s, tournament_obj.tournament_details_id);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
tournament_obj.unpack_impl(stream);
|
||||
fc_elog(fc::logger::get("tournament"), "Deserialized state ${state} from bytes ${bytes}",
|
||||
("state", tournament_obj.get_state())("bytes", fc::to_hex(stringified_stream.c_str(), stringified_stream.size())));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This secondary index will allow a reverse lookup of all tournaments
|
||||
* a particular account has registered for. This will be attached
|
||||
* to the tournament details index because the registrations are contained
|
||||
* in the tournament details object, but it will index the tournament ids
|
||||
* since that is most useful to the GUI.
|
||||
*/
|
||||
class tournament_players_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;
|
||||
|
||||
/** given an account, map it to the set of tournaments in which that account is registered as a player */
|
||||
map< account_id_type, flat_set<tournament_id_type> > account_to_joined_tournaments;
|
||||
|
||||
vector<tournament_id_type> get_registered_tournaments_for_account( const account_id_type& a )const;
|
||||
protected:
|
||||
|
||||
flat_set<account_id_type> before_account_ids;
|
||||
};
|
||||
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::chain::tournament_details_object, (graphene::db::object),
|
||||
(tournament_id)
|
||||
(registered_players)
|
||||
(payers)
|
||||
(players_payers)
|
||||
(matches))
|
||||
//FC_REFLECT_TYPENAME(graphene::chain::tournament_object) // manually serialized
|
||||
FC_REFLECT(graphene::chain::tournament_object, (creator))
|
||||
FC_REFLECT_ENUM(graphene::chain::tournament_state,
|
||||
(accepting_registrations)
|
||||
(awaiting_start)
|
||||
(in_progress)
|
||||
(registration_period_expired)
|
||||
(concluded))
|
||||
|
||||
|
|
@ -181,7 +181,8 @@ namespace graphene { namespace chain {
|
|||
ordered_non_unique< tag<by_account>,
|
||||
member<vesting_balance_object, account_id_type, &vesting_balance_object::owner>
|
||||
>,
|
||||
ordered_unique< tag<by_asset_balance>,
|
||||
//ordered_unique< tag<by_asset_balance>,
|
||||
ordered_non_unique< tag<by_asset_balance>,
|
||||
composite_key<
|
||||
vesting_balance_object,
|
||||
member_offset<vesting_balance_object, asset_id_type, (size_t) (offset_s(vesting_balance_object,balance) + offset_s(asset,asset_id))>,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ namespace graphene { namespace chain {
|
|||
account_id_type witness_account;
|
||||
uint64_t last_aslot = 0;
|
||||
public_key_type signing_key;
|
||||
secret_hash_type next_secret_hash;
|
||||
secret_hash_type previous_secret;
|
||||
optional< vesting_balance_id_type > pay_vb;
|
||||
vote_id_type vote_id;
|
||||
uint64_t total_votes = 0;
|
||||
|
|
@ -74,6 +76,8 @@ FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object),
|
|||
(witness_account)
|
||||
(last_aslot)
|
||||
(signing_key)
|
||||
(next_secret_hash)
|
||||
(previous_secret)
|
||||
(pay_vb)
|
||||
(vote_id)
|
||||
(total_votes)
|
||||
|
|
|
|||
|
|
@ -25,11 +25,34 @@
|
|||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/witness_scheduler.hpp>
|
||||
#include <graphene/chain/witness_scheduler_rng.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class witness_schedule_object;
|
||||
|
||||
typedef hash_ctr_rng<
|
||||
/* HashClass = */ fc::sha256,
|
||||
/* SeedLength = */ GRAPHENE_RNG_SEED_LENGTH
|
||||
> witness_scheduler_rng;
|
||||
|
||||
typedef generic_witness_scheduler<
|
||||
/* WitnessID = */ witness_id_type,
|
||||
/* RNG = */ witness_scheduler_rng,
|
||||
/* CountType = */ decltype( chain_parameters::maximum_witness_count ),
|
||||
/* OffsetType = */ uint32_t,
|
||||
/* debug = */ true
|
||||
> witness_scheduler;
|
||||
|
||||
typedef generic_far_future_witness_scheduler<
|
||||
/* WitnessID = */ witness_id_type,
|
||||
/* RNG = */ witness_scheduler_rng,
|
||||
/* CountType = */ decltype( chain_parameters::maximum_witness_count ),
|
||||
/* OffsetType = */ uint32_t,
|
||||
/* debug = */ true
|
||||
> far_future_witness_scheduler;
|
||||
|
||||
class witness_schedule_object : public graphene::db::abstract_object<witness_schedule_object>
|
||||
{
|
||||
public:
|
||||
|
|
@ -37,12 +60,39 @@ class witness_schedule_object : public graphene::db::abstract_object<witness_sch
|
|||
static const uint8_t type_id = impl_witness_schedule_object_type;
|
||||
|
||||
vector< witness_id_type > current_shuffled_witnesses;
|
||||
|
||||
witness_scheduler scheduler;
|
||||
uint32_t last_scheduling_block;
|
||||
uint64_t slots_since_genesis = 0;
|
||||
fc::array< char, sizeof(secret_hash_type) > rng_seed;
|
||||
|
||||
/**
|
||||
* Not necessary for consensus, but used for figuring out the participation rate.
|
||||
* The nth bit is 0 if the nth slot was unfilled, else it is 1.
|
||||
*/
|
||||
fc::uint128 recent_slots_filled;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
|
||||
FC_REFLECT( graphene::chain::witness_scheduler,
|
||||
(_turns)
|
||||
(_tokens)
|
||||
(_min_token_count)
|
||||
(_ineligible_waiting_for_token)
|
||||
(_ineligible_no_turn)
|
||||
(_eligible)
|
||||
(_schedule)
|
||||
(_lame_duck)
|
||||
)
|
||||
FC_REFLECT_DERIVED(
|
||||
graphene::chain::witness_schedule_object,
|
||||
(graphene::db::object),
|
||||
(scheduler)
|
||||
(last_scheduling_block)
|
||||
(slots_since_genesis)
|
||||
(rng_seed)
|
||||
(recent_slots_filled)
|
||||
(current_shuffled_witnesses)
|
||||
)
|
||||
|
|
|
|||
440
libraries/chain/include/graphene/chain/witness_scheduler.hpp
Normal file
440
libraries/chain/include/graphene/chain/witness_scheduler.hpp
Normal file
|
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Cryptonomex, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
||||
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted until September 8, 2015, provided that the following conditions are met:
|
||||
*
|
||||
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
// @ROL helpfull dumps when debugging
|
||||
//#define ROL_DUMP
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
//using boost::container::flat_set;
|
||||
|
||||
enum witness_scheduler_relax_flags
|
||||
{
|
||||
emit_turn = 0x01,
|
||||
emit_token = 0x02
|
||||
};
|
||||
|
||||
template< typename WitnessID, typename RNG, typename CountType, typename OffsetType, bool debug = true >
|
||||
class generic_witness_scheduler
|
||||
{
|
||||
public:
|
||||
void check_invariant() const
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
CountType tokens = _ineligible_no_turn.size() + _eligible.size();
|
||||
CountType turns = _eligible.size();
|
||||
for( const std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
|
||||
turns += (item.second ? 1 : 0 );
|
||||
|
||||
assert( _tokens == tokens );
|
||||
assert( _turns == turns );
|
||||
#endif
|
||||
|
||||
set< WitnessID > witness_set;
|
||||
// make sure each witness_id occurs only once among the three states
|
||||
auto process_id = [&]( WitnessID item )
|
||||
{
|
||||
assert( witness_set.find( item ) == witness_set.end() );
|
||||
witness_set.insert( item );
|
||||
} ;
|
||||
|
||||
for( const std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
|
||||
process_id( item.first );
|
||||
for( const WitnessID& item : _ineligible_no_turn )
|
||||
process_id( item );
|
||||
for( const WitnessID& item : _eligible )
|
||||
process_id( item );
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deterministically evolve over time
|
||||
*/
|
||||
uint32_t relax()
|
||||
{
|
||||
uint32_t relax_flags = 0;
|
||||
|
||||
if( debug ) check_invariant();
|
||||
assert( _min_token_count > 0 );
|
||||
|
||||
// turn distribution
|
||||
if( _turns == 0 )
|
||||
{
|
||||
relax_flags |= emit_turn;
|
||||
for( const WitnessID& item : _ineligible_no_turn )
|
||||
_eligible.push_back( item );
|
||||
_turns += _ineligible_no_turn.size();
|
||||
_ineligible_no_turn.clear();
|
||||
if( debug ) check_invariant();
|
||||
|
||||
for( std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
|
||||
{
|
||||
assert( item.second == false );
|
||||
item.second = true;
|
||||
}
|
||||
_turns += _ineligible_waiting_for_token.size();
|
||||
if( debug ) check_invariant();
|
||||
}
|
||||
|
||||
// token distribution
|
||||
while( true )
|
||||
{
|
||||
if( _ineligible_waiting_for_token.empty() )
|
||||
{
|
||||
// eligible must be non-empty
|
||||
assert( !_eligible.empty() );
|
||||
return relax_flags;
|
||||
}
|
||||
|
||||
if( _tokens >= _min_token_count )
|
||||
{
|
||||
if( !_eligible.empty() )
|
||||
return relax_flags;
|
||||
}
|
||||
|
||||
const std::pair< WitnessID, bool >& item = _ineligible_waiting_for_token.front();
|
||||
if( item.second )
|
||||
_eligible.push_back( item.first );
|
||||
else
|
||||
_ineligible_no_turn.push_back( item.first );
|
||||
_ineligible_waiting_for_token.pop_front();
|
||||
relax_flags |= emit_token;
|
||||
_tokens++;
|
||||
if( debug ) check_invariant();
|
||||
}
|
||||
|
||||
return relax_flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another element to _schedule
|
||||
*/
|
||||
uint32_t produce_schedule( RNG& rng )
|
||||
{
|
||||
uint32_t relax_flags = relax();
|
||||
if( debug ) check_invariant();
|
||||
if( _eligible.empty() )
|
||||
return relax_flags;
|
||||
|
||||
decltype( rng( _eligible.size() ) ) pos = rng( _eligible.size() );
|
||||
assert( (pos >= 0) && (pos < _eligible.size()) );
|
||||
auto it = _eligible.begin() + pos;
|
||||
_schedule.push_back( *it );
|
||||
_ineligible_waiting_for_token.emplace_back( *it, false );
|
||||
_eligible.erase( it );
|
||||
_turns--;
|
||||
_tokens--;
|
||||
if( debug ) check_invariant();
|
||||
return relax_flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull an element from _schedule
|
||||
*/
|
||||
WitnessID consume_schedule()
|
||||
{
|
||||
assert( _schedule.size() > 0 );
|
||||
|
||||
WitnessID result = _schedule.front();
|
||||
_schedule.pop_front();
|
||||
|
||||
auto it = _lame_duck.find( result );
|
||||
if( it != _lame_duck.end() )
|
||||
_lame_duck.erase( it );
|
||||
if( debug ) check_invariant();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all witnesses in the removal_set from
|
||||
* future scheduling (but not from the current schedule).
|
||||
*/
|
||||
template< typename T >
|
||||
void remove_all( const T& removal_set )
|
||||
{
|
||||
if( debug ) check_invariant();
|
||||
|
||||
_ineligible_waiting_for_token.erase(
|
||||
std::remove_if(
|
||||
_ineligible_waiting_for_token.begin(),
|
||||
_ineligible_waiting_for_token.end(),
|
||||
[&]( const std::pair< WitnessID, bool >& item ) -> bool
|
||||
{
|
||||
bool found = removal_set.find( item.first ) != removal_set.end();
|
||||
_turns -= (found & item.second) ? 1 : 0;
|
||||
return found;
|
||||
} ),
|
||||
_ineligible_waiting_for_token.end() );
|
||||
if( debug ) check_invariant();
|
||||
|
||||
_ineligible_no_turn.erase(
|
||||
std::remove_if(
|
||||
_ineligible_no_turn.begin(),
|
||||
_ineligible_no_turn.end(),
|
||||
[&]( WitnessID item ) -> bool
|
||||
{
|
||||
bool found = (removal_set.find( item ) != removal_set.end());
|
||||
_tokens -= (found ? 1 : 0);
|
||||
return found;
|
||||
} ),
|
||||
_ineligible_no_turn.end() );
|
||||
if( debug ) check_invariant();
|
||||
|
||||
_eligible.erase(
|
||||
std::remove_if(
|
||||
_eligible.begin(),
|
||||
_eligible.end(),
|
||||
[&]( WitnessID item ) -> bool
|
||||
{
|
||||
bool found = (removal_set.find( item ) != removal_set.end());
|
||||
_tokens -= (found ? 1 : 0);
|
||||
_turns -= (found ? 1 : 0);
|
||||
return found;
|
||||
} ),
|
||||
_eligible.end() );
|
||||
if( debug ) check_invariant();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to call insert_all() and remove_all()
|
||||
* as needed to update to the given revised_set.
|
||||
*/
|
||||
template< typename T >
|
||||
void insert_all( const T& insertion_set )
|
||||
{
|
||||
if( debug ) check_invariant();
|
||||
for( const WitnessID wid : insertion_set )
|
||||
{
|
||||
_eligible.push_back( wid );
|
||||
}
|
||||
_turns += insertion_set.size();
|
||||
_tokens += insertion_set.size();
|
||||
if( debug ) check_invariant();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to call insert_all() and remove_all()
|
||||
* as needed to update to the given revised_set.
|
||||
*
|
||||
* This function calls find() on revised_set for all current
|
||||
* witnesses. Running time is O(n*log(n)) if the revised_set
|
||||
* implementation of find() is O(log(n)).
|
||||
*
|
||||
* TODO: Rewriting to use std::set_difference may marginally
|
||||
* increase efficiency, but a benchmark is needed to justify this.
|
||||
*/
|
||||
template< typename T >
|
||||
void update( const T& revised_set )
|
||||
{
|
||||
#ifdef ROL_DUMP
|
||||
wdump((revised_set));
|
||||
#endif
|
||||
set< WitnessID > current_set;
|
||||
set< WitnessID > schedule_set;
|
||||
|
||||
/* current_set.reserve(
|
||||
_ineligible_waiting_for_token.size()
|
||||
+ _ineligible_no_turn.size()
|
||||
+ _eligible.size()
|
||||
+ _schedule.size() );
|
||||
*/
|
||||
for( const auto& item : _ineligible_waiting_for_token )
|
||||
current_set.insert( item.first );
|
||||
for( const WitnessID& item : _ineligible_no_turn )
|
||||
current_set.insert( item );
|
||||
for( const WitnessID& item : _eligible )
|
||||
current_set.insert( item );
|
||||
for( const WitnessID& item : _schedule )
|
||||
{
|
||||
current_set.insert( item );
|
||||
schedule_set.insert( item );
|
||||
}
|
||||
|
||||
set< WitnessID > insertion_set;
|
||||
//insertion_set.reserve( revised_set.size() );
|
||||
for( const WitnessID& item : revised_set )
|
||||
{
|
||||
if( current_set.find( item ) == current_set.end() )
|
||||
insertion_set.insert( item );
|
||||
}
|
||||
|
||||
set< WitnessID > removal_set;
|
||||
//removal_set.reserve( current_set.size() );
|
||||
for( const WitnessID& item : current_set )
|
||||
{
|
||||
if( revised_set.find( item ) == revised_set.end() )
|
||||
{
|
||||
if( schedule_set.find( item ) == schedule_set.end() )
|
||||
removal_set.insert( item );
|
||||
else
|
||||
_lame_duck.insert( item );
|
||||
}
|
||||
}
|
||||
|
||||
insert_all( insertion_set );
|
||||
remove_all( removal_set );
|
||||
#ifdef ROL_DUMP
|
||||
wdump((_eligible));
|
||||
wdump((_schedule));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of scheduled witnesses
|
||||
*/
|
||||
|
||||
size_t size( )const
|
||||
{
|
||||
return _schedule.size();
|
||||
}
|
||||
|
||||
bool get_slot( OffsetType offset, WitnessID& wit )const
|
||||
{
|
||||
#ifdef ROL_DUMP
|
||||
wdump((_schedule)(_schedule.size())(offset));
|
||||
#endif
|
||||
if (_schedule.empty())
|
||||
return false; //@ROL wit is 1.6.0!
|
||||
if( offset >= _schedule.size() )
|
||||
{
|
||||
wit = _schedule[ 0 ];
|
||||
return false;
|
||||
}
|
||||
wit = _schedule[ offset ];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the schedule, then re-schedule the given witness as the
|
||||
* first witness.
|
||||
*/
|
||||
void reset_schedule( WitnessID first_witness )
|
||||
{
|
||||
#ifdef ROL_DUMP
|
||||
wdump((first_witness));
|
||||
#endif
|
||||
_schedule.clear();
|
||||
for( const WitnessID& wid : _ineligible_no_turn )
|
||||
{
|
||||
_eligible.push_back( wid );
|
||||
}
|
||||
_turns += _ineligible_no_turn.size();
|
||||
_ineligible_no_turn.clear();
|
||||
for( const auto& item : _ineligible_waiting_for_token )
|
||||
{
|
||||
_eligible.push_back( item.first );
|
||||
_turns += (item.second ? 0 : 1);
|
||||
}
|
||||
_tokens += _ineligible_waiting_for_token.size();
|
||||
_ineligible_waiting_for_token.clear();
|
||||
if( debug ) check_invariant();
|
||||
#ifdef ROL_DUMP
|
||||
wdump((_eligible));
|
||||
#endif
|
||||
auto it = std::find( _eligible.begin(), _eligible.end(), first_witness );
|
||||
//@ROL: maybe good idea, when wit is 1.6.0 if ( it == _eligible.end() ) return;
|
||||
assert( it != _eligible.end() );
|
||||
|
||||
_schedule.push_back( *it );
|
||||
_ineligible_waiting_for_token.emplace_back( *it, false );
|
||||
_eligible.erase( it );
|
||||
_turns--;
|
||||
_tokens--;
|
||||
if( debug ) check_invariant();
|
||||
return;
|
||||
}
|
||||
|
||||
// keep track of total turns / tokens in existence
|
||||
CountType _turns = 0;
|
||||
CountType _tokens = 0;
|
||||
|
||||
// new tokens handed out when _tokens < _min_token_count
|
||||
CountType _min_token_count;
|
||||
|
||||
// WitnessID appears in exactly one of the following:
|
||||
// has no token; second indicates whether we have a turn or not:
|
||||
std::deque < std::pair< WitnessID, bool > > _ineligible_waiting_for_token; // ".." | "T."
|
||||
// has token, but no turn
|
||||
std::vector< WitnessID > _ineligible_no_turn; // ".t"
|
||||
// has token and turn
|
||||
std::vector< WitnessID > _eligible; // "Tt"
|
||||
|
||||
// scheduled
|
||||
std::deque < WitnessID > _schedule;
|
||||
|
||||
// in _schedule, but not to be replaced
|
||||
set< WitnessID > _lame_duck;
|
||||
};
|
||||
|
||||
template< typename WitnessID, typename RNG, typename CountType, typename OffsetType, bool debug = true >
|
||||
class generic_far_future_witness_scheduler
|
||||
{
|
||||
public:
|
||||
generic_far_future_witness_scheduler(
|
||||
const generic_witness_scheduler< WitnessID, RNG, CountType, OffsetType, debug >& base_scheduler,
|
||||
RNG rng
|
||||
)
|
||||
{
|
||||
generic_witness_scheduler< WitnessID, RNG, CountType, OffsetType, debug > extended_scheduler = base_scheduler;
|
||||
_begin_offset = base_scheduler.size()+1;
|
||||
while( (extended_scheduler.produce_schedule( rng ) & emit_turn) == 0 )
|
||||
_begin_offset++;
|
||||
assert( _begin_offset == extended_scheduler.size() );
|
||||
|
||||
_end_offset = _begin_offset;
|
||||
while( (extended_scheduler.produce_schedule( rng ) & emit_turn) == 0 )
|
||||
_end_offset++;
|
||||
assert( _end_offset == extended_scheduler.size()-1 );
|
||||
_schedule.resize( extended_scheduler._schedule.size() );
|
||||
std::copy( extended_scheduler._schedule.begin(),
|
||||
extended_scheduler._schedule.end(),
|
||||
_schedule.begin() );
|
||||
return;
|
||||
}
|
||||
|
||||
bool get_slot( OffsetType offset, WitnessID& wit )const
|
||||
{
|
||||
if( offset <= _end_offset )
|
||||
wit = _schedule[ offset ];
|
||||
else
|
||||
wit = _schedule[ _begin_offset +
|
||||
(
|
||||
(offset - _begin_offset) %
|
||||
(_end_offset + 1 - _begin_offset)
|
||||
) ];
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector< WitnessID > _schedule;
|
||||
OffsetType _begin_offset;
|
||||
OffsetType _end_offset;
|
||||
};
|
||||
|
||||
} }
|
||||
124
libraries/chain/include/graphene/chain/witness_scheduler_rng.hpp
Normal file
124
libraries/chain/include/graphene/chain/witness_scheduler_rng.hpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Cryptonomex, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
||||
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted until September 8, 2015, provided that the following conditions are met:
|
||||
*
|
||||
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <boost/multiprecision/integer.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
/**
|
||||
* Always returns 0. Useful for testing.
|
||||
*/
|
||||
class nullary_rng
|
||||
{
|
||||
public:
|
||||
nullary_rng() {}
|
||||
virtual ~nullary_rng() {}
|
||||
|
||||
template< typename T > T operator()( T max )
|
||||
{ return T(0); }
|
||||
} ;
|
||||
|
||||
/**
|
||||
* The sha256_ctr_rng generates bits using SHA256 in counter (CTR)
|
||||
* mode.
|
||||
*/
|
||||
template< class HashClass, int SeedLength=sizeof(secret_hash_type) >
|
||||
class hash_ctr_rng
|
||||
{
|
||||
public:
|
||||
hash_ctr_rng( const char* seed, uint64_t counter = 0 )
|
||||
: _counter( counter ), _current_offset( 0 )
|
||||
{
|
||||
memcpy( _seed, seed, SeedLength );
|
||||
_reset_current_value();
|
||||
return;
|
||||
}
|
||||
|
||||
virtual ~hash_ctr_rng() {}
|
||||
|
||||
uint64_t get_bits( uint8_t count )
|
||||
{
|
||||
uint64_t result = 0;
|
||||
uint64_t mask = 1;
|
||||
// grab the requested number of bits
|
||||
while( count > 0 )
|
||||
{
|
||||
result |=
|
||||
(
|
||||
(
|
||||
(
|
||||
_current_value.data()[ (_current_offset >> 3) & 0x1F ]
|
||||
& ( 1 << (_current_offset & 0x07) )
|
||||
)
|
||||
!= 0
|
||||
) ? mask : 0
|
||||
);
|
||||
mask += mask;
|
||||
--count;
|
||||
++_current_offset;
|
||||
if( _current_offset == (_current_value.data_size() << 3) )
|
||||
{
|
||||
_counter++;
|
||||
_current_offset = 0;
|
||||
_reset_current_value();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t operator()( uint64_t bound )
|
||||
{
|
||||
if( bound <= 1 )
|
||||
return 0;
|
||||
uint8_t bitcount = boost::multiprecision::detail::find_msb( bound ) + 1;
|
||||
|
||||
// probability of loop exiting is >= 1/2, so probability of
|
||||
// running N times is bounded above by (1/2)^N
|
||||
while( true )
|
||||
{
|
||||
uint64_t result = get_bits( bitcount );
|
||||
if( result < bound )
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// convenience method which does casting for types other than uint64_t
|
||||
template< typename T > T operator()( T bound )
|
||||
{ return (T) ( (*this)(uint64_t( bound )) ); }
|
||||
|
||||
void _reset_current_value()
|
||||
{
|
||||
// internal implementation detail, called to update
|
||||
// _current_value when _counter changes
|
||||
typename HashClass::encoder enc;
|
||||
enc.write( _seed , SeedLength );
|
||||
enc.write( (char *) &_counter, 8 );
|
||||
_current_value = enc.result();
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t _counter;
|
||||
char _seed[ SeedLength ];
|
||||
HashClass _current_value;
|
||||
uint16_t _current_offset;
|
||||
|
||||
static const int seed_length = SeedLength;
|
||||
} ;
|
||||
|
||||
} }
|
||||
|
|
@ -53,6 +53,11 @@ void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_o
|
|||
if( _sell_asset->options.blacklist_markets.size() )
|
||||
FC_ASSERT( _sell_asset->options.blacklist_markets.find(_receive_asset->id) == _sell_asset->options.blacklist_markets.end() );
|
||||
|
||||
// $$$ I. DEX Task The Peerplays DEX should only allow UIA and sidechain assets to be paired (traded) with the core token (PPY)
|
||||
FC_ASSERT(_receive_asset->id == asset_id_type() || _sell_asset->id == asset_id_type(),
|
||||
"No asset in the trade is CORE.");
|
||||
|
||||
|
||||
FC_ASSERT( is_authorized_asset( d, *_seller, *_sell_asset ) );
|
||||
FC_ASSERT( is_authorized_asset( d, *_seller, *_receive_asset ) );
|
||||
|
||||
|
|
|
|||
406
libraries/chain/match_object.cpp
Normal file
406
libraries/chain/match_object.cpp
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* 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 <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/archive/binary_oarchive.hpp>
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/msm/back/tools.hpp>
|
||||
#include <boost/range/algorithm/copy.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Events
|
||||
struct initiate_match
|
||||
{
|
||||
database& db;
|
||||
initiate_match(database& db) :
|
||||
db(db)
|
||||
{}
|
||||
};
|
||||
|
||||
struct game_complete
|
||||
{
|
||||
database& db;
|
||||
const game_object& game;
|
||||
game_complete(database& db, const game_object& game) : db(db), game(game) {};
|
||||
};
|
||||
|
||||
struct match_state_machine_ : public msm::front::state_machine_def<match_state_machine_>
|
||||
{
|
||||
// disable a few state machine features we don't use for performance
|
||||
typedef int no_exception_thrown;
|
||||
typedef int no_message_queue;
|
||||
|
||||
// States
|
||||
struct waiting_on_previous_matches : public msm::front::state<>{};
|
||||
struct match_in_progress : public msm::front::state<>
|
||||
{
|
||||
void on_entry(const game_complete& event, match_state_machine_& fsm)
|
||||
{
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"Game ${game_id} in match ${id} is complete",
|
||||
("game_id", event.game.id)("id", fsm.match_obj->id));
|
||||
}
|
||||
void on_entry(const initiate_match& event, match_state_machine_& fsm)
|
||||
{
|
||||
match_object& match = *fsm.match_obj;
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"Match ${id} is now in progress",
|
||||
("id", match.id));
|
||||
match.number_of_wins.resize(match.players.size());
|
||||
match.start_time = event.db.head_block_time();
|
||||
|
||||
fsm.start_next_game(event.db);
|
||||
}
|
||||
};
|
||||
struct match_complete : public msm::front::state<>
|
||||
{
|
||||
void on_entry(const game_complete& event, match_state_machine_& fsm)
|
||||
{
|
||||
|
||||
match_object& match = *fsm.match_obj;
|
||||
//wdump((match));
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"Match ${id} is complete",
|
||||
("id", match.id));
|
||||
|
||||
optional<account_id_type> last_game_winner;
|
||||
std::map<account_id_type, unsigned> scores_by_player;
|
||||
for (const flat_set<account_id_type>& game_winners : match.game_winners)
|
||||
for (const account_id_type& account_id : game_winners)
|
||||
{
|
||||
++scores_by_player[account_id];
|
||||
last_game_winner = account_id;
|
||||
}
|
||||
|
||||
bool all_scores_same = true;
|
||||
optional<account_id_type> high_scoring_account;
|
||||
unsigned high_score = 0;
|
||||
for (const auto& value : scores_by_player)
|
||||
if (value.second > high_score)
|
||||
{
|
||||
if (high_scoring_account)
|
||||
all_scores_same = false;
|
||||
high_score = value.second;
|
||||
high_scoring_account = value.first;
|
||||
}
|
||||
|
||||
if (high_scoring_account)
|
||||
{
|
||||
if (all_scores_same && last_game_winner)
|
||||
match.match_winners.insert(*last_game_winner);
|
||||
else
|
||||
match.match_winners.insert(*high_scoring_account);
|
||||
}
|
||||
else
|
||||
{
|
||||
// III. Rock Paper Scissors Game Need to review how Ties are dealt with.
|
||||
short i = match.number_of_ties == match.games.size() ? 0 : event.db.get_random_bits(match.players.size()) ;
|
||||
match.match_winners.insert(match.players[i]);
|
||||
++match.number_of_wins[i];
|
||||
if (match.number_of_ties == match.games.size())
|
||||
match.game_winners[match.game_winners.size()-1].insert(match.players[i]);
|
||||
}
|
||||
|
||||
match.end_time = event.db.head_block_time();
|
||||
const tournament_object& tournament_obj = match.tournament_id(event.db);
|
||||
event.db.modify(tournament_obj, [&](tournament_object& tournament) {
|
||||
tournament.on_match_completed(event.db, match);
|
||||
});
|
||||
}
|
||||
void on_entry(const initiate_match& event, match_state_machine_& fsm)
|
||||
{
|
||||
match_object& match = *fsm.match_obj;
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"Match ${id} is complete, it was a buy",
|
||||
("id", match));
|
||||
match.number_of_wins.resize(match.players.size());
|
||||
boost::copy(match.players, std::inserter(match.match_winners, match.match_winners.end()));
|
||||
match.start_time = event.db.head_block_time();
|
||||
match.end_time = event.db.head_block_time();
|
||||
// NOTE: when the match is a buy, we don't send a match completed event to
|
||||
// the tournament_obj, because it is already in the middle of handling
|
||||
// an event; it will figure out that the match has completed on its own.
|
||||
}
|
||||
};
|
||||
typedef waiting_on_previous_matches initial_state;
|
||||
|
||||
typedef match_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
bool was_final_game(const game_complete& event)
|
||||
{
|
||||
const tournament_object& tournament_obj = match_obj->tournament_id(event.db);
|
||||
|
||||
if (match_obj->games.size() >= tournament_obj.options.number_of_wins * 4)
|
||||
{
|
||||
wdump((match_obj->games.size()));
|
||||
return true;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < match_obj->players.size(); ++i)
|
||||
{
|
||||
// this guard is called before the winner of the current game factored in to our running totals,
|
||||
// so we must add the current game to our count
|
||||
uint32_t win_for_this_game = event.game.winners.find(match_obj->players[i]) != event.game.winners.end() ? 1 : 0;
|
||||
if (match_obj->number_of_wins[i] + win_for_this_game >= tournament_obj.options.number_of_wins)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool match_is_a_buy(const initiate_match& event)
|
||||
{
|
||||
return match_obj->players.size() < 2;
|
||||
}
|
||||
|
||||
void record_completed_game(const game_complete& event)
|
||||
{
|
||||
if (event.game.winners.empty())
|
||||
++match_obj->number_of_ties;
|
||||
else
|
||||
for (unsigned i = 0; i < match_obj->players.size(); ++i)
|
||||
if (event.game.winners.find(match_obj->players[i]) != event.game.winners.end())
|
||||
++match_obj->number_of_wins[i];
|
||||
match_obj->game_winners.emplace_back(event.game.winners);
|
||||
}
|
||||
|
||||
void start_next_game(database& db)
|
||||
{
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"In start_next_game");
|
||||
const game_object& game =
|
||||
db.create<game_object>( [&]( game_object& game ) {
|
||||
game.match_id = match_obj->id;
|
||||
game.players = match_obj->players;
|
||||
game.game_details = rock_paper_scissors_game_details();
|
||||
game.start_game(db, game.players);
|
||||
});
|
||||
match_obj->games.push_back(game.id);
|
||||
}
|
||||
|
||||
void record_and_start_next_game(const game_complete& event)
|
||||
{
|
||||
record_completed_game(event);
|
||||
start_next_game(event.db);
|
||||
}
|
||||
|
||||
// Transition table for tournament
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < waiting_on_previous_matches, initiate_match, match_in_progress >,
|
||||
g_row < waiting_on_previous_matches, initiate_match, match_complete, &x::match_is_a_buy >,
|
||||
// +-------------------------------+-------------------------+----------------------------+---------------------+----------------------+
|
||||
a_row < match_in_progress, game_complete, match_in_progress, &x::record_and_start_next_game >,
|
||||
row < match_in_progress, game_complete, match_complete, &x::record_completed_game, &x::was_final_game >
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
> {};
|
||||
|
||||
|
||||
match_object* match_obj;
|
||||
match_state_machine_(match_object* match_obj) : match_obj(match_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<match_state_machine_> match_state_machine;
|
||||
}
|
||||
|
||||
class match_object::impl {
|
||||
public:
|
||||
match_state_machine state_machine;
|
||||
|
||||
impl(match_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
match_object::match_object() :
|
||||
number_of_ties(0),
|
||||
my(new impl(this))
|
||||
{
|
||||
}
|
||||
|
||||
match_object::match_object(const match_object& rhs) :
|
||||
graphene::db::abstract_object<match_object>(rhs),
|
||||
tournament_id(rhs.tournament_id),
|
||||
players(rhs.players),
|
||||
games(rhs.games),
|
||||
game_winners(rhs.game_winners),
|
||||
number_of_wins(rhs.number_of_wins),
|
||||
number_of_ties(rhs.number_of_ties),
|
||||
match_winners(rhs.match_winners),
|
||||
start_time(rhs.start_time),
|
||||
end_time(rhs.end_time),
|
||||
my(new impl(this))
|
||||
{
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.match_obj = this;
|
||||
}
|
||||
|
||||
match_object& match_object::operator=(const match_object& rhs)
|
||||
{
|
||||
//graphene::db::abstract_object<match_object>::operator=(rhs);
|
||||
id = rhs.id;
|
||||
tournament_id = rhs.tournament_id;
|
||||
players = rhs.players;
|
||||
games = rhs.games;
|
||||
game_winners = rhs.game_winners;
|
||||
number_of_wins = rhs.number_of_wins;
|
||||
number_of_ties = rhs.number_of_ties;
|
||||
match_winners = rhs.match_winners;
|
||||
start_time = rhs.start_time;
|
||||
end_time = rhs.end_time;
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.match_obj = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
match_object::~match_object()
|
||||
{
|
||||
}
|
||||
|
||||
bool verify_match_state_constants()
|
||||
{
|
||||
unsigned error_count = 0;
|
||||
typedef msm::back::generate_state_set<match_state_machine::stt>::type all_states;
|
||||
static char const* filled_state_names[mpl::size<all_states>::value];
|
||||
mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >
|
||||
(msm::back::fill_state_names<match_state_machine::stt>(filled_state_names));
|
||||
for (unsigned i = 0; i < mpl::size<all_states>::value; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
|
||||
// at least contain the string we're looking for
|
||||
const char* fc_reflected_value_name = fc::reflector<match_state>::to_string((match_state)i);
|
||||
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
|
||||
fc_elog(fc::logger::get("match"),
|
||||
"Error, state string mismatch between fc and boost::msm for int value ${int_value}: boost::msm -> ${boost_string}, fc::reflect -> ${fc_string}",
|
||||
("int_value", i)("boost_string", filled_state_names[i])("fc_string", fc_reflected_value_name));
|
||||
}
|
||||
catch (const fc::bad_cast_exception&)
|
||||
{
|
||||
fc_elog(fc::logger::get("match"),
|
||||
"Error, no reflection for value ${int_value} in enum match_state",
|
||||
("int_value", i));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
|
||||
return error_count == 0;
|
||||
}
|
||||
|
||||
match_state match_object::get_state() const
|
||||
{
|
||||
static bool state_constants_are_correct = verify_match_state_constants();
|
||||
(void)&state_constants_are_correct;
|
||||
match_state state = (match_state)my->state_machine.current_state()[0];
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void match_object::pack_impl(std::ostream& stream) const
|
||||
{
|
||||
boost::archive::binary_oarchive oa(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
oa << my->state_machine;
|
||||
}
|
||||
|
||||
void match_object::unpack_impl(std::istream& stream)
|
||||
{
|
||||
boost::archive::binary_iarchive ia(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
ia >> my->state_machine;
|
||||
}
|
||||
|
||||
void match_object::on_initiate_match(database& db)
|
||||
{
|
||||
my->state_machine.process_event(initiate_match(db));
|
||||
}
|
||||
|
||||
void match_object::on_game_complete(database& db, const game_object& game)
|
||||
{
|
||||
my->state_machine.process_event(game_complete(db, game));
|
||||
}
|
||||
#if 0
|
||||
game_id_type match_object::start_next_game(database& db, match_id_type match_id)
|
||||
{
|
||||
const game_object& game =
|
||||
db.create<game_object>( [&]( game_object& game ) {
|
||||
game.match_id = match_id;
|
||||
game.players = players;
|
||||
});
|
||||
return game.id;
|
||||
}
|
||||
#endif
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc {
|
||||
// Manually reflect match_object to variant to properly reflect "state"
|
||||
void to_variant(const graphene::chain::match_object& match_obj, fc::variant& v)
|
||||
{ try {
|
||||
fc_elog(fc::logger::get("tournament"), "In match_obj to_variant");
|
||||
elog("In match_obj to_variant");
|
||||
fc::mutable_variant_object o;
|
||||
o("id", match_obj.id)
|
||||
("tournament_id", match_obj.tournament_id)
|
||||
("players", match_obj.players)
|
||||
("games", match_obj.games)
|
||||
("game_winners", match_obj.game_winners)
|
||||
("number_of_wins", match_obj.number_of_wins)
|
||||
("number_of_ties", match_obj.number_of_ties)
|
||||
("match_winners", match_obj.match_winners)
|
||||
("start_time", match_obj.start_time)
|
||||
("end_time", match_obj.end_time)
|
||||
("state", match_obj.get_state());
|
||||
|
||||
v = o;
|
||||
} FC_RETHROW_EXCEPTIONS(warn, "") }
|
||||
|
||||
// Manually reflect match_object to variant to properly reflect "state"
|
||||
void from_variant(const fc::variant& v, graphene::chain::match_object& match_obj)
|
||||
{ try {
|
||||
fc_elog(fc::logger::get("tournament"), "In match_obj from_variant");
|
||||
match_obj.id = v["id"].as<graphene::chain::match_id_type>();
|
||||
match_obj.tournament_id = v["tournament_id"].as<graphene::chain::tournament_id_type>();
|
||||
match_obj.players = v["players"].as<std::vector<graphene::chain::account_id_type> >();
|
||||
match_obj.games = v["games"].as<std::vector<graphene::chain::game_id_type> >();
|
||||
match_obj.game_winners = v["game_winners"].as<std::vector<flat_set<graphene::chain::account_id_type> > >();
|
||||
match_obj.number_of_wins = v["number_of_wins"].as<std::vector<uint32_t> >();
|
||||
match_obj.number_of_ties = v["number_of_ties"].as<uint32_t>();
|
||||
match_obj.match_winners = v["match_winners"].as<flat_set<graphene::chain::account_id_type> >();
|
||||
match_obj.start_time = v["start_time"].as<time_point_sec>();
|
||||
match_obj.end_time = v["end_time"].as<optional<time_point_sec> >();
|
||||
graphene::chain::match_state state = v["state"].as<graphene::chain::match_state>();
|
||||
const_cast<int*>(match_obj.my->state_machine.current_state())[0] = (int)state;
|
||||
} FC_RETHROW_EXCEPTIONS(warn, "") }
|
||||
} //end namespace fc
|
||||
|
||||
|
||||
|
|
@ -194,6 +194,11 @@ namespace graphene { namespace chain {
|
|||
FC_ASSERT( max_bet_multiplier >= GRAPHENE_BETTING_MIN_MULTIPLIER &&
|
||||
max_bet_multiplier <= GRAPHENE_BETTING_MAX_MULTIPLIER );
|
||||
FC_ASSERT( min_bet_multiplier < max_bet_multiplier );
|
||||
FC_ASSERT( rake_fee_percentage >= TOURNAMENT_MINIMAL_RAKE_FEE_PERCENTAGE,
|
||||
"Rake fee percentage must not be less than ${min}", ("min",TOURNAMENT_MINIMAL_RAKE_FEE_PERCENTAGE));
|
||||
FC_ASSERT( rake_fee_percentage <= TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE,
|
||||
"Rake fee percentage must not be greater than ${max}", ("max", TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE));
|
||||
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
75
libraries/chain/protocol/tournament.cpp
Normal file
75
libraries/chain/protocol/tournament.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 <graphene/chain/protocol/tournament.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void tournament_options::validate() const
|
||||
{
|
||||
//FC_ASSERT( number_of_players >= 2 && (number_of_players & (number_of_players - 1)) == 0,
|
||||
// "Number of players must be a power of two" );
|
||||
}
|
||||
|
||||
share_type tournament_create_operation::calculate_fee(const fee_parameters_type& k)const
|
||||
{
|
||||
return k.fee + calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );
|
||||
}
|
||||
|
||||
void tournament_create_operation::validate()const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
options.validate();
|
||||
}
|
||||
|
||||
share_type tournament_join_operation::calculate_fee(const fee_parameters_type& k)const
|
||||
{
|
||||
return k.fee;
|
||||
}
|
||||
|
||||
void tournament_join_operation::validate()const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
share_type tournament_leave_operation::calculate_fee(const fee_parameters_type& k)const
|
||||
{
|
||||
return k.fee;
|
||||
}
|
||||
|
||||
void tournament_leave_operation::validate()const
|
||||
{
|
||||
// todo FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
|
||||
share_type game_move_operation::calculate_fee(const fee_parameters_type& k)const
|
||||
{
|
||||
return k.fee;
|
||||
}
|
||||
|
||||
void game_move_operation::validate()const
|
||||
{
|
||||
}
|
||||
|
||||
} } // namespace graphene::chain
|
||||
238
libraries/chain/tournament_evaluator.cpp
Normal file
238
libraries/chain/tournament_evaluator.cpp
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include <graphene/chain/tournament_evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result tournament_create_evaluator::do_evaluate( const tournament_create_operation& op )
|
||||
{ try {
|
||||
database& d = db();
|
||||
FC_ASSERT(op.options.registration_deadline >= d.head_block_time(), "Registration deadline has already passed");
|
||||
|
||||
const fc::time_point_sec maximum_registration_deadline = d.head_block_time() + d.get_global_properties().parameters.maximum_registration_deadline;
|
||||
FC_ASSERT(op.options.registration_deadline <= maximum_registration_deadline,
|
||||
"Registration deadline must be before ${maximum_registration_deadline}",
|
||||
("maximum_registration_deadline", maximum_registration_deadline));
|
||||
FC_ASSERT(op.options.buy_in.amount >= 0, "Tournament buy-in may not be negative");
|
||||
|
||||
FC_ASSERT(op.options.number_of_players > 1, "If you're going to play with yourself, do it off-chain");
|
||||
const uint32_t maximum_players_in_tournament = d.get_global_properties().parameters.maximum_players_in_tournament;
|
||||
FC_ASSERT(op.options.number_of_players <= maximum_players_in_tournament,
|
||||
"Tournaments may not have more than ${maximum_players_in_tournament} players",
|
||||
("maximum_players_in_tournament", maximum_players_in_tournament));
|
||||
|
||||
FC_ASSERT(op.options.whitelist.empty() ||
|
||||
op.options.whitelist.size() >= op.options.number_of_players,
|
||||
"Whitelist must allow enough players to fill the tournament");
|
||||
const uint32_t maximum_tournament_whitelist_length = d.get_global_properties().parameters.maximum_tournament_whitelist_length;
|
||||
FC_ASSERT(op.options.whitelist.size() < maximum_tournament_whitelist_length,
|
||||
"Whitelist must not be longer than ${maximum_tournament_whitelist_length}",
|
||||
("maximum_tournament_whitelist_length", maximum_tournament_whitelist_length));
|
||||
|
||||
for (const account_id_type& account_id : op.options.whitelist)
|
||||
{
|
||||
account_id(d);
|
||||
}
|
||||
|
||||
if (op.options.start_time)
|
||||
{
|
||||
FC_ASSERT(!op.options.start_delay, "Cannot specify both a fixed start time and a delay");
|
||||
FC_ASSERT(*op.options.start_time >= op.options.registration_deadline,
|
||||
"Cannot start before registration deadline expires");
|
||||
const uint32_t maximum_start_time_in_future = d.get_global_properties().parameters.maximum_tournament_start_time_in_future;
|
||||
FC_ASSERT((*op.options.start_time - d.head_block_time()).to_seconds() <= maximum_start_time_in_future,
|
||||
"Start time is too far in the future");
|
||||
}
|
||||
else if (op.options.start_delay)
|
||||
{
|
||||
FC_ASSERT(!op.options.start_time, "Cannot specify both a fixed start time and a delay");
|
||||
const uint32_t maximum_start_delay = d.get_global_properties().parameters.maximum_tournament_start_delay;
|
||||
FC_ASSERT(*op.options.start_delay < maximum_start_delay,
|
||||
"Start delay is too long");
|
||||
}
|
||||
else
|
||||
FC_THROW("Must specify either a fixed start time or a delay");
|
||||
|
||||
const uint32_t maximum_tournament_number_of_wins = d.get_global_properties().parameters.maximum_tournament_number_of_wins;
|
||||
FC_ASSERT(op.options.number_of_wins > 0,
|
||||
"Matches require positive number of wins");
|
||||
FC_ASSERT(op.options.number_of_wins <= maximum_tournament_number_of_wins,
|
||||
"Matches may not require more than ${number_of_wins} wins",
|
||||
("number_of_wins", maximum_tournament_number_of_wins));
|
||||
|
||||
// round_delay constraints
|
||||
const uint32_t minimum_round_delay = d.get_global_properties().parameters.min_round_delay;
|
||||
FC_ASSERT(op.options.round_delay >= minimum_round_delay,
|
||||
"Delay between games must not be less then ${min}",
|
||||
("min", minimum_round_delay));
|
||||
const uint32_t maximum_round_delay = d.get_global_properties().parameters.max_round_delay;
|
||||
FC_ASSERT(op.options.round_delay <= maximum_round_delay,
|
||||
"Delay between games must not be greater then ${max}",
|
||||
("max", maximum_round_delay));
|
||||
|
||||
const rock_paper_scissors_game_options& game_options = op.options.game_options.get<rock_paper_scissors_game_options>();
|
||||
|
||||
// time_per_commit_move constraints
|
||||
const uint32_t minimum_time_per_commit_move = d.get_global_properties().parameters.min_time_per_commit_move;
|
||||
FC_ASSERT(game_options.time_per_commit_move >= minimum_time_per_commit_move,
|
||||
"Time to commit the next move must not be less than ${min}",
|
||||
("min", minimum_time_per_commit_move));
|
||||
const uint32_t maximum_time_per_commit_move = d.get_global_properties().parameters.max_time_per_commit_move;
|
||||
FC_ASSERT(game_options.time_per_commit_move <= maximum_time_per_commit_move,
|
||||
"Time to commit the next move must not be greater than ${max}",
|
||||
("max", maximum_time_per_commit_move));
|
||||
|
||||
// time_per_commit_reveal constraints
|
||||
const uint32_t minimum_time_per_reveal_move = d.get_global_properties().parameters.min_time_per_reveal_move;
|
||||
FC_ASSERT(game_options.time_per_reveal_move >= minimum_time_per_reveal_move,
|
||||
"Time to reveal the move must not be less than ${min}",
|
||||
("min", minimum_time_per_reveal_move));
|
||||
const uint32_t maximum_time_per_reveal_move = d.get_global_properties().parameters.max_time_per_reveal_move;
|
||||
FC_ASSERT(game_options.time_per_reveal_move <= maximum_time_per_reveal_move,
|
||||
"Time to reveal the move must not be greater than ${max}",
|
||||
("max", maximum_time_per_reveal_move));
|
||||
|
||||
//cli-wallet supports 5 gesture games as well, but limit to 3 now as GUI wallet only supports 3 gesture games currently
|
||||
FC_ASSERT(game_options.number_of_gestures == 3,
|
||||
"GUI Wallet only supports 3 gestures currently");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type tournament_create_evaluator::do_apply( const tournament_create_operation& op )
|
||||
{ try {
|
||||
const tournament_details_object& tournament_details =
|
||||
db().create<tournament_details_object>( [&]( tournament_details_object& a ) {
|
||||
});
|
||||
|
||||
const tournament_object& new_tournament =
|
||||
db().create<tournament_object>( [&]( tournament_object& t ) {
|
||||
t.options = op.options;
|
||||
t.creator = op.creator;
|
||||
t.tournament_details_id = tournament_details.id;
|
||||
});
|
||||
|
||||
// TODO: look up how to do this in the initial create
|
||||
db().modify(tournament_details, [&]( tournament_details_object& a ) {
|
||||
a.tournament_id = new_tournament.id;
|
||||
});
|
||||
|
||||
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"Created tournament ${id} with details id ${details_id}",
|
||||
("id", new_tournament.id)("details_id", tournament_details.id));
|
||||
return new_tournament.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result tournament_join_evaluator::do_evaluate( const tournament_join_operation& op )
|
||||
{ try {
|
||||
const database& d = db();
|
||||
_tournament_obj = &op.tournament_id(d);
|
||||
fc_ilog(fc::logger::get("tournament"), "details_id = ${id}",("id", _tournament_obj->tournament_details_id));
|
||||
_tournament_details_obj = &_tournament_obj->tournament_details_id(d);
|
||||
_payer_account = &op.payer_account_id(d);
|
||||
const account_object& player_account = op.player_account_id(d);
|
||||
//const account_object& player_account = op.player_account_id(d);
|
||||
_buy_in_asset_type = &op.buy_in.asset_id(d);
|
||||
|
||||
FC_ASSERT(_tournament_obj->get_state() == tournament_state::accepting_registrations,
|
||||
"Can only join a tournament during registration period");
|
||||
FC_ASSERT(_tournament_details_obj->registered_players.size() < _tournament_obj->options.number_of_players,
|
||||
"Tournament is already full");
|
||||
FC_ASSERT(d.head_block_time() <= _tournament_obj->options.registration_deadline,
|
||||
"Registration deadline has already passed");
|
||||
|
||||
FC_ASSERT(_tournament_obj->options.whitelist.empty() ||
|
||||
_tournament_obj->options.whitelist.find(op.player_account_id) != _tournament_obj->options.whitelist.end(),
|
||||
"Player is not on the whitelist for this tournament");
|
||||
|
||||
FC_ASSERT(_tournament_details_obj->registered_players.find(op.player_account_id) == _tournament_details_obj->registered_players.end(),
|
||||
"Player is already registered for this tournament");
|
||||
FC_ASSERT(op.buy_in == _tournament_obj->options.buy_in, "Buy-in is incorrect");
|
||||
|
||||
GRAPHENE_ASSERT(!_buy_in_asset_type->is_transfer_restricted(),
|
||||
transfer_restricted_transfer_asset,
|
||||
"Asset {asset} has transfer_restricted flag enabled",
|
||||
("asset", op.buy_in.asset_id));
|
||||
|
||||
GRAPHENE_ASSERT(is_authorized_asset(d, player_account, *_buy_in_asset_type),
|
||||
transfer_from_account_not_whitelisted,
|
||||
"player account ${player} is not whitelisted for asset ${asset}",
|
||||
("player", op.player_account_id)
|
||||
("asset", op.buy_in.asset_id));
|
||||
|
||||
GRAPHENE_ASSERT(is_authorized_asset(d, *_payer_account, *_buy_in_asset_type),
|
||||
transfer_from_account_not_whitelisted,
|
||||
"payer account ${payer} is not whitelisted for asset ${asset}",
|
||||
("payer", op.payer_account_id)
|
||||
("asset", op.buy_in.asset_id));
|
||||
|
||||
bool sufficient_balance = d.get_balance(*_payer_account, *_buy_in_asset_type).amount >= op.buy_in.amount;
|
||||
FC_ASSERT(sufficient_balance,
|
||||
"Insufficient Balance: paying account '${payer}' has insufficient balance to pay buy-in of ${buy_in} (balance is ${balance})",
|
||||
("payer", _payer_account->name)
|
||||
("buy_in", d.to_pretty_string(op.buy_in))
|
||||
("balance",d.to_pretty_string(d.get_balance(*_payer_account, *_buy_in_asset_type))));
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result tournament_join_evaluator::do_apply( const tournament_join_operation& op )
|
||||
{ try {
|
||||
db().modify(*_tournament_obj, [&](tournament_object& tournament_obj){
|
||||
tournament_obj.on_player_registered(db(), op.payer_account_id, op.player_account_id);
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result tournament_leave_evaluator::do_evaluate( const tournament_leave_operation& op )
|
||||
{
|
||||
try {
|
||||
const database& d = db();
|
||||
_tournament_obj = &op.tournament_id(d);
|
||||
fc_ilog(fc::logger::get("tournament"), "details_id = ${id}",("id", _tournament_obj->tournament_details_id));
|
||||
|
||||
_tournament_details_obj = &_tournament_obj->tournament_details_id(d);
|
||||
FC_ASSERT(_tournament_details_obj->registered_players.find(op.player_account_id) != _tournament_details_obj->registered_players.end(),
|
||||
"Player is not registered for this tournament");
|
||||
|
||||
FC_ASSERT(op.canceling_account_id == op.player_account_id ||
|
||||
op.canceling_account_id == _tournament_details_obj->players_payers.at(op.player_account_id),
|
||||
"Only player or payer can unregister the player from a tournament");
|
||||
|
||||
FC_ASSERT(_tournament_obj->get_state() == tournament_state::accepting_registrations,
|
||||
"Can only leave a tournament during registration period");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result tournament_leave_evaluator::do_apply( const tournament_leave_operation& op )
|
||||
{ try {
|
||||
db().modify(*_tournament_obj, [&](tournament_object& tournament_obj){
|
||||
tournament_obj.on_player_unregistered(db(), op.player_account_id);
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result game_move_evaluator::do_evaluate( const game_move_operation& o )
|
||||
{ try {
|
||||
const database& d = db();
|
||||
_game_obj = &o.game_id(d);
|
||||
_game_obj->evaluate_move_operation(d, o);
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
||||
void_result game_move_evaluator::do_apply( const game_move_operation& o )
|
||||
{ try {
|
||||
db().modify(*_game_obj, [&](game_object& game_obj){
|
||||
game_obj.on_move(db(), o);
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
} }
|
||||
|
||||
|
||||
755
libraries/chain/tournament_object.cpp
Normal file
755
libraries/chain/tournament_object.cpp
Normal file
|
|
@ -0,0 +1,755 @@
|
|||
/*
|
||||
* 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 <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/archive/binary_oarchive.hpp>
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/msm/back/tools.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Events
|
||||
struct player_registered
|
||||
{
|
||||
database& db;
|
||||
account_id_type payer_id;
|
||||
account_id_type player_id;
|
||||
player_registered(database& db, account_id_type payer_id, account_id_type player_id) :
|
||||
db(db), payer_id(payer_id), player_id(player_id)
|
||||
{}
|
||||
};
|
||||
struct player_unregistered
|
||||
{
|
||||
database& db;
|
||||
account_id_type player_id;
|
||||
player_unregistered(database& db, account_id_type player_id) :
|
||||
db(db), player_id(player_id)
|
||||
{}
|
||||
};
|
||||
struct registration_deadline_passed
|
||||
{
|
||||
database& db;
|
||||
registration_deadline_passed(database& db) : db(db) {};
|
||||
};
|
||||
struct start_time_arrived
|
||||
{
|
||||
database& db;
|
||||
start_time_arrived(database& db) : db(db) {};
|
||||
};
|
||||
|
||||
struct match_completed
|
||||
{
|
||||
database& db;
|
||||
const match_object& match;
|
||||
match_completed(database& db, const match_object& match) : db(db), match(match) {}
|
||||
};
|
||||
|
||||
struct tournament_state_machine_ : public msm::front::state_machine_def<tournament_state_machine_>
|
||||
{
|
||||
// disable a few state machine features we don't use for performance
|
||||
typedef int no_exception_thrown;
|
||||
typedef int no_message_queue;
|
||||
|
||||
// States
|
||||
struct accepting_registrations : public msm::front::state<>{};
|
||||
struct awaiting_start : public msm::front::state<>
|
||||
{
|
||||
void on_entry(const player_registered& event, tournament_state_machine_& fsm)
|
||||
{
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"Tournament ${id} now has enough players registered to begin",
|
||||
("id", fsm.tournament_obj->id));
|
||||
if (fsm.tournament_obj->options.start_time)
|
||||
fsm.tournament_obj->start_time = fsm.tournament_obj->options.start_time;
|
||||
else
|
||||
fsm.tournament_obj->start_time = event.db.head_block_time() + fc::seconds(*fsm.tournament_obj->options.start_delay);
|
||||
}
|
||||
};
|
||||
struct in_progress : public msm::front::state<>
|
||||
{
|
||||
// reverse the bits in an integer
|
||||
static uint32_t reverse_bits(uint32_t x)
|
||||
{
|
||||
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
|
||||
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
|
||||
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
|
||||
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
|
||||
return ((x >> 16) | (x << 16));
|
||||
}
|
||||
|
||||
match_id_type create_match(database& db, tournament_id_type tournament_id,
|
||||
const vector<account_id_type>& players)
|
||||
{
|
||||
const match_object& match =
|
||||
db.create<match_object>( [&]( match_object& match ) {
|
||||
match.tournament_id = tournament_id;
|
||||
match.players = players;
|
||||
match.number_of_wins.resize(match.players.size());
|
||||
match.start_time = db.head_block_time();
|
||||
if (match.players.size() == 1)
|
||||
{
|
||||
// this is a bye
|
||||
match.end_time = db.head_block_time();
|
||||
}
|
||||
});
|
||||
return match.id;
|
||||
}
|
||||
|
||||
void on_entry(const start_time_arrived& event, tournament_state_machine_& fsm)
|
||||
{
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"Tournament ${id} is beginning",
|
||||
("id", fsm.tournament_obj->id));
|
||||
const tournament_details_object& tournament_details_obj = fsm.tournament_obj->tournament_details_id(event.db);
|
||||
|
||||
// Create the "seeding" order for the tournament as a random shuffle of the players.
|
||||
//
|
||||
// If this were a game of skill where players were ranked, this algorithm expects the
|
||||
// most skilled players to the front of the list
|
||||
vector<account_id_type> seeded_players(tournament_details_obj.registered_players.begin(),
|
||||
tournament_details_obj.registered_players.end());
|
||||
for (unsigned i = seeded_players.size() - 1; i >= 1; --i)
|
||||
{
|
||||
unsigned j = (unsigned)event.db.get_random_bits(i + 1);
|
||||
std::swap(seeded_players[i], seeded_players[j]);
|
||||
}
|
||||
|
||||
// Create all matches in the tournament now.
|
||||
// If the number of players isn't a power of two, we will compensate with byes
|
||||
// in the first round.
|
||||
const uint32_t num_players = fsm.tournament_obj->options.number_of_players;
|
||||
uint32_t num_rounds = boost::multiprecision::detail::find_msb(num_players - 1) + 1;
|
||||
uint32_t num_matches = (1 << num_rounds) - 1;
|
||||
uint32_t num_matches_in_first_round = 1 << (num_rounds - 1);
|
||||
|
||||
// First, assign players to their first round of matches in the paired_players
|
||||
// array, where the first two play against each other, the second two play against
|
||||
// each other, etc.
|
||||
// Anyone with account_id_type() as their opponent gets a bye
|
||||
vector<account_id_type> paired_players;
|
||||
paired_players.resize(num_matches_in_first_round * 2);
|
||||
for (uint32_t player_num = 0; player_num < num_players; ++player_num)
|
||||
{
|
||||
uint32_t player_position = reverse_bits(player_num ^ (player_num >> 1)) >> (32 - num_rounds);
|
||||
paired_players[player_position] = seeded_players[player_num];
|
||||
}
|
||||
|
||||
// now create the match objects for this first round
|
||||
vector<match_id_type> matches;
|
||||
matches.reserve(num_matches);
|
||||
|
||||
// create a bunch of empty matches
|
||||
for (unsigned i = 0; i < num_matches; ++i)
|
||||
matches.emplace_back(create_match(event.db, fsm.tournament_obj->id, vector<account_id_type>()));
|
||||
|
||||
// then walk through our paired players by twos, starting the first matches
|
||||
for (unsigned i = 0; i < num_matches_in_first_round; ++i)
|
||||
{
|
||||
vector<account_id_type> players;
|
||||
players.emplace_back(paired_players[2 * i]);
|
||||
if (paired_players[2 * i + 1] != account_id_type())
|
||||
players.emplace_back(paired_players[2 * i + 1]);
|
||||
event.db.modify(matches[i](event.db), [&](match_object& match) {
|
||||
match.players = players;
|
||||
match.on_initiate_match(event.db);
|
||||
});
|
||||
}
|
||||
event.db.modify(tournament_details_obj, [&](tournament_details_object& tournament_details_obj){
|
||||
tournament_details_obj.matches = matches;
|
||||
});
|
||||
|
||||
// find "bye" matches, complete missing player in the next match
|
||||
for (unsigned i = 0; i < num_matches_in_first_round; ++i)
|
||||
{
|
||||
const match_object& match = matches[i](event.db);
|
||||
if (match.players.size() == 1) // is "bye"
|
||||
{
|
||||
unsigned tournament_num_matches = tournament_details_obj.matches.size();
|
||||
unsigned next_round_match_index = (i + tournament_num_matches + 1) / 2;
|
||||
assert(next_round_match_index < tournament_num_matches);
|
||||
const match_object& next_round_match = tournament_details_obj.matches[next_round_match_index](event.db);
|
||||
event.db.modify(next_round_match, [&](match_object& next_match) {
|
||||
next_match.players.emplace_back(match.players[0]);
|
||||
if (next_match.players.size() > 1) // both previous matches were "bye"
|
||||
next_match.on_initiate_match(event.db);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
void on_entry(const match_completed& event, tournament_state_machine_& fsm)
|
||||
{
|
||||
tournament_object& tournament = *fsm.tournament_obj;
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"Match ${match_id} in tournament tournament ${tournament_id} is still in progress",
|
||||
("match_id", event.match.id)("tournament_id", tournament.id));
|
||||
|
||||
// this wasn't the final match that just finished, so figure out if we can start the next match.
|
||||
// The next match can start if both this match and the previous match have completed
|
||||
const tournament_details_object& tournament_details_obj = fsm.tournament_obj->tournament_details_id(event.db);
|
||||
unsigned num_matches = tournament_details_obj.matches.size();
|
||||
auto this_match_iter = std::find(tournament_details_obj.matches.begin(), tournament_details_obj.matches.end(), event.match.id);
|
||||
assert(this_match_iter != tournament_details_obj.matches.end());
|
||||
unsigned this_match_index = std::distance(tournament_details_obj.matches.begin(), this_match_iter);
|
||||
// TODO: we currently create all matches at startup, so they are numbered sequentially. We could get the index
|
||||
// by subtracting match.id as long as this behavior doesn't change
|
||||
|
||||
unsigned next_round_match_index = (this_match_index + num_matches + 1) / 2;
|
||||
assert(next_round_match_index < num_matches);
|
||||
const match_object& next_round_match = tournament_details_obj.matches[next_round_match_index](event.db);
|
||||
|
||||
// each match will have two players, match.players[0] and match.players[1].
|
||||
// for consistency, we want to feed the winner of this match into the correct
|
||||
// slot in the next match
|
||||
unsigned winner_index_in_next_match = (this_match_index + num_matches + 1) % 2;
|
||||
unsigned other_match_index = num_matches - ((num_matches - next_round_match_index) * 2 + winner_index_in_next_match);
|
||||
const match_object& other_match = tournament_details_obj.matches[other_match_index](event.db);
|
||||
|
||||
// the winners of the matches event.match and other_match will play in next_round_match
|
||||
|
||||
assert(event.match.match_winners.size() <= 1);
|
||||
|
||||
event.db.modify(next_round_match, [&](match_object& next_match_obj) {
|
||||
|
||||
if (!event.match.match_winners.empty()) // if there is a winner
|
||||
{
|
||||
if (winner_index_in_next_match == 0)
|
||||
next_match_obj.players.insert(next_match_obj.players.begin(), *event.match.match_winners.begin());
|
||||
else
|
||||
next_match_obj.players.push_back(*event.match.match_winners.begin());
|
||||
}
|
||||
|
||||
if (other_match.get_state() == match_state::match_complete)
|
||||
{
|
||||
next_match_obj.on_initiate_match(event.db);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
struct registration_period_expired : public msm::front::state<>
|
||||
{
|
||||
void on_entry(const registration_deadline_passed& event, tournament_state_machine_& fsm)
|
||||
{
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"Tournament ${id} is canceled",
|
||||
("id", fsm.tournament_obj->id));
|
||||
// repay everyone who paid into the prize pool
|
||||
const tournament_details_object& details = fsm.tournament_obj->tournament_details_id(event.db);
|
||||
for (const auto& payer_pair : details.payers)
|
||||
{
|
||||
// TODO: create a virtual operation to record the refund
|
||||
// we'll think of this as just releasing an asset that the user had locked up
|
||||
// for a period of time, not as a transfer back to the user; it doesn't matter
|
||||
// if they are currently authorized to transfer this asset, they never really
|
||||
// transferred it in the first place
|
||||
asset amount(payer_pair.second, fsm.tournament_obj->options.buy_in.asset_id);
|
||||
event.db.adjust_balance(payer_pair.first, amount);
|
||||
|
||||
// Generating a virtual operation that shows the payment
|
||||
tournament_payout_operation op;
|
||||
op.tournament_id = fsm.tournament_obj->id;
|
||||
op.payout_amount = amount;
|
||||
op.payout_account_id = payer_pair.first;
|
||||
op.type = payout_type::buyin_refund;
|
||||
event.db.push_applied_operation(op);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct concluded : public msm::front::state<>
|
||||
{
|
||||
void on_entry(const match_completed& event, tournament_state_machine_& fsm)
|
||||
{
|
||||
tournament_object& tournament_obj = *fsm.tournament_obj;
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"Tournament ${id} is complete",
|
||||
("id", tournament_obj.id));
|
||||
tournament_obj.end_time = event.db.head_block_time();
|
||||
|
||||
// Distribute prize money when a tournament ends
|
||||
#ifndef NDEBUG
|
||||
const tournament_details_object& details = tournament_obj.tournament_details_id(event.db);
|
||||
share_type total_prize = 0;
|
||||
for (const auto& payer_pair : details.payers)
|
||||
{
|
||||
total_prize += payer_pair.second;
|
||||
}
|
||||
assert(total_prize == tournament_obj.prize_pool);
|
||||
#endif
|
||||
assert(event.match.match_winners.size() == 1);
|
||||
const account_id_type& winner = *event.match.match_winners.begin();
|
||||
uint16_t rake_fee_percentage = event.db.get_global_properties().parameters.rake_fee_percentage;
|
||||
share_type rake_amount = 0;
|
||||
|
||||
const asset_object & asset_obj = tournament_obj.options.buy_in.asset_id(event.db);
|
||||
optional<asset_dividend_data_id_type> dividend_id = asset_obj.dividend_data_id;
|
||||
|
||||
if (dividend_id.valid())
|
||||
{
|
||||
rake_amount = (fc::uint128_t(tournament_obj.prize_pool.value) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100).to_uint64();
|
||||
}
|
||||
asset won_prize(tournament_obj.prize_pool - rake_amount, tournament_obj.options.buy_in.asset_id);
|
||||
tournament_payout_operation op;
|
||||
|
||||
if (won_prize.amount.value)
|
||||
{
|
||||
// Adjusting balance of winner
|
||||
event.db.adjust_balance(winner, won_prize);
|
||||
|
||||
// Generating a virtual operation that shows the payment
|
||||
op.tournament_id = tournament_obj.id;
|
||||
op.payout_amount = won_prize;
|
||||
op.payout_account_id = winner;
|
||||
op.type = payout_type::prize_award;
|
||||
event.db.push_applied_operation(op);
|
||||
}
|
||||
|
||||
if (dividend_id.valid() && rake_amount.value)
|
||||
{
|
||||
// Adjusting balance of dividend_distribution_account
|
||||
const asset_dividend_data_id_type& asset_dividend_data_id_= *dividend_id;
|
||||
const asset_dividend_data_object& dividend_obj = asset_dividend_data_id_(event.db);
|
||||
const account_id_type& rake_account_id = dividend_obj.dividend_distribution_account;
|
||||
asset rake(rake_amount, tournament_obj.options.buy_in.asset_id);
|
||||
event.db.adjust_balance(rake_account_id, rake);
|
||||
|
||||
// Generating a virtual operation that shows the payment
|
||||
op.payout_amount = rake;
|
||||
op.payout_account_id = rake_account_id;
|
||||
op.type = payout_type::rake_fee;
|
||||
event.db.push_applied_operation(op);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef accepting_registrations initial_state;
|
||||
|
||||
typedef tournament_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
// Guards
|
||||
bool will_be_fully_registered(const player_registered& event)
|
||||
{
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"In will_be_fully_registered guard, returning ${value}",
|
||||
("value", tournament_obj->registered_players == tournament_obj->options.number_of_players - 1));
|
||||
return tournament_obj->registered_players == tournament_obj->options.number_of_players - 1;
|
||||
}
|
||||
|
||||
bool was_final_match(const match_completed& event)
|
||||
{
|
||||
const tournament_details_object& tournament_details_obj = tournament_obj->tournament_details_id(event.db);
|
||||
auto final_match_id = tournament_details_obj.matches[tournament_details_obj.matches.size() - 1];
|
||||
bool was_final = event.match.id == final_match_id;
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"In was_final_match guard, returning ${value}",
|
||||
("value", was_final));
|
||||
return was_final;
|
||||
}
|
||||
|
||||
void register_player(const player_registered& event)
|
||||
{
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"In register_player action, player_id is ${player_id}, payer_id is ${payer_id}",
|
||||
("player_id", event.player_id)("payer_id", event.payer_id));
|
||||
|
||||
event.db.adjust_balance(event.payer_id, -tournament_obj->options.buy_in);
|
||||
const tournament_details_object& tournament_details_obj = tournament_obj->tournament_details_id(event.db);
|
||||
event.db.modify(tournament_details_obj, [&](tournament_details_object& tournament_details_obj){
|
||||
tournament_details_obj.payers[event.payer_id] += tournament_obj->options.buy_in.amount;
|
||||
tournament_details_obj.registered_players.insert(event.player_id);
|
||||
tournament_details_obj.players_payers[event.player_id] = event.payer_id;
|
||||
});
|
||||
++tournament_obj->registered_players;
|
||||
tournament_obj->prize_pool += tournament_obj->options.buy_in.amount;
|
||||
}
|
||||
|
||||
void unregister_player(const player_unregistered& event)
|
||||
{
|
||||
fc_ilog(fc::logger::get("tournament"),
|
||||
"In unregister_player action, player_id is ${player_id}",
|
||||
("player_id", event.player_id));
|
||||
|
||||
const tournament_details_object& tournament_details_obj = tournament_obj->tournament_details_id(event.db);
|
||||
account_id_type payer_id = tournament_details_obj.players_payers.at(event.player_id);
|
||||
event.db.adjust_balance(payer_id, tournament_obj->options.buy_in);
|
||||
event.db.modify(tournament_details_obj, [&](tournament_details_object& tournament_details_obj){
|
||||
tournament_details_obj.payers[payer_id] -= tournament_obj->options.buy_in.amount;
|
||||
if (tournament_details_obj.payers[payer_id] <= 0)
|
||||
tournament_details_obj.payers.erase(payer_id);
|
||||
tournament_details_obj.registered_players.erase(event.player_id);
|
||||
tournament_details_obj.players_payers.erase(event.player_id);
|
||||
});
|
||||
--tournament_obj->registered_players;
|
||||
tournament_obj->prize_pool -= tournament_obj->options.buy_in.amount;
|
||||
}
|
||||
|
||||
// Transition table for tournament
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
a_row < accepting_registrations, player_registered, accepting_registrations, &x::register_player >,
|
||||
a_row < accepting_registrations, player_unregistered, accepting_registrations, &x::unregister_player >,
|
||||
row < accepting_registrations, player_registered, awaiting_start, &x::register_player, &x::will_be_fully_registered >,
|
||||
_row < accepting_registrations, registration_deadline_passed, registration_period_expired >,
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
a_row < awaiting_start, player_unregistered, accepting_registrations, &x::unregister_player >,
|
||||
_row < awaiting_start, start_time_arrived, in_progress >,
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < in_progress, match_completed, in_progress >,
|
||||
g_row < in_progress, match_completed, concluded, &x::was_final_match >
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
> {};
|
||||
|
||||
|
||||
tournament_object* tournament_obj;
|
||||
tournament_state_machine_(tournament_object* tournament_obj) : tournament_obj(tournament_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<tournament_state_machine_> tournament_state_machine;
|
||||
}
|
||||
|
||||
class tournament_object::impl {
|
||||
public:
|
||||
tournament_state_machine state_machine;
|
||||
|
||||
impl(tournament_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
tournament_object::tournament_object() :
|
||||
my(new impl(this))
|
||||
{
|
||||
}
|
||||
|
||||
tournament_object::tournament_object(const tournament_object& rhs) :
|
||||
graphene::db::abstract_object<tournament_object>(rhs),
|
||||
creator(rhs.creator),
|
||||
options(rhs.options),
|
||||
start_time(rhs.start_time),
|
||||
end_time(rhs.end_time),
|
||||
prize_pool(rhs.prize_pool),
|
||||
registered_players(rhs.registered_players),
|
||||
tournament_details_id(rhs.tournament_details_id),
|
||||
my(new impl(this))
|
||||
{
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.tournament_obj = this;
|
||||
}
|
||||
|
||||
tournament_object& tournament_object::operator=(const tournament_object& rhs)
|
||||
{
|
||||
//graphene::db::abstract_object<tournament_object>::operator=(rhs);
|
||||
id = rhs.id;
|
||||
creator = rhs.creator;
|
||||
options = rhs.options;
|
||||
start_time = rhs.start_time;
|
||||
end_time = rhs.end_time;
|
||||
prize_pool = rhs.prize_pool;
|
||||
registered_players = rhs.registered_players;
|
||||
tournament_details_id = rhs.tournament_details_id;
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.tournament_obj = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
tournament_object::~tournament_object()
|
||||
{
|
||||
}
|
||||
|
||||
bool verify_tournament_state_constants()
|
||||
{
|
||||
unsigned error_count = 0;
|
||||
typedef msm::back::generate_state_set<tournament_state_machine::stt>::type all_states;
|
||||
static char const* filled_state_names[mpl::size<all_states>::value];
|
||||
mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >
|
||||
(msm::back::fill_state_names<tournament_state_machine::stt>(filled_state_names));
|
||||
for (unsigned i = 0; i < mpl::size<all_states>::value; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
|
||||
// at least contain the string we're looking for
|
||||
const char* fc_reflected_value_name = fc::reflector<tournament_state>::to_string((tournament_state)i);
|
||||
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
|
||||
fc_elog(fc::logger::get("tournament"),
|
||||
"Error, state string mismatch between fc and boost::msm for int value ${int_value}: boost::msm -> ${boost_string}, fc::reflect -> ${fc_string}",
|
||||
("int_value", i)("boost_string", filled_state_names[i])("fc_string", fc_reflected_value_name));
|
||||
}
|
||||
catch (const fc::bad_cast_exception&)
|
||||
{
|
||||
fc_elog(fc::logger::get("tournament"),
|
||||
"Error, no reflection for value ${int_value} in enum tournament_state",
|
||||
("int_value", i));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
|
||||
return error_count == 0;
|
||||
}
|
||||
|
||||
tournament_state tournament_object::get_state() const
|
||||
{
|
||||
static bool state_constants_are_correct = verify_tournament_state_constants();
|
||||
(void)&state_constants_are_correct;
|
||||
tournament_state state = (tournament_state)my->state_machine.current_state()[0];
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void tournament_object::pack_impl(std::ostream& stream) const
|
||||
{
|
||||
boost::archive::binary_oarchive oa(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
oa << my->state_machine;
|
||||
}
|
||||
|
||||
void tournament_object::unpack_impl(std::istream& stream)
|
||||
{
|
||||
boost::archive::binary_iarchive ia(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
ia >> my->state_machine;
|
||||
}
|
||||
|
||||
void tournament_object::on_registration_deadline_passed(database& db)
|
||||
{
|
||||
my->state_machine.process_event(registration_deadline_passed(db));
|
||||
}
|
||||
|
||||
void tournament_object::on_player_registered(database& db, account_id_type payer_id, account_id_type player_id)
|
||||
{
|
||||
my->state_machine.process_event(player_registered(db, payer_id, player_id));
|
||||
}
|
||||
|
||||
void tournament_object::on_player_unregistered(database& db, account_id_type player_id)
|
||||
{
|
||||
my->state_machine.process_event(player_unregistered(db, player_id));
|
||||
}
|
||||
|
||||
void tournament_object::on_start_time_arrived(database& db)
|
||||
{
|
||||
my->state_machine.process_event(start_time_arrived(db));
|
||||
}
|
||||
|
||||
void tournament_object::on_match_completed(database& db, const match_object& match)
|
||||
{
|
||||
my->state_machine.process_event(match_completed(db, match));
|
||||
}
|
||||
|
||||
void tournament_object::check_for_new_matches_to_start(database& db) const
|
||||
{
|
||||
const tournament_details_object& tournament_details_obj = tournament_details_id(db);
|
||||
|
||||
unsigned num_matches = tournament_details_obj.matches.size();
|
||||
uint32_t num_rounds = boost::multiprecision::detail::find_msb(num_matches + 1);
|
||||
|
||||
// Scan the matches by round to find the last round where all matches are complete
|
||||
int last_complete_round = -1;
|
||||
bool first_incomplete_match_was_waiting = false;
|
||||
for (unsigned round_num = 0; round_num < num_rounds; ++round_num)
|
||||
{
|
||||
uint32_t num_matches_in_this_round = 1 << (num_rounds - round_num - 1);
|
||||
uint32_t first_match_in_round = (num_matches - (num_matches >> round_num));
|
||||
bool all_matches_in_round_complete = true;
|
||||
for (uint32_t match_num = first_match_in_round; match_num < first_match_in_round + num_matches_in_this_round; ++match_num)
|
||||
{
|
||||
const match_object& match = tournament_details_obj.matches[match_num](db);
|
||||
if (match.get_state() != match_state::match_complete)
|
||||
{
|
||||
first_incomplete_match_was_waiting = match.get_state() == match_state::waiting_on_previous_matches;
|
||||
all_matches_in_round_complete = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_matches_in_round_complete)
|
||||
last_complete_round = round_num;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (last_complete_round == -1)
|
||||
return;
|
||||
|
||||
// We shouldn't be here if the final match is complete
|
||||
assert(last_complete_round != num_rounds - 1);
|
||||
if (last_complete_round == num_rounds - 1)
|
||||
return;
|
||||
|
||||
if (first_incomplete_match_was_waiting)
|
||||
{
|
||||
// all previous matches have completed, and the first match in this round hasn't been
|
||||
// started (which means none of the matches in this round should have started)
|
||||
unsigned first_incomplete_round = last_complete_round + 1;
|
||||
uint32_t num_matches_in_incomplete_round = 1 << (num_rounds - first_incomplete_round - 1);
|
||||
uint32_t first_match_in_incomplete_round = num_matches - (num_matches >> first_incomplete_round);
|
||||
for (uint32_t match_num = first_match_in_incomplete_round;
|
||||
match_num < first_match_in_incomplete_round + num_matches_in_incomplete_round;
|
||||
++match_num)
|
||||
{
|
||||
int left_child_index = (num_matches - 1) - ((num_matches - 1 - match_num) * 2 + 2);
|
||||
int right_child_index = left_child_index + 1;
|
||||
const match_object& match_to_start = tournament_details_obj.matches[left_child_index](db);
|
||||
const match_object& left_match = tournament_details_obj.matches[left_child_index](db);
|
||||
const match_object& right_match = tournament_details_obj.matches[right_child_index](db);
|
||||
std::vector<account_id_type> winners;
|
||||
if (!left_match.match_winners.empty())
|
||||
{
|
||||
assert(left_match.match_winners.size() == 1);
|
||||
winners.emplace_back(*left_match.match_winners.begin());
|
||||
}
|
||||
if (!right_match.match_winners.empty())
|
||||
{
|
||||
assert(right_match.match_winners.size() == 1);
|
||||
winners.emplace_back(*right_match.match_winners.begin());
|
||||
}
|
||||
db.modify(match_to_start, [&](match_object& match) {
|
||||
match.players = winners;
|
||||
//match.state = ready_to_begin;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fc::sha256 rock_paper_scissors_throw::calculate_hash() const
|
||||
{
|
||||
std::vector<char> full_throw_packed(fc::raw::pack(*this));
|
||||
return fc::sha256::hash(full_throw_packed.data(), full_throw_packed.size());
|
||||
}
|
||||
|
||||
|
||||
vector<tournament_id_type> tournament_players_index::get_registered_tournaments_for_account( const account_id_type& a )const
|
||||
{
|
||||
auto iter = account_to_joined_tournaments.find(a);
|
||||
if (iter != account_to_joined_tournaments.end())
|
||||
return vector<tournament_id_type>(iter->second.begin(), iter->second.end());
|
||||
return vector<tournament_id_type>();
|
||||
}
|
||||
|
||||
void tournament_players_index::object_inserted(const object& obj)
|
||||
{
|
||||
assert( dynamic_cast<const tournament_details_object*>(&obj) ); // for debug only
|
||||
const tournament_details_object& details = static_cast<const tournament_details_object&>(obj);
|
||||
|
||||
for (const account_id_type& account_id : details.registered_players)
|
||||
account_to_joined_tournaments[account_id].insert(details.tournament_id);
|
||||
}
|
||||
|
||||
void tournament_players_index::object_removed(const object& obj)
|
||||
{
|
||||
assert( dynamic_cast<const tournament_details_object*>(&obj) ); // for debug only
|
||||
const tournament_details_object& details = static_cast<const tournament_details_object&>(obj);
|
||||
|
||||
for (const account_id_type& account_id : details.registered_players)
|
||||
{
|
||||
auto iter = account_to_joined_tournaments.find(account_id);
|
||||
if (iter != account_to_joined_tournaments.end())
|
||||
iter->second.erase(details.tournament_id);
|
||||
}
|
||||
}
|
||||
|
||||
void tournament_players_index::about_to_modify(const object& before)
|
||||
{
|
||||
assert( dynamic_cast<const tournament_details_object*>(&before) ); // for debug only
|
||||
const tournament_details_object& details = static_cast<const tournament_details_object&>(before);
|
||||
before_account_ids = details.registered_players;
|
||||
}
|
||||
|
||||
void tournament_players_index::object_modified(const object& after)
|
||||
{
|
||||
assert( dynamic_cast<const tournament_details_object*>(&after) ); // for debug only
|
||||
const tournament_details_object& details = static_cast<const tournament_details_object&>(after);
|
||||
|
||||
{
|
||||
vector<account_id_type> newly_registered_players(details.registered_players.size());
|
||||
auto end_iter = std::set_difference(details.registered_players.begin(), details.registered_players.end(),
|
||||
before_account_ids.begin(), before_account_ids.end(),
|
||||
newly_registered_players.begin());
|
||||
newly_registered_players.resize(end_iter - newly_registered_players.begin());
|
||||
for (const account_id_type& account_id : newly_registered_players)
|
||||
account_to_joined_tournaments[account_id].insert(details.tournament_id);
|
||||
}
|
||||
|
||||
{
|
||||
vector<account_id_type> newly_unregistered_players(before_account_ids.size());
|
||||
auto end_iter = std::set_difference(before_account_ids.begin(), before_account_ids.end(),
|
||||
details.registered_players.begin(), details.registered_players.end(),
|
||||
newly_unregistered_players.begin());
|
||||
newly_unregistered_players.resize(end_iter - newly_unregistered_players.begin());
|
||||
for (const account_id_type& account_id : newly_unregistered_players)
|
||||
{
|
||||
auto iter = account_to_joined_tournaments.find(account_id);
|
||||
if (iter != account_to_joined_tournaments.end())
|
||||
iter->second.erase(details.tournament_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc {
|
||||
// Manually reflect tournament_object to variant to properly reflect "state"
|
||||
void to_variant(const graphene::chain::tournament_object& tournament_obj, fc::variant& v)
|
||||
{
|
||||
fc_elog(fc::logger::get("tournament"), "In tournament_obj to_variant");
|
||||
elog("In tournament_obj to_variant");
|
||||
fc::mutable_variant_object o;
|
||||
o("id", tournament_obj.id)
|
||||
("creator", tournament_obj.creator)
|
||||
("options", tournament_obj.options)
|
||||
("start_time", tournament_obj.start_time)
|
||||
("end_time", tournament_obj.end_time)
|
||||
("prize_pool", tournament_obj.prize_pool)
|
||||
("registered_players", tournament_obj.registered_players)
|
||||
("tournament_details_id", tournament_obj.tournament_details_id)
|
||||
("state", tournament_obj.get_state());
|
||||
|
||||
v = o;
|
||||
}
|
||||
|
||||
// Manually reflect tournament_object to variant to properly reflect "state"
|
||||
void from_variant(const fc::variant& v, graphene::chain::tournament_object& tournament_obj)
|
||||
{
|
||||
fc_elog(fc::logger::get("tournament"), "In tournament_obj from_variant");
|
||||
tournament_obj.id = v["id"].as<graphene::chain::tournament_id_type>();
|
||||
tournament_obj.creator = v["creator"].as<graphene::chain::account_id_type>();
|
||||
tournament_obj.options = v["options"].as<graphene::chain::tournament_options>();
|
||||
tournament_obj.start_time = v["start_time"].as<optional<time_point_sec> >();
|
||||
tournament_obj.end_time = v["end_time"].as<optional<time_point_sec> >();
|
||||
tournament_obj.prize_pool = v["prize_pool"].as<graphene::chain::share_type>();
|
||||
tournament_obj.registered_players = v["registered_players"].as<uint32_t>();
|
||||
tournament_obj.tournament_details_id = v["tournament_details_id"].as<graphene::chain::tournament_details_id_type>();
|
||||
graphene::chain::tournament_state state = v["state"].as<graphene::chain::tournament_state>();
|
||||
const_cast<int*>(tournament_obj.my->state_machine.current_state())[0] = (int)state;
|
||||
}
|
||||
} //end namespace fc
|
||||
|
||||
|
||||
|
|
@ -46,6 +46,7 @@ object_id_type witness_create_evaluator::do_apply( const witness_create_operatio
|
|||
const auto& new_witness_object = db().create<witness_object>( [&]( witness_object& obj ){
|
||||
obj.witness_account = op.witness_account;
|
||||
obj.signing_key = op.block_signing_key;
|
||||
obj.next_secret_hash = op.initial_secret;
|
||||
obj.vote_id = vote_id;
|
||||
obj.url = op.url;
|
||||
});
|
||||
|
|
@ -69,6 +70,8 @@ void_result witness_update_evaluator::do_apply( const witness_update_operation&
|
|||
wit.url = *op.new_url;
|
||||
if( op.new_signing_key.valid() )
|
||||
wit.signing_key = *op.new_signing_key;
|
||||
if( op.new_initial_secret.valid() )
|
||||
wit.next_secret_hash = *op.new_initial_secret;
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
|
|||
|
|
@ -166,7 +166,15 @@ struct egenesis_info
|
|||
else if( genesis_json.valid() )
|
||||
{
|
||||
// If genesis not exist, generate from genesis_json
|
||||
genesis = fc::json::from_string( *genesis_json ).as< genesis_state_type >();
|
||||
try
|
||||
{
|
||||
genesis = fc::json::from_string( *genesis_json ).as< genesis_state_type >();
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
edump((e));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
|
||||
#define GRAPHENE_NET_TEST_SEED_IP "104.236.44.210" // autogenerated
|
||||
#define GRAPHENE_NET_TEST_P2P_PORT 1700
|
||||
#define GRAPHENE_NET_DEFAULT_P2P_PORT 1776
|
||||
#define GRAPHENE_NET_DEFAULT_P2P_PORT 2776
|
||||
#define GRAPHENE_NET_DEFAULT_DESIRED_CONNECTIONS 20
|
||||
#define GRAPHENE_NET_DEFAULT_MAX_CONNECTIONS 200
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
add_subdirectory( witness )
|
||||
add_subdirectory( account_history )
|
||||
add_subdirectory( accounts_list )
|
||||
add_subdirectory( market_history )
|
||||
add_subdirectory( delayed_node )
|
||||
add_subdirectory( bookie )
|
||||
add_subdirectory( generate_genesis )
|
||||
add_subdirectory( generate_uia_sharedrop_genesis )
|
||||
add_subdirectory( debug_witness )
|
||||
|
|
|
|||
21
libraries/plugins/accounts_list/CMakeLists.txt
Normal file
21
libraries/plugins/accounts_list/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
file(GLOB HEADERS "include/graphene/accouns_list/*.hpp")
|
||||
|
||||
add_library( graphene_accounts_list
|
||||
accounts_list_plugin.cpp
|
||||
)
|
||||
|
||||
target_link_libraries( graphene_accounts_list graphene_chain graphene_app )
|
||||
target_include_directories( graphene_accounts_list
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
|
||||
if(MSVC)
|
||||
set_source_files_properties( accounts_list_plugin.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
|
||||
endif(MSVC)
|
||||
|
||||
install( TARGETS
|
||||
graphene_accounts_list
|
||||
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
135
libraries/plugins/accounts_list/accounts_list_plugin.cpp
Normal file
135
libraries/plugins/accounts_list/accounts_list_plugin.cpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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 <graphene/accounts_list/accounts_list_plugin.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 accounts_list {
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
|
||||
class accounts_list_plugin_impl
|
||||
{
|
||||
public:
|
||||
accounts_list_plugin_impl(accounts_list_plugin& _plugin)
|
||||
: _self( _plugin )
|
||||
{ }
|
||||
virtual ~accounts_list_plugin_impl();
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
void list_accounts();
|
||||
|
||||
graphene::chain::database& database()
|
||||
{
|
||||
return _self.database();
|
||||
}
|
||||
|
||||
accounts_list_plugin& _self;
|
||||
vector<account_balance_object> _listed_balances;
|
||||
};
|
||||
|
||||
accounts_list_plugin_impl::~accounts_list_plugin_impl()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void accounts_list_plugin_impl::list_accounts()
|
||||
{
|
||||
graphene::chain::database& db = database();
|
||||
_listed_balances.clear();
|
||||
|
||||
auto& balance_index = db.get_index_type<graphene::chain::account_balance_index>().indices().get<graphene::chain::by_asset_balance>();
|
||||
for (auto balance_iter = balance_index.begin();
|
||||
balance_iter != balance_index.end() &&
|
||||
balance_iter->asset_type == graphene::chain::asset_id_type() &&
|
||||
balance_iter->balance > 0; ++balance_iter)
|
||||
{
|
||||
//idump((balance_iter->owner(db) .name)(*balance_iter));
|
||||
_listed_balances.emplace_back(*balance_iter);
|
||||
}
|
||||
|
||||
}
|
||||
} // end namespace detail
|
||||
|
||||
accounts_list_plugin::accounts_list_plugin() :
|
||||
my( new detail::accounts_list_plugin_impl(*this) )
|
||||
{
|
||||
}
|
||||
|
||||
accounts_list_plugin::~accounts_list_plugin()
|
||||
{
|
||||
}
|
||||
|
||||
std::string accounts_list_plugin::plugin_name()const
|
||||
{
|
||||
return "accounts_list";
|
||||
}
|
||||
|
||||
void accounts_list_plugin::plugin_set_program_options(
|
||||
boost::program_options::options_description& /*cli*/,
|
||||
boost::program_options::options_description& /*cfg*/
|
||||
)
|
||||
{
|
||||
// cli.add_options()
|
||||
// ("list-account", boost::program_options::value<std::vector<std::string>>()->composing()->multitoken(), "Account ID to list (may specify multiple times)")
|
||||
// ;
|
||||
// cfg.add(cli);
|
||||
}
|
||||
|
||||
void accounts_list_plugin::plugin_initialize(const boost::program_options::variables_map& /*options*/)
|
||||
{
|
||||
//ilog("accounts list plugin: plugin_initialize()");
|
||||
list_accounts();
|
||||
}
|
||||
|
||||
void accounts_list_plugin::plugin_startup()
|
||||
{
|
||||
//ilog("accounts list plugin: plugin_startup()");
|
||||
}
|
||||
|
||||
vector<account_balance_object> accounts_list_plugin::list_accounts() const
|
||||
{
|
||||
ilog("accounts list plugin: list_accounts()");
|
||||
my->list_accounts();
|
||||
//idump((my->_listed_balances));
|
||||
return my->_listed_balances;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/app/plugin.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
|
||||
#include <fc/thread/future.hpp>
|
||||
|
||||
namespace graphene { namespace accounts_list {
|
||||
using namespace chain;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
class accounts_list_plugin_impl;
|
||||
}
|
||||
|
||||
class accounts_list_plugin : public graphene::app::plugin
|
||||
{
|
||||
public:
|
||||
accounts_list_plugin();
|
||||
virtual ~accounts_list_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;
|
||||
|
||||
vector<account_balance_object>list_accounts()const;
|
||||
|
||||
friend class detail::accounts_list_plugin_impl;
|
||||
std::unique_ptr<detail::accounts_list_plugin_impl> my;
|
||||
};
|
||||
|
||||
} } //graphene::accounts_list
|
||||
|
||||
17
libraries/plugins/generate_genesis/CMakeLists.txt
Normal file
17
libraries/plugins/generate_genesis/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
file(GLOB HEADERS "include/graphene/generate_genesis/*.hpp")
|
||||
|
||||
add_library( graphene_generate_genesis
|
||||
generate_genesis.cpp
|
||||
)
|
||||
|
||||
target_link_libraries( graphene_generate_genesis graphene_chain graphene_app graphene_time )
|
||||
target_include_directories( graphene_generate_genesis
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
|
||||
install( TARGETS
|
||||
graphene_generate_genesis
|
||||
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
399
libraries/plugins/generate_genesis/generate_genesis.cpp
Normal file
399
libraries/plugins/generate_genesis/generate_genesis.cpp
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
/*
|
||||
* 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 <graphene/generate_genesis/generate_genesis_plugin.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/genesis_state.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
||||
#include <graphene/chain/market_object.hpp>
|
||||
#include <graphene/chain/vesting_balance_object.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace graphene::generate_genesis_plugin;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
void generate_genesis_plugin::plugin_set_program_options(
|
||||
boost::program_options::options_description& command_line_options,
|
||||
boost::program_options::options_description& config_file_options)
|
||||
{
|
||||
command_line_options.add_options()
|
||||
("output-genesis-file,o", bpo::value<std::string>()->default_value("genesis.json"), "Genesis file to create")
|
||||
("output-csvlog-file,o", bpo::value<std::string>()->default_value("log.csv"), "CSV log file to create")
|
||||
("snapshot-block-number", bpo::value<uint32_t>(), "Block number at which to snapshot balances")
|
||||
;
|
||||
config_file_options.add(command_line_options);
|
||||
}
|
||||
|
||||
std::string generate_genesis_plugin::plugin_name()const
|
||||
{
|
||||
return "generate_genesis";
|
||||
}
|
||||
|
||||
void generate_genesis_plugin::plugin_initialize(const boost::program_options::variables_map& options)
|
||||
{ try {
|
||||
ilog("generate genesis plugin: plugin_initialize() begin");
|
||||
_options = &options;
|
||||
|
||||
_genesis_filename = options["output-genesis-file"].as<std::string>();
|
||||
_csvlog_filename = options["output-csvlog-file"].as<std::string>();
|
||||
if (options.count("snapshot-block-number"))
|
||||
_block_to_snapshot = options["snapshot-block-number"].as<uint32_t>();
|
||||
database().applied_block.connect([this](const graphene::chain::signed_block& b){ block_applied(b); });
|
||||
ilog("generate genesis plugin: plugin_initialize() end");
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
void generate_genesis_plugin::plugin_startup()
|
||||
{ try {
|
||||
ilog("generate genesis plugin: plugin_startup() begin");
|
||||
if (_block_to_snapshot)
|
||||
{
|
||||
chain::database& d = database();
|
||||
if (d.head_block_num() == *_block_to_snapshot)
|
||||
{
|
||||
ilog("generate genesis plugin: already at snapshot block");
|
||||
generate_snapshot();
|
||||
}
|
||||
else if (d.head_block_num() > *_block_to_snapshot)
|
||||
elog("generate genesis plugin: already passed snapshot block, you must reindex to return to the snapshot state");
|
||||
else
|
||||
elog("generate genesis plugin: waiting for block ${snapshot_block} to generate snapshot, current head is ${head}",
|
||||
("snapshot_block", _block_to_snapshot)("head", d.head_block_num()));
|
||||
}
|
||||
else
|
||||
ilog("generate genesis plugin: no snapshot block number provided, plugin is disabled");
|
||||
|
||||
ilog("generate genesis plugin: plugin_startup() end");
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void generate_genesis_plugin::block_applied(const graphene::chain::signed_block& b)
|
||||
{
|
||||
if (_block_to_snapshot && b.block_num() == *_block_to_snapshot)
|
||||
{
|
||||
ilog("generate genesis plugin: snapshot block has arrived");
|
||||
generate_snapshot();
|
||||
}
|
||||
}
|
||||
|
||||
std::string modify_account_name(const std::string& name)
|
||||
{
|
||||
return std::string("bts-") + name;
|
||||
}
|
||||
|
||||
bool is_special_account(const graphene::chain::account_id_type& account_id)
|
||||
{
|
||||
return account_id.instance < 100;
|
||||
}
|
||||
|
||||
bool is_scam(const std::string& account_name)
|
||||
{
|
||||
static std::set<std::string> scam_accounts{
|
||||
"polonie-wallet",
|
||||
"polonie-xwallet",
|
||||
"poloniewallet",
|
||||
"poloniex-deposit",
|
||||
"poloniex-wallet",
|
||||
"poloniexwall-et",
|
||||
"poloniexwallett",
|
||||
"poloniexwall-t",
|
||||
"poloniexwalle",
|
||||
"poloniex",
|
||||
"poloneix",
|
||||
"poloniex1",
|
||||
"bittrex-deopsit",
|
||||
"bittrex-deposi",
|
||||
"bittrex-depositt",
|
||||
"bittrex-dposit",
|
||||
"bittrex",
|
||||
"bittrex-deposits",
|
||||
"coinbase",
|
||||
"blocktrade",
|
||||
"locktrades",
|
||||
"yun.bts",
|
||||
"transwiser-walle",
|
||||
"transwiser-wallets",
|
||||
"ranswiser-wallet",
|
||||
"yun.btc",
|
||||
"pay.coinbase.com",
|
||||
"pay.bts.com",
|
||||
"btc38.com",
|
||||
"yunbi.com",
|
||||
"coinbase.com",
|
||||
"ripple.com"
|
||||
};
|
||||
return scam_accounts.find(account_name) != scam_accounts.end();
|
||||
}
|
||||
|
||||
bool is_exchange(const std::string& account_name)
|
||||
{
|
||||
static std::set<std::string> exchange_accounts{
|
||||
"poloniexcoldstorage",
|
||||
"btc38-public-for-bts-cold",
|
||||
"poloniexwallet",
|
||||
"btercom",
|
||||
"yunbi-cold-wallet",
|
||||
"btc38-btsx-octo-72722",
|
||||
"bittrex-deposit",
|
||||
"btc38btsxwithdrawal"
|
||||
};
|
||||
return exchange_accounts.find(account_name) != exchange_accounts.end();
|
||||
}
|
||||
|
||||
bool exclude_account_from_sharedrop(graphene::chain::database& d, const graphene::chain::account_id_type& account_id)
|
||||
{
|
||||
if (is_special_account(account_id))
|
||||
return true;
|
||||
const std::string& account_name = account_id(d).name;
|
||||
return is_exchange(account_name) || is_scam(account_name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void generate_genesis_plugin::generate_snapshot()
|
||||
{ try {
|
||||
ilog("generate genesis plugin: generating snapshot now");
|
||||
graphene::chain::genesis_state_type new_genesis_state;
|
||||
chain::database& d = database();
|
||||
|
||||
// we'll distribute 5% of (some amount of tokens), so:
|
||||
graphene::chain::share_type total_amount_to_distribute(27302662972);
|
||||
|
||||
my_account_balance_object_index_type db_balances;
|
||||
graphene::chain::share_type total_bts_balance;
|
||||
|
||||
auto& balance_index = d.get_index_type<graphene::chain::account_balance_index>().indices().get<graphene::chain::by_asset_balance>();
|
||||
for (auto balance_iter = balance_index.begin(); balance_iter != balance_index.end() && balance_iter->asset_type == graphene::chain::asset_id_type(); ++balance_iter)
|
||||
{
|
||||
if (balance_iter->balance > 0 && !exclude_account_from_sharedrop(d, balance_iter->owner))
|
||||
{
|
||||
total_bts_balance += balance_iter->balance;
|
||||
|
||||
my_account_balance_object new_balance_object;
|
||||
new_balance_object.account_id = balance_iter->owner;
|
||||
new_balance_object.balance = balance_iter->balance;
|
||||
db_balances.insert(new_balance_object);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// account for BTS tied up in market orders
|
||||
auto limit_order_index = d.get_index_type<graphene::chain::limit_order_index>().indices();
|
||||
for (const graphene::chain::limit_order_object& limit_order : limit_order_index)
|
||||
if (limit_order.amount_for_sale().asset_id == graphene::chain::asset_id_type())
|
||||
{
|
||||
graphene::chain::share_type limit_order_amount = limit_order.amount_for_sale().amount;
|
||||
if (limit_order_amount > 0 && !exclude_account_from_sharedrop(d, limit_order.seller))
|
||||
{
|
||||
total_bts_balance += limit_order_amount;
|
||||
|
||||
auto my_balance_iter = db_balances.find(limit_order.seller);
|
||||
if (my_balance_iter == db_balances.end())
|
||||
{
|
||||
my_account_balance_object balance_object;
|
||||
balance_object.account_id = limit_order.seller;
|
||||
balance_object.orders = limit_order_amount;
|
||||
db_balances.insert(balance_object);
|
||||
}
|
||||
else
|
||||
{
|
||||
db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) {
|
||||
balance_object.orders += limit_order_amount;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// account for BTS tied up in collateral for SmartCoins
|
||||
auto call_order_index = d.get_index_type<graphene::chain::call_order_index>().indices();
|
||||
for (const graphene::chain::call_order_object& call_order : call_order_index)
|
||||
if (call_order.get_collateral().asset_id == graphene::chain::asset_id_type())
|
||||
{
|
||||
graphene::chain::share_type call_order_amount = call_order.get_collateral().amount;
|
||||
if (call_order_amount > 0 && !exclude_account_from_sharedrop(d, call_order.borrower))
|
||||
{
|
||||
total_bts_balance += call_order_amount;
|
||||
|
||||
auto my_balance_iter = db_balances.find(call_order.borrower);
|
||||
if (my_balance_iter == db_balances.end())
|
||||
{
|
||||
my_account_balance_object balance_object;
|
||||
balance_object.account_id = call_order.borrower;
|
||||
balance_object.collateral = call_order_amount;
|
||||
db_balances.insert(balance_object);
|
||||
}
|
||||
else
|
||||
{
|
||||
db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) {
|
||||
balance_object.collateral += call_order_amount;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// account available-but-unclaimed BTS in vesting balances
|
||||
auto vesting_balance_index = d.get_index_type<graphene::chain::vesting_balance_index>().indices();
|
||||
for (const graphene::chain::vesting_balance_object& vesting_balance : vesting_balance_index)
|
||||
if (vesting_balance.balance.asset_id == graphene::chain::asset_id_type())
|
||||
{
|
||||
graphene::chain::share_type vesting_balance_amount = vesting_balance.get_allowed_withdraw(d.head_block_time()).amount;
|
||||
if (vesting_balance_amount > 0 && !exclude_account_from_sharedrop(d, vesting_balance.owner))
|
||||
{
|
||||
total_bts_balance += vesting_balance_amount;
|
||||
|
||||
auto my_balance_iter = db_balances.find(vesting_balance.owner);
|
||||
if (my_balance_iter == db_balances.end())
|
||||
{
|
||||
my_account_balance_object balance_object;
|
||||
balance_object.account_id = vesting_balance.owner;
|
||||
balance_object.vesting = vesting_balance_amount;
|
||||
db_balances.insert(balance_object);
|
||||
}
|
||||
else
|
||||
{
|
||||
db_balances.modify(my_balance_iter, [&](my_account_balance_object& balance_object) {
|
||||
balance_object.vesting += vesting_balance_amount;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graphene::chain::share_type total_shares_dropped;
|
||||
|
||||
// Now, we assume we're distributing balances to all BTS holders proportionally, figure
|
||||
// the smallest balance we can distribute and still assign the user a satoshi of the share drop
|
||||
graphene::chain::share_type effective_total_bts_balance;
|
||||
auto& by_effective_balance_index = db_balances.get<by_effective_balance>();
|
||||
auto balance_iter = by_effective_balance_index.begin();
|
||||
for (; balance_iter != by_effective_balance_index.end(); ++balance_iter)
|
||||
{
|
||||
fc::uint128 share_drop_amount = total_amount_to_distribute.value;
|
||||
share_drop_amount *= balance_iter->get_effective_balance().value;
|
||||
share_drop_amount /= total_bts_balance.value;
|
||||
if (!share_drop_amount.to_uint64())
|
||||
break; // balances are decreasing, so every balance after will also round to zero
|
||||
total_shares_dropped += share_drop_amount.to_uint64();
|
||||
effective_total_bts_balance += balance_iter->get_effective_balance();
|
||||
}
|
||||
|
||||
// our iterator is just after the smallest balance we will process,
|
||||
// walk it backwards towards the larger balances, distributing the sharedrop as we go
|
||||
graphene::chain::share_type remaining_amount_to_distribute = total_amount_to_distribute;
|
||||
graphene::chain::share_type bts_balance_remaining = effective_total_bts_balance;
|
||||
|
||||
do {
|
||||
--balance_iter;
|
||||
fc::uint128 share_drop_amount = remaining_amount_to_distribute.value;
|
||||
share_drop_amount *= balance_iter->get_effective_balance().value;
|
||||
share_drop_amount /= bts_balance_remaining.value;
|
||||
graphene::chain::share_type amount_distributed = share_drop_amount.to_uint64();
|
||||
|
||||
by_effective_balance_index.modify(balance_iter, [&](my_account_balance_object& balance_object) {
|
||||
balance_object.sharedrop += amount_distributed;
|
||||
});
|
||||
|
||||
remaining_amount_to_distribute -= amount_distributed;
|
||||
bts_balance_remaining -= balance_iter->get_effective_balance();
|
||||
} while (balance_iter != by_effective_balance_index.begin());
|
||||
assert(remaining_amount_to_distribute == 0);
|
||||
|
||||
std::ofstream logfile;
|
||||
logfile.open(_csvlog_filename);
|
||||
assert(logfile.is_open());
|
||||
logfile << "name,balance+orders+collateral+vesting,balance,orders,collateral,vesting,sharedrop\n";
|
||||
for (const my_account_balance_object& balance : by_effective_balance_index)
|
||||
logfile << balance.account_id(d).name << "," <<
|
||||
balance.get_effective_balance().value << "," <<
|
||||
balance.balance.value << "," <<
|
||||
balance.orders.value << "," <<
|
||||
balance.collateral.value << "," <<
|
||||
balance.vesting.value << "," <<
|
||||
balance.sharedrop.value << "\n";
|
||||
ilog("CSV log written to file ${filename}", ("filename", _csvlog_filename));
|
||||
logfile.close();
|
||||
|
||||
// remove all balance objects with zero sharedrops
|
||||
by_effective_balance_index.erase(by_effective_balance_index.lower_bound(0),
|
||||
by_effective_balance_index.end());
|
||||
|
||||
// inefficient way of crawling the graph, but we only do it once
|
||||
std::set<graphene::chain::account_id_type> already_generated;
|
||||
for (;;)
|
||||
{
|
||||
unsigned accounts_generated_this_round = 0;
|
||||
for (const my_account_balance_object& balance : by_effective_balance_index)
|
||||
{
|
||||
const graphene::chain::account_object& account_obj = balance.account_id(d);
|
||||
if (already_generated.find(balance.account_id) == already_generated.end())
|
||||
{
|
||||
graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority owner;
|
||||
owner.weight_threshold = account_obj.owner.weight_threshold;
|
||||
owner.key_auths = account_obj.owner.key_auths;
|
||||
for (const auto& value : account_obj.owner.account_auths)
|
||||
{
|
||||
owner.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second));
|
||||
db_balances.insert(my_account_balance_object{value.first}); // make sure the account is generated, even if it has a zero balance
|
||||
}
|
||||
owner.key_auths = account_obj.owner.key_auths;
|
||||
owner.address_auths = account_obj.owner.address_auths;
|
||||
|
||||
graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority active;
|
||||
active.weight_threshold = account_obj.active.weight_threshold;
|
||||
active.key_auths = account_obj.active.key_auths;
|
||||
for (const auto& value : account_obj.active.account_auths)
|
||||
{
|
||||
active.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second));
|
||||
db_balances.insert(my_account_balance_object{value.first}); // make sure the account is generated, even if it has a zero balance
|
||||
}
|
||||
active.key_auths = account_obj.active.key_auths;
|
||||
active.address_auths = account_obj.active.address_auths;
|
||||
|
||||
new_genesis_state.initial_bts_accounts.emplace_back(
|
||||
graphene::chain::genesis_state_type::initial_bts_account_type(modify_account_name(account_obj.name),
|
||||
owner, active,
|
||||
balance.sharedrop));
|
||||
already_generated.insert(balance.account_id);
|
||||
++accounts_generated_this_round;
|
||||
}
|
||||
}
|
||||
if (accounts_generated_this_round == 0)
|
||||
break;
|
||||
}
|
||||
fc::json::save_to_file(new_genesis_state, _genesis_filename);
|
||||
ilog("New genesis state written to file ${filename}", ("filename", _genesis_filename));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
void generate_genesis_plugin::plugin_shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/app/plugin.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
#include <fc/thread/future.hpp>
|
||||
|
||||
namespace graphene { namespace generate_genesis_plugin {
|
||||
|
||||
class generate_genesis_plugin : public graphene::app::plugin {
|
||||
public:
|
||||
~generate_genesis_plugin() {
|
||||
}
|
||||
|
||||
std::string plugin_name()const override;
|
||||
|
||||
virtual void plugin_set_program_options(
|
||||
boost::program_options::options_description &command_line_options,
|
||||
boost::program_options::options_description &config_file_options
|
||||
) override;
|
||||
|
||||
virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;
|
||||
virtual void plugin_startup() override;
|
||||
virtual void plugin_shutdown() override;
|
||||
|
||||
private:
|
||||
void block_applied(const graphene::chain::signed_block& b);
|
||||
void generate_snapshot();
|
||||
|
||||
boost::program_options::variables_map _options;
|
||||
|
||||
fc::optional<uint32_t> _block_to_snapshot;
|
||||
std::string _genesis_filename;
|
||||
std::string _csvlog_filename;
|
||||
};
|
||||
|
||||
class my_account_balance_object
|
||||
{
|
||||
public:
|
||||
// constructor copying from base class
|
||||
//my_account_balance_object(const graphene::chain::account_balance_object& abo) : graphene::chain::account_balance_object(abo) {}
|
||||
graphene::chain::account_id_type account_id;
|
||||
|
||||
graphene::chain::share_type balance;
|
||||
graphene::chain::share_type orders;
|
||||
graphene::chain::share_type collateral;
|
||||
graphene::chain::share_type vesting;
|
||||
graphene::chain::share_type sharedrop;
|
||||
graphene::chain::share_type get_effective_balance() const { return balance + orders + collateral + vesting; }
|
||||
};
|
||||
using namespace boost::multi_index;
|
||||
struct by_account{};
|
||||
struct by_effective_balance{};
|
||||
typedef multi_index_container<my_account_balance_object,
|
||||
indexed_by<ordered_unique<tag<by_account>,
|
||||
member<my_account_balance_object, graphene::chain::account_id_type, &my_account_balance_object::account_id> >,
|
||||
ordered_non_unique<tag<by_effective_balance>,
|
||||
const_mem_fun<my_account_balance_object, graphene::chain::share_type, &my_account_balance_object::get_effective_balance>,
|
||||
std::greater<graphene::chain::share_type> > > > my_account_balance_object_index_type;
|
||||
|
||||
|
||||
} } //graphene::generate_genesis_plugin
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
file(GLOB HEADERS "include/graphene/generate_uia_sharedrop_genesis/*.hpp")
|
||||
|
||||
add_library( graphene_generate_uia_sharedrop_genesis
|
||||
generate_uia_sharedrop_genesis.cpp
|
||||
)
|
||||
|
||||
target_link_libraries( graphene_generate_uia_sharedrop_genesis graphene_chain graphene_app graphene_time )
|
||||
target_include_directories( graphene_generate_uia_sharedrop_genesis
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
|
||||
install( TARGETS
|
||||
graphene_generate_uia_sharedrop_genesis
|
||||
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* 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 <graphene/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/genesis_state.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
||||
#include <graphene/chain/market_object.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace graphene::generate_uia_sharedrop_genesis;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
void generate_uia_sharedrop_genesis_plugin::plugin_set_program_options(boost::program_options::options_description& command_line_options,
|
||||
boost::program_options::options_description& config_file_options)
|
||||
{
|
||||
command_line_options.add_options()
|
||||
("input-uia-sharedrop-genesis-file", bpo::value<std::string>()->default_value("genesis.json"), "Genesis file to read")
|
||||
("output-uia-sharedrop-genesis-file", bpo::value<std::string>()->default_value("genesis.json"), "Genesis file to create")
|
||||
("output-uia-sharedrop-csvlog-file", bpo::value<std::string>()->default_value("log.csv"), "CSV log file to create")
|
||||
("uia-sharedrop-snapshot-block-number", bpo::value<uint32_t>(), "Block number at which to snapshot balances")
|
||||
;
|
||||
config_file_options.add(command_line_options);
|
||||
}
|
||||
|
||||
std::string generate_uia_sharedrop_genesis_plugin::plugin_name()const
|
||||
{
|
||||
return "generate_uia_sharedrop_genesis";
|
||||
}
|
||||
|
||||
void generate_uia_sharedrop_genesis_plugin::plugin_initialize(const boost::program_options::variables_map& options)
|
||||
{ try {
|
||||
ilog("generate uia sharedrop genesis plugin: plugin_initialize() begin");
|
||||
_options = &options;
|
||||
|
||||
_output_genesis_filename = options["output-uia-sharedrop-genesis-file"].as<std::string>();
|
||||
_input_genesis_filename = options["input-uia-sharedrop-genesis-file"].as<std::string>();
|
||||
_csvlog_filename = options["output-uia-sharedrop-csvlog-file"].as<std::string>();
|
||||
if (options.count("uia-sharedrop-snapshot-block-number"))
|
||||
_block_to_snapshot = options["uia-sharedrop-snapshot-block-number"].as<uint32_t>();
|
||||
database().applied_block.connect([this](const graphene::chain::signed_block& b){ block_applied(b); });
|
||||
ilog("generate uia sharedrop genesis plugin: plugin_initialize() end");
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
void generate_uia_sharedrop_genesis_plugin::plugin_startup()
|
||||
{ try {
|
||||
ilog("generate uia sharedrop genesis plugin: plugin_startup() begin");
|
||||
if (_block_to_snapshot)
|
||||
{
|
||||
chain::database& d = database();
|
||||
if (d.head_block_num() == *_block_to_snapshot)
|
||||
{
|
||||
ilog("generate uia sharedrop genesis plugin: already at snapshot block");
|
||||
generate_snapshot();
|
||||
}
|
||||
else if (d.head_block_num() > *_block_to_snapshot)
|
||||
elog("generate uia sharedrop genesis plugin: already passed snapshot block, you must reindex to return to the snapshot state");
|
||||
else
|
||||
elog("generate uia sharedrop genesis plugin: waiting for block ${snapshot_block} to generate snapshot, current head is ${head}",
|
||||
("snapshot_block", _block_to_snapshot)("head", d.head_block_num()));
|
||||
}
|
||||
else
|
||||
ilog("generate uia sharedrop genesis plugin: no snapshot block number provided, plugin is disabled");
|
||||
ilog("generate uia sharedrop genesis plugin: plugin_startup() end");
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void generate_uia_sharedrop_genesis_plugin::block_applied(const graphene::chain::signed_block& b)
|
||||
{
|
||||
if (_block_to_snapshot && b.block_num() == *_block_to_snapshot)
|
||||
{
|
||||
ilog("generate uia sharedrop genesis plugin: snapshot block has arrived");
|
||||
generate_snapshot();
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// anonymous namespace for file-scoped helper functions
|
||||
|
||||
std::string modify_account_name(const std::string& name)
|
||||
{
|
||||
return std::string("bts-") + name;
|
||||
}
|
||||
|
||||
std::string unmodify_account_name(const std::string& name)
|
||||
{
|
||||
FC_ASSERT(name.substr(0, 4) == "bts-");
|
||||
return name.substr(4);
|
||||
}
|
||||
|
||||
bool is_special_account(const graphene::chain::account_id_type& account_id)
|
||||
{
|
||||
return account_id.instance < 100;
|
||||
}
|
||||
|
||||
bool is_scam(const std::string& account_name)
|
||||
{
|
||||
static std::set<std::string> scam_accounts{
|
||||
"polonie-wallet",
|
||||
"polonie-xwallet",
|
||||
"poloniewallet",
|
||||
"poloniex-deposit",
|
||||
"poloniex-wallet",
|
||||
"poloniexwall-et",
|
||||
"poloniexwallett",
|
||||
"poloniexwall-t",
|
||||
"poloniexwalle",
|
||||
"poloniex",
|
||||
"poloneix",
|
||||
"poloniex1",
|
||||
"bittrex-deopsit",
|
||||
"bittrex-deposi",
|
||||
"bittrex-depositt",
|
||||
"bittrex-dposit",
|
||||
"bittrex",
|
||||
"bittrex-deposits",
|
||||
"coinbase",
|
||||
"blocktrade",
|
||||
"locktrades",
|
||||
"yun.bts",
|
||||
"transwiser-walle",
|
||||
"transwiser-wallets",
|
||||
"ranswiser-wallet",
|
||||
"yun.btc",
|
||||
"pay.coinbase.com",
|
||||
"pay.bts.com",
|
||||
"btc38.com",
|
||||
"yunbi.com",
|
||||
"coinbase.com",
|
||||
"ripple.com"
|
||||
};
|
||||
return scam_accounts.find(account_name) != scam_accounts.end();
|
||||
}
|
||||
|
||||
bool is_exchange(const std::string& account_name)
|
||||
{
|
||||
static std::set<std::string> exchange_accounts{
|
||||
"poloniexcoldstorage",
|
||||
"btc38-public-for-bts-cold",
|
||||
"poloniexwallet",
|
||||
"btercom",
|
||||
"yunbi-cold-wallet",
|
||||
"btc38-btsx-octo-72722",
|
||||
"bittrex-deposit",
|
||||
"btc38btsxwithdrawal"
|
||||
};
|
||||
return exchange_accounts.find(account_name) != exchange_accounts.end();
|
||||
}
|
||||
}
|
||||
|
||||
void generate_uia_sharedrop_genesis_plugin::generate_snapshot()
|
||||
{
|
||||
ilog("generate genesis plugin: generating snapshot now");
|
||||
chain::database& d = database();
|
||||
|
||||
// Lookup the ID of the UIA we will be sharedropping on
|
||||
std::string uia_symbol("PEERPLAYS");
|
||||
const auto& assets_by_symbol = d.get_index_type<graphene::chain::asset_index>().indices().get<graphene::chain::by_symbol>();
|
||||
auto itr = assets_by_symbol.find(uia_symbol);
|
||||
FC_ASSERT(itr != assets_by_symbol.end(), "Unable to find asset named ${uia_symbol}", ("uia_symbol", uia_symbol));
|
||||
graphene::chain::asset_id_type uia_id = itr->get_id();
|
||||
ilog("Scanning for all balances of asset ${uia_symbol} (${uia_id})", ("uia_symbol", uia_symbol)("uia_id", uia_id));
|
||||
|
||||
uia_sharedrop_balance_object_index_type sharedrop_balances;
|
||||
|
||||
// load the balances from the input genesis file, if any
|
||||
graphene::chain::genesis_state_type new_genesis_state;
|
||||
if (!_input_genesis_filename.empty())
|
||||
{
|
||||
new_genesis_state = fc::json::from_file<graphene::chain::genesis_state_type>(_input_genesis_filename);
|
||||
for (const graphene::chain::genesis_state_type::initial_bts_account_type& initial_bts_account : new_genesis_state.initial_bts_accounts)
|
||||
{
|
||||
std::string account_name = unmodify_account_name(initial_bts_account.name);
|
||||
auto& account_by_name_index = d.get_index_type<graphene::chain::account_index>().indices().get<graphene::chain::by_name>();
|
||||
auto account_iter = account_by_name_index.find(account_name);
|
||||
FC_ASSERT(account_iter != account_by_name_index.end(), "No account ${name}", ("name", account_name));
|
||||
uia_sharedrop_balance_object balance_object;
|
||||
balance_object.account_id = account_iter->id;
|
||||
balance_object.genesis = initial_bts_account.core_balance;
|
||||
sharedrop_balances.insert(balance_object);
|
||||
ilog("Loaded genesis balance for ${name}: ${balance}", ("name", account_name)("balance", initial_bts_account.core_balance));
|
||||
}
|
||||
}
|
||||
new_genesis_state.initial_bts_accounts.clear();
|
||||
|
||||
auto& balance_index = d.get_index_type<graphene::chain::account_balance_index>().indices().get<graphene::chain::by_asset_balance>();
|
||||
for (auto balance_iter = balance_index.begin(); balance_iter != balance_index.end(); ++balance_iter)
|
||||
if (balance_iter->asset_type == uia_id && balance_iter->balance != graphene::chain::share_type())
|
||||
{
|
||||
if (is_special_account(balance_iter->owner) || is_exchange(balance_iter->owner(d).name) || is_scam(balance_iter->owner(d).name))
|
||||
{
|
||||
ilog("skipping balance in ${account_id} because special or exchange", ("account_id", balance_iter->owner));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto sharedrop_balance_iter = sharedrop_balances.find(balance_iter->owner);
|
||||
if (sharedrop_balance_iter == sharedrop_balances.end())
|
||||
{
|
||||
uia_sharedrop_balance_object balance_object;
|
||||
balance_object.account_id = balance_iter->owner;
|
||||
balance_object.balance = balance_iter->balance;
|
||||
sharedrop_balances.insert(balance_object);
|
||||
}
|
||||
else
|
||||
{
|
||||
sharedrop_balances.modify(sharedrop_balance_iter, [&](uia_sharedrop_balance_object& balance_object) {
|
||||
balance_object.balance = balance_iter->balance;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// scan for PEERPLAYS tied up in market orders
|
||||
auto& limit_order_index = d.get_index_type<graphene::chain::limit_order_index>().indices().get<graphene::chain::by_account>();
|
||||
for (auto limit_order_iter = limit_order_index.begin(); limit_order_iter != limit_order_index.end(); ++limit_order_iter)
|
||||
{
|
||||
if (limit_order_iter->sell_price.base.asset_id == uia_id)
|
||||
{
|
||||
if (is_special_account(limit_order_iter->seller) || is_exchange(limit_order_iter->seller(d).name) || is_scam(limit_order_iter->seller(d).name))
|
||||
ilog("Skipping account ${name} because special/scam/exchange", ("name", limit_order_iter->seller(d).name));
|
||||
else
|
||||
{
|
||||
auto sharedrop_balance_iter = sharedrop_balances.find(limit_order_iter->seller);
|
||||
if (sharedrop_balance_iter == sharedrop_balances.end())
|
||||
{
|
||||
//ilog("found order for new account ${account_id}", ("account_id", limit_order_iter->seller));
|
||||
uia_sharedrop_balance_object balance_object;
|
||||
balance_object.account_id = limit_order_iter->seller;
|
||||
balance_object.orders = limit_order_iter->for_sale;
|
||||
sharedrop_balances.insert(balance_object);
|
||||
}
|
||||
else
|
||||
{
|
||||
//ilog("found order for existing account ${account_id}", ("account_id", limit_order_iter->seller));
|
||||
sharedrop_balances.modify(sharedrop_balance_iter, [&](uia_sharedrop_balance_object& balance_object) {
|
||||
balance_object.orders += limit_order_iter->for_sale;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compute the sharedrop
|
||||
for (auto sharedrop_balance_iter = sharedrop_balances.begin(); sharedrop_balance_iter != sharedrop_balances.end();)
|
||||
{
|
||||
auto this_iter = sharedrop_balance_iter;
|
||||
++sharedrop_balance_iter;
|
||||
sharedrop_balances.modify(this_iter, [&](uia_sharedrop_balance_object& balance_object) {
|
||||
balance_object.sharedrop = balance_object.genesis + (balance_object.balance + balance_object.orders) * 10;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Generate CSV file of all sharedrops and the balances we used to calculate them
|
||||
std::ofstream csv_log_file;
|
||||
csv_log_file.open(_csvlog_filename);
|
||||
assert(csv_log_file.is_open());
|
||||
csv_log_file << "name,genesis,balance,orders,sharedrop\n";
|
||||
for (const uia_sharedrop_balance_object& balance_object : sharedrop_balances)
|
||||
csv_log_file << balance_object.account_id(d).name << "," << balance_object.genesis.value << "," << balance_object.balance.value << "," << balance_object.orders.value << "," << balance_object.sharedrop.value << "\n";
|
||||
ilog("CSV log written to file ${filename}", ("filename", _csvlog_filename));
|
||||
csv_log_file.close();
|
||||
|
||||
//auto& account_index = d.get_index_type<graphene::chain::account_index>();
|
||||
//auto& account_by_id_index = account_index.indices().get<graphene::chain::by_id>();
|
||||
// inefficient way of crawling the graph, but we only do it once
|
||||
std::set<graphene::chain::account_id_type> already_generated;
|
||||
for (;;)
|
||||
{
|
||||
unsigned accounts_generated_this_round = 0;
|
||||
for (const uia_sharedrop_balance_object& balance_object : sharedrop_balances)
|
||||
{
|
||||
const graphene::chain::account_id_type& account_id = balance_object.account_id;
|
||||
const graphene::chain::share_type& sharedrop_amount = balance_object.sharedrop;
|
||||
const graphene::chain::account_object& account_obj = account_id(d);
|
||||
if (already_generated.find(account_id) == already_generated.end())
|
||||
{
|
||||
graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority owner;
|
||||
owner.weight_threshold = account_obj.owner.weight_threshold;
|
||||
owner.key_auths = account_obj.owner.key_auths;
|
||||
for (const auto& value : account_obj.owner.account_auths)
|
||||
{
|
||||
owner.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second));
|
||||
auto owner_balance_iter = sharedrop_balances.find(value.first);
|
||||
if (owner_balance_iter == sharedrop_balances.end())
|
||||
{
|
||||
uia_sharedrop_balance_object balance_object;
|
||||
balance_object.account_id = value.first;
|
||||
sharedrop_balances.insert(balance_object);
|
||||
}
|
||||
}
|
||||
owner.key_auths = account_obj.owner.key_auths;
|
||||
owner.address_auths = account_obj.owner.address_auths;
|
||||
|
||||
graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority active;
|
||||
active.weight_threshold = account_obj.active.weight_threshold;
|
||||
active.key_auths = account_obj.active.key_auths;
|
||||
for (const auto& value : account_obj.active.account_auths)
|
||||
{
|
||||
active.account_auths.insert(std::make_pair(modify_account_name(value.first(d).name), value.second));
|
||||
auto active_balance_iter = sharedrop_balances.find(value.first);
|
||||
if (active_balance_iter == sharedrop_balances.end())
|
||||
{
|
||||
uia_sharedrop_balance_object balance_object;
|
||||
balance_object.account_id = value.first;
|
||||
sharedrop_balances.insert(balance_object);
|
||||
}
|
||||
}
|
||||
active.key_auths = account_obj.active.key_auths;
|
||||
active.address_auths = account_obj.active.address_auths;
|
||||
|
||||
new_genesis_state.initial_bts_accounts.emplace_back(
|
||||
graphene::chain::genesis_state_type::initial_bts_account_type(modify_account_name(account_obj.name),
|
||||
owner, active,
|
||||
sharedrop_amount));
|
||||
already_generated.insert(account_id);
|
||||
++accounts_generated_this_round;
|
||||
}
|
||||
}
|
||||
if (accounts_generated_this_round == 0)
|
||||
break;
|
||||
}
|
||||
fc::json::save_to_file(new_genesis_state, _output_genesis_filename);
|
||||
ilog("New genesis state written to file ${filename}", ("filename", _output_genesis_filename));
|
||||
}
|
||||
|
||||
void generate_uia_sharedrop_genesis_plugin::plugin_shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/app/plugin.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
#include <fc/thread/future.hpp>
|
||||
|
||||
namespace graphene { namespace generate_uia_sharedrop_genesis {
|
||||
|
||||
class generate_uia_sharedrop_genesis_plugin : public graphene::app::plugin {
|
||||
public:
|
||||
~generate_uia_sharedrop_genesis_plugin() {
|
||||
}
|
||||
|
||||
std::string plugin_name()const override;
|
||||
|
||||
virtual void plugin_set_program_options(
|
||||
boost::program_options::options_description &command_line_options,
|
||||
boost::program_options::options_description &config_file_options
|
||||
) override;
|
||||
|
||||
virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;
|
||||
virtual void plugin_startup() override;
|
||||
virtual void plugin_shutdown() override;
|
||||
|
||||
private:
|
||||
void block_applied(const graphene::chain::signed_block& b);
|
||||
void generate_snapshot();
|
||||
|
||||
boost::program_options::variables_map _options;
|
||||
|
||||
fc::optional<uint32_t> _block_to_snapshot;
|
||||
std::string _input_genesis_filename;
|
||||
std::string _output_genesis_filename;
|
||||
std::string _csvlog_filename;
|
||||
};
|
||||
|
||||
class uia_sharedrop_balance_object
|
||||
{
|
||||
public:
|
||||
graphene::chain::account_id_type account_id;
|
||||
|
||||
graphene::chain::share_type genesis;
|
||||
graphene::chain::share_type balance;
|
||||
graphene::chain::share_type orders;
|
||||
|
||||
graphene::chain::share_type sharedrop;
|
||||
};
|
||||
|
||||
using namespace boost::multi_index;
|
||||
struct by_account{};
|
||||
typedef multi_index_container<uia_sharedrop_balance_object,
|
||||
indexed_by<ordered_unique<tag<by_account>,
|
||||
member<uia_sharedrop_balance_object, graphene::chain::account_id_type, &uia_sharedrop_balance_object::account_id> > > > uia_sharedrop_balance_object_index_type;
|
||||
|
||||
} } //graphene::generate_uia_sharedrop_genesis_plugin
|
||||
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
||||
#include <boost/range/algorithm_ext/insert.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
||||
|
|
@ -66,11 +68,14 @@ void witness_plugin::plugin_set_program_options(
|
|||
{
|
||||
auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan")));
|
||||
string witness_id_example = fc::json::to_string(chain::witness_id_type(5));
|
||||
string witness_id_example2 = fc::json::to_string(chain::witness_id_type(6));
|
||||
command_line_options.add_options()
|
||||
("enable-stale-production", bpo::bool_switch()->notifier([this](bool e){_production_enabled = e;}), "Enable block production, even if the chain is stale.")
|
||||
("required-participation", bpo::bool_switch()->notifier([this](int e){_required_witness_participation = uint32_t(e*GRAPHENE_1_PERCENT);}), "Percent of witnesses (0-99) that must be participating in order to produce blocks")
|
||||
("witness-id,w", bpo::value<vector<string>>()->composing()->multitoken(),
|
||||
("ID of witness controlled by this node (e.g. " + witness_id_example + ", quotes are required, may specify multiple times)").c_str())
|
||||
("witness-ids,W", bpo::value<string>(),
|
||||
("IDs of multiple witnesses controlled by this node (e.g. [" + witness_id_example + ", " + witness_id_example2 + "], quotes are required)").c_str())
|
||||
("private-key", bpo::value<vector<string>>()->composing()->multitoken()->
|
||||
DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))),
|
||||
"Tuple of [PublicKey, WIF private key] (may specify multiple times)")
|
||||
|
|
@ -88,6 +93,8 @@ void witness_plugin::plugin_initialize(const boost::program_options::variables_m
|
|||
ilog("witness plugin: plugin_initialize() begin");
|
||||
_options = &options;
|
||||
LOAD_VALUE_SET(options, "witness-id", _witnesses, chain::witness_id_type)
|
||||
if (options.count("witness-ids"))
|
||||
boost::insert(_witnesses, fc::json::from_string(options.at("witness-ids").as<string>()).as<vector<chain::witness_id_type>>());
|
||||
|
||||
if( options.count("private-key") )
|
||||
{
|
||||
|
|
@ -191,6 +198,7 @@ block_production_condition::block_production_condition_enum witness_plugin::bloc
|
|||
break;
|
||||
case block_production_condition::not_time_yet:
|
||||
//ilog("Not producing block because slot has not yet arrived");
|
||||
dlog("Not producing block because slot has not yet arrived");
|
||||
break;
|
||||
case block_production_condition::no_private_key:
|
||||
ilog("Not producing block because I don't have the private key for ${scheduled_key}", (capture) );
|
||||
|
|
@ -247,6 +255,7 @@ block_production_condition::block_production_condition_enum witness_plugin::mayb
|
|||
assert( now > db.head_block_time() );
|
||||
|
||||
graphene::chain::witness_id_type scheduled_witness = db.get_scheduled_witness( slot );
|
||||
|
||||
// we must control the witness scheduled to produce the next block.
|
||||
if( _witnesses.find( scheduled_witness ) == _witnesses.end() )
|
||||
{
|
||||
|
|
@ -255,6 +264,7 @@ block_production_condition::block_production_condition_enum witness_plugin::mayb
|
|||
}
|
||||
|
||||
fc::time_point_sec scheduled_time = db.get_slot_time( slot );
|
||||
wdump((slot)(scheduled_witness)(scheduled_time)(now));
|
||||
graphene::chain::public_key_type scheduled_key = scheduled_witness( db ).signing_key;
|
||||
auto private_key_itr = _private_keys.find( scheduled_key );
|
||||
|
||||
|
|
@ -271,18 +281,28 @@ block_production_condition::block_production_condition_enum witness_plugin::mayb
|
|||
return block_production_condition::low_participation;
|
||||
}
|
||||
|
||||
// the local clock must be at least 1 second ahead of head_block_time.
|
||||
//if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
|
||||
//if( (now - db.head_block_time()).to_seconds() < GRAPHENE_MIN_BLOCK_INTERVAL ) {
|
||||
// return block_production_condition::local_clock; //Not producing block because head block is less than a second old.
|
||||
//}
|
||||
|
||||
if( llabs((scheduled_time - now).count()) > fc::milliseconds( 500 ).count() )
|
||||
{
|
||||
capture("scheduled_time", scheduled_time)("now", now);
|
||||
return block_production_condition::lag;
|
||||
}
|
||||
|
||||
//if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
|
||||
ilog("Witness ${id} production slot has arrived; generating a block now...", ("id", scheduled_witness));
|
||||
|
||||
auto block = db.generate_block(
|
||||
scheduled_time,
|
||||
scheduled_witness,
|
||||
private_key_itr->second,
|
||||
_production_skip_flags
|
||||
);
|
||||
|
||||
capture("n", block.block_num())("t", block.timestamp)("c", now);
|
||||
fc::async( [this,block](){ p2p_node().broadcast(net::block_message(block)); } );
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ struct static_variant_map_visitor
|
|||
template< typename T >
|
||||
result_type operator()( const T& dummy )
|
||||
{
|
||||
assert( which == static_cast<int>(m.which_to_name.size()) );
|
||||
//assert( which == (int)m.which_to_name.size() );
|
||||
std::string name = clean_name( fc::get_typename<T>::name() );
|
||||
m.name_to_which[ name ] = which;
|
||||
m.which_to_name.push_back( name );
|
||||
|
|
|
|||
|
|
@ -191,6 +191,8 @@ struct wallet_data
|
|||
key_label_index_type labeled_keys;
|
||||
blind_receipt_index_type blind_receipts;
|
||||
|
||||
std::map<rock_paper_scissors_throw_commit, rock_paper_scissors_throw_reveal> committed_game_moves;
|
||||
|
||||
string ws_server = "ws://localhost:8090";
|
||||
string ws_user;
|
||||
string ws_password;
|
||||
|
|
@ -364,7 +366,9 @@ class wallet_api
|
|||
* @param start the sequence number where to start looping back throw the history
|
||||
* @returns a list of \c operation_history_objects
|
||||
*/
|
||||
vector<operation_detail> get_relative_account_history(string name, uint32_t stop, int limit, uint32_t start)const;
|
||||
vector<operation_detail> get_relative_account_history(string name, uint32_t stop, int limit, uint32_t start)const;
|
||||
|
||||
vector<account_balance_object> list_core_accounts()const;
|
||||
|
||||
vector<bucket_object> get_market_history(string symbol, string symbol2, uint32_t bucket, fc::time_point_sec start, fc::time_point_sec end)const;
|
||||
vector<limit_order_object> get_limit_orders(string a, string b, uint32_t limit)const;
|
||||
|
|
@ -627,6 +631,10 @@ class wallet_api
|
|||
* @return Whether a public key is known
|
||||
*/
|
||||
bool is_public_key_registered(string public_key) const;
|
||||
/**
|
||||
* @param role - active | owner | memo
|
||||
*/
|
||||
pair<public_key_type,string> get_private_key_from_password( string account, string role, string password )const;
|
||||
|
||||
/** Converts a signed_transaction in JSON form to its binary representation.
|
||||
*
|
||||
|
|
@ -1392,6 +1400,38 @@ class wallet_api
|
|||
bool approve,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Change your witness votes.
|
||||
*
|
||||
* An account can publish a list of all witnesses they approve of.
|
||||
* Each account's vote is weighted according to the number of shares of the
|
||||
* core asset owned by that account at the time the votes are tallied.
|
||||
* This command allows you to add or remove one or more witnesses from this list
|
||||
* in one call. When you are changing your vote on several witnesses, this
|
||||
* may be easier than multiple `vote_for_witness` and
|
||||
* `set_desired_witness_and_committee_member_count` calls.
|
||||
*
|
||||
* @note you cannot vote against a witness, you can only vote for the witness
|
||||
* or not vote for the witness.
|
||||
*
|
||||
* @param voting_account the name or id of the account who is voting with their shares
|
||||
* @param witnesses_to_approve the names or ids of the witnesses owner accounts you wish
|
||||
* to approve (these will be added to the list of witnesses
|
||||
* you currently approve). This list can be empty.
|
||||
* @param witnesses_to_reject the names or ids of the witnesses owner accounts you wish
|
||||
* to reject (these will be removed fromthe list of witnesses
|
||||
* you currently approve). This list can be empty.
|
||||
* @param desired_number_of_witnesses the number of witnesses you believe the network
|
||||
* should have. You must vote for at least this many
|
||||
* witnesses. You can set this to 0 to abstain from
|
||||
* voting on the number of witnesses.
|
||||
* @param broadcast true if you wish to broadcast the transaction
|
||||
* @return the signed transaction changing your vote for the given witnesses
|
||||
*/
|
||||
signed_transaction update_witness_votes(string voting_account,
|
||||
std::vector<std::string> witnesses_to_approve,
|
||||
std::vector<std::string> witnesses_to_reject,
|
||||
uint16_t desired_number_of_witnesses,
|
||||
bool broadcast = false);
|
||||
/** Set the voting proxy for an account.
|
||||
*
|
||||
* If a user does not wish to take an active part in voting, they can choose
|
||||
|
|
@ -1587,6 +1627,63 @@ class wallet_api
|
|||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Creates a new tournament
|
||||
* @param creator the accout that is paying the fee to create the tournament
|
||||
* @param options the options detailing the specifics of the tournament
|
||||
* @return the signed version of the transaction
|
||||
*/
|
||||
signed_transaction tournament_create( string creator, tournament_options options, bool broadcast = false );
|
||||
|
||||
/** Join an existing tournament
|
||||
* @param payer_account the account that is paying the buy-in and the fee to join the tournament
|
||||
* @param player_account the account that will be playing in the tournament
|
||||
* @param buy_in_amount buy_in to pay
|
||||
* @param buy_in_asset_symbol buy_in asset
|
||||
* @param tournament_id the tournament the user wishes to join
|
||||
* @param broadcast true if you wish to broadcast the transaction
|
||||
* @return the signed version of the transaction
|
||||
*/
|
||||
signed_transaction tournament_join( string payer_account, string player_account, tournament_id_type tournament_id, string buy_in_amount, string buy_in_asset_symbol, bool broadcast = false );
|
||||
|
||||
/** Leave an existing tournament
|
||||
* @param payer_account the account that is paying the fee
|
||||
* @param player_account the account that would be playing in the tournament
|
||||
* @param tournament_id the tournament the user wishes to leave
|
||||
* @param broadcast true if you wish to broadcast the transaction
|
||||
* @return the signed version of the transaction
|
||||
*/
|
||||
signed_transaction tournament_leave(string payer_account, string player_account, tournament_id_type tournament_id, bool broadcast = false);
|
||||
|
||||
/** Get a list of upcoming tournaments
|
||||
* @param limit the number of tournaments to return
|
||||
*/
|
||||
vector<tournament_object> get_upcoming_tournaments(uint32_t limit);
|
||||
|
||||
vector<tournament_object> get_tournaments(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start);
|
||||
|
||||
vector<tournament_object> get_tournaments_by_state(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start,
|
||||
tournament_state state);
|
||||
|
||||
/** Get specific information about a tournament
|
||||
* @param tournament_id the ID of the tournament
|
||||
*/
|
||||
tournament_object get_tournament(tournament_id_type id);
|
||||
|
||||
/** Play a move in the rock-paper-scissors game
|
||||
* @param game_id the id of the game
|
||||
* @param player_account the name of the player
|
||||
* @param gesture rock, paper, or scissors
|
||||
* @return the signed version of the transaction
|
||||
*/
|
||||
signed_transaction rps_throw(game_id_type game_id,
|
||||
string player_account,
|
||||
rock_paper_scissors_gesture gesture,
|
||||
bool broadcast);
|
||||
|
||||
void dbg_make_uia(string creator, string symbol);
|
||||
void dbg_make_mia(string creator, string symbol);
|
||||
void dbg_push_blocks( std::string src_filename, uint32_t count );
|
||||
|
|
@ -1634,6 +1731,7 @@ FC_REFLECT( graphene::wallet::wallet_data,
|
|||
(pending_account_registrations)(pending_witness_registrations)
|
||||
(labeled_keys)
|
||||
(blind_receipts)
|
||||
(committed_game_moves)
|
||||
(ws_server)
|
||||
(ws_user)
|
||||
(ws_password)
|
||||
|
|
@ -1704,6 +1802,7 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(import_balance)
|
||||
(suggest_brain_key)
|
||||
(derive_owner_keys_from_brain_key)
|
||||
(get_private_key_from_password)
|
||||
(register_account)
|
||||
(upgrade_account)
|
||||
(create_account_with_brain_key)
|
||||
|
|
@ -1742,6 +1841,7 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(withdraw_vesting)
|
||||
(vote_for_committee_member)
|
||||
(vote_for_witness)
|
||||
(update_witness_votes)
|
||||
(set_voting_proxy)
|
||||
(set_desired_witness_and_committee_member_count)
|
||||
(get_account)
|
||||
|
|
@ -1751,6 +1851,7 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(get_account_history)
|
||||
(get_relative_account_history)
|
||||
(is_public_key_registered)
|
||||
(list_core_accounts)
|
||||
(get_market_history)
|
||||
(get_global_properties)
|
||||
(get_dynamic_global_properties)
|
||||
|
|
@ -1802,5 +1903,13 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(propose_create_betting_market)
|
||||
(place_bet)
|
||||
(propose_resolve_betting_market_group)
|
||||
(tournament_create)
|
||||
(tournament_join)
|
||||
(tournament_leave)
|
||||
(rps_throw)
|
||||
(get_upcoming_tournaments)
|
||||
(get_tournaments)
|
||||
(get_tournaments_by_state)
|
||||
(get_tournament)
|
||||
(get_order_book)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
u/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
#include <boost/version.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/multiprecision/integer.hpp>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
|
|
@ -60,9 +61,17 @@
|
|||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/thread/mutex.hpp>
|
||||
#include <fc/thread/scoped_lock.hpp>
|
||||
#include <fc/crypto/rand.hpp>
|
||||
|
||||
#include <graphene/app/api.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include <graphene/chain/protocol/rock_paper_scissors.hpp>
|
||||
#include <graphene/chain/rock_paper_scissors.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/utilities/git_revision.hpp>
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
|
@ -125,6 +134,7 @@ public:
|
|||
std::string operator()(const account_update_operation& op)const;
|
||||
std::string operator()(const asset_create_operation& op)const;
|
||||
std::string operator()(const asset_dividend_distribution_operation& op)const;
|
||||
std::string operator()(const tournament_payout_operation& op)const;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
|
|
@ -339,6 +349,126 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
// return true if any of my_accounts are players in this tournament
|
||||
bool tournament_is_relevant_to_my_accounts(const tournament_object& tournament_obj)
|
||||
{
|
||||
tournament_details_object tournament_details = get_object<tournament_details_object>(tournament_obj.tournament_details_id);
|
||||
for (const account_object& account_obj : _wallet.my_accounts)
|
||||
if (tournament_details.registered_players.find(account_obj.id) != tournament_details.registered_players.end())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fc::mutex _subscribed_object_changed_mutex;
|
||||
void subscribed_object_changed(const variant& changed_objects_variant)
|
||||
{
|
||||
fc::scoped_lock<fc::mutex> lock(_resync_mutex);
|
||||
fc::variants changed_objects = changed_objects_variant.get_array();
|
||||
for (const variant& changed_object_variant : changed_objects)
|
||||
{
|
||||
// changed_object_variant is either the object, or just the id if the object was removed
|
||||
if (changed_object_variant.is_object())
|
||||
{
|
||||
try
|
||||
{
|
||||
object_id_type id = changed_object_variant["id"].as<tournament_id_type>();
|
||||
tournament_object current_tournament_obj = changed_object_variant.as<tournament_object>();
|
||||
auto tournament_cache_iter = tournament_cache.find(id);
|
||||
if (tournament_cache_iter != tournament_cache.end())
|
||||
{
|
||||
const tournament_object& cached_tournament_obj = *tournament_cache_iter;
|
||||
if (cached_tournament_obj.get_state() != current_tournament_obj.get_state())
|
||||
{
|
||||
ilog("Tournament ${id} changed state from ${old} to ${new}",
|
||||
("id", id)
|
||||
("old", cached_tournament_obj.get_state())
|
||||
("new", current_tournament_obj.get_state()));
|
||||
if (current_tournament_obj.get_state() == tournament_state::in_progress)
|
||||
monitor_matches_in_tournament(current_tournament_obj);
|
||||
}
|
||||
tournament_cache.modify(tournament_cache_iter, [&](tournament_object& obj) { obj = current_tournament_obj; });
|
||||
}
|
||||
else if (tournament_is_relevant_to_my_accounts(current_tournament_obj))
|
||||
{
|
||||
ilog ("We were just notified about an in-progress tournament ${id} relevant to our accounts",
|
||||
("id", current_tournament_obj.id));
|
||||
tournament_cache.insert(current_tournament_obj);
|
||||
if (current_tournament_obj.get_state() == tournament_state::in_progress)
|
||||
monitor_matches_in_tournament(current_tournament_obj);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
// idump((e));
|
||||
}
|
||||
try
|
||||
{
|
||||
object_id_type id = changed_object_variant["id"].as<match_id_type>();
|
||||
match_object current_match_obj = changed_object_variant.as<match_object>();
|
||||
auto match_cache_iter = match_cache.find(id);
|
||||
if (match_cache_iter != match_cache.end())
|
||||
{
|
||||
const match_object& cached_match_obj = *match_cache_iter;
|
||||
if (cached_match_obj.get_state() != current_match_obj.get_state() ||
|
||||
cached_match_obj.games.size() != current_match_obj.games.size())
|
||||
{
|
||||
ilog("match ${id} changed state from ${old} to ${new}",
|
||||
("id", id)
|
||||
("old", cached_match_obj.get_state())
|
||||
("new", current_match_obj.get_state()));
|
||||
match_in_new_state(current_match_obj);
|
||||
}
|
||||
match_cache.modify(match_cache_iter, [&](match_object& obj) { obj = current_match_obj; });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
// idump((e));
|
||||
}
|
||||
try
|
||||
{
|
||||
object_id_type id = changed_object_variant["id"].as<game_id_type>();
|
||||
game_object current_game_obj = changed_object_variant.as<game_object>();
|
||||
auto game_cache_iter = game_cache.find(id);
|
||||
if (game_cache_iter != game_cache.end())
|
||||
{
|
||||
const game_object& cached_game_obj = *game_cache_iter;
|
||||
if (cached_game_obj.get_state() != current_game_obj.get_state())
|
||||
{
|
||||
ilog("game ${id} changed state from ${old} to ${new}",
|
||||
("id", id)
|
||||
("old", cached_game_obj.get_state())
|
||||
("new", current_game_obj.get_state()));
|
||||
game_in_new_state(current_game_obj);
|
||||
}
|
||||
game_cache.modify(game_cache_iter, [&](game_object& obj) { obj = current_game_obj; });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
// idump((e));
|
||||
}
|
||||
try
|
||||
{
|
||||
object_id_type id = changed_object_variant["id"].as<account_id_type>();
|
||||
if (_wallet.my_accounts.find(id) != _wallet.my_accounts.end())
|
||||
{
|
||||
account_object account = changed_object_variant.as<account_object>();
|
||||
_wallet.update_account(account);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
// idump((e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void enable_umask_protection()
|
||||
{
|
||||
#ifdef __unix__
|
||||
|
|
@ -411,10 +541,16 @@ public:
|
|||
on_block_applied( block_id );
|
||||
} );
|
||||
|
||||
_remote_db->set_subscribe_callback( [this](const variant& object )
|
||||
{
|
||||
on_subscribe_callback( object );
|
||||
}, false );
|
||||
|
||||
_wallet.chain_id = _chain_id;
|
||||
_wallet.ws_server = initial_data.ws_server;
|
||||
_wallet.ws_user = initial_data.ws_user;
|
||||
_wallet.ws_password = initial_data.ws_password;
|
||||
|
||||
}
|
||||
virtual ~wallet_api_impl()
|
||||
{
|
||||
|
|
@ -449,6 +585,12 @@ public:
|
|||
fc::async([this]{resync();}, "Resync after block");
|
||||
}
|
||||
|
||||
void on_subscribe_callback( const variant& object )
|
||||
{
|
||||
//idump((object));
|
||||
fc::async([this, object]{subscribed_object_changed(object);}, "Object changed");
|
||||
}
|
||||
|
||||
bool copy_wallet_file( string destination_filename )
|
||||
{
|
||||
fc::path src_path = get_wallet_filename();
|
||||
|
|
@ -516,6 +658,7 @@ public:
|
|||
result["participation"] = (100*dynamic_props.recent_slots_filled.popcount()) / 128.0;
|
||||
result["active_witnesses"] = global_props.active_witnesses;
|
||||
result["active_committee_members"] = global_props.active_committee_members;
|
||||
result["entropy"] = dynamic_props.random;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -711,6 +854,147 @@ public:
|
|||
|
||||
vector< signed_transaction > import_balance( string name_or_id, const vector<string>& wif_keys, bool broadcast );
|
||||
|
||||
void game_in_new_state(const game_object& game_obj)
|
||||
{ try {
|
||||
if (game_obj.get_state() == game_state::expecting_commit_moves)
|
||||
{
|
||||
if (game_obj.players.size() != 2) // we only support RPS, a 2 player game
|
||||
return;
|
||||
const rock_paper_scissors_game_details& rps_details = game_obj.game_details.get<rock_paper_scissors_game_details>();
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{
|
||||
if (!rps_details.commit_moves.at(i)) // if this player hasn't committed their move
|
||||
{
|
||||
const account_id_type& account_id = game_obj.players[i];
|
||||
if (_wallet.my_accounts.find(account_id) != _wallet.my_accounts.end()) // and they're us
|
||||
{
|
||||
ilog("Game ${game_id}: it is ${account_name}'s turn to commit their move",
|
||||
("game_id", game_obj.id)
|
||||
("account_name", get_account(account_id).name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (game_obj.get_state() == game_state::expecting_reveal_moves)
|
||||
{
|
||||
if (game_obj.players.size() != 2) // we only support RPS, a 2 player game
|
||||
return;
|
||||
const rock_paper_scissors_game_details& rps_details = game_obj.game_details.get<rock_paper_scissors_game_details>();
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{
|
||||
if (rps_details.commit_moves.at(i) &&
|
||||
!rps_details.reveal_moves.at(i)) // if this player has committed but not revealed
|
||||
{
|
||||
const account_id_type& account_id = game_obj.players[i];
|
||||
if (_wallet.my_accounts.find(account_id) != _wallet.my_accounts.end()) // and they're us
|
||||
{
|
||||
if (self.is_locked())
|
||||
ilog("Game ${game_id}: unable to broadcast ${account_name}'s reveal because the wallet is locked",
|
||||
("game_id", game_obj.id)
|
||||
("account_name", get_account(account_id).name));
|
||||
else
|
||||
{
|
||||
ilog("Game ${game_id}: it is ${account_name}'s turn to reveal their move",
|
||||
("game_id", game_obj.id)
|
||||
("account_name", get_account(account_id).name));
|
||||
|
||||
auto iter = _wallet.committed_game_moves.find(*rps_details.commit_moves.at(i));
|
||||
if (iter != _wallet.committed_game_moves.end())
|
||||
{
|
||||
const rock_paper_scissors_throw_reveal& reveal = iter->second;
|
||||
|
||||
game_move_operation move_operation;
|
||||
move_operation.game_id = game_obj.id;
|
||||
move_operation.player_account_id = account_id;
|
||||
move_operation.move = reveal;
|
||||
|
||||
signed_transaction trx;
|
||||
trx.operations = {move_operation};
|
||||
set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees);
|
||||
trx.validate();
|
||||
ilog("Broadcasting reveal...");
|
||||
trx = sign_transaction(trx, true);
|
||||
ilog("Reveal broadcast, transaction id is ${id}", ("id", trx.id()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} FC_RETHROW_EXCEPTIONS(warn, "") }
|
||||
|
||||
void match_in_new_state(const match_object& match_obj)
|
||||
{ try {
|
||||
if (match_obj.get_state() == match_state::match_in_progress)
|
||||
{
|
||||
for (const account_id_type& account_id : match_obj.players)
|
||||
{
|
||||
if (_wallet.my_accounts.find(account_id) != _wallet.my_accounts.end())
|
||||
{
|
||||
ilog("Match ${match} is now in progress for player ${account}",
|
||||
("match", match_obj.id)("account", get_account(account_id).name));
|
||||
for (const game_id_type& game_id : match_obj.games)
|
||||
{
|
||||
game_object game_obj = get_object<game_object>(game_id);
|
||||
auto insert_result = game_cache.insert(game_obj);
|
||||
if (insert_result.second)
|
||||
game_in_new_state(game_obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} FC_RETHROW_EXCEPTIONS(warn, "") }
|
||||
|
||||
// Cache all matches in the tournament, which will also register us for
|
||||
// updates on those matches
|
||||
void monitor_matches_in_tournament(const tournament_object& tournament_obj)
|
||||
{ try {
|
||||
tournament_details_object tournament_details = get_object<tournament_details_object>(tournament_obj.tournament_details_id);
|
||||
for (const match_id_type& match_id : tournament_details.matches)
|
||||
{
|
||||
match_object match_obj = get_object<match_object>(match_id);
|
||||
auto insert_result = match_cache.insert(match_obj);
|
||||
if (insert_result.second)
|
||||
match_in_new_state(match_obj);
|
||||
}
|
||||
} FC_RETHROW_EXCEPTIONS(warn, "") }
|
||||
|
||||
void resync_active_tournaments()
|
||||
{
|
||||
// check to see if any of our accounts are registered for tournaments
|
||||
// the real purpose of this is to ensure that we are subscribed for callbacks on these tournaments
|
||||
ilog("Checking my accounts for active tournaments",);
|
||||
tournament_cache.clear();
|
||||
match_cache.clear();
|
||||
game_cache.clear();
|
||||
for (const account_object& my_account : _wallet.my_accounts)
|
||||
{
|
||||
std::vector<tournament_id_type> tournament_ids = _remote_db->get_registered_tournaments(my_account.id, 100);
|
||||
for (const tournament_id_type& tournament_id : tournament_ids)
|
||||
{
|
||||
try
|
||||
{
|
||||
tournament_object tournament = get_object<tournament_object>(tournament_id);
|
||||
auto insert_result = tournament_cache.insert(tournament);
|
||||
if (insert_result.second)
|
||||
{
|
||||
// then this is the first time we've seen this tournament
|
||||
monitor_matches_in_tournament(tournament);
|
||||
}
|
||||
tournament_ids.push_back(tournament.id);
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
edump((e)(tournament_id));
|
||||
}
|
||||
}
|
||||
if (!tournament_ids.empty())
|
||||
ilog("Account ${my_account} is registered for tournaments: ${tournaments}", ("my_account", my_account.name)("tournaments", tournament_ids));
|
||||
else
|
||||
ilog("Account ${my_account} is not registered for any tournaments", ("my_account", my_account.name));
|
||||
}
|
||||
}
|
||||
|
||||
bool load_wallet_file(string wallet_filename = "")
|
||||
{
|
||||
// TODO: Merge imported wallet with existing wallet,
|
||||
|
|
@ -771,6 +1055,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
resync_active_tournaments();
|
||||
|
||||
return true;
|
||||
}
|
||||
void save_wallet_file(string wallet_filename = "")
|
||||
|
|
@ -1476,6 +1762,11 @@ public:
|
|||
witness_create_op.witness_account = witness_account.id;
|
||||
witness_create_op.block_signing_key = witness_public_key;
|
||||
witness_create_op.url = url;
|
||||
secret_hash_type::encoder enc;
|
||||
fc::raw::pack(enc, witness_private_key);
|
||||
fc::raw::pack(enc, secret_hash_type());
|
||||
witness_create_op.initial_secret = secret_hash_type::hash(enc.result());
|
||||
|
||||
|
||||
if (_remote_db->get_witness_by_account(witness_create_op.witness_account))
|
||||
FC_THROW("Account ${owner_account} is already a witness", ("owner_account", owner_account));
|
||||
|
|
@ -1504,8 +1795,10 @@ public:
|
|||
witness_update_op.witness_account = witness_account.id;
|
||||
if( url != "" )
|
||||
witness_update_op.new_url = url;
|
||||
if( block_signing_key != "" )
|
||||
if( block_signing_key != "" ) {
|
||||
witness_update_op.new_signing_key = public_key_type( block_signing_key );
|
||||
witness_update_op.new_initial_secret = secret_hash_type::hash(secret_hash_type());
|
||||
}
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( witness_update_op );
|
||||
|
|
@ -1725,9 +2018,9 @@ public:
|
|||
} FC_CAPTURE_AND_RETHROW( (voting_account)(committee_member)(approve)(broadcast) ) }
|
||||
|
||||
signed_transaction vote_for_witness(string voting_account,
|
||||
string witness,
|
||||
bool approve,
|
||||
bool broadcast /* = false */)
|
||||
string witness,
|
||||
bool approve,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
account_object voting_account_object = get_account(voting_account);
|
||||
account_id_type witness_owner_account_id = get_account_id(witness);
|
||||
|
|
@ -1758,6 +2051,47 @@ public:
|
|||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (voting_account)(witness)(approve)(broadcast) ) }
|
||||
|
||||
signed_transaction update_witness_votes(string voting_account,
|
||||
std::vector<std::string> witnesses_to_approve,
|
||||
std::vector<std::string> witnesses_to_reject,
|
||||
uint16_t desired_number_of_witnesses,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
account_object voting_account_object = get_account(voting_account);
|
||||
for (const std::string& witness : witnesses_to_approve)
|
||||
{
|
||||
account_id_type witness_owner_account_id = get_account_id(witness);
|
||||
fc::optional<witness_object> witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id);
|
||||
if (!witness_obj)
|
||||
FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness));
|
||||
auto insert_result = voting_account_object.options.votes.insert(witness_obj->vote_id);
|
||||
if (!insert_result.second)
|
||||
FC_THROW("Account ${account} was already voting for witness ${witness}", ("account", voting_account)("witness", witness));
|
||||
}
|
||||
for (const std::string& witness : witnesses_to_reject)
|
||||
{
|
||||
account_id_type witness_owner_account_id = get_account_id(witness);
|
||||
fc::optional<witness_object> witness_obj = _remote_db->get_witness_by_account(witness_owner_account_id);
|
||||
if (!witness_obj)
|
||||
FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness));
|
||||
unsigned votes_removed = voting_account_object.options.votes.erase(witness_obj->vote_id);
|
||||
if (!votes_removed)
|
||||
FC_THROW("Account ${account} is already not voting for witness ${witness}", ("account", voting_account)("witness", witness));
|
||||
}
|
||||
voting_account_object.options.num_witness = desired_number_of_witnesses;
|
||||
|
||||
account_update_operation account_update_op;
|
||||
account_update_op.account = voting_account_object.id;
|
||||
account_update_op.new_options = voting_account_object.options;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( account_update_op );
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (voting_account)(witnesses_to_approve)(witnesses_to_reject)(desired_number_of_witnesses)(broadcast) ) }
|
||||
|
||||
signed_transaction set_voting_proxy(string account_to_modify,
|
||||
optional<string> voting_account,
|
||||
bool broadcast /* = false */)
|
||||
|
|
@ -2144,6 +2478,21 @@ public:
|
|||
return ss.str();
|
||||
};
|
||||
|
||||
m["list_core_accounts"] = [this](variant result, const fc::variants& a)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
auto balances = result.as<vector<account_balance_object>>();
|
||||
for (const account_balance_object& balance: balances)
|
||||
{
|
||||
const account_object& account = get_account(balance.owner);
|
||||
//ss << account.name << " " << std::string(balance.id) << " " << balance.balance.value << "\n";
|
||||
ss << account.name << " " << std::string(balance.id) << " " << get_asset(balance.asset_type).amount_to_pretty_string(balance.balance) << "\n";
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
m["get_blind_balances"] = [this](variant result, const fc::variants& a)
|
||||
{
|
||||
auto r = result.as<vector<asset>>();
|
||||
|
|
@ -2207,6 +2556,155 @@ public:
|
|||
}
|
||||
return ss.str();
|
||||
};
|
||||
m["get_upcoming_tournaments"] = m["get_tournaments"] = m["get_tournaments_by_state"] = [this](variant result, const fc::variants& a)
|
||||
{
|
||||
const vector<tournament_object> tournaments = result.as<vector<tournament_object> >();
|
||||
std::stringstream ss;
|
||||
ss << "ID GAME BUY IN PLAYERS\n";
|
||||
ss << "====================================================================================\n";
|
||||
for( const tournament_object& tournament_obj : tournaments )
|
||||
{
|
||||
asset_object buy_in_asset = get_asset(tournament_obj.options.buy_in.asset_id);
|
||||
ss << fc::variant(tournament_obj.id).as<std::string>() << " "
|
||||
<< buy_in_asset.amount_to_pretty_string(tournament_obj.options.buy_in.amount) << " "
|
||||
<< tournament_obj.options.number_of_players << " players\n";
|
||||
switch (tournament_obj.get_state())
|
||||
{
|
||||
case tournament_state::accepting_registrations:
|
||||
{
|
||||
ss << " Waiting for players, " << tournament_obj.registered_players << " of " << tournament_obj.options.number_of_players << " have registered\n";
|
||||
ss << " If enough players register, the game will start ";
|
||||
if (tournament_obj.options.start_time)
|
||||
ss << "at " << tournament_obj.options.start_time->to_iso_string() << "\n";
|
||||
else
|
||||
ss << *tournament_obj.options.start_delay << " seconds after the last player registers\n";
|
||||
break;
|
||||
}
|
||||
case tournament_state::awaiting_start:
|
||||
{
|
||||
ss << " All players have registered, tournament will start at " << tournament_obj.start_time->to_iso_string() << "\n";
|
||||
break;
|
||||
}
|
||||
case tournament_state::in_progress:
|
||||
{
|
||||
ss << " Tournament started at " << tournament_obj.start_time->to_iso_string() << "\n";
|
||||
break;
|
||||
}
|
||||
case tournament_state::registration_period_expired:
|
||||
{
|
||||
ss << " Tournament was canceled at " << tournament_obj.options.registration_deadline.to_iso_string() << ", not enough players registered\n";
|
||||
break;
|
||||
}
|
||||
case tournament_state::concluded:
|
||||
{
|
||||
ss << " Tournament finished at " << tournament_obj.end_time->to_iso_string() << "\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
};
|
||||
m["get_tournament"] = [this](variant result, const fc::variants& a)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
tournament_object tournament = result.as<tournament_object>();
|
||||
tournament_details_object tournament_details = _remote_db->get_objects({result["tournament_details_id"].as<object_id_type>()})[0].as<tournament_details_object>();
|
||||
tournament_state state = tournament.get_state();
|
||||
if (state == tournament_state::accepting_registrations)
|
||||
{
|
||||
ss << "Tournament is accepting registrations\n";
|
||||
ss << "Players " << tournament.registered_players << "/" << tournament.options.number_of_players << ":\n";
|
||||
for (const account_id_type& player : tournament_details.registered_players)
|
||||
ss << "\t" << get_account(player).name << "\n";
|
||||
}
|
||||
else if (state == tournament_state::registration_period_expired)
|
||||
{
|
||||
ss << "Tournament registration period expired\n";
|
||||
ss << "Players " << tournament.registered_players << "/" << tournament.options.number_of_players << ":\n";
|
||||
for (const account_id_type& player : tournament_details.registered_players)
|
||||
ss << "\t" << get_account(player).name << "\n";
|
||||
}
|
||||
else if (state == tournament_state::awaiting_start)
|
||||
{
|
||||
ss << "Tournament starts at " << tournament.start_time->to_iso_string() << "\n";
|
||||
ss << "Players:\n";
|
||||
for (const account_id_type& player : tournament_details.registered_players)
|
||||
ss << "\t" << get_account(player).name << "\n";
|
||||
}
|
||||
else if (state == tournament_state::in_progress ||
|
||||
state == tournament_state::concluded)
|
||||
{
|
||||
unsigned num_matches = tournament_details.matches.size();
|
||||
uint32_t num_rounds = boost::multiprecision::detail::find_msb(tournament_details.matches.size() + 1);
|
||||
unsigned num_rows = (num_matches + 1) * 2 - 1;
|
||||
for (unsigned row = 0; row < num_rows; ++row)
|
||||
{
|
||||
for (unsigned round = 0; round <= num_rounds; ++round)
|
||||
{
|
||||
unsigned row_offset = (1 << round) - 1;
|
||||
unsigned row_vertical_spacing = 1 << (round + 1);
|
||||
if (row >= row_offset &&
|
||||
(row - row_offset) % row_vertical_spacing == 0)
|
||||
{
|
||||
unsigned player_number_in_round = (row - row_offset) / row_vertical_spacing;
|
||||
unsigned first_player_in_round = (num_matches - (num_matches >> round)) * 2;
|
||||
unsigned player_number = first_player_in_round + player_number_in_round;
|
||||
|
||||
unsigned match_number = player_number / 2;
|
||||
unsigned player_in_match = player_number % 2;
|
||||
|
||||
std::string player_name;
|
||||
if (round == num_rounds)
|
||||
{
|
||||
match_object match = get_object<match_object>(tournament_details.matches[num_matches - 1]);
|
||||
if (match.get_state() == match_state::match_complete &&
|
||||
!match.match_winners.empty())
|
||||
{
|
||||
assert(match.match_winners.size() == 1);
|
||||
player_name = get_account(*match.match_winners.begin()).name;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
match_object match = get_object<match_object>(tournament_details.matches[match_number]);
|
||||
if (!match.players.empty())
|
||||
{
|
||||
if (player_in_match < match.players.size())
|
||||
player_name = get_account(match.players[player_in_match]).name;
|
||||
else
|
||||
player_name = "[bye]";
|
||||
}
|
||||
}
|
||||
|
||||
ss << "__";
|
||||
ss << std::setfill('_') << std::setw(10) << player_name.substr(0,10);
|
||||
ss << "__";
|
||||
}
|
||||
else
|
||||
ss << " ";
|
||||
|
||||
if (round != num_rounds)
|
||||
{
|
||||
unsigned round_horizontal_spacing = 1 << round;
|
||||
unsigned next_row_vertical_spacing = 1 << (round + 2);
|
||||
for (unsigned i = 0; i < round_horizontal_spacing; ++i)
|
||||
{
|
||||
if ((row - 1 - i - row_offset) % next_row_vertical_spacing == 0)
|
||||
ss << "\\";
|
||||
else if ((row - row_vertical_spacing + i - row_offset) % next_row_vertical_spacing == 0)
|
||||
ss << "/";
|
||||
else
|
||||
ss << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
ss << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
};
|
||||
m["get_order_book"] = [this](variant result, const fc::variants& a)
|
||||
{
|
||||
auto orders = result.as<order_book>();
|
||||
|
|
@ -2668,6 +3166,24 @@ public:
|
|||
|
||||
static_variant_map _operation_which_map = create_static_variant_map< operation >();
|
||||
|
||||
typedef multi_index_container<
|
||||
tournament_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > > > > tournament_index_type;
|
||||
tournament_index_type tournament_cache;
|
||||
|
||||
typedef multi_index_container<
|
||||
match_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > > > > match_index_type;
|
||||
match_index_type match_cache;
|
||||
|
||||
typedef multi_index_container<
|
||||
game_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > > > > game_index_type;
|
||||
game_index_type game_cache;
|
||||
|
||||
#ifdef __unix__
|
||||
mode_t _old_umask;
|
||||
#endif
|
||||
|
|
@ -2794,6 +3310,18 @@ std::string operation_printer::operator()(const asset_dividend_distribution_oper
|
|||
return "";
|
||||
}
|
||||
|
||||
std::string operation_printer::operator()(const tournament_payout_operation& op)const
|
||||
{
|
||||
asset_object payout_asset = wallet.get_asset(op.payout_amount.asset_id);
|
||||
|
||||
out << "Tournament #" << std::string(object_id_type(op.tournament_id)) << " Payout : "
|
||||
<< "Account '" << wallet.get_account(op.payout_account_id).name
|
||||
<< "', Amount " << payout_asset.amount_to_pretty_string(op.payout_amount) << ", Type "
|
||||
<< (op.type == payout_type::buyin_refund ? "buyin refund" : (op.type == payout_type::rake_fee ? "rake fee" : "prize award"))
|
||||
<< ".";
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string operation_result_printer::operator()(const void_result& x) const
|
||||
{
|
||||
return "";
|
||||
|
|
@ -2936,6 +3464,10 @@ vector<operation_detail> wallet_api::get_relative_account_history(string name, u
|
|||
}
|
||||
return result;
|
||||
}
|
||||
vector<account_balance_object> wallet_api::list_core_accounts()const
|
||||
{
|
||||
return my->_remote_hist->list_core_accounts();
|
||||
}
|
||||
|
||||
vector<bucket_object> wallet_api::get_market_history( string symbol1, string symbol2, uint32_t bucket , fc::time_point_sec start, fc::time_point_sec end )const
|
||||
{
|
||||
|
|
@ -2998,6 +3530,13 @@ bool wallet_api::is_public_key_registered(string public_key) const
|
|||
return is_known;
|
||||
}
|
||||
|
||||
pair<public_key_type,string> wallet_api::get_private_key_from_password( string account, string role, string password )const {
|
||||
auto seed = password + account + role;
|
||||
FC_ASSERT( seed.size() );
|
||||
auto secret = fc::sha256::hash( seed.c_str(), seed.size() );
|
||||
auto priv = fc::ecc::private_key::regenerate( secret );
|
||||
return std::make_pair( public_key_type( priv.get_public_key() ), key_to_wif( priv ) );
|
||||
}
|
||||
|
||||
string wallet_api::serialize_transaction( signed_transaction tx )const
|
||||
{
|
||||
|
|
@ -3465,6 +4004,15 @@ signed_transaction wallet_api::vote_for_witness(string voting_account,
|
|||
return my->vote_for_witness(voting_account, witness, approve, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::update_witness_votes(string voting_account,
|
||||
std::vector<std::string> witnesses_to_approve,
|
||||
std::vector<std::string> witnesses_to_reject,
|
||||
uint16_t desired_number_of_witnesses,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->update_witness_votes(voting_account, witnesses_to_approve, witnesses_to_reject, desired_number_of_witnesses, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::set_voting_proxy(string account_to_modify,
|
||||
optional<string> voting_account,
|
||||
bool broadcast /* = false */)
|
||||
|
|
@ -3724,6 +4272,7 @@ void wallet_api::unlock(string password)
|
|||
my->_keys = std::move(pk.keys);
|
||||
my->_checksum = pk.checksum;
|
||||
my->self.lock_changed(false);
|
||||
my->resync_active_tournaments();
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void wallet_api::set_password( string password )
|
||||
|
|
@ -4673,6 +5222,150 @@ signed_transaction wallet_api::propose_resolve_betting_market_group(
|
|||
return my->sign_transaction(tx, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::tournament_create( string creator, tournament_options options, bool broadcast )
|
||||
{
|
||||
FC_ASSERT( !is_locked() );
|
||||
account_object creator_account_obj = get_account(creator);
|
||||
|
||||
signed_transaction tx;
|
||||
tournament_create_operation op;
|
||||
op.creator = creator_account_obj.get_id();
|
||||
op.options = options;
|
||||
tx.operations = {op};
|
||||
my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees );
|
||||
tx.validate();
|
||||
|
||||
return my->sign_transaction( tx, broadcast );
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::tournament_join( string payer_account,
|
||||
string player_account,
|
||||
tournament_id_type tournament_id,
|
||||
string buy_in_amount,
|
||||
string buy_in_asset_symbol,
|
||||
bool broadcast )
|
||||
{
|
||||
FC_ASSERT( !is_locked() );
|
||||
account_object payer_account_obj = get_account(payer_account);
|
||||
account_object player_account_obj = get_account(player_account);
|
||||
//graphene::chain::tournament_object tournament_obj = my->get_object<graphene::chain::tournament_object>(tournament_id);
|
||||
|
||||
fc::optional<asset_object> buy_in_asset_obj = get_asset(buy_in_asset_symbol);
|
||||
FC_ASSERT(buy_in_asset_obj, "Could not find asset matching ${asset}", ("asset", buy_in_asset_symbol));
|
||||
|
||||
signed_transaction tx;
|
||||
tournament_join_operation op;
|
||||
op.payer_account_id = payer_account_obj.get_id();
|
||||
op.player_account_id = player_account_obj.get_id();
|
||||
op.tournament_id = tournament_id;
|
||||
op.buy_in = buy_in_asset_obj->amount_from_string(buy_in_amount);
|
||||
|
||||
tx.operations = {op};
|
||||
my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees );
|
||||
tx.validate();
|
||||
|
||||
return my->sign_transaction( tx, broadcast );
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::tournament_leave( string canceling_account,
|
||||
string player_account,
|
||||
tournament_id_type tournament_id,
|
||||
bool broadcast)
|
||||
{
|
||||
FC_ASSERT( !is_locked() );
|
||||
account_object player_account_obj = get_account(player_account);
|
||||
account_object canceling_account_obj = get_account(canceling_account);
|
||||
//graphene::chain::tournament_object tournament_obj = my->get_object<graphene::chain::tournament_object>(tournament_id);
|
||||
|
||||
signed_transaction tx;
|
||||
tournament_leave_operation op;
|
||||
op.canceling_account_id = canceling_account_obj.get_id();
|
||||
op.player_account_id = player_account_obj.get_id();
|
||||
op.tournament_id = tournament_id;
|
||||
|
||||
tx.operations = {op};
|
||||
my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees );
|
||||
tx.validate();
|
||||
|
||||
return my->sign_transaction( tx, broadcast );
|
||||
}
|
||||
|
||||
vector<tournament_object> wallet_api::get_upcoming_tournaments(uint32_t limit)
|
||||
{
|
||||
return my->_remote_db->get_tournaments_in_state(tournament_state::accepting_registrations, limit);
|
||||
}
|
||||
vector<tournament_object> wallet_api::get_tournaments(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start) {
|
||||
return my->_remote_db->get_tournaments(stop, limit, start);
|
||||
}
|
||||
|
||||
vector<tournament_object> wallet_api::get_tournaments_by_state(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start,
|
||||
tournament_state state) {
|
||||
return my->_remote_db->get_tournaments_by_state(stop, limit, start, state);
|
||||
}
|
||||
|
||||
tournament_object wallet_api::get_tournament(tournament_id_type id)
|
||||
{
|
||||
return my->_remote_db->get_objects({id})[0].as<tournament_object>();
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::rps_throw(game_id_type game_id,
|
||||
string player_account,
|
||||
rock_paper_scissors_gesture gesture,
|
||||
bool broadcast)
|
||||
{
|
||||
FC_ASSERT( !is_locked() );
|
||||
|
||||
// check whether the gesture is appropriate for the game we're playing
|
||||
graphene::chain::game_object game_obj = my->get_object<graphene::chain::game_object>(game_id);
|
||||
graphene::chain::match_object match_obj = my->get_object<graphene::chain::match_object>(game_obj.match_id);
|
||||
graphene::chain::tournament_object tournament_obj = my->get_object<graphene::chain::tournament_object>(match_obj.tournament_id);
|
||||
graphene::chain::rock_paper_scissors_game_options game_options =
|
||||
tournament_obj.options.game_options.get<graphene::chain::rock_paper_scissors_game_options>();
|
||||
if ((int)gesture >= game_options.number_of_gestures)
|
||||
FC_THROW("Gesture ${gesture} not supported in this game", ("gesture", gesture));
|
||||
|
||||
account_object player_account_obj = get_account(player_account);
|
||||
|
||||
// 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 transmitting after both players commit
|
||||
my->_wallet.committed_game_moves[commit_throw] = reveal_throw;
|
||||
|
||||
// broadcast the commit
|
||||
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};
|
||||
my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees );
|
||||
tx.validate();
|
||||
|
||||
return my->sign_transaction( tx, broadcast );
|
||||
}
|
||||
|
||||
// default ctor necessary for FC_REFLECT
|
||||
signed_block_with_info::signed_block_with_info()
|
||||
{
|
||||
}
|
||||
|
||||
order_book wallet_api::get_order_book( const string& base, const string& quote, unsigned limit )
|
||||
{
|
||||
return( my->_remote_db->get_order_book( base, quote, limit ) );
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ struct if_reflected<fc::true_type>
|
|||
template< typename T >
|
||||
static void process_class( class_processor* proc, const T* dummy )
|
||||
{
|
||||
if_enum< typename fc::reflector<T>::is_enum >::process_class(proc, dummy);
|
||||
if_enum< typename fc::reflector<T>::is_enum >::process_class(proc, dummy);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ int main( int argc, char** argv )
|
|||
("dev-account-count", bpo::value<uint32_t>()->default_value(0), "Prefix for dev accounts")
|
||||
("dev-balance-count", bpo::value<uint32_t>()->default_value(0), "Prefix for dev balances")
|
||||
("dev-balance-amount", bpo::value<uint64_t>()->default_value(uint64_t(1000)*uint64_t(1000)*uint64_t(100000)), "Amount in each dev balance")
|
||||
("nop", "just write the genesis file out after reading it in, do not alter any keys or add accounts or balances. used to pretty-print a genesis file")
|
||||
("replace-all-keys", bpo::value<boost::filesystem::path>(), "Replace all keys/addresses in the genesis files with dev keys based on dev-key-prefix and dump the new keys to this filename.")
|
||||
;
|
||||
|
||||
bpo::variables_map options;
|
||||
|
|
@ -116,54 +118,134 @@ int main( int argc, char** argv )
|
|||
genesis = graphene::app::detail::create_example_genesis();
|
||||
}
|
||||
|
||||
std::string dev_key_prefix = options["dev-key-prefix"].as<std::string>();
|
||||
|
||||
auto get_dev_key = [&]( std::string prefix, uint32_t i ) -> public_key_type
|
||||
if (!options.count("nop"))
|
||||
{
|
||||
return fc::ecc::private_key::regenerate( fc::sha256::hash( dev_key_prefix + prefix + std::to_string(i) ) ).get_public_key();
|
||||
};
|
||||
|
||||
uint32_t dev_account_count = options["dev-account-count"].as<uint32_t>();
|
||||
std::string dev_account_prefix = options["dev-account-prefix"].as<std::string>();
|
||||
for(uint32_t i=0;i<dev_account_count;i++)
|
||||
{
|
||||
genesis_state_type::initial_account_type acct(
|
||||
dev_account_prefix+std::to_string(i),
|
||||
get_dev_key( "owner-", i ),
|
||||
get_dev_key( "active-", i ),
|
||||
false );
|
||||
|
||||
genesis.initial_accounts.push_back( acct );
|
||||
}
|
||||
|
||||
uint32_t dev_balance_count = options["dev-balance-count"].as<uint32_t>();
|
||||
uint64_t dev_balance_amount = options["dev-balance-amount"].as<uint64_t>();
|
||||
for(uint32_t i=0;i<dev_balance_count;i++)
|
||||
{
|
||||
genesis_state_type::initial_balance_type bal;
|
||||
bal.owner = address( get_dev_key( "balance-", i ) );
|
||||
bal.asset_symbol = "CORE";
|
||||
bal.amount = dev_balance_amount;
|
||||
genesis.initial_balances.push_back( bal );
|
||||
}
|
||||
|
||||
std::map< std::string, size_t > name2index;
|
||||
size_t num_accounts = genesis.initial_accounts.size();
|
||||
for( size_t i=0; i<num_accounts; i++ )
|
||||
name2index[ genesis.initial_accounts[i].name ] = i;
|
||||
|
||||
for( uint32_t i=0; i<genesis.initial_active_witnesses; i++ )
|
||||
{
|
||||
genesis_state_type::initial_witness_type& wit = genesis.initial_witness_candidates[ i ];
|
||||
genesis_state_type::initial_account_type& wit_acct = genesis.initial_accounts[ name2index[ wit.owner_name ] ];
|
||||
if( wit.owner_name.substr(0, 4) != "init" )
|
||||
std::string dev_key_prefix = options["dev-key-prefix"].as<std::string>();
|
||||
auto get_dev_key = [&]( std::string prefix, uint32_t i ) -> public_key_type
|
||||
{
|
||||
std::cerr << "need " << genesis.initial_active_witnesses << " init accounts as first entries in initial_active_witnesses\n";
|
||||
return 1;
|
||||
return fc::ecc::private_key::regenerate( fc::sha256::hash( dev_key_prefix + prefix + std::to_string(i) ) ).get_public_key();
|
||||
};
|
||||
|
||||
if (options.count("replace-all-keys"))
|
||||
{
|
||||
unsigned dev_keys_used = 0;
|
||||
std::map<std::string, fc::ecc::private_key> replacement_keys;
|
||||
auto get_replacement_key = [&](const std::string& original_key) -> fc::ecc::private_key {
|
||||
auto iter = replacement_keys.find(original_key);
|
||||
if (iter != replacement_keys.end())
|
||||
return iter->second;
|
||||
fc::ecc::private_key new_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(dev_key_prefix + std::to_string(dev_keys_used++)));
|
||||
replacement_keys[original_key] = new_private_key;
|
||||
return new_private_key;
|
||||
};
|
||||
|
||||
for (genesis_state_type::initial_balance_type& initial_balance : genesis.initial_balances)
|
||||
{
|
||||
std::string address_string = (std::string)initial_balance.owner;
|
||||
initial_balance.owner = address(get_replacement_key(address_string).get_public_key());
|
||||
}
|
||||
|
||||
for (genesis_state_type::initial_vesting_balance_type& initial_balance : genesis.initial_vesting_balances)
|
||||
{
|
||||
std::string address_string = (std::string)initial_balance.owner;
|
||||
initial_balance.owner = address(get_replacement_key(address_string).get_public_key());
|
||||
}
|
||||
|
||||
for (genesis_state_type::initial_witness_type& initial_witness : genesis.initial_witness_candidates)
|
||||
{
|
||||
std::string public_key_string = (std::string)initial_witness.block_signing_key;
|
||||
initial_witness.block_signing_key = get_replacement_key(public_key_string).get_public_key();
|
||||
}
|
||||
|
||||
for (genesis_state_type::initial_account_type& initial_account : genesis.initial_accounts)
|
||||
{
|
||||
std::string public_key_string = (std::string)initial_account.owner_key;
|
||||
initial_account.owner_key = get_replacement_key(public_key_string).get_public_key();
|
||||
public_key_string = (std::string)initial_account.active_key;
|
||||
initial_account.active_key = get_replacement_key(public_key_string).get_public_key();
|
||||
}
|
||||
|
||||
for (genesis_state_type::initial_bts_account_type& initial_account : genesis.initial_bts_accounts)
|
||||
{
|
||||
for (auto iter = initial_account.owner_authority.key_auths.begin();
|
||||
iter != initial_account.owner_authority.key_auths.end(); ++iter)
|
||||
{
|
||||
std::string public_key_string = (std::string)iter->first;
|
||||
iter->first = get_replacement_key(public_key_string).get_public_key();
|
||||
}
|
||||
for (auto iter = initial_account.active_authority.key_auths.begin();
|
||||
iter != initial_account.active_authority.key_auths.end(); ++iter)
|
||||
{
|
||||
std::string public_key_string = (std::string)iter->first;
|
||||
iter->first = get_replacement_key(public_key_string).get_public_key();
|
||||
}
|
||||
for (auto iter = initial_account.owner_authority.address_auths.begin();
|
||||
iter != initial_account.owner_authority.address_auths.end(); ++iter)
|
||||
{
|
||||
std::string address_string = (std::string)iter->first;
|
||||
iter->first = address(get_replacement_key(address_string).get_public_key());
|
||||
}
|
||||
for (auto iter = initial_account.active_authority.address_auths.begin();
|
||||
iter != initial_account.active_authority.address_auths.end(); ++iter)
|
||||
{
|
||||
std::string address_string = (std::string)iter->first;
|
||||
iter->first = address(get_replacement_key(address_string).get_public_key());
|
||||
}
|
||||
}
|
||||
fc::path keys_csv_path = options["replace-all-keys"].as<boost::filesystem::path>();
|
||||
std::ofstream keys_csv(keys_csv_path.string());
|
||||
keys_csv << "wif_private_key,public_key,address\n";
|
||||
for (const auto& value : replacement_keys)
|
||||
keys_csv << graphene::utilities::key_to_wif(value.second) << "," << std::string(public_key_type(value.second.get_public_key()))
|
||||
<< "," << std::string(address(value.second.get_public_key())) << "\n";
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t dev_account_count = options["dev-account-count"].as<uint32_t>();
|
||||
std::string dev_account_prefix = options["dev-account-prefix"].as<std::string>();
|
||||
for(uint32_t i=0;i<dev_account_count;i++)
|
||||
{
|
||||
genesis_state_type::initial_account_type acct(
|
||||
dev_account_prefix+std::to_string(i),
|
||||
get_dev_key( "owner-", i ),
|
||||
get_dev_key( "active-", i ),
|
||||
false );
|
||||
|
||||
genesis.initial_accounts.push_back( acct );
|
||||
}
|
||||
|
||||
uint32_t dev_balance_count = options["dev-balance-count"].as<uint32_t>();
|
||||
uint64_t dev_balance_amount = options["dev-balance-amount"].as<uint64_t>();
|
||||
for(uint32_t i=0;i<dev_balance_count;i++)
|
||||
{
|
||||
genesis_state_type::initial_balance_type bal;
|
||||
bal.owner = address( get_dev_key( "balance-", i ) );
|
||||
bal.asset_symbol = "CORE";
|
||||
bal.amount = dev_balance_amount;
|
||||
genesis.initial_balances.push_back( bal );
|
||||
}
|
||||
|
||||
std::map< std::string, size_t > name2index;
|
||||
size_t num_accounts = genesis.initial_accounts.size();
|
||||
for( size_t i=0; i<num_accounts; i++ )
|
||||
name2index[ genesis.initial_accounts[i].name ] = i;
|
||||
|
||||
for( uint32_t i=0; i<genesis.initial_active_witnesses; i++ )
|
||||
{
|
||||
genesis_state_type::initial_witness_type& wit = genesis.initial_witness_candidates[ i ];
|
||||
genesis_state_type::initial_account_type& wit_acct = genesis.initial_accounts[ name2index[ wit.owner_name ] ];
|
||||
if( wit.owner_name.substr(0, 4) != "init" )
|
||||
{
|
||||
std::cerr << "need " << genesis.initial_active_witnesses << " init accounts as first entries in initial_active_witnesses\n";
|
||||
return 1;
|
||||
}
|
||||
wit.block_signing_key = get_dev_key( "wit-block-signing-", i );
|
||||
wit_acct.owner_key = get_dev_key( "wit-owner-", i );
|
||||
wit_acct.active_key = get_dev_key( "wit-active-", i );
|
||||
}
|
||||
}
|
||||
wit.block_signing_key = get_dev_key( "wit-block-signing-", i );
|
||||
wit_acct.owner_key = get_dev_key( "wit-owner-", i );
|
||||
wit_acct.active_key = get_dev_key( "wit-active-", i );
|
||||
}
|
||||
|
||||
fc::path output_filename = options["out"].as<boost::filesystem::path>();
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@
|
|||
#include <graphene/chain/event_group_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <iostream>
|
||||
|
|
@ -383,9 +386,10 @@ template<typename T, bool reflected>
|
|||
struct serializer
|
||||
{
|
||||
static_assert( fc::reflector<T>::is_defined::value == reflected, "invalid template arguments" );
|
||||
|
||||
static void init()
|
||||
{
|
||||
serializer_init_helper< T, typename fc::reflector<T>::is_enum >::init();
|
||||
serializer_init_helper< T, typename fc::reflector<T>::is_enum >::init();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,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_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
|
||||
witness_node
|
||||
|
|
|
|||
|
|
@ -25,7 +25,10 @@
|
|||
|
||||
#include <graphene/witness/witness.hpp>
|
||||
#include <graphene/account_history/account_history_plugin.hpp>
|
||||
#include <graphene/accounts_list/accounts_list_plugin.hpp>
|
||||
#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 <fc/exception/exception.hpp>
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
|
@ -74,6 +77,9 @@ int main(int argc, char** argv) {
|
|||
auto witness_plug = node->register_plugin<witness_plugin::witness_plugin>();
|
||||
auto history_plug = node->register_plugin<account_history::account_history_plugin>();
|
||||
auto market_history_plug = node->register_plugin<market_history::market_history_plugin>();
|
||||
//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>();
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,4 +33,12 @@ file(GLOB BETTING_TESTS "betting/*.cpp")
|
|||
add_executable( betting_test ${BETTING_TESTS} ${COMMON_SOURCES} )
|
||||
target_link_libraries( betting_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} )
|
||||
|
||||
file(GLOB TOURNAMENT_TESTS "tournament/*.cpp")
|
||||
add_executable( tournament_test ${TOURNAMENT_TESTS} ${COMMON_SOURCES} )
|
||||
target_link_libraries( tournament_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
|
||||
|
||||
file(GLOB RANDOM_SOURCES "random/*.cpp")
|
||||
add_executable( random_test ${RANDOM_SOURCES} ${COMMON_SOURCES} )
|
||||
target_link_libraries( random_test graphene_chain graphene_app graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
|
||||
|
||||
add_subdirectory( generate_empty_blocks )
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include <graphene/chain/sport_object.hpp>
|
||||
#include <graphene/chain/event_group_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
|
||||
#include <graphene/utilities/tempdir.hpp>
|
||||
|
||||
|
|
@ -77,6 +78,7 @@ database_fixture::database_fixture()
|
|||
if( arg == "--show-test-names" )
|
||||
std::cout << "running test " << boost::unit_test::framework::current_test_case().p_name << std::endl;
|
||||
}
|
||||
|
||||
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>();
|
||||
|
|
@ -85,9 +87,11 @@ database_fixture::database_fixture()
|
|||
boost::program_options::variables_map options;
|
||||
|
||||
genesis_state.initial_timestamp = time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
|
||||
genesis_state.initial_timestamp = time_point_sec( (fc::time_point::now().sec_since_epoch() / GRAPHENE_DEFAULT_BLOCK_INTERVAL) * GRAPHENE_DEFAULT_BLOCK_INTERVAL );
|
||||
// genesis_state.initial_parameters.witness_schedule_algorithm = GRAPHENE_WITNESS_SHUFFLED_ALGORITHM;
|
||||
|
||||
genesis_state.initial_active_witnesses = 10;
|
||||
for( int i = 0; i < genesis_state.initial_active_witnesses; ++i )
|
||||
for( unsigned i = 0; i < genesis_state.initial_active_witnesses; ++i )
|
||||
{
|
||||
auto name = "init"+fc::to_string(i);
|
||||
genesis_state.initial_accounts.emplace_back(name,
|
||||
|
|
@ -164,11 +168,17 @@ void database_fixture::verify_asset_supplies( const database& db )
|
|||
const simple_index<account_statistics_object>& statistics_index = db.get_index_type<simple_index<account_statistics_object>>();
|
||||
const auto& balance_index = db.get_index_type<account_balance_index>().indices();
|
||||
const auto& settle_index = db.get_index_type<force_settlement_index>().indices();
|
||||
const auto& tournaments_index = db.get_index_type<tournament_index>().indices();
|
||||
|
||||
map<asset_id_type,share_type> total_balances;
|
||||
map<asset_id_type,share_type> total_debts;
|
||||
share_type core_in_orders;
|
||||
share_type reported_core_in_orders;
|
||||
|
||||
for( const tournament_object& t : tournaments_index )
|
||||
if (t.get_state() != tournament_state::concluded && t.get_state() != tournament_state::registration_period_expired)
|
||||
total_balances[t.options.buy_in.asset_id] += t.prize_pool;
|
||||
|
||||
for( const account_balance_object& b : balance_index )
|
||||
total_balances[b.asset_type] += b.balance;
|
||||
for( const force_settlement_object& s : settle_index )
|
||||
|
|
@ -682,6 +692,10 @@ const witness_object& database_fixture::create_witness( const account_object& ow
|
|||
witness_create_operation op;
|
||||
op.witness_account = owner.id;
|
||||
op.block_signing_key = signing_private_key.get_public_key();
|
||||
secret_hash_type::encoder enc;
|
||||
fc::raw::pack(enc, signing_private_key);
|
||||
fc::raw::pack(enc, secret_hash_type());
|
||||
op.initial_secret = secret_hash_type::hash(enc.result());
|
||||
trx.operations.push_back(op);
|
||||
trx.validate();
|
||||
processed_transaction ptx = db.push_transaction(trx, ~0);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <graphene/chain/protocol/protocol.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/witness_schedule_object.hpp>
|
||||
#include <graphene/chain/vesting_balance_object.hpp>
|
||||
|
||||
#include <fc/crypto/digest.hpp>
|
||||
|
|
|
|||
20
tests/random.sh
Executable file
20
tests/random.sh
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
i=1
|
||||
while [ 0 ]; do
|
||||
|
||||
echo "*** $i `date`"
|
||||
if [ -f random-2 ]; then
|
||||
mv random-2 random-2-last
|
||||
fi
|
||||
./random_test --log_level=message 2> random-2
|
||||
echo "*** $i `date`"
|
||||
echo
|
||||
if [ "$1" = "-c" ]; then
|
||||
sleep 2
|
||||
else
|
||||
break
|
||||
fi
|
||||
i=$[i + 1]
|
||||
|
||||
done
|
||||
154
tests/random/random_tests.cpp
Normal file
154
tests/random/random_tests.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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 <boost/test/unit_test.hpp>
|
||||
//#include <fc/crypto/openssl.hpp>
|
||||
//#include <openssl/rand.h>
|
||||
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include "../common/database_fixture.hpp"
|
||||
#include <graphene/utilities/tempdir.hpp>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
using namespace graphene::chain;
|
||||
|
||||
bool test_standard_rand = false;
|
||||
bool all_tests = false;
|
||||
bool game_is_over = false;
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
std::cout << "." << std::endl;
|
||||
game_is_over = true;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(tournament_tests)
|
||||
|
||||
bool one_test(database_fixture& df, int test_nr = 0, int tsamples = 0, int psamples = 0, std::string options = "")
|
||||
{
|
||||
game_is_over = false;
|
||||
|
||||
std::string command = "dieharder -g 200 -d " + std::to_string(test_nr) ;
|
||||
if (tsamples)
|
||||
command += " -t " + std::to_string(tsamples);
|
||||
if (psamples)
|
||||
command += " -p " + std::to_string(psamples);
|
||||
if (!options.empty())
|
||||
command += " " + options;
|
||||
|
||||
FILE *io = popen(command.c_str(), "w");
|
||||
BOOST_CHECK(io);
|
||||
if(!io)
|
||||
return false;
|
||||
|
||||
int r;
|
||||
void *binary_data;
|
||||
size_t binary_data_length = sizeof(r);
|
||||
int m = 0; // 0 not generate blocks, > 0 generate block every m generated numbers 100 - 1000 are reasonable
|
||||
int i = 0;
|
||||
|
||||
while ( !game_is_over && !feof(io) && !ferror(io) )
|
||||
{
|
||||
if (test_standard_rand) {
|
||||
r = rand();
|
||||
} else {
|
||||
if (i) {
|
||||
--i;
|
||||
df.generate_block();
|
||||
} else {
|
||||
i = m;
|
||||
}
|
||||
r = df.db.get_random_bits((uint64_t)INT_MAX+1);
|
||||
}
|
||||
|
||||
binary_data = (void *) &r;
|
||||
size_t l =
|
||||
fwrite(binary_data, 1, binary_data_length, io);
|
||||
if (l != binary_data_length) break;
|
||||
//fflush(io);
|
||||
}
|
||||
|
||||
pclose(io);
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( basic, database_fixture )
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string o(" dieharder ");
|
||||
o.append(all_tests ? "all" : "selected").
|
||||
append(" tests of ").
|
||||
append(test_standard_rand ? "rand" : "get_random_bits");
|
||||
BOOST_TEST_MESSAGE("Hello" + o);
|
||||
|
||||
std::vector<int> selected = {0, 1, 3, 5, 6, 15};
|
||||
#if 1
|
||||
// trying to randomize starting point
|
||||
int r = std::rand() % 100;
|
||||
for(int i = 0; i < r ; ++i)
|
||||
db.get_random_bits(INT_MAX);
|
||||
#endif
|
||||
for (int i = 0; i < 18; ++i)
|
||||
{
|
||||
if (!all_tests && std::find(selected.begin(), selected.end(), i) == selected.end())
|
||||
continue;
|
||||
BOOST_TEST_MESSAGE("#" + std::to_string(i));
|
||||
if (!one_test(*this, i))
|
||||
break;
|
||||
}
|
||||
BOOST_TEST_MESSAGE("Bye" + o);
|
||||
}
|
||||
catch (fc::exception& e)
|
||||
{
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
//#define BOOST_TEST_MODULE "C++ Unit Tests for Graphene Blockchain Database"
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
|
||||
for (int i=1; i<argc; i++)
|
||||
{
|
||||
const std::string arg = argv[i];
|
||||
std::cout << "#" << i << " " << arg << std::endl;
|
||||
if(arg == "-R")
|
||||
test_standard_rand = true;
|
||||
else if(arg == "-A")
|
||||
all_tests = true;
|
||||
}
|
||||
std::srand(time(NULL));
|
||||
std::cout << "Random number generator seeded to " << time(NULL) << std::endl;
|
||||
if (signal(SIGPIPE, sig_handler) == SIG_ERR)
|
||||
std::cout << "Can't catch SIGPIPE signal!" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
10
tests/random/readme
Normal file
10
tests/random/readme
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
options:
|
||||
-R test standard 'rand' instead of 'get_random_bits';
|
||||
-A run all dieharder tests, default is to run only selected tests.
|
||||
|
||||
conclusion:
|
||||
quality of randomness of 'get_random_bits' is comparable with randomness standard 'rand' function,
|
||||
however about an order of magnitude slower than 'rand';
|
||||
and distinctly weaker compared to e.g. /dev/urandom that is assessed by dieharder as almost perfect.
|
||||
|
||||
|
|
@ -29,12 +29,14 @@
|
|||
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/witness_scheduler_rng.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
|
||||
#include <graphene/db/simple_index.hpp>
|
||||
|
||||
#include <fc/crypto/digest.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/crypto/hash_ctr_rng.hpp>
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
|
@ -197,6 +199,134 @@ BOOST_AUTO_TEST_CASE( memo_test )
|
|||
BOOST_CHECK_EQUAL(m.get_message(receiver, sender.get_public_key()), "Hello, world!");
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_AUTO_TEST_CASE( witness_rng_test_bits )
|
||||
{
|
||||
try
|
||||
{
|
||||
const uint64_t COUNT = 131072;
|
||||
const uint64_t HASH_SIZE = 32;
|
||||
string ref_bits = "";
|
||||
ref_bits.reserve( COUNT * HASH_SIZE );
|
||||
static const char seed_data[] = "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55";
|
||||
|
||||
for( uint64_t i=0; i<COUNT; i++ )
|
||||
{
|
||||
// grab the bits
|
||||
fc::sha256::encoder enc;
|
||||
enc.write( seed_data, HASH_SIZE );
|
||||
enc.put( char((i ) & 0xFF) );
|
||||
enc.put( char((i >> 0x08) & 0xFF) );
|
||||
enc.put( char((i >> 0x10) & 0xFF) );
|
||||
enc.put( char((i >> 0x18) & 0xFF) );
|
||||
enc.put( char((i >> 0x20) & 0xFF) );
|
||||
enc.put( char((i >> 0x28) & 0xFF) );
|
||||
enc.put( char((i >> 0x30) & 0xFF) );
|
||||
enc.put( char((i >> 0x38) & 0xFF) );
|
||||
|
||||
fc::sha256 result = enc.result();
|
||||
auto result_data = result.data();
|
||||
std::copy( result_data, result_data+HASH_SIZE, std::back_inserter( ref_bits ) );
|
||||
}
|
||||
|
||||
fc::sha256 seed = fc::sha256::hash( string("") );
|
||||
// seed = sha256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||
BOOST_CHECK( memcmp( seed.data(), seed_data, HASH_SIZE ) == 0 );
|
||||
|
||||
fc::hash_ctr_rng<fc::sha256, 32> test_rng(seed.data(), 0);
|
||||
// python2 -c 'import hashlib; import struct; h = lambda x : hashlib.sha256(x).digest(); i = lambda x : struct.pack("<Q", x); print( h( h("") + i(0) ) )' | hd
|
||||
string ref_bits_hex =
|
||||
"5c5d42dcf39f71c0226ca720d8d518db615b5773f038e5e491963f6f47621bbd" // h( h("") + i(0) )
|
||||
"43fd6dae047c400060be262e6d443200eacd1fafcb77828638085c2e2341fd8d" // h( h("") + i(1) )
|
||||
"d666330a7441dc7279b786e65aba32817275989cfc691b3901f000fb0f14cd05" // h( h("") + i(2) )
|
||||
"34bd93f83d7bac4a667d62fee39bd5eb1991fbadc29a5f216ea746772ca31544" // h( h("") + i(3) )
|
||||
"d3b41a093eab01cd25f987a909b2f4812b0f38475e0fe40f6f42a12c6e018aa7" // ...
|
||||
"c8db17b946c5a6bceaa7b903c93e6ccb8cc6c09b0cfd2108d930de1a79c3a68e"
|
||||
"cc1945b36c82e356b6d127057d036a150cb03b760e9c9e706c560f32a749e80d"
|
||||
"872b28fe97e289d4f6f361f3427d454113e3b513892d129398dac4daf8a0e43e"
|
||||
"8d7a5a2f3cbb245fa471e87e30a38d9c775c985c28db6e521e34cf1e88507c26"
|
||||
"c662f230eed0f10899c3a74a2d1bfb88d732909b206a2aed3ae0bda728fac8fe"
|
||||
"38eface8b1d473e45cbb40603bcef8bf2219e55669c7a2cfb5f8d52610689f14"
|
||||
"3b1d1734273b069a7de7cc6dd2e80db09d1feff200c9bdaf033cd553ea40e05d"
|
||||
"16653ca7aa7f790a95c6a8d41e5694b0c6bff806c3ce3e0e320253d408fb6f27"
|
||||
"b55df71d265de0b86a1cdf45d1d9c53da8ebf0ceec136affa12228d0d372e698"
|
||||
"37e9305ce57d386d587039b49b67104fd4d8467e87546237afc9a90cf8c677f9"
|
||||
"fc26784c94f754cf7aeacb6189e705e2f1873ea112940560f11dbbebb22a8922"
|
||||
;
|
||||
char* ref_bits_chars = new char[ ref_bits_hex.length() / 2 ];
|
||||
fc::from_hex( ref_bits_hex, ref_bits_chars, ref_bits_hex.length() / 2 );
|
||||
string ref_bits_str( ref_bits_chars, ref_bits_hex.length() / 2 );
|
||||
delete[] ref_bits_chars;
|
||||
ref_bits_chars = nullptr;
|
||||
|
||||
BOOST_CHECK( ref_bits_str.length() < ref_bits.length() );
|
||||
BOOST_CHECK( ref_bits_str == ref_bits.substr( 0, ref_bits_str.length() ) );
|
||||
//std::cout << "ref_bits_str: " << fc::to_hex( ref_bits_str.c_str(), std::min( ref_bits_str.length(), size_t(256) ) ) << "\n";
|
||||
//std::cout << "ref_bits : " << fc::to_hex( ref_bits .c_str(), std::min( ref_bits.length(), size_t(256) ) ) << "\n";
|
||||
|
||||
// when we get to this point, our code to generate the RNG byte output is working.
|
||||
// now let's test get_bits() as follows:
|
||||
|
||||
uint64_t ref_get_bits_offset = 0;
|
||||
|
||||
auto ref_get_bits = [&]( uint8_t count ) -> uint64_t
|
||||
{
|
||||
uint64_t result = 0;
|
||||
uint64_t i = ref_get_bits_offset;
|
||||
uint64_t mask = 1;
|
||||
while( count > 0 )
|
||||
{
|
||||
if( ref_bits[ i >> 3 ] & (1 << (i & 7)) )
|
||||
result |= mask;
|
||||
mask += mask;
|
||||
i++;
|
||||
count--;
|
||||
}
|
||||
ref_get_bits_offset = i;
|
||||
return result;
|
||||
};
|
||||
|
||||
// use PRNG to decide between 0-64 bits
|
||||
std::minstd_rand rng;
|
||||
rng.seed( 9999 );
|
||||
std::uniform_int_distribution< uint16_t > bit_dist( 0, 64 );
|
||||
for( int i=0; i<10000; i++ )
|
||||
{
|
||||
uint8_t bit_count = bit_dist( rng );
|
||||
uint64_t ref_bits = ref_get_bits( bit_count );
|
||||
uint64_t test_bits = test_rng.get_bits( bit_count );
|
||||
//std::cout << i << ": get(" << int(bit_count) << ") -> " << test_bits << " (expect " << ref_bits << ")\n";
|
||||
if( bit_count < 64 )
|
||||
{
|
||||
BOOST_CHECK( ref_bits < (uint64_t( 1 ) << bit_count ) );
|
||||
BOOST_CHECK( test_bits < (uint64_t( 1 ) << bit_count ) );
|
||||
}
|
||||
BOOST_CHECK( ref_bits == test_bits );
|
||||
if( ref_bits != test_bits )
|
||||
break;
|
||||
}
|
||||
|
||||
std::uniform_int_distribution< uint64_t > whole_dist(
|
||||
0, std::numeric_limits<uint64_t>::max() );
|
||||
for( int i=0; i<10000; i++ )
|
||||
{
|
||||
uint8_t bit_count = bit_dist( rng );
|
||||
uint64_t bound = whole_dist( rng ) & ((uint64_t(1) << bit_count) - 1);
|
||||
//std::cout << "bound:" << bound << "\n";
|
||||
uint64_t rnum = test_rng( bound );
|
||||
//std::cout << "rnum:" << rnum << "\n";
|
||||
if( bound > 1 )
|
||||
{
|
||||
BOOST_CHECK( rnum < bound );
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_CHECK( rnum == 0 );
|
||||
}
|
||||
}
|
||||
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( exceptions )
|
||||
{
|
||||
GRAPHENE_CHECK_THROW(FC_THROW_EXCEPTION(balance_claim_invalid_claim_amount, "Etc"), balance_claim_invalid_claim_amount);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <graphene/chain/committee_member_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/market_object.hpp>
|
||||
#include <graphene/chain/witness_schedule_object.hpp>
|
||||
|
||||
#include <graphene/utilities/tempdir.hpp>
|
||||
|
||||
|
|
@ -916,6 +917,45 @@ BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture )
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( witness_scheduler_missed_blocks, database_fixture )
|
||||
{ try {
|
||||
|
||||
uint8_t witness_schedule_algorithm = db.get_global_properties().parameters.witness_schedule_algorithm;
|
||||
if (witness_schedule_algorithm != GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.witness_schedule_algorithm = GRAPHENE_WITNESS_SCHEDULED_ALGORITHM;
|
||||
});
|
||||
|
||||
db.get_near_witness_schedule();
|
||||
generate_block();
|
||||
auto near_schedule = db.get_near_witness_schedule();
|
||||
|
||||
std::for_each(near_schedule.begin(), near_schedule.end(), [&](witness_id_type id) {
|
||||
generate_block(0);
|
||||
BOOST_CHECK(db.get_dynamic_global_properties().current_witness == id);
|
||||
});
|
||||
|
||||
near_schedule = db.get_near_witness_schedule();
|
||||
generate_block(0, init_account_priv_key, 2);
|
||||
BOOST_CHECK(db.get_dynamic_global_properties().current_witness == near_schedule[2]);
|
||||
|
||||
near_schedule.erase(near_schedule.begin(), near_schedule.begin() + 3);
|
||||
auto new_schedule = db.get_near_witness_schedule();
|
||||
new_schedule.erase(new_schedule.end() - 3, new_schedule.end());
|
||||
BOOST_CHECK(new_schedule == near_schedule);
|
||||
|
||||
std::for_each(near_schedule.begin(), near_schedule.end(), [&](witness_id_type id) {
|
||||
generate_block(0);
|
||||
BOOST_CHECK(db.get_dynamic_global_properties().current_witness == id);
|
||||
|
||||
if (db.get_global_properties().parameters.witness_schedule_algorithm != witness_schedule_algorithm)
|
||||
db.modify(db.get_global_properties(), [&witness_schedule_algorithm](global_property_object& p) {
|
||||
p.parameters.witness_schedule_algorithm = witness_schedule_algorithm;
|
||||
});
|
||||
|
||||
});
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( rsf_missed_blocks, database_fixture )
|
||||
{
|
||||
try
|
||||
|
|
@ -924,7 +964,11 @@ BOOST_FIXTURE_TEST_CASE( rsf_missed_blocks, database_fixture )
|
|||
|
||||
auto rsf = [&]() -> string
|
||||
{
|
||||
fc::uint128 rsf = db.get_dynamic_global_properties().recent_slots_filled;
|
||||
fc::uint128 rsf;
|
||||
if (db.get_global_properties().parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
|
||||
rsf = db.get(witness_schedule_id_type()).recent_slots_filled;
|
||||
else
|
||||
rsf = db.get_dynamic_global_properties().recent_slots_filled;
|
||||
string result = "";
|
||||
result.reserve(128);
|
||||
for( int i=0; i<128; i++ )
|
||||
|
|
|
|||
|
|
@ -457,18 +457,57 @@ BOOST_AUTO_TEST_CASE( witness_create )
|
|||
while( ((db.get_dynamic_global_properties().current_aslot + 1) % witnesses.size()) != 0 )
|
||||
generate_block();
|
||||
|
||||
int produced = 0;
|
||||
// Make sure we get scheduled at least once in witnesses.size()*2 blocks
|
||||
// may take this many unless we measure where in the scheduling round we are
|
||||
// TODO: intense_test that repeats this loop many times
|
||||
for( size_t i=0, n=witnesses.size()*2; i<n; i++ )
|
||||
if (db.get_global_properties().parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
|
||||
{
|
||||
signed_block block = generate_block();
|
||||
if( block.witness == nathan_witness_id )
|
||||
produced++;
|
||||
generate_blocks(witnesses.size());
|
||||
|
||||
// make sure we're scheduled to produce
|
||||
vector<witness_id_type> near_witnesses = db.get_near_witness_schedule();
|
||||
BOOST_CHECK( std::find( near_witnesses.begin(), near_witnesses.end(), nathan_witness_id )
|
||||
!= near_witnesses.end() );
|
||||
|
||||
struct generator_helper {
|
||||
database_fixture& f;
|
||||
witness_id_type nathan_id;
|
||||
fc::ecc::private_key nathan_key;
|
||||
bool nathan_generated_block;
|
||||
|
||||
void operator()(witness_id_type id) {
|
||||
if( id == nathan_id )
|
||||
{
|
||||
nathan_generated_block = true;
|
||||
f.generate_block(0, nathan_key);
|
||||
} else
|
||||
f.generate_block(0);
|
||||
BOOST_CHECK_EQUAL(f.db.get_dynamic_global_properties().current_witness.instance.value, id.instance.value);
|
||||
f.db.get_near_witness_schedule();
|
||||
}
|
||||
};
|
||||
|
||||
generator_helper h = std::for_each(near_witnesses.begin(), near_witnesses.end(),
|
||||
generator_helper{*this, nathan_witness_id, nathan_private_key, false});
|
||||
BOOST_CHECK(h.nathan_generated_block);
|
||||
|
||||
BOOST_CHECK_EQUAL( db.witness_participation_rate(), GRAPHENE_100_PERCENT );
|
||||
}
|
||||
BOOST_CHECK_GE( produced, 1 );
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
if (db.get_global_properties().parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
|
||||
{
|
||||
int produced = 0;
|
||||
// Make sure we get scheduled at least once in witnesses.size()*2 blocks
|
||||
// may take this many unless we measure where in the scheduling round we are
|
||||
// TODO: intense_test that repeats this loop many times
|
||||
for( size_t i=0, n=witnesses.size()*2; i<n; i++ )
|
||||
{
|
||||
signed_block block = generate_block();
|
||||
if( block.witness == nathan_witness_id )
|
||||
produced++;
|
||||
}
|
||||
BOOST_CHECK_GE( produced, 1 );
|
||||
}
|
||||
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
/**
|
||||
* This test should verify that the asset_global_settle operation works as expected,
|
||||
|
|
|
|||
2684
tests/tournament/tournament_tests.cpp
Normal file
2684
tests/tournament/tournament_tests.cpp
Normal file
File diff suppressed because it is too large
Load diff
24
tests/tournaments.sh
Executable file
24
tests/tournaments.sh
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
i=1
|
||||
file='tournament-2'
|
||||
file2=$file
|
||||
while [ 0 ]; do
|
||||
|
||||
echo "*** $i `date`"
|
||||
if [ "$1" = "-c" ]; then
|
||||
file2=$file-`date +%Y-%m-%d:%H:%M:%S`
|
||||
|
||||
elif [ -f $file2 ]; then
|
||||
mv $file2 $file2-last
|
||||
fi
|
||||
./tournament_test --log_level=message 2> $file2
|
||||
echo
|
||||
if [ "$1" = "-c" ]; then
|
||||
sleep 2
|
||||
else
|
||||
break
|
||||
fi
|
||||
i=$[i + 1]
|
||||
|
||||
done
|
||||
Loading…
Reference in a new issue