lottery end implemented
This commit is contained in:
parent
2acbf3d929
commit
930ddb06c1
26 changed files with 957 additions and 72 deletions
|
|
@ -237,6 +237,17 @@ struct get_impacted_account_visitor
|
|||
{
|
||||
_impacted.insert( op.buyer );
|
||||
}
|
||||
void operator()( const lottery_reward_operation& op ) {
|
||||
_impacted.insert( op.winner );
|
||||
}
|
||||
void operator()( const lottery_end_operation& op ) {
|
||||
for( auto participant : op.participants ) {
|
||||
_impacted.insert(participant.first);
|
||||
}
|
||||
}
|
||||
void operator()( const sweeps_vesting_claim_operation& op ) {
|
||||
_impacted.insert( op.account );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ add_library( graphene_chain
|
|||
|
||||
account_object.cpp
|
||||
asset_object.cpp
|
||||
lottery.cpp
|
||||
fba_object.cpp
|
||||
proposal_object.cpp
|
||||
vesting_balance_object.cpp
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <graphene/chain/asset_evaluator.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/balance_object.hpp>
|
||||
#include <graphene/chain/market_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
|
|
@ -75,6 +76,7 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o
|
|||
{
|
||||
auto dotpos = op.symbol.rfind( '.' );
|
||||
if( dotpos != std::string::npos )
|
||||
|
||||
{
|
||||
auto prefix = op.symbol.substr( 0, dotpos );
|
||||
auto asset_symbol_itr = asset_indx.find( prefix );
|
||||
|
|
@ -117,7 +119,11 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o
|
|||
FC_ASSERT( op.bitasset_opts );
|
||||
FC_ASSERT( op.precision == op.bitasset_opts->short_backing_asset(d).precision );
|
||||
}
|
||||
|
||||
|
||||
if( op.extension.which() == asset_extension::tag<lottery_asset_options>::value ) {
|
||||
FC_ASSERT( op.common_options.max_supply >= 5 );
|
||||
op.extension.get<lottery_asset_options>().validate();
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
|
@ -164,8 +170,11 @@ object_id_type asset_create_evaluator::do_apply( const asset_create_operation& o
|
|||
a.options = op.common_options;
|
||||
if( op.extension.which() == asset_extension::tag<lottery_asset_options>::value ) {
|
||||
a.precision = 0;
|
||||
a.lottery_options = op.extension.get<lottery_asset_options>();
|
||||
a.lottery_options->balance = asset( 0, a.lottery_options->ticket_price.asset_id );
|
||||
a.lottery_options = op.extension.get<lottery_asset_options>(); //a.lottery_options->balance = asset( 0, a.lottery_options->ticket_price.asset_id );
|
||||
a.lottery_options->owner = a.id;
|
||||
db().create<lottery_balance_object>([&](lottery_balance_object& lbo) {
|
||||
lbo.lottery_id = a.id;
|
||||
});
|
||||
}
|
||||
if( a.options.core_exchange_rate.base.asset_id.instance.value == 0 )
|
||||
a.options.core_exchange_rate.quote.asset_id = next_asset_id;
|
||||
|
|
@ -187,6 +196,7 @@ void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o )
|
|||
const asset_object& a = o.asset_to_issue.asset_id(d);
|
||||
FC_ASSERT( o.issuer == a.issuer );
|
||||
FC_ASSERT( !a.is_market_issued(), "Cannot manually issue a market-issued asset." );
|
||||
FC_ASSERT( !a.is_lottery(), "Cannot manually issue a lottery asset." );
|
||||
|
||||
to_account = &o.issue_to_account(d);
|
||||
FC_ASSERT( is_authorized_asset( d, *to_account, a ) );
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ share_type asset_bitasset_data_object::max_force_settlement_volume(share_type cu
|
|||
return volume.to_uint64();
|
||||
}
|
||||
|
||||
void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point_sec current_time)
|
||||
void asset_bitasset_data_object::update_median_feeds(time_point_sec current_time)
|
||||
{
|
||||
current_feed_publication_time = current_time;
|
||||
vector<std::reference_wrapper<const price_feed>> current_feeds;
|
||||
|
|
@ -164,3 +164,130 @@ string asset_object::amount_to_string(share_type amount) const
|
|||
result += "." + fc::to_string(scaled_precision.value + decimals).erase(0,1);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
vector<account_id_type> asset_object::get_holders( database& db ) const
|
||||
{
|
||||
auto& asset_bal_idx = db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();
|
||||
|
||||
uint64_t max_supply = get_id()(db).options.max_supply.value;
|
||||
|
||||
vector<account_id_type> holders; // repeating if balance > 1
|
||||
holders.reserve(max_supply);
|
||||
const auto range = asset_bal_idx.equal_range( boost::make_tuple( get_id() ) );
|
||||
for( const account_balance_object& bal : boost::make_iterator_range( range.first, range.second ) )
|
||||
for( uint64_t balance = bal.balance.value; balance > 0; --balance)
|
||||
holders.push_back( bal.owner );
|
||||
return holders;
|
||||
}
|
||||
|
||||
void asset_object::distribute_benefactors_part( database& db )
|
||||
{
|
||||
transaction_evaluation_state eval( &db );
|
||||
uint64_t jackpot = get_id()( db ).dynamic_data( db ).current_supply.value * lottery_options->ticket_price.amount.value;
|
||||
|
||||
for( auto benefactor : lottery_options->benefactors ) {
|
||||
lottery_reward_operation reward_op;
|
||||
reward_op.lottery = get_id();
|
||||
reward_op.winner = benefactor.id;
|
||||
reward_op.is_benefactor_reward = true;
|
||||
reward_op.win_percentage = benefactor.share;
|
||||
reward_op.amount = asset( jackpot * benefactor.share / GRAPHENE_100_PERCENT, db.get_balance(id).asset_id );
|
||||
db.apply_operation(eval, reward_op);
|
||||
}
|
||||
}
|
||||
|
||||
map< account_id_type, vector< uint16_t > > asset_object::distribute_winners_part( database& db )
|
||||
{
|
||||
transaction_evaluation_state eval( &db );
|
||||
|
||||
auto holders = get_holders( db );
|
||||
FC_ASSERT( dynamic_data( db ).current_supply == holders.size() );
|
||||
map<account_id_type, vector<uint16_t> > structurized_participants;
|
||||
for( account_id_type holder : holders )
|
||||
{
|
||||
if( !structurized_participants.count( holder ) )
|
||||
structurized_participants.emplace( holder, vector< uint16_t >() );
|
||||
}
|
||||
uint64_t jackpot = get_id()( db ).dynamic_data( db ).current_supply.value * lottery_options->ticket_price.amount.value;
|
||||
auto winner_numbers = db.get_winner_numbers( get_id(), holders.size(), lottery_options->winning_tickets.size() );
|
||||
|
||||
auto& tickets( lottery_options->winning_tickets );
|
||||
|
||||
if( holders.size() < tickets.size() ) {
|
||||
uint16_t percents_to_distribute = 0;
|
||||
for( auto i = tickets.begin() + holders.size(); i != tickets.end(); ) {
|
||||
percents_to_distribute += *i;
|
||||
i = tickets.erase(i);
|
||||
}
|
||||
for( auto t = tickets.begin(); t != tickets.begin() + holders.size(); ++t )
|
||||
*t += percents_to_distribute / holders.size();
|
||||
}
|
||||
|
||||
for( int c = 0; c < winner_numbers.size(); ++c ) {
|
||||
auto winner_num = winner_numbers[c];
|
||||
lottery_reward_operation reward_op;
|
||||
reward_op.lottery = get_id();
|
||||
reward_op.winner = holders[winner_num];
|
||||
reward_op.win_percentage = tickets[c];
|
||||
reward_op.amount = asset( jackpot * tickets[c] * ( 1. - db.get_global_properties().parameters.sweeps_distribution_percentage / (double)GRAPHENE_100_PERCENT ) / GRAPHENE_100_PERCENT , db.get_balance(id).asset_id );
|
||||
db.apply_operation(eval, reward_op);
|
||||
|
||||
structurized_participants[ holders[ winner_num ] ].push_back( tickets[c] );
|
||||
}
|
||||
return structurized_participants;
|
||||
}
|
||||
|
||||
void asset_object::distribute_sweeps_holders_part( database& db )
|
||||
{
|
||||
transaction_evaluation_state eval( &db );
|
||||
|
||||
auto& asset_bal_idx = db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();
|
||||
|
||||
auto global_params = db.get_global_properties().parameters;
|
||||
uint64_t distribution_asset_supply = global_params.sweeps_distribution_asset( db ).dynamic_data( db ).current_supply.value;
|
||||
const auto range = asset_bal_idx.equal_range( boost::make_tuple( global_params.sweeps_distribution_asset ) );
|
||||
|
||||
uint64_t holders_sum = 0;
|
||||
for( const account_balance_object& holder_balance : boost::make_iterator_range( range.first, range.second ) )
|
||||
{
|
||||
int64_t holder_part = db.get_balance(id).amount.value / (double)distribution_asset_supply * holder_balance.balance.value * SWEEPS_VESTING_BALANCE_MULTIPLIER;
|
||||
db.adjust_sweeps_vesting_balance( holder_balance.owner, holder_part );
|
||||
holders_sum += holder_part;
|
||||
}
|
||||
uint64_t balance_rest = db.get_balance( get_id() ).amount.value * SWEEPS_VESTING_BALANCE_MULTIPLIER - holders_sum;
|
||||
db.adjust_sweeps_vesting_balance( SWEEPS_ACCUMULATOR_ACCOUNT, balance_rest );
|
||||
db.adjust_balance( get_id(), -db.get_balance( get_id() ) );
|
||||
}
|
||||
|
||||
void asset_object::end_lottery( database& db )
|
||||
{
|
||||
idump(( "end_lottery" ));
|
||||
transaction_evaluation_state eval(&db);
|
||||
|
||||
FC_ASSERT( is_lottery() );
|
||||
FC_ASSERT( lottery_options->is_active && ( lottery_options->end_date <= db.head_block_time() ) );
|
||||
|
||||
auto participants = distribute_winners_part( db );
|
||||
if( participants.size() > 0) {
|
||||
distribute_benefactors_part( db );
|
||||
distribute_sweeps_holders_part( db );
|
||||
}
|
||||
|
||||
lottery_end_operation end_op;
|
||||
end_op.lottery = id;
|
||||
end_op.participants = participants;
|
||||
db.apply_operation(eval, end_op);
|
||||
}
|
||||
|
||||
void lottery_balance_object::adjust_balance( const asset& delta )
|
||||
{
|
||||
FC_ASSERT( delta.asset_id == balance.asset_id );
|
||||
balance += delta;
|
||||
}
|
||||
|
||||
void sweeps_vesting_balance_object::adjust_balance( const asset& delta )
|
||||
{
|
||||
FC_ASSERT( delta.asset_id == asset_id );
|
||||
balance += delta.amount.value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/balance_object.hpp>
|
||||
#include <graphene/chain/vesting_balance_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
|
||||
|
|
@ -45,6 +46,15 @@ asset database::get_balance(const account_object& owner, const asset_object& ass
|
|||
return get_balance(owner.get_id(), asset_obj.get_id());
|
||||
}
|
||||
|
||||
asset database::get_balance(asset_id_type lottery_id)const
|
||||
{
|
||||
auto& index = get_index_type<lottery_balance_index>().indices().get<by_owner>();
|
||||
auto itr = index.find( lottery_id );
|
||||
if( itr == index.end() )
|
||||
return asset(0, asset_id_type( ));
|
||||
return itr->get_balance();
|
||||
}
|
||||
|
||||
string database::to_pretty_string( const asset& a )const
|
||||
{
|
||||
return a.asset_id(*this).amount_to_pretty_string(a.amount);
|
||||
|
|
@ -78,6 +88,63 @@ void database::adjust_balance(account_id_type account, asset delta )
|
|||
|
||||
} FC_CAPTURE_AND_RETHROW( (account)(delta) ) }
|
||||
|
||||
|
||||
void database::adjust_balance(asset_id_type lottery_id, asset delta)
|
||||
{
|
||||
if( delta.amount == 0 )
|
||||
return;
|
||||
|
||||
auto& index = get_index_type<lottery_balance_index>().indices().get<by_owner>();
|
||||
auto itr = index.find(lottery_id);
|
||||
if(itr == index.end())
|
||||
{
|
||||
FC_ASSERT( delta.amount > 0, "Insufficient Balance: ${a}'s balance is less than required ${r}",
|
||||
("a",lottery_id)
|
||||
("b","test")
|
||||
("r",to_pretty_string(-delta)));
|
||||
create<lottery_balance_object>([lottery_id,&delta](lottery_balance_object& b) {
|
||||
b.lottery_id = lottery_id;
|
||||
b.balance = asset(delta.amount, delta.asset_id);
|
||||
});
|
||||
} else {
|
||||
if( delta.amount < 0 )
|
||||
FC_ASSERT( itr->get_balance() >= -delta, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", ("a",lottery_id)("b",to_pretty_string(itr->get_balance()))("r",to_pretty_string(-delta)));
|
||||
modify(*itr, [delta](lottery_balance_object& b) {
|
||||
b.adjust_balance(delta);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void database::adjust_sweeps_vesting_balance(account_id_type account, int64_t delta)
|
||||
{
|
||||
if( delta == 0 )
|
||||
return;
|
||||
|
||||
asset_id_type asset_id = get_global_properties().parameters.sweeps_distribution_asset;
|
||||
|
||||
auto& index = get_index_type<sweeps_vesting_balance_index>().indices().get<by_owner>();
|
||||
auto itr = index.find(account);
|
||||
if(itr == index.end())
|
||||
{
|
||||
FC_ASSERT( delta > 0, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}",
|
||||
("a",account)
|
||||
("b","test")
|
||||
("r",-delta));
|
||||
create<sweeps_vesting_balance_object>([account,&delta,&asset_id](sweeps_vesting_balance_object& b) {
|
||||
b.owner = account;
|
||||
b.asset_id = asset_id;
|
||||
b.balance = delta;
|
||||
});
|
||||
} else {
|
||||
if( delta < 0 )
|
||||
FC_ASSERT( itr->get_balance() >= -delta, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", ("a",account)("b",itr->get_balance())("r",-delta));
|
||||
modify(*itr, [&delta,&asset_id](sweeps_vesting_balance_object& b) {
|
||||
b.adjust_balance( asset( delta, asset_id ) );
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
optional< vesting_balance_id_type > database::deposit_lazy_vesting(
|
||||
const optional< vesting_balance_id_type >& ovbid,
|
||||
share_type amount, uint32_t req_vesting_seconds,
|
||||
|
|
|
|||
|
|
@ -536,7 +536,9 @@ void database::_apply_block( const signed_block& next_block )
|
|||
// Are we at the maintenance interval?
|
||||
if( maint_needed )
|
||||
perform_chain_maintenance(next_block, global_props);
|
||||
|
||||
|
||||
check_ending_lotteries();
|
||||
|
||||
create_block_summary(next_block);
|
||||
clear_expired_transactions();
|
||||
clear_expired_proposals();
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
#include <ctime>
|
||||
#include <algorithm>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -99,7 +100,7 @@ uint32_t database::last_non_undoable_block_num() const
|
|||
return head_block_num() - _undo_db.size();
|
||||
}
|
||||
|
||||
std::vector<uint32_t> database::get_seeds(asset_id_type for_asset, uint32_t count_winners) const
|
||||
std::vector<uint32_t> database::get_seeds(asset_id_type for_asset, uint8_t count_winners) const
|
||||
{
|
||||
FC_ASSERT( count_winners <= 64 );
|
||||
std::string salted_string = std::string(_random_number_generator._seed) + std::to_string(for_asset.instance.value);
|
||||
|
|
@ -117,21 +118,22 @@ std::vector<uint32_t> database::get_seeds(asset_id_type for_asset, uint32_t coun
|
|||
return result;
|
||||
}
|
||||
|
||||
const std::unordered_set<uint8_t> database::get_winner_numbers( asset_id_type for_asset, uint8_t count_members, uint32_t count_winners ) const
|
||||
const std::vector<uint32_t> database::get_winner_numbers( asset_id_type for_asset, uint32_t count_members, uint8_t count_winners ) const
|
||||
{
|
||||
std::unordered_set<uint8_t> result;
|
||||
std::vector<uint32_t> result;
|
||||
if( count_members < count_winners ) count_winners = count_members;
|
||||
if( count_winners == 0 ) return result;
|
||||
result.reserve(count_winners);
|
||||
|
||||
auto seeds = get_seeds(for_asset, count_winners);
|
||||
|
||||
for (auto current_seed = seeds.begin(); current_seed != seeds.end(); ++current_seed) {
|
||||
uint8_t winner_num = *current_seed % count_members;
|
||||
int iter_count = 0;
|
||||
while (result.count(winner_num)) {
|
||||
while( std::find(result.begin(), result.end(), winner_num) != result.end() ) {
|
||||
*current_seed = (*current_seed * 1103515245 + 12345) / 65536; //using gcc's consts for pseudorandom
|
||||
winner_num = *current_seed % count_members;
|
||||
}
|
||||
result.emplace(winner_num);
|
||||
result.push_back(winner_num);
|
||||
if (result.size() >= count_winners) break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -182,6 +182,9 @@ void database::initialize_evaluators()
|
|||
register_evaluator<game_move_evaluator>();
|
||||
register_evaluator<tournament_leave_evaluator>();
|
||||
register_evaluator<ticket_purchase_evaluator>();
|
||||
register_evaluator<lottery_reward_evaluator>();
|
||||
register_evaluator<lottery_end_evaluator>();
|
||||
register_evaluator<sweeps_vesting_claim_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
@ -236,6 +239,10 @@ void database::initialize_indexes()
|
|||
add_index< primary_index< simple_index< fba_accumulator_object > > >();
|
||||
add_index< primary_index<pending_dividend_payout_balance_for_holder_object_index > >();
|
||||
add_index< primary_index<total_distributed_dividend_balance_object_index > >();
|
||||
|
||||
add_index< primary_index<lottery_balance_index > >();
|
||||
add_index< primary_index<sweeps_vesting_balance_index > >();
|
||||
|
||||
}
|
||||
|
||||
void database::init_genesis(const genesis_state_type& genesis_state)
|
||||
|
|
|
|||
|
|
@ -185,4 +185,25 @@ void database::close(bool rewind)
|
|||
_fork_db.reset();
|
||||
}
|
||||
|
||||
|
||||
void database::check_ending_lotteries()
|
||||
{
|
||||
const auto& lotteries_idx = get_index_type<asset_index>().indices().get<active_lotteries>();
|
||||
for( auto checking_asset: lotteries_idx )
|
||||
{
|
||||
if( !checking_asset.is_lottery() ) break;
|
||||
if( !checking_asset.lottery_options->is_active ) break;
|
||||
if( checking_asset.lottery_options->end_date >= head_block_time() ) break;
|
||||
checking_asset.end_lottery(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void database::check_lottery_end_by_participants( asset_id_type asset_id )
|
||||
{
|
||||
asset_object asset_to_check = asset_id( *this );
|
||||
if( !asset_to_check.is_lottery() ) return;
|
||||
if( !asset_to_check.lottery_options->ending_on_soldout ) return;
|
||||
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// bitshares-core #429 rounding issue when creating assets
|
||||
#ifndef HARDFORK_CORE_429_TIME
|
||||
#define HARDFORK_CORE_429_TIME (fc::time_point_sec( 1510320000 ))
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -40,8 +40,9 @@
|
|||
namespace graphene { namespace chain {
|
||||
class account_object;
|
||||
class database;
|
||||
class transaction_evaluation_state;
|
||||
using namespace graphene::db;
|
||||
|
||||
|
||||
/**
|
||||
* @brief tracks the asset information that changes frequently
|
||||
* @ingroup object
|
||||
|
|
@ -116,7 +117,7 @@ namespace graphene { namespace chain {
|
|||
/// Convert an asset to a textual representation with symbol, i.e. "123.45 USD"
|
||||
string amount_to_pretty_string(const asset &amount)const
|
||||
{ FC_ASSERT(amount.asset_id == id); return amount_to_pretty_string(amount.amount); }
|
||||
|
||||
|
||||
/// Ticker symbol for this asset, i.e. "USD"
|
||||
string symbol;
|
||||
/// Maximum number of digits after the decimal point (must be <= 12)
|
||||
|
|
@ -129,6 +130,12 @@ namespace graphene { namespace chain {
|
|||
// Extra data associated with lottery options. This field is non-null if is_lottery() returns true
|
||||
optional<lottery_asset_options> lottery_options;
|
||||
time_point_sec get_lottery_expiration() const;
|
||||
vector<account_id_type> get_holders( database& db ) const;
|
||||
void distribute_benefactors_part( database& db );
|
||||
map< account_id_type, vector< uint16_t > > distribute_winners_part( database& db );
|
||||
void distribute_sweeps_holders_part( database& db );
|
||||
void end_lottery( database& db );
|
||||
|
||||
/// Current supply, fee pool, and collected fees are stored in a separate object as they change frequently.
|
||||
asset_dynamic_data_id_type dynamic_asset_data_id;
|
||||
/// Extra data associated with BitAssets. This field is non-null if and only if is_market_issued() returns true
|
||||
|
|
@ -351,8 +358,81 @@ namespace graphene { namespace chain {
|
|||
> total_distributed_dividend_balance_object_multi_index_type;
|
||||
typedef generic_index<total_distributed_dividend_balance_object, total_distributed_dividend_balance_object_multi_index_type> total_distributed_dividend_balance_object_index;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup object
|
||||
*/
|
||||
class lottery_balance_object : public abstract_object<lottery_balance_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = impl_lottery_balance_object_type;
|
||||
|
||||
asset_id_type lottery_id;
|
||||
asset balance;
|
||||
|
||||
asset get_balance()const { return balance; }
|
||||
void adjust_balance(const asset& delta);
|
||||
};
|
||||
|
||||
|
||||
struct by_owner;
|
||||
|
||||
/**
|
||||
* @ingroup object_index
|
||||
*/
|
||||
using lottery_balance_index_type = multi_index_container<
|
||||
lottery_balance_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_owner>,
|
||||
member<lottery_balance_object, asset_id_type, &lottery_balance_object::lottery_id>
|
||||
>
|
||||
>
|
||||
>;
|
||||
|
||||
/**
|
||||
* @ingroup object_index
|
||||
*/
|
||||
using lottery_balance_index = generic_index<lottery_balance_object, lottery_balance_index_type>;
|
||||
|
||||
|
||||
class sweeps_vesting_balance_object : public abstract_object<sweeps_vesting_balance_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = impl_sweeps_vesting_balance_object_type;
|
||||
|
||||
|
||||
account_id_type owner;
|
||||
uint64_t balance;
|
||||
asset_id_type asset_id;
|
||||
time_point_sec last_claim_date;
|
||||
|
||||
uint64_t get_balance()const { return balance; }
|
||||
void adjust_balance(const asset& delta);
|
||||
asset available_for_claim() const { return asset( balance / SWEEPS_VESTING_BALANCE_MULTIPLIER , asset_id ); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup object_index
|
||||
*/
|
||||
using sweeps_vesting_balance_index_type = multi_index_container<
|
||||
sweeps_vesting_balance_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_owner>,
|
||||
member<sweeps_vesting_balance_object, account_id_type, &sweeps_vesting_balance_object::owner>
|
||||
>
|
||||
>
|
||||
>;
|
||||
|
||||
/**
|
||||
* @ingroup object_index
|
||||
*/
|
||||
using sweeps_vesting_balance_index = generic_index<sweeps_vesting_balance_object, sweeps_vesting_balance_index_type>;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::asset_dynamic_data_object, (graphene::db::object),
|
||||
|
|
@ -395,3 +475,9 @@ FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object),
|
|||
(buyback_account)
|
||||
(dividend_data_id)
|
||||
)
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::lottery_balance_object, (graphene::db::object),
|
||||
(lottery_id)(balance) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::sweeps_vesting_balance_object, (graphene::db::object),
|
||||
(owner)(balance)(asset_id)(last_claim_date) )
|
||||
|
|
|
|||
|
|
@ -194,3 +194,8 @@
|
|||
#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
|
||||
|
||||
#define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT)
|
||||
#define SWEEPS_DEFAULT_DISTRIBUTION_ASSET asset_id_type(0)
|
||||
#define SWEEPS_VESTING_BALANCE_MULTIPLIER 100000000
|
||||
#define SWEEPS_ACCUMULATOR_ACCOUNT account_id_type(0)
|
||||
|
|
|
|||
|
|
@ -243,6 +243,9 @@ namespace graphene { namespace chain {
|
|||
vector<witness_id_type> get_near_witness_schedule()const;
|
||||
void update_witness_schedule();
|
||||
void update_witness_schedule(const signed_block& next_block);
|
||||
|
||||
void check_lottery_end_by_participants( asset_id_type asset_id );
|
||||
void check_ending_lotteries();
|
||||
|
||||
//////////////////// db_getter.cpp ////////////////////
|
||||
|
||||
|
|
@ -253,8 +256,8 @@ namespace graphene { namespace chain {
|
|||
const dynamic_global_property_object& get_dynamic_global_properties()const;
|
||||
const node_property_object& get_node_properties()const;
|
||||
const fee_schedule& current_fee_schedule()const;
|
||||
const std::unordered_set<uint8_t> get_winner_numbers(asset_id_type for_asset, uint8_t count_members, uint32_t count_winners)const;
|
||||
std::vector<uint32_t> get_seeds(asset_id_type for_asset, uint32_t count_winners)const;
|
||||
const std::vector<uint32_t> get_winner_numbers( asset_id_type for_asset, uint32_t count_members, uint8_t count_winners ) const;
|
||||
std::vector<uint32_t> get_seeds( asset_id_type for_asset, uint8_t count_winners )const;
|
||||
uint64_t get_random_bits( uint64_t bound );
|
||||
|
||||
time_point_sec head_block_time()const;
|
||||
|
|
@ -293,13 +296,26 @@ namespace graphene { namespace chain {
|
|||
asset get_balance(account_id_type owner, asset_id_type asset_id)const;
|
||||
/// This is an overloaded method.
|
||||
asset get_balance(const account_object& owner, const asset_object& asset_obj)const;
|
||||
|
||||
/**
|
||||
* @brief Get balance connected with lottery asset; if assset isnt lottery - return asset(0, 0)
|
||||
*/
|
||||
asset get_balance(asset_id_type lottery_id)const;
|
||||
/**
|
||||
* @brief Adjust a particular account's balance in a given asset by a delta
|
||||
* @param account ID of account whose balance should be adjusted
|
||||
* @param delta Asset ID and amount to adjust balance by
|
||||
*/
|
||||
void adjust_balance(account_id_type account, asset delta);
|
||||
/**
|
||||
* @brief Adjust a lottery's balance in a given asset by a delta
|
||||
* @param asset ID(should be lottery) balance should be adjusted
|
||||
* @param delta Asset ID and amount to adjust balance by
|
||||
*/
|
||||
void adjust_balance(asset_id_type lottery_id, asset delta);
|
||||
/**
|
||||
* @brief Adjust a particular account's sweeps vesting balance in a given asset by a delta
|
||||
*/
|
||||
void adjust_sweeps_vesting_balance(account_id_type account, int64_t delta);
|
||||
|
||||
/**
|
||||
* @brief Helper to make lazy deposit to CDD VBO.
|
||||
|
|
@ -423,7 +439,7 @@ namespace graphene { namespace chain {
|
|||
private:
|
||||
void _apply_block( const signed_block& next_block );
|
||||
processed_transaction _apply_transaction( const signed_transaction& trx );
|
||||
|
||||
|
||||
///Steps involved in applying a new block
|
||||
///@{
|
||||
|
||||
|
|
@ -456,7 +472,7 @@ namespace graphene { namespace chain {
|
|||
void update_active_witnesses();
|
||||
void update_active_committee_members();
|
||||
void update_worker_votes();
|
||||
|
||||
|
||||
template<class... Types>
|
||||
void perform_account_maintenance(std::tuple<Types...> helpers);
|
||||
///@}
|
||||
|
|
|
|||
|
|
@ -34,10 +34,46 @@ namespace graphene { namespace chain {
|
|||
typedef ticket_purchase_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const ticket_purchase_operation& o );
|
||||
object_id_type do_apply( const ticket_purchase_operation& o );
|
||||
void_result do_apply( const ticket_purchase_operation& o );
|
||||
|
||||
const asset_object* lottery;
|
||||
const asset_dynamic_data_object* asset_dynamic_data;
|
||||
};
|
||||
|
||||
class lottery_reward_evaluator : public evaluator<lottery_reward_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef lottery_reward_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const lottery_reward_operation& o );
|
||||
void_result do_apply( const lottery_reward_operation& o );
|
||||
|
||||
const asset_object* lottery;
|
||||
const asset_dynamic_data_object* asset_dynamic_data;
|
||||
};
|
||||
|
||||
class lottery_end_evaluator : public evaluator<lottery_end_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef lottery_end_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const lottery_end_operation& o );
|
||||
void_result do_apply( const lottery_end_operation& o );
|
||||
|
||||
const asset_object* lottery;
|
||||
const asset_dynamic_data_object* asset_dynamic_data;
|
||||
};
|
||||
|
||||
class sweeps_vesting_claim_evaluator : public evaluator<sweeps_vesting_claim_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sweeps_vesting_claim_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const sweeps_vesting_claim_operation& o );
|
||||
void_result do_apply( const sweeps_vesting_claim_operation& o );
|
||||
|
||||
// const asset_object* lottery;
|
||||
// const asset_dynamic_data_object* asset_dynamic_data;
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
|
|
@ -26,32 +26,34 @@
|
|||
#include <graphene/chain/protocol/memo.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class database;
|
||||
|
||||
bool is_valid_symbol( const string& symbol );
|
||||
|
||||
struct benefactor {
|
||||
struct benefactor {
|
||||
account_id_type id;
|
||||
double share;
|
||||
uint16_t share; // percent * GRAPHENE_1_PERCENT
|
||||
benefactor() = default;
|
||||
benefactor ( const benefactor & ) = default;
|
||||
benefactor( account_id_type _id, double _share ) : id( _id ), share( _share ) {}
|
||||
benefactor( const benefactor & ) = default;
|
||||
benefactor( account_id_type _id, uint16_t _share ) : id( _id ), share( _share ) {}
|
||||
};
|
||||
|
||||
struct lottery_asset_options {
|
||||
struct lottery_asset_options
|
||||
{
|
||||
std::vector<benefactor> benefactors;
|
||||
|
||||
asset_id_type owner;
|
||||
// specifying winning tickets as shares that will be issued
|
||||
std::vector<double> winning_tickets;
|
||||
std::vector<uint16_t> winning_tickets;
|
||||
asset ticket_price;
|
||||
asset balance;
|
||||
time_point_sec end_date;
|
||||
bool ending_on_soldout;
|
||||
bool is_active;
|
||||
|
||||
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
typedef static_variant< void_t, lottery_asset_options > asset_extension;
|
||||
|
||||
|
||||
/**
|
||||
* @brief The asset_options struct contains options available on all assets in the network
|
||||
|
|
@ -596,10 +598,28 @@ namespace graphene { namespace chain {
|
|||
account_id_type fee_payer()const { return issuer; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
|
||||
struct sweeps_vesting_claim_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
account_id_type account;
|
||||
asset amount_to_claim;
|
||||
extensions_type extensions;
|
||||
|
||||
|
||||
account_id_type fee_payer()const { return account; }
|
||||
void validate()const {};
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::sweeps_vesting_claim_operation, (fee)(account)(amount_to_claim)(extensions) )
|
||||
FC_REFLECT( graphene::chain::sweeps_vesting_claim_operation::fee_parameters_type, (fee) )
|
||||
|
||||
FC_REFLECT( graphene::chain::asset_claim_fees_operation, (fee)(issuer)(amount_to_claim)(extensions) )
|
||||
FC_REFLECT( graphene::chain::asset_claim_fees_operation::fee_parameters_type, (fee) )
|
||||
|
||||
|
|
@ -638,7 +658,7 @@ FC_REFLECT( graphene::chain::bitasset_options,
|
|||
|
||||
FC_REFLECT( graphene::chain::benefactor, (id)(share) )
|
||||
|
||||
FC_REFLECT( graphene::chain::lottery_asset_options, (benefactors)(winning_tickets)(ticket_price)(balance)(end_date)(ending_on_soldout)(is_active) )
|
||||
FC_REFLECT( graphene::chain::lottery_asset_options, (benefactors)(winning_tickets)(ticket_price)(end_date)(ending_on_soldout)(is_active) )
|
||||
|
||||
|
||||
FC_REFLECT( graphene::chain::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) )
|
||||
|
|
|
|||
|
|
@ -85,6 +85,11 @@ namespace graphene { namespace chain {
|
|||
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;
|
||||
//
|
||||
uint16_t sweeps_distribution_percentage = SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE;
|
||||
asset_id_type sweeps_distribution_asset = SWEEPS_DEFAULT_DISTRIBUTION_ASSET;
|
||||
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
/** defined in fee_schedule.cpp */
|
||||
|
|
@ -136,5 +141,7 @@ FC_REFLECT( graphene::chain::chain_parameters,
|
|||
(maximum_tournament_start_time_in_future)
|
||||
(maximum_tournament_start_delay)
|
||||
(maximum_tournament_number_of_wins)
|
||||
(sweeps_distribution_percentage)
|
||||
(sweeps_distribution_asset)
|
||||
(extensions)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
/**
|
||||
* @ingroup operations
|
||||
|
|
@ -51,8 +52,53 @@ namespace graphene { namespace chain {
|
|||
share_type calculate_fee( const fee_parameters_type& k )const;
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
struct lottery_reward_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
uint64_t fee = 0;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
// from what lottery is ticket
|
||||
asset_id_type lottery;
|
||||
// winner account
|
||||
account_id_type winner;
|
||||
// amount that won
|
||||
asset amount;
|
||||
// percentage of jackpot that user won
|
||||
uint16_t win_percentage;
|
||||
// true if recieved from benefators section of lottery; false otherwise
|
||||
bool is_benefactor_reward;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return account_id_type(); }
|
||||
void validate()const {};
|
||||
share_type calculate_fee( const fee_parameters_type& k )const { return k.fee; };
|
||||
};
|
||||
|
||||
struct lottery_end_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
uint64_t fee = 0;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
// from what lottery is ticket
|
||||
asset_id_type lottery;
|
||||
|
||||
// std::vector<account_id_type> participants;
|
||||
|
||||
map<account_id_type, vector< uint16_t> > participants;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return account_id_type(); }
|
||||
void validate() const {}
|
||||
share_type calculate_fee( const fee_parameters_type& k )const { return k.fee; }
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::ticket_purchase_operation,
|
||||
(fee)
|
||||
|
|
@ -62,4 +108,25 @@ FC_REFLECT( graphene::chain::ticket_purchase_operation,
|
|||
(amount)
|
||||
(extensions)
|
||||
)
|
||||
FC_REFLECT( graphene::chain::ticket_purchase_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::ticket_purchase_operation::fee_parameters_type, (fee) )
|
||||
|
||||
|
||||
FC_REFLECT( graphene::chain::lottery_reward_operation,
|
||||
(fee)
|
||||
(lottery)
|
||||
(winner)
|
||||
(amount)
|
||||
(win_percentage)
|
||||
(is_benefactor_reward)
|
||||
(extensions)
|
||||
)
|
||||
FC_REFLECT( graphene::chain::lottery_reward_operation::fee_parameters_type, (fee) )
|
||||
|
||||
|
||||
FC_REFLECT( graphene::chain::lottery_end_operation,
|
||||
(fee)
|
||||
(lottery)
|
||||
(participants)
|
||||
(extensions)
|
||||
)
|
||||
FC_REFLECT( graphene::chain::lottery_end_operation::fee_parameters_type, (fee) )
|
||||
|
|
|
|||
|
|
@ -101,7 +101,10 @@ namespace graphene { namespace chain {
|
|||
asset_dividend_distribution_operation, // VIRTUAL
|
||||
tournament_payout_operation, // VIRTUAL
|
||||
tournament_leave_operation,
|
||||
ticket_purchase_operation
|
||||
ticket_purchase_operation,
|
||||
lottery_reward_operation,
|
||||
lottery_end_operation,
|
||||
sweeps_vesting_claim_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -162,7 +162,9 @@ namespace graphene { namespace chain {
|
|||
impl_fba_accumulator_object_type,
|
||||
impl_asset_dividend_data_type,
|
||||
impl_pending_dividend_payout_balance_for_holder_object_type,
|
||||
impl_distributed_dividend_balance_data_type
|
||||
impl_distributed_dividend_balance_data_type,
|
||||
impl_lottery_balance_object_type,
|
||||
impl_sweeps_vesting_balance_object_type
|
||||
};
|
||||
|
||||
//typedef fc::unsigned_int object_id_type;
|
||||
|
|
@ -190,7 +192,7 @@ namespace graphene { namespace chain {
|
|||
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;
|
||||
typedef object_id< protocol_ids, force_settlement_object_type, force_settlement_object> force_settlement_id_type;
|
||||
typedef object_id< protocol_ids, committee_member_object_type, committee_member_object> committee_member_id_type;
|
||||
typedef object_id< protocol_ids, committee_member_object_type, committee_member_object> committee_member_id_type;
|
||||
typedef object_id< protocol_ids, witness_object_type, witness_object> witness_id_type;
|
||||
typedef object_id< protocol_ids, limit_order_object_type, limit_order_object> limit_order_id_type;
|
||||
typedef object_id< protocol_ids, call_order_object_type, call_order_object> call_order_id_type;
|
||||
|
|
@ -225,28 +227,34 @@ namespace graphene { namespace chain {
|
|||
class tournament_details_object;
|
||||
class asset_dividend_data_object;
|
||||
class pending_dividend_payout_balance_for_holder_object;
|
||||
class lottery_balance_object;
|
||||
class sweeps_vesting_balance_object;
|
||||
|
||||
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_asset_dynamic_data_type, asset_dynamic_data_object> asset_dynamic_data_id_type;
|
||||
typedef object_id< implementation_ids, impl_asset_bitasset_data_type, asset_bitasset_data_object> asset_bitasset_data_id_type;
|
||||
typedef object_id< implementation_ids, impl_asset_dividend_data_type, asset_dividend_data_object> asset_dividend_data_id_type;
|
||||
typedef object_id< implementation_ids, impl_pending_dividend_payout_balance_for_holder_object_type, pending_dividend_payout_balance_for_holder_object> pending_dividend_payout_balance_for_holder_object_type;
|
||||
typedef object_id< implementation_ids, impl_account_balance_object_type, account_balance_object> account_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_account_statistics_object_type,account_statistics_object> account_statistics_id_type;
|
||||
typedef object_id< implementation_ids, impl_transaction_object_type, transaction_object> transaction_obj_id_type;
|
||||
typedef object_id< implementation_ids, impl_block_summary_object_type, block_summary_object> block_summary_id_type;
|
||||
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_asset_dynamic_data_type, asset_dynamic_data_object> asset_dynamic_data_id_type;
|
||||
typedef object_id< implementation_ids, impl_asset_bitasset_data_type, asset_bitasset_data_object> asset_bitasset_data_id_type;
|
||||
typedef object_id< implementation_ids, impl_asset_dividend_data_type, asset_dividend_data_object> asset_dividend_data_id_type;
|
||||
typedef object_id< implementation_ids,
|
||||
impl_pending_dividend_payout_balance_for_holder_object_type,
|
||||
pending_dividend_payout_balance_for_holder_object> pending_dividend_payout_balance_for_holder_object_type;
|
||||
typedef object_id< implementation_ids, impl_account_balance_object_type, account_balance_object> account_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_account_statistics_object_type, account_statistics_object> account_statistics_id_type;
|
||||
typedef object_id< implementation_ids, impl_transaction_object_type, transaction_object> transaction_obj_id_type;
|
||||
typedef object_id< implementation_ids, impl_block_summary_object_type, block_summary_object> block_summary_id_type;
|
||||
|
||||
typedef object_id< implementation_ids,
|
||||
impl_account_transaction_history_object_type,
|
||||
account_transaction_history_object> account_transaction_history_id_type;
|
||||
typedef object_id< implementation_ids, impl_chain_property_object_type, chain_property_object> chain_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_witness_schedule_object_type, witness_schedule_object> witness_schedule_id_type;
|
||||
typedef object_id< implementation_ids, impl_budget_record_object_type, budget_record_object > budget_record_id_type;
|
||||
typedef object_id< implementation_ids, impl_blinded_balance_object_type, blinded_balance_object > blinded_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_special_authority_object_type, special_authority_object > special_authority_id_type;
|
||||
typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type;
|
||||
typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type;
|
||||
account_transaction_history_object> account_transaction_history_id_type;
|
||||
typedef object_id< implementation_ids, impl_chain_property_object_type, chain_property_object> chain_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_witness_schedule_object_type, witness_schedule_object> witness_schedule_id_type;
|
||||
typedef object_id< implementation_ids, impl_budget_record_object_type, budget_record_object > budget_record_id_type;
|
||||
typedef object_id< implementation_ids, impl_blinded_balance_object_type, blinded_balance_object > blinded_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_special_authority_object_type, special_authority_object > special_authority_id_type;
|
||||
typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type;
|
||||
typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type;
|
||||
typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type;
|
||||
|
||||
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
|
||||
typedef fc::ripemd160 block_id_type;
|
||||
|
|
@ -388,6 +396,8 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
|||
(impl_asset_dividend_data_type)
|
||||
(impl_pending_dividend_payout_balance_for_holder_object_type)
|
||||
(impl_distributed_dividend_balance_data_type)
|
||||
(impl_lottery_balance_object_type)
|
||||
(impl_sweeps_vesting_balance_object_type)
|
||||
)
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::share_type )
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
* Copyright (c) 2017 Peerplays, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
|
|
@ -41,25 +41,91 @@ void_result ticket_purchase_evaluator::do_evaluate( const ticket_purchase_operat
|
|||
lottery = &op.lottery(db());
|
||||
FC_ASSERT( lottery->is_lottery() );
|
||||
|
||||
asset_dynamic_data = &lottery->dynamic_asset_data_id(db());
|
||||
asset_dynamic_data = &lottery->dynamic_asset_data_id(db());
|
||||
FC_ASSERT( asset_dynamic_data->current_supply < lottery->options.max_supply );
|
||||
|
||||
auto lottery_options = *lottery->lottery_options;
|
||||
FC_ASSERT( lottery_options.is_active );
|
||||
FC_ASSERT( lottery_options.ticket_price.asset_id == op.amount.asset_id );
|
||||
FC_ASSERT( (double)op.amount.amount.value / lottery_options.ticket_price.amount.value == (double)op.tickets_to_buy );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type ticket_purchase_evaluator::do_apply( const ticket_purchase_operation& op )
|
||||
void_result ticket_purchase_evaluator::do_apply( const ticket_purchase_operation& op )
|
||||
{ try {
|
||||
db().adjust_balance( op.buyer, -op.amount );
|
||||
db().adjust_balance( op.lottery, op.amount );
|
||||
db().adjust_balance( op.buyer, asset( op.tickets_to_buy, lottery->id ) );
|
||||
db().modify( *asset_dynamic_data, [&]( asset_dynamic_data_object& data ){
|
||||
data.current_supply += op.tickets_to_buy;
|
||||
});
|
||||
db().modify( *lottery, [&]( asset_object& ao ){
|
||||
ao.lottery_options->balance += op.amount;
|
||||
});
|
||||
db().check_lottery_end_by_participants( op.lottery );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result lottery_reward_evaluator::do_evaluate( const lottery_reward_operation& op )
|
||||
{ try {
|
||||
lottery = &op.lottery(db());
|
||||
FC_ASSERT( lottery->is_lottery() );
|
||||
|
||||
auto lottery_options = *lottery->lottery_options;
|
||||
FC_ASSERT( lottery_options.is_active );
|
||||
FC_ASSERT( db().get_balance(op.lottery).amount > 0 );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result lottery_reward_evaluator::do_apply( const lottery_reward_operation& op )
|
||||
{ try {
|
||||
db().adjust_balance( op.lottery, -op.amount);
|
||||
db().adjust_balance( op.winner, op.amount );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
||||
void_result lottery_end_evaluator::do_evaluate( const lottery_end_operation& op )
|
||||
{ try {
|
||||
lottery = &op.lottery(db());
|
||||
FC_ASSERT( lottery->is_lottery() );
|
||||
|
||||
asset_dynamic_data = &lottery->dynamic_asset_data_id(db());
|
||||
|
||||
auto lottery_options = *lottery->lottery_options;
|
||||
FC_ASSERT( lottery_options.is_active );
|
||||
FC_ASSERT( db().get_balance(lottery->get_id()).amount == 0 );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result lottery_end_evaluator::do_apply( const lottery_end_operation& op )
|
||||
{ try {
|
||||
db().modify( *asset_dynamic_data, [&]( asset_dynamic_data_object& data ) {
|
||||
data.current_supply = 0;
|
||||
});
|
||||
for( auto account_info : op.participants )
|
||||
{
|
||||
db().adjust_balance( account_info.first, -db().get_balance( account_info.first, op.lottery ) );
|
||||
}
|
||||
db().modify( *lottery, [](asset_object& ao) {
|
||||
ao.lottery_options->is_active = false;
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result sweeps_vesting_claim_evaluator::do_evaluate( const sweeps_vesting_claim_operation& op )
|
||||
{ try {
|
||||
const auto& sweeps_vesting_index = db().get_index_type<sweeps_vesting_balance_index>().indices().get<by_owner>();
|
||||
auto vesting = sweeps_vesting_index.find(op.account);
|
||||
FC_ASSERT( vesting != sweeps_vesting_index.end() );
|
||||
FC_ASSERT( op.amount_to_claim <= vesting->available_for_claim() );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result sweeps_vesting_claim_evaluator::do_apply( const sweeps_vesting_claim_operation& op )
|
||||
{ try {
|
||||
db().adjust_sweeps_vesting_balance( op.account, -op.amount_to_claim.amount.value * SWEEPS_VESTING_BALANCE_MULTIPLIER );
|
||||
db().adjust_balance( op.account, op.amount_to_claim );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/protocol/asset_ops.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -247,4 +248,18 @@ void asset_claim_fees_operation::validate()const {
|
|||
FC_ASSERT( amount_to_claim.amount > 0 );
|
||||
}
|
||||
|
||||
|
||||
void lottery_asset_options::validate() const
|
||||
{
|
||||
FC_ASSERT( winning_tickets.size() <= 64 );
|
||||
uint16_t total = 0;
|
||||
for( auto benefactor : benefactors ) {
|
||||
total += benefactor.share;
|
||||
}
|
||||
for( auto share : winning_tickets ) {
|
||||
total += share;
|
||||
}
|
||||
FC_ASSERT( total == GRAPHENE_100_PERCENT, "distribution amount not equals GRAPHENE_100_PERCENT" );
|
||||
}
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
asset fee_schedule::calculate_fee( const operation& op, const price& core_exchange_rate )const
|
||||
{
|
||||
//idump( (op)(core_exchange_rate) );
|
||||
//+( (op)(core_exchange_rate) );
|
||||
fee_parameters params; params.set_which(op.which());
|
||||
auto itr = parameters.find(params);
|
||||
if( itr != parameters.end() ) params = *itr;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ namespace graphene { namespace chain {
|
|||
void ticket_purchase_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
FC_ASSERT( tickets_to_buy > 0 );
|
||||
}
|
||||
|
||||
share_type ticket_purchase_operation::calculate_fee( const fee_parameters_type& k )const
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ namespace graphene { namespace db {
|
|||
const object& get( object_id_type id )const
|
||||
{
|
||||
auto maybe_found = find( id );
|
||||
if (maybe_found == nullptr)
|
||||
idump(("fail"));
|
||||
FC_ASSERT( maybe_found != nullptr, "Unable to find Object", ("id",id) );
|
||||
return *maybe_found;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,9 +171,10 @@ void database_fixture::verify_asset_supplies( const database& db )
|
|||
total_balances[t.options.buy_in.asset_id] += t.prize_pool;
|
||||
|
||||
for( const asset_object& ai : asst_index)
|
||||
if (ai.is_lottery())
|
||||
total_balances[ ai.lottery_options->balance.asset_id ] += ai.lottery_options->balance.amount;
|
||||
|
||||
if (ai.is_lottery()) {
|
||||
asset balance = db.get_balance( ai.get_id() );
|
||||
total_balances[ balance.asset_id ] += balance.amount;
|
||||
}
|
||||
for( const account_balance_object& b : balance_index )
|
||||
total_balances[b.asset_type] += b.balance;
|
||||
for( const force_settlement_object& s : settle_index )
|
||||
|
|
@ -214,13 +215,18 @@ void database_fixture::verify_asset_supplies( const database& db )
|
|||
for( const fba_accumulator_object& fba : db.get_index_type< simple_index< fba_accumulator_object > >() )
|
||||
total_balances[ asset_id_type() ] += fba.accumulated_fba_fees;
|
||||
|
||||
uint64_t sweeps_vestings = 0;
|
||||
for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() )
|
||||
sweeps_vestings += svbo.balance;
|
||||
|
||||
total_balances[db.get_global_properties().parameters.sweeps_distribution_asset] += sweeps_vestings / SWEEPS_VESTING_BALANCE_MULTIPLIER;
|
||||
total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget;
|
||||
|
||||
for( const auto& item : total_debts )
|
||||
{
|
||||
BOOST_CHECK_EQUAL(item.first(db).dynamic_asset_data_id(db).current_supply.value, item.second.value);
|
||||
}
|
||||
|
||||
|
||||
BOOST_CHECK_EQUAL( core_in_orders.value , reported_core_in_orders.value );
|
||||
BOOST_CHECK_EQUAL( total_balances[asset_id_type()].value , core_asset_data.current_supply.value - core_asset_data.confidential_supply.value);
|
||||
// wlog("*** End asset supply verification ***");
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ BOOST_FIXTURE_TEST_SUITE( lottery_tests, database_fixture )
|
|||
BOOST_AUTO_TEST_CASE( create_lottery_asset_test )
|
||||
{
|
||||
try {
|
||||
generate_block();
|
||||
idump((db.head_block_time()));
|
||||
asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();
|
||||
asset_create_operation creator;
|
||||
creator.issuer = account_id_type();
|
||||
|
|
@ -49,7 +51,7 @@ BOOST_AUTO_TEST_CASE( create_lottery_asset_test )
|
|||
char symbol[5] = "LOT";
|
||||
symbol[3] = (char)('A' - 1 + test_asset_id.instance.value); symbol[4] = '\0'; // symbol depending on asset_id
|
||||
creator.symbol = symbol;
|
||||
creator.common_options.max_supply = 20;
|
||||
creator.common_options.max_supply = 200;
|
||||
creator.precision = 0;
|
||||
creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/
|
||||
creator.common_options.issuer_permissions = charge_market_fee|white_list|override_authority|transfer_restricted|disable_confidential;
|
||||
|
|
@ -58,18 +60,21 @@ BOOST_AUTO_TEST_CASE( create_lottery_asset_test )
|
|||
creator.common_options.whitelist_authorities = creator.common_options.blacklist_authorities = {account_id_type()};
|
||||
|
||||
lottery_asset_options lottery_options;
|
||||
lottery_options.benefactors.push_back( benefactor( account_id_type(), 0.5 ) );
|
||||
lottery_options.end_date = db.get_dynamic_global_properties().time + fc::minutes(5);
|
||||
lottery_options.benefactors.push_back( benefactor( account_id_type(), 25 * GRAPHENE_1_PERCENT ) );
|
||||
lottery_options.end_date = db.head_block_time() + fc::minutes(5);
|
||||
lottery_options.ticket_price = asset(100);
|
||||
lottery_options.winning_tickets.push_back(0.5);
|
||||
lottery_options.winning_tickets = { 5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT };
|
||||
lottery_options.is_active = test_asset_id.instance.value % 2;
|
||||
|
||||
creator.extension = lottery_options;
|
||||
|
||||
trx.operations.push_back(std::move(creator));
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
idump((lottery_options.end_date));
|
||||
idump((db.head_block_time()));
|
||||
generate_block();
|
||||
|
||||
auto test_asset = test_asset_id(db);
|
||||
// idump((test_asset.is_lottery()));
|
||||
} catch (fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
|
|
@ -120,9 +125,10 @@ BOOST_AUTO_TEST_CASE( tickets_purchase_test )
|
|||
trx.operations.push_back(std::move(tpo));
|
||||
graphene::chain::test::set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
generate_block();
|
||||
trx.operations.clear();
|
||||
|
||||
BOOST_CHECK( tpo.amount == test_asset.lottery_options->balance );
|
||||
BOOST_CHECK( tpo.amount == db.get_balance( test_asset.get_id() ) );
|
||||
BOOST_CHECK( tpo.tickets_to_buy == get_balance( account_id_type(), test_asset.id ) );
|
||||
|
||||
} catch (fc::exception& e) {
|
||||
|
|
@ -161,4 +167,295 @@ BOOST_AUTO_TEST_CASE( tickets_purchase_fail_test )
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( lottery_end_by_stage_test )
|
||||
{
|
||||
try {
|
||||
asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();
|
||||
INVOKE( create_lottery_asset_test );
|
||||
auto test_asset = test_asset_id(db);
|
||||
for( int i = 1; i < 17; ++i ) {
|
||||
if( i == 4 || i == 1 || i == 16 || i == 15 ) continue;
|
||||
if( i != 0 )
|
||||
transfer(account_id_type(), account_id_type(i), asset(100000));
|
||||
ticket_purchase_operation tpo;
|
||||
tpo.fee = asset();
|
||||
tpo.buyer = account_id_type(i);
|
||||
tpo.lottery = test_asset.id;
|
||||
tpo.tickets_to_buy = i;
|
||||
tpo.amount = asset(100 * (i));
|
||||
trx.operations.push_back(std::move(tpo));
|
||||
graphene::chain::test::set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
generate_block();
|
||||
trx.operations.clear();
|
||||
}
|
||||
test_asset = test_asset_id(db);
|
||||
uint64_t benefactor_balance_before_end = db.get_balance( account_id_type(), asset_id_type() ).amount.value;
|
||||
uint64_t jackpot = db.get_balance( test_asset.get_id() ).amount.value;
|
||||
uint16_t winners_part = 0;
|
||||
for( uint16_t win: test_asset.lottery_options->winning_tickets )
|
||||
winners_part += win;
|
||||
|
||||
uint16_t participants_percents_sum = 0;
|
||||
auto participants = test_asset.distribute_winners_part( db );
|
||||
for( auto p : participants )
|
||||
for( auto e : p.second)
|
||||
participants_percents_sum += e;
|
||||
|
||||
BOOST_CHECK( participants_percents_sum == winners_part );
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value == (jackpot * (GRAPHENE_100_PERCENT - winners_part) / (double)GRAPHENE_100_PERCENT) + jackpot * winners_part * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT / (double)GRAPHENE_100_PERCENT );
|
||||
test_asset.distribute_benefactors_part( db );
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value == jackpot * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT * winners_part / (double)GRAPHENE_100_PERCENT );
|
||||
test_asset.distribute_sweeps_holders_part( db );
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value == 0 );
|
||||
|
||||
uint64_t benefactor_recieved = db.get_balance( account_id_type(), asset_id_type() ).amount.value - benefactor_balance_before_end;
|
||||
test_asset = test_asset_id(db);
|
||||
BOOST_CHECK(jackpot * test_asset.lottery_options->benefactors[0].share / GRAPHENE_100_PERCENT == benefactor_recieved);
|
||||
} catch (fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( lottery_end_by_stage_with_fractional_test )
|
||||
{
|
||||
|
||||
try {
|
||||
asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();
|
||||
INVOKE( create_lottery_asset_test );
|
||||
db.modify(test_asset_id(db), [&](asset_object& ao) {
|
||||
ao.lottery_options->is_active = true;
|
||||
});
|
||||
auto test_asset = test_asset_id(db);
|
||||
for( int i = 1; i < 17; ++i ) {
|
||||
if( i == 4 ) continue;
|
||||
if( i != 0 )
|
||||
transfer(account_id_type(), account_id_type(i), asset(100000));
|
||||
ticket_purchase_operation tpo;
|
||||
tpo.fee = asset();
|
||||
tpo.buyer = account_id_type(i);
|
||||
tpo.lottery = test_asset.id;
|
||||
tpo.tickets_to_buy = i;
|
||||
tpo.amount = asset(100 * (i));
|
||||
trx.operations.push_back(std::move(tpo));
|
||||
graphene::chain::test::set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
generate_block();
|
||||
trx.operations.clear();
|
||||
}
|
||||
test_asset = test_asset_id(db);
|
||||
uint64_t benefactor_balance_before_end = db.get_balance( account_id_type(), asset_id_type() ).amount.value;
|
||||
uint64_t jackpot = db.get_balance( test_asset.get_id() ).amount.value;
|
||||
uint16_t winners_part = 0;
|
||||
for( uint16_t win: test_asset.lottery_options->winning_tickets )
|
||||
winners_part += win;
|
||||
|
||||
uint16_t participants_percents_sum = 0;
|
||||
auto participants = test_asset.distribute_winners_part( db );
|
||||
for( auto p : participants )
|
||||
for( auto e : p.second)
|
||||
participants_percents_sum += e;
|
||||
|
||||
BOOST_CHECK( participants_percents_sum == winners_part );
|
||||
// balance should be bigger than expected because of rouning during distribution
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value > (jackpot * (GRAPHENE_100_PERCENT - winners_part) / (double)GRAPHENE_100_PERCENT) + jackpot * winners_part * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT / (double)GRAPHENE_100_PERCENT );
|
||||
test_asset.distribute_benefactors_part( db );
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value > jackpot * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT * winners_part / (double)GRAPHENE_100_PERCENT );
|
||||
test_asset.distribute_sweeps_holders_part( db );
|
||||
// but at the end is always equals 0
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value == 0 );
|
||||
|
||||
uint64_t benefactor_recieved = db.get_balance( account_id_type(), asset_id_type() ).amount.value - benefactor_balance_before_end;
|
||||
test_asset = test_asset_id(db);
|
||||
BOOST_CHECK(jackpot * test_asset.lottery_options->benefactors[0].share / GRAPHENE_100_PERCENT == benefactor_recieved);
|
||||
} catch (fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( lottery_end_test )
|
||||
{
|
||||
try {
|
||||
asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();
|
||||
INVOKE( create_lottery_asset_test );
|
||||
auto test_asset = test_asset_id(db);
|
||||
for( int i = 1; i < 17; ++i ) {
|
||||
if( i == 4 ) continue;
|
||||
if( i != 0 )
|
||||
transfer(account_id_type(), account_id_type(i), asset(100000));
|
||||
ticket_purchase_operation tpo;
|
||||
tpo.fee = asset();
|
||||
tpo.buyer = account_id_type(i);
|
||||
tpo.lottery = test_asset.id;
|
||||
tpo.tickets_to_buy = i;
|
||||
tpo.amount = asset(100 * (i));
|
||||
trx.operations.push_back(std::move(tpo));
|
||||
graphene::chain::test::set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
test_asset = test_asset_id(db);
|
||||
uint64_t creator_balance_before_end = db.get_balance( account_id_type(), asset_id_type() ).amount.value;
|
||||
uint64_t jackpot = db.get_balance( test_asset.get_id() ).amount.value;
|
||||
uint16_t winners_part = 0;
|
||||
for( uint8_t win: test_asset.lottery_options->winning_tickets )
|
||||
winners_part += win;
|
||||
|
||||
while( db.head_block_time() < ( test_asset.lottery_options->end_date + fc::seconds(30) ) )
|
||||
generate_block();
|
||||
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value == 0 );
|
||||
uint64_t creator_recieved = db.get_balance( account_id_type(), asset_id_type() ).amount.value - creator_balance_before_end;
|
||||
test_asset = test_asset_id(db);
|
||||
BOOST_CHECK(jackpot * test_asset.lottery_options->benefactors[0].share / GRAPHENE_100_PERCENT == creator_recieved);
|
||||
} catch (fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( claim_sweeps_vesting_balance_test )
|
||||
{
|
||||
try {
|
||||
asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();
|
||||
INVOKE( lottery_end_test );
|
||||
auto test_asset = test_asset_id(db);
|
||||
account_id_type benefactor = test_asset.lottery_options->benefactors[0].id;
|
||||
const auto& svbo_index = db.get_index_type<sweeps_vesting_balance_index>().indices().get<by_owner>();
|
||||
auto benefactor_svbo = svbo_index.find(benefactor);
|
||||
BOOST_CHECK( benefactor_svbo != svbo_index.end() );
|
||||
|
||||
auto balance_before_claim = db.get_balance( benefactor, SWEEPS_DEFAULT_DISTRIBUTION_ASSET );
|
||||
auto available_for_claim = benefactor_svbo->available_for_claim();
|
||||
sweeps_vesting_claim_operation claim;
|
||||
claim.account = benefactor;
|
||||
claim.amount_to_claim = available_for_claim;
|
||||
trx.clear();
|
||||
graphene::chain::test::set_expiration(db, trx);
|
||||
trx.operations.push_back(claim);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
generate_block();
|
||||
|
||||
BOOST_CHECK( db.get_balance( benefactor, SWEEPS_DEFAULT_DISTRIBUTION_ASSET ) - balance_before_claim == available_for_claim );
|
||||
benefactor_svbo = svbo_index.find(benefactor);
|
||||
BOOST_CHECK( benefactor_svbo->available_for_claim().amount == 0 );
|
||||
} catch( fc::exception& e ) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( more_winners_then_participants_test )
|
||||
{
|
||||
try {
|
||||
asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();
|
||||
INVOKE( create_lottery_asset_test );
|
||||
auto test_asset = test_asset_id(db);
|
||||
for( int i = 1; i < 4; ++i ) {
|
||||
if( i == 4 ) continue;
|
||||
if( i != 0 )
|
||||
transfer(account_id_type(), account_id_type(i), asset(1000000));
|
||||
ticket_purchase_operation tpo;
|
||||
tpo.fee = asset();
|
||||
tpo.buyer = account_id_type(i);
|
||||
tpo.lottery = test_asset.id;
|
||||
tpo.tickets_to_buy = 1;
|
||||
tpo.amount = asset(100);
|
||||
trx.operations.push_back(std::move(tpo));
|
||||
graphene::chain::test::set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
test_asset = test_asset_id(db);
|
||||
auto holders = test_asset.get_holders(db);
|
||||
idump(( db.get_balance(test_asset.get_id()) ));
|
||||
auto participants = test_asset.distribute_winners_part( db );
|
||||
test_asset.distribute_benefactors_part( db );
|
||||
test_asset.distribute_sweeps_holders_part( db );
|
||||
generate_block();
|
||||
idump(( db.get_balance(test_asset.get_id()) ));
|
||||
idump(( participants ));
|
||||
for( auto p: participants ) {
|
||||
idump(( get_operation_history(p.first) ));
|
||||
}
|
||||
auto benefactor_history = get_operation_history( account_id_type() );
|
||||
for( auto h: benefactor_history ) {
|
||||
idump((h));
|
||||
}
|
||||
} catch( fc::exception& e ) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( ending_by_date_test )
|
||||
{
|
||||
try {
|
||||
asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();
|
||||
INVOKE( create_lottery_asset_test );
|
||||
auto test_asset = test_asset_id(db);
|
||||
for( int i = 1; i < 4; ++i ) {
|
||||
if( i == 4 ) continue;
|
||||
if( i != 0 )
|
||||
transfer(account_id_type(), account_id_type(i), asset(1000000));
|
||||
ticket_purchase_operation tpo;
|
||||
tpo.fee = asset();
|
||||
tpo.buyer = account_id_type(i);
|
||||
tpo.lottery = test_asset.id;
|
||||
tpo.tickets_to_buy = 1;
|
||||
tpo.amount = asset(100);
|
||||
trx.operations.push_back(std::move(tpo));
|
||||
graphene::chain::test::set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
test_asset = test_asset_id(db);
|
||||
auto holders = test_asset.get_holders(db);
|
||||
idump(( db.get_balance(test_asset.get_id()) ));
|
||||
while( db.head_block_time() < ( test_asset.lottery_options->end_date + fc::seconds(30) ) )
|
||||
generate_block();
|
||||
idump(( db.get_balance(test_asset.get_id()) ));
|
||||
vector<account_id_type> participants = { account_id_type(1), account_id_type(2), account_id_type(3) };
|
||||
for( auto p: participants ) {
|
||||
idump(( get_operation_history(p) ));
|
||||
}
|
||||
auto benefactor_history = get_operation_history( account_id_type() );
|
||||
for( auto h: benefactor_history ) {
|
||||
if( h.op.which() == operation::tag<lottery_reward_operation>::value ) {
|
||||
auto reward_op = h.op.get<lottery_reward_operation>();
|
||||
idump((reward_op));
|
||||
BOOST_CHECK( reward_op.is_benefactor_reward );
|
||||
BOOST_CHECK( reward_op.amount.amount.value == 75 );
|
||||
BOOST_CHECK( reward_op.amount.asset_id == test_asset.lottery_options->ticket_price.asset_id );
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch( fc::exception& e ) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( try_to_end_empty_lottery_test )
|
||||
{
|
||||
try {
|
||||
asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();
|
||||
INVOKE( create_lottery_asset_test );
|
||||
auto test_asset = test_asset_id(db);
|
||||
while( db.head_block_time() < ( test_asset.lottery_options->end_date + fc::seconds(30) ) )
|
||||
generate_block();
|
||||
test_asset = test_asset_id(db);
|
||||
BOOST_CHECK( !test_asset.lottery_options->is_active );
|
||||
} catch( fc::exception& e ) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
Loading…
Reference in a new issue