NFT Lottery (#384)

This commit is contained in:
sierra19XX 2021-01-29 06:27:54 +11:00 committed by GitHub
parent 5a5154b260
commit b99a19bd72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 2005 additions and 129 deletions

View file

@ -234,7 +234,6 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
vector<offer_history_object> get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const; vector<offer_history_object> get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const;
vector<offer_history_object> get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const; vector<offer_history_object> get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const;
uint32_t api_limit_get_lower_bound_symbol = 100; uint32_t api_limit_get_lower_bound_symbol = 100;
uint32_t api_limit_get_limit_orders = 300; uint32_t api_limit_get_limit_orders = 300;
uint32_t api_limit_get_limit_orders_by_account = 101; uint32_t api_limit_get_limit_orders_by_account = 101;
@ -248,6 +247,9 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
// Account Role // Account Role
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const; vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
// rng
vector<uint64_t> get_random_number_ex(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates) const;
uint64_t get_random_number(uint64_t bound) const;
//private: //private:
const account_object* get_account_from_string( const std::string& name_or_id, const account_object* get_account_from_string( const std::string& name_or_id,
@ -3174,6 +3176,32 @@ vector<account_role_object> database_api_impl::get_account_roles_by_owner(accoun
} }
return result; return result;
} }
//////////////////////////////////////////////////////////////////////
// //
// Random numbers //
// //
//////////////////////////////////////////////////////////////////////
vector<uint64_t> database_api::get_random_number_ex(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates) const
{
return my->get_random_number_ex(minimum, maximum, selections, duplicates);
}
vector<uint64_t> database_api_impl::get_random_number_ex(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates) const
{
return _db.get_random_numbers(minimum, maximum, selections, duplicates);
}
uint64_t database_api::get_random_number(uint64_t bound) const
{
return my->get_random_number(bound);
}
uint64_t database_api_impl::get_random_number(uint64_t bound) const {
vector<uint64_t> v = get_random_number_ex(0, bound, 1, false);
return v.at(0);
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// // // //
// Private methods // // Private methods //

View file

@ -934,7 +934,28 @@ class database_api
// ACCOUNT ROLE // // ACCOUNT ROLE //
////////////////// //////////////////
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const; vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
private:
/////////////////////////////
// Random number generator //
/////////////////////////////
/**
* @brief Returns the random number
* @param minimum Lower bound of segment containing random number
* @param maximum Upper bound of segment containing random number
* @param selections Number of random numbers to return
* @param duplicates Allow duplicated numbers
* @return Vector containing random numbers from segment [minimum, maximum)
*/
vector<uint64_t> get_random_number_ex(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates) const;
/**
* @brief Returns the random number
* @param bound Upper bound of segment containing random number
* @return Random number from segment [0, bound)
*/
uint64_t get_random_number(uint64_t bound) const;
private:
std::shared_ptr< database_api_impl > my; std::shared_ptr< database_api_impl > my;
}; };
@ -1125,4 +1146,7 @@ FC_API(graphene::app::database_api,
// Account Roles // Account Roles
(get_account_roles_by_owner) (get_account_roles_by_owner)
// rngs
(get_random_number_ex)
(get_random_number)
) )

View file

@ -715,7 +715,8 @@ void database::_apply_block( const signed_block& next_block )
perform_chain_maintenance(next_block, global_props); perform_chain_maintenance(next_block, global_props);
check_ending_lotteries(); check_ending_lotteries();
check_ending_nft_lotteries();
create_block_summary(next_block); create_block_summary(next_block);
place_delayed_bets(); // must happen after update_global_dynamic_data() updates the time place_delayed_bets(); // must happen after update_global_dynamic_data() updates the time
clear_expired_transactions(); clear_expired_transactions();

View file

@ -109,7 +109,7 @@ uint32_t database::last_non_undoable_block_num() const
return head_block_num() - _undo_db.size(); return head_block_num() - _undo_db.size();
} }
std::vector<uint32_t> database::get_seeds(asset_id_type for_asset, uint8_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 ); FC_ASSERT( count_winners <= 64 );
std::string salted_string = std::string(_random_number_generator._seed) + std::to_string(for_asset.instance.value); std::string salted_string = std::string(_random_number_generator._seed) + std::to_string(for_asset.instance.value);
@ -315,4 +315,36 @@ bool database::is_son_active( son_id_type son_id )
return (it_son != active_son_ids.end()); return (it_son != active_son_ids.end());
} }
vector<uint64_t> database::get_random_numbers(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates)
{
FC_ASSERT( selections <= 100000 );
if (duplicates == false) {
FC_ASSERT( maximum - minimum >= selections );
}
vector<uint64_t> v;
v.reserve(selections);
if (duplicates) {
for (uint64_t i = 0; i < selections; i++) {
int64_t rnd = get_random_bits(maximum - minimum) + minimum;
v.push_back(rnd);
}
} else {
vector<uint64_t> tmpv;
tmpv.reserve(selections);
for (uint64_t i = minimum; i < maximum; i++) {
tmpv.push_back(i);
}
for (uint64_t i = 0; (i < selections) && (tmpv.size() > 0); i++) {
uint64_t idx = get_random_bits(tmpv.size());
v.push_back(tmpv.at(idx));
tmpv.erase(tmpv.begin() + idx);
}
}
return v;
}
} } } }

View file

@ -53,6 +53,7 @@
#include <graphene/chain/custom_account_authority_object.hpp> #include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/offer_object.hpp> #include <graphene/chain/offer_object.hpp>
#include <graphene/chain/account_role_object.hpp> #include <graphene/chain/account_role_object.hpp>
#include <graphene/chain/random_number_object.hpp>
#include <graphene/chain/nft_object.hpp> #include <graphene/chain/nft_object.hpp>
@ -94,12 +95,14 @@
#include <graphene/chain/offer_evaluator.hpp> #include <graphene/chain/offer_evaluator.hpp>
#include <graphene/chain/nft_evaluator.hpp> #include <graphene/chain/nft_evaluator.hpp>
#include <graphene/chain/account_role_evaluator.hpp> #include <graphene/chain/account_role_evaluator.hpp>
#include <graphene/chain/nft_lottery_evaluator.hpp>
#include <graphene/chain/son_evaluator.hpp> #include <graphene/chain/son_evaluator.hpp>
#include <graphene/chain/son_wallet_evaluator.hpp> #include <graphene/chain/son_wallet_evaluator.hpp>
#include <graphene/chain/son_wallet_deposit_evaluator.hpp> #include <graphene/chain/son_wallet_deposit_evaluator.hpp>
#include <graphene/chain/son_wallet_withdraw_evaluator.hpp> #include <graphene/chain/son_wallet_withdraw_evaluator.hpp>
#include <graphene/chain/sidechain_address_evaluator.hpp> #include <graphene/chain/sidechain_address_evaluator.hpp>
#include <graphene/chain/sidechain_transaction_evaluator.hpp> #include <graphene/chain/sidechain_transaction_evaluator.hpp>
#include <graphene/chain/random_number_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp> #include <graphene/chain/protocol/fee_schedule.hpp>
@ -203,6 +206,12 @@ const uint8_t offer_history_object::type_id;
const uint8_t account_role_object::space_id; const uint8_t account_role_object::space_id;
const uint8_t account_role_object::type_id; const uint8_t account_role_object::type_id;
const uint8_t nft_lottery_balance_object::space_id;
const uint8_t nft_lottery_balance_object::type_id;
const uint8_t random_number_object::space_id;
const uint8_t random_number_object::type_id;
void database::initialize_evaluators() void database::initialize_evaluators()
{ {
_operation_evaluators.resize(255); _operation_evaluators.resize(255);
@ -295,6 +304,9 @@ void database::initialize_evaluators()
register_evaluator<account_role_create_evaluator>(); register_evaluator<account_role_create_evaluator>();
register_evaluator<account_role_update_evaluator>(); register_evaluator<account_role_update_evaluator>();
register_evaluator<account_role_delete_evaluator>(); register_evaluator<account_role_delete_evaluator>();
register_evaluator<nft_lottery_token_purchase_evaluator>();
register_evaluator<nft_lottery_reward_evaluator>();
register_evaluator<nft_lottery_end_evaluator>();
register_evaluator<create_son_evaluator>(); register_evaluator<create_son_evaluator>();
register_evaluator<update_son_evaluator>(); register_evaluator<update_son_evaluator>();
register_evaluator<deregister_son_evaluator>(); register_evaluator<deregister_son_evaluator>();
@ -314,6 +326,7 @@ void database::initialize_evaluators()
register_evaluator<sidechain_transaction_sign_evaluator>(); register_evaluator<sidechain_transaction_sign_evaluator>();
register_evaluator<sidechain_transaction_send_evaluator>(); register_evaluator<sidechain_transaction_send_evaluator>();
register_evaluator<sidechain_transaction_settle_evaluator>(); register_evaluator<sidechain_transaction_settle_evaluator>();
register_evaluator<random_number_store_evaluator>();
} }
void database::initialize_indexes() void database::initialize_indexes()
@ -403,7 +416,9 @@ void database::initialize_indexes()
add_index< primary_index<lottery_balance_index > >(); add_index< primary_index<lottery_balance_index > >();
add_index< primary_index<sweeps_vesting_balance_index > >(); add_index< primary_index<sweeps_vesting_balance_index > >();
add_index< primary_index<offer_history_index > >(); add_index< primary_index<offer_history_index > >();
add_index< primary_index<nft_lottery_balance_index > >();
add_index< primary_index<son_stats_index > >(); add_index< primary_index<son_stats_index > >();
add_index< primary_index<random_number_index > >();
} }

View file

@ -28,6 +28,7 @@
#include <graphene/chain/witness_schedule_object.hpp> #include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/special_authority_object.hpp> #include <graphene/chain/special_authority_object.hpp>
#include <graphene/chain/operation_history_object.hpp> #include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp> #include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/io/fstream.hpp> #include <fc/io/fstream.hpp>
@ -304,6 +305,24 @@ void database::check_ending_lotteries()
} catch( ... ) {} } catch( ... ) {}
} }
void database::check_ending_nft_lotteries()
{
try {
const auto &nft_lotteries_idx = get_index_type<nft_metadata_index>().indices().get<active_nft_lotteries>();
for (auto checking_token : nft_lotteries_idx)
{
FC_ASSERT(checking_token.is_lottery());
const auto &lottery_options = checking_token.lottery_data->lottery_options;
FC_ASSERT(lottery_options.is_active);
// Check the current supply of lottery tokens
auto current_supply = checking_token.get_token_current_supply(*this);
if ((lottery_options.ending_on_soldout && (current_supply == checking_token.max_supply)) ||
(lottery_options.end_date != time_point_sec() && (lottery_options.end_date <= head_block_time())))
checking_token.end_lottery(*this);
}
} catch( ... ) {}
}
void database::check_lottery_end_by_participants( asset_id_type asset_id ) void database::check_lottery_end_by_participants( asset_id_type asset_id )
{ {
try { try {

View file

@ -364,6 +364,13 @@ struct get_impacted_account_visitor
void operator()( const account_role_delete_operation& op ){ void operator()( const account_role_delete_operation& op ){
_impacted.insert( op.owner ); _impacted.insert( op.owner );
} }
void operator()( const nft_lottery_token_purchase_operation& op ){
_impacted.insert( op.buyer );
}
void operator()( const nft_lottery_reward_operation& op ) {
_impacted.insert( op.winner );
}
void operator()( const nft_lottery_end_operation& op ) {}
void operator()( const son_create_operation& op ) { void operator()( const son_create_operation& op ) {
_impacted.insert( op.owner_account ); _impacted.insert( op.owner_account );
} }
@ -421,6 +428,9 @@ struct get_impacted_account_visitor
void operator()( const sidechain_transaction_settle_operation& op ) { void operator()( const sidechain_transaction_settle_operation& op ) {
_impacted.insert( op.payer ); _impacted.insert( op.payer );
} }
void operator()( const random_number_store_operation& op ) {
_impacted.insert( op.account );
}
}; };
void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) { void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
@ -590,6 +600,8 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
break; break;
case impl_fba_accumulator_object_type: case impl_fba_accumulator_object_type:
break; break;
case impl_nft_lottery_balance_object_type:
break;
default: default:
break; break;
} }

View file

@ -286,6 +286,7 @@ namespace graphene { namespace chain {
void check_lottery_end_by_participants( asset_id_type asset_id ); void check_lottery_end_by_participants( asset_id_type asset_id );
void check_ending_lotteries(); void check_ending_lotteries();
void check_ending_nft_lotteries();
//////////////////// db_getter.cpp //////////////////// //////////////////// db_getter.cpp ////////////////////
@ -324,6 +325,7 @@ namespace graphene { namespace chain {
uint32_t last_non_undoable_block_num() const; uint32_t last_non_undoable_block_num() const;
vector<authority> get_account_custom_authorities(account_id_type account, const operation& op)const; vector<authority> get_account_custom_authorities(account_id_type account, const operation& op)const;
vector<uint64_t> get_random_numbers(uint64_t minimum, uint64_t maximum, uint64_t selections, bool duplicates);
//////////////////// db_init.cpp //////////////////// //////////////////// db_init.cpp ////////////////////
void initialize_evaluators(); void initialize_evaluators();

View file

@ -0,0 +1,39 @@
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/database.hpp>
namespace graphene
{
namespace chain
{
class nft_lottery_token_purchase_evaluator : public evaluator<nft_lottery_token_purchase_evaluator>
{
public:
typedef nft_lottery_token_purchase_operation operation_type;
void_result do_evaluate(const nft_lottery_token_purchase_operation &o);
object_id_type do_apply(const nft_lottery_token_purchase_operation &o);
};
class nft_lottery_reward_evaluator : public evaluator<nft_lottery_reward_evaluator>
{
public:
typedef nft_lottery_reward_operation operation_type;
void_result do_evaluate(const nft_lottery_reward_operation &o);
void_result do_apply(const nft_lottery_reward_operation &o);
};
class nft_lottery_end_evaluator : public evaluator<nft_lottery_end_evaluator>
{
public:
typedef nft_lottery_end_operation operation_type;
void_result do_evaluate(const nft_lottery_end_operation &o);
void_result do_apply(const nft_lottery_end_operation &o);
};
} // namespace chain
} // namespace graphene

View file

@ -6,6 +6,29 @@
namespace graphene { namespace chain { namespace graphene { namespace chain {
using namespace graphene::db; using namespace graphene::db;
class nft_lottery_balance_object : public abstract_object<nft_lottery_balance_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_nft_lottery_balance_object_type;
// Total Progressive jackpot carried over from previous lotteries
asset total_progressive_jackpot;
// Current total jackpot in this lottery inclusive of the progressive jackpot
asset jackpot;
// Total tickets sold
share_type sweeps_tickets_sold;
};
struct nft_lottery_data
{
nft_lottery_data() {}
nft_lottery_data(const nft_lottery_options &options, nft_lottery_balance_id_type lottery_id)
: lottery_options(options), lottery_balance_id(lottery_id) {}
nft_lottery_options lottery_options;
nft_lottery_balance_id_type lottery_balance_id;
};
class nft_metadata_object : public abstract_object<nft_metadata_object> class nft_metadata_object : public abstract_object<nft_metadata_object>
{ {
public: public:
@ -21,6 +44,21 @@ namespace graphene { namespace chain {
bool is_transferable = false; bool is_transferable = false;
bool is_sellable = true; bool is_sellable = true;
optional<account_role_id_type> account_role; optional<account_role_id_type> account_role;
share_type max_supply = GRAPHENE_MAX_SHARE_SUPPLY;
optional<nft_lottery_data> lottery_data;
nft_metadata_id_type get_id() const { return id; }
bool is_lottery() const { return lottery_data.valid(); }
uint32_t get_owner_num() const { return owner.instance.value; }
time_point_sec get_lottery_expiration() const;
asset get_lottery_jackpot(const database &db) const;
share_type get_token_current_supply(const database &db) const;
vector<account_id_type> get_holders(const database &db) const;
vector<uint64_t> get_ticket_ids(const 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);
}; };
class nft_object : public abstract_object<nft_object> class nft_object : public abstract_object<nft_object>
@ -36,8 +74,23 @@ namespace graphene { namespace chain {
std::string token_uri; std::string token_uri;
}; };
struct nft_lottery_comparer
{
bool operator()(const nft_metadata_object& lhs, const nft_metadata_object& rhs) const
{
if ( !lhs.is_lottery() ) return false;
if ( !lhs.lottery_data->lottery_options.is_active && !rhs.is_lottery()) return true; // not active lotteries first, just assets then
if ( !lhs.lottery_data->lottery_options.is_active ) return false;
if ( lhs.lottery_data->lottery_options.is_active && ( !rhs.is_lottery() || !rhs.lottery_data->lottery_options.is_active ) ) return true;
return lhs.get_lottery_expiration() > rhs.get_lottery_expiration();
}
};
struct by_name; struct by_name;
struct by_symbol; struct by_symbol;
struct active_nft_lotteries;
struct by_nft_lottery;
struct by_nft_lottery_owner;
using nft_metadata_multi_index_type = multi_index_container< using nft_metadata_multi_index_type = multi_index_container<
nft_metadata_object, nft_metadata_object,
indexed_by< indexed_by<
@ -49,6 +102,34 @@ namespace graphene { namespace chain {
>, >,
ordered_unique< tag<by_symbol>, ordered_unique< tag<by_symbol>,
member<nft_metadata_object, std::string, &nft_metadata_object::symbol> member<nft_metadata_object, std::string, &nft_metadata_object::symbol>
>,
ordered_non_unique< tag<active_nft_lotteries>,
identity< nft_metadata_object >,
nft_lottery_comparer
>,
ordered_unique< tag<by_nft_lottery>,
composite_key<
nft_metadata_object,
const_mem_fun<nft_metadata_object, bool, &nft_metadata_object::is_lottery>,
member<object, object_id_type, &object::id>
>,
composite_key_compare<
std::greater< bool >,
std::greater< object_id_type >
>
>,
ordered_unique< tag<by_nft_lottery_owner>,
composite_key<
nft_metadata_object,
const_mem_fun<nft_metadata_object, bool, &nft_metadata_object::is_lottery>,
const_mem_fun<nft_metadata_object, uint32_t, &nft_metadata_object::get_owner_num>,
member<object, object_id_type, &object::id>
>,
composite_key_compare<
std::greater< bool >,
std::greater< uint32_t >,
std::greater< object_id_type >
>
> >
> >
>; >;
@ -86,8 +167,23 @@ namespace graphene { namespace chain {
>; >;
using nft_index = generic_index<nft_object, nft_multi_index_type>; using nft_index = generic_index<nft_object, nft_multi_index_type>;
using nft_lottery_balance_index_type = multi_index_container<
nft_lottery_balance_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >
>
>;
using nft_lottery_balance_index = generic_index<nft_lottery_balance_object, nft_lottery_balance_index_type>;
} } // graphene::chain } } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::nft_lottery_balance_object, (graphene::db::object),
(total_progressive_jackpot)
(jackpot)
(sweeps_tickets_sold) )
FC_REFLECT( graphene::chain::nft_lottery_data, (lottery_options)(lottery_balance_id) )
FC_REFLECT_DERIVED( graphene::chain::nft_metadata_object, (graphene::db::object), FC_REFLECT_DERIVED( graphene::chain::nft_metadata_object, (graphene::db::object),
(owner) (owner)
(name) (name)
@ -97,7 +193,9 @@ FC_REFLECT_DERIVED( graphene::chain::nft_metadata_object, (graphene::db::object)
(revenue_split) (revenue_split)
(is_transferable) (is_transferable)
(is_sellable) (is_sellable)
(account_role) ) (account_role)
(max_supply)
(lottery_data) )
FC_REFLECT_DERIVED( graphene::chain::nft_object, (graphene::db::object), FC_REFLECT_DERIVED( graphene::chain::nft_object, (graphene::db::object),
(nft_metadata_id) (nft_metadata_id)

View file

@ -0,0 +1,86 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene
{
namespace chain
{
struct nft_lottery_token_purchase_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
// Lottery NFT Metadata
nft_metadata_id_type lottery_id;
// Buyer purchasing lottery tickets
account_id_type buyer;
// count of tickets to buy
uint64_t tickets_to_buy;
// amount that can spent
asset amount;
extensions_type extensions;
account_id_type fee_payer() const { return buyer; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const;
};
struct nft_lottery_reward_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
// Lottery NFT Metadata
nft_metadata_id_type lottery_id;
// 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;
uint64_t winner_ticket_id;
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 nft_lottery_end_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
// Lottery NFT Metadata
nft_metadata_id_type lottery_id;
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; }
};
} // namespace chain
} // namespace graphene
FC_REFLECT(graphene::chain::nft_lottery_token_purchase_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::nft_lottery_reward_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::nft_lottery_end_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::nft_lottery_token_purchase_operation, (fee)(lottery_id)(buyer)(tickets_to_buy)(amount)(extensions))
FC_REFLECT(graphene::chain::nft_lottery_reward_operation, (fee)(lottery_id)(winner)(amount)(win_percentage)(is_benefactor_reward)(winner_ticket_id)(extensions))
FC_REFLECT(graphene::chain::nft_lottery_end_operation, (fee)(lottery_id)(extensions))

View file

@ -4,6 +4,29 @@
namespace graphene { namespace chain { namespace graphene { namespace chain {
struct nft_lottery_benefactor {
account_id_type id;
uint16_t share; // percent * GRAPHENE_1_PERCENT
nft_lottery_benefactor() = default;
nft_lottery_benefactor( const nft_lottery_benefactor & ) = default;
nft_lottery_benefactor( account_id_type _id, uint16_t _share ) : id( _id ), share( _share ) {}
};
struct nft_lottery_options
{
std::vector<nft_lottery_benefactor> benefactors;
// specifying winning tickets as shares that will be issued
std::vector<uint16_t> winning_tickets;
asset ticket_price;
time_point_sec end_date;
bool ending_on_soldout;
bool is_active;
bool delete_tickets_after_draw = false;
std::vector<nft_metadata_id_type> progressive_jackpots;
void validate() const;
};
struct nft_metadata_create_operation : public base_operation struct nft_metadata_create_operation : public base_operation
{ {
struct fee_parameters_type struct fee_parameters_type
@ -23,6 +46,10 @@ namespace graphene { namespace chain {
bool is_sellable = true; bool is_sellable = true;
// Accounts Role // Accounts Role
optional<account_role_id_type> account_role; optional<account_role_id_type> account_role;
// Max number of NFTs that can be minted from the metadata
optional<share_type> max_supply;
// Lottery configuration
optional<nft_lottery_options> lottery_options;
extensions_type extensions; extensions_type extensions;
account_id_type fee_payer()const { return owner; } account_id_type fee_payer()const { return owner; }
@ -133,6 +160,9 @@ namespace graphene { namespace chain {
} } // graphene::chain } } // graphene::chain
FC_REFLECT( graphene::chain::nft_lottery_benefactor, (id)(share) )
FC_REFLECT( graphene::chain::nft_lottery_options, (benefactors)(winning_tickets)(ticket_price)(end_date)(ending_on_soldout)(is_active)(delete_tickets_after_draw)(progressive_jackpots) )
FC_REFLECT( graphene::chain::nft_metadata_create_operation::fee_parameters_type, (fee) (price_per_kbyte) ) FC_REFLECT( graphene::chain::nft_metadata_create_operation::fee_parameters_type, (fee) (price_per_kbyte) )
FC_REFLECT( graphene::chain::nft_metadata_update_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::nft_metadata_update_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::nft_mint_operation::fee_parameters_type, (fee) (price_per_kbyte) ) FC_REFLECT( graphene::chain::nft_mint_operation::fee_parameters_type, (fee) (price_per_kbyte) )
@ -140,7 +170,7 @@ FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation::fee_parameters_ty
FC_REFLECT( graphene::chain::nft_approve_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::nft_approve_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) ) FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (max_supply) (lottery_options) (extensions) )
FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) ) FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) )
FC_REFLECT( graphene::chain::nft_mint_operation, (fee) (payer) (nft_metadata_id) (owner) (approved) (approved_operators) (token_uri) (extensions) ) FC_REFLECT( graphene::chain::nft_mint_operation, (fee) (payer) (nft_metadata_id) (owner) (approved) (approved_operators) (token_uri) (extensions) )
FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) (extensions) ) FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) (extensions) )

View file

@ -50,12 +50,14 @@
#include <graphene/chain/protocol/offer.hpp> #include <graphene/chain/protocol/offer.hpp>
#include <graphene/chain/protocol/nft_ops.hpp> #include <graphene/chain/protocol/nft_ops.hpp>
#include <graphene/chain/protocol/account_role.hpp> #include <graphene/chain/protocol/account_role.hpp>
#include <graphene/chain/protocol/nft_lottery.hpp>
#include <graphene/chain/protocol/son.hpp> #include <graphene/chain/protocol/son.hpp>
#include <graphene/chain/protocol/sidechain_address.hpp> #include <graphene/chain/protocol/sidechain_address.hpp>
#include <graphene/chain/protocol/son_wallet.hpp> #include <graphene/chain/protocol/son_wallet.hpp>
#include <graphene/chain/protocol/son_wallet_deposit.hpp> #include <graphene/chain/protocol/son_wallet_deposit.hpp>
#include <graphene/chain/protocol/son_wallet_withdraw.hpp> #include <graphene/chain/protocol/son_wallet_withdraw.hpp>
#include <graphene/chain/protocol/sidechain_transaction.hpp> #include <graphene/chain/protocol/sidechain_transaction.hpp>
#include <graphene/chain/protocol/random_number.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
@ -184,7 +186,11 @@ namespace graphene { namespace chain {
sidechain_transaction_create_operation, sidechain_transaction_create_operation,
sidechain_transaction_sign_operation, sidechain_transaction_sign_operation,
sidechain_transaction_send_operation, sidechain_transaction_send_operation,
sidechain_transaction_settle_operation sidechain_transaction_settle_operation,
nft_lottery_token_purchase_operation,
nft_lottery_reward_operation,
nft_lottery_end_operation,
random_number_store_operation
> operation; > operation;
/// @} // operations group /// @} // operations group

View file

@ -0,0 +1,25 @@
#pragma once
namespace graphene { namespace chain {
struct random_number_store_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 5000 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type account;
vector<uint64_t> random_number;
std::string data;
account_id_type fee_payer()const { return account; }
};
} } // graphene::chain
FC_REFLECT( graphene::chain::random_number_store_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::random_number_store_operation, (fee)
(account)
(random_number)
(data) )

View file

@ -183,6 +183,7 @@ namespace graphene { namespace chain {
son_wallet_withdraw_object_type, son_wallet_withdraw_object_type,
sidechain_address_object_type, sidechain_address_object_type,
sidechain_transaction_object_type, sidechain_transaction_object_type,
random_number_object_type,
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
}; };
@ -214,7 +215,8 @@ namespace graphene { namespace chain {
impl_sweeps_vesting_balance_object_type, impl_sweeps_vesting_balance_object_type,
impl_offer_history_object_type, impl_offer_history_object_type,
impl_son_statistics_object_type, impl_son_statistics_object_type,
impl_son_schedule_object_type impl_son_schedule_object_type,
impl_nft_lottery_balance_object_type
}; };
//typedef fc::unsigned_int object_id_type; //typedef fc::unsigned_int object_id_type;
@ -258,6 +260,7 @@ namespace graphene { namespace chain {
class son_wallet_withdraw_object; class son_wallet_withdraw_object;
class sidechain_address_object; class sidechain_address_object;
class sidechain_transaction_object; class sidechain_transaction_object;
class random_number_object;
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; 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, asset_object_type, asset_object> asset_id_type;
@ -297,6 +300,7 @@ namespace graphene { namespace chain {
typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type; typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type;
typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type; typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type;
typedef object_id< protocol_ids, sidechain_transaction_object_type,sidechain_transaction_object> sidechain_transaction_id_type; typedef object_id< protocol_ids, sidechain_transaction_object_type,sidechain_transaction_object> sidechain_transaction_id_type;
typedef object_id< protocol_ids, random_number_object_type, random_number_object> random_number_id_type;
// implementation types // implementation types
class global_property_object; class global_property_object;
@ -321,6 +325,7 @@ namespace graphene { namespace chain {
class lottery_balance_object; class lottery_balance_object;
class sweeps_vesting_balance_object; class sweeps_vesting_balance_object;
class offer_history_object; class offer_history_object;
class nft_lottery_balance_object;
class son_statistics_object; class son_statistics_object;
class son_schedule_object; class son_schedule_object;
@ -352,6 +357,7 @@ namespace graphene { namespace chain {
typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_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 object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type;
typedef object_id< implementation_ids, impl_offer_history_object_type, offer_history_object> offer_history_id_type; typedef object_id< implementation_ids, impl_offer_history_object_type, offer_history_object> offer_history_id_type;
typedef object_id< implementation_ids, impl_nft_lottery_balance_object_type, nft_lottery_balance_object> nft_lottery_balance_id_type;
typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type; typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type;
typedef object_id< implementation_ids, impl_son_schedule_object_type, son_schedule_object> son_schedule_id_type; typedef object_id< implementation_ids, impl_son_schedule_object_type, son_schedule_object> son_schedule_id_type;
@ -496,6 +502,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
(son_wallet_withdraw_object_type) (son_wallet_withdraw_object_type)
(sidechain_address_object_type) (sidechain_address_object_type)
(sidechain_transaction_object_type) (sidechain_transaction_object_type)
(random_number_object_type)
(OBJECT_TYPE_COUNT) (OBJECT_TYPE_COUNT)
) )
FC_REFLECT_ENUM( graphene::chain::impl_object_type, FC_REFLECT_ENUM( graphene::chain::impl_object_type,
@ -526,6 +533,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
(impl_offer_history_object_type) (impl_offer_history_object_type)
(impl_son_statistics_object_type) (impl_son_statistics_object_type)
(impl_son_schedule_object_type) (impl_son_schedule_object_type)
(impl_nft_lottery_balance_object_type)
) )
FC_REFLECT_TYPENAME( graphene::chain::share_type ) FC_REFLECT_TYPENAME( graphene::chain::share_type )
@ -575,6 +583,7 @@ FC_REFLECT_TYPENAME( graphene::chain::offer_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::nft_metadata_id_type ) FC_REFLECT_TYPENAME( graphene::chain::nft_metadata_id_type )
FC_REFLECT_TYPENAME( graphene::chain::nft_id_type ) FC_REFLECT_TYPENAME( graphene::chain::nft_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_role_id_type ) FC_REFLECT_TYPENAME( graphene::chain::account_role_id_type )
FC_REFLECT_TYPENAME( graphene::chain::nft_lottery_balance_id_type )
FC_REFLECT_TYPENAME( graphene::chain::son_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_id_type )
FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type )
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type )
@ -582,7 +591,7 @@ FC_REFLECT_TYPENAME( graphene::chain::son_wallet_deposit_id_type )
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type )
FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type ) FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type )
FC_REFLECT_TYPENAME( graphene::chain::sidechain_transaction_id_type ) FC_REFLECT_TYPENAME( graphene::chain::sidechain_transaction_id_type )
FC_REFLECT_TYPENAME( graphene::chain::random_number_id_type )
FC_REFLECT( graphene::chain::void_t, ) FC_REFLECT( graphene::chain::void_t, )

View file

@ -0,0 +1,19 @@
#pragma once
#include <graphene/chain/database.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace chain {
class random_number_store_evaluator : public evaluator<random_number_store_evaluator>
{
public:
typedef random_number_store_operation operation_type;
void_result do_evaluate( const random_number_store_operation& o );
object_id_type do_apply( const random_number_store_operation& o );
};
} } // graphene::chain

View file

@ -0,0 +1,41 @@
#pragma once
namespace graphene { namespace chain {
using namespace graphene::db;
class random_number_object : public abstract_object<random_number_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = random_number_object_type;
account_id_type account; /* account who requested random number */
time_point_sec timestamp; /* date and time when the number is read */
vector<uint64_t> random_number; /* random number(s) */
std::string data; /* custom data in json format */
};
struct by_account;
struct by_timestamp;
using random_number_multi_index_type = multi_index_container<
random_number_object,
indexed_by<
ordered_unique< tag<by_id>,
member<object, object_id_type, &object::id>
>,
ordered_non_unique< tag<by_account>,
member<random_number_object, account_id_type, &random_number_object::account>
>,
ordered_non_unique< tag<by_timestamp>,
member<random_number_object, time_point_sec, &random_number_object::timestamp>
>
>
>;
using random_number_index = generic_index<random_number_object, random_number_multi_index_type>;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::random_number_object, (graphene::db::object),
(account) (timestamp)
(random_number) (data) )

View file

@ -57,6 +57,9 @@ namespace graphene
case operation::tag<account_role_create_operation>::value: case operation::tag<account_role_create_operation>::value:
case operation::tag<account_role_update_operation>::value: case operation::tag<account_role_update_operation>::value:
case operation::tag<account_role_delete_operation>::value: case operation::tag<account_role_delete_operation>::value:
case operation::tag<nft_lottery_token_purchase_operation>::value:
case operation::tag<nft_lottery_reward_operation>::value:
case operation::tag<nft_lottery_end_operation>::value:
FC_ASSERT(block_time >= HARDFORK_NFT_TIME, "Custom permissions and roles not allowed on this operation yet!"); FC_ASSERT(block_time >= HARDFORK_NFT_TIME, "Custom permissions and roles not allowed on this operation yet!");
break; break;
default: default:

View file

@ -24,6 +24,26 @@ void_result nft_metadata_create_evaluator::do_evaluate( const nft_metadata_creat
const auto& ar_obj = (*op.account_role)(db()); const auto& ar_obj = (*op.account_role)(db());
FC_ASSERT(ar_obj.owner == op.owner, "Only the Account Role created by the owner can be attached"); FC_ASSERT(ar_obj.owner == op.owner, "Only the Account Role created by the owner can be attached");
} }
// Lottery Related
if (!op.lottery_options) {
return void_result();
}
FC_ASSERT((*op.lottery_options).end_date > now || (*op.lottery_options).end_date == time_point_sec());
if (op.max_supply) {
FC_ASSERT(*op.max_supply >= 5);
}
for(auto lottery_id: (*op.lottery_options).progressive_jackpots) {
const auto& lottery_obj = lottery_id(db());
FC_ASSERT(lottery_obj.owner == op.owner, "Only the Owner can attach progressive jackpots");
FC_ASSERT(lottery_obj.is_lottery(), "Only lottery objects can be attached as progressive jackpots");
FC_ASSERT(lottery_obj.lottery_data->lottery_options.is_active == false, "Lottery should not be active");
FC_ASSERT(lottery_obj.lottery_data->lottery_options.ticket_price.asset_id == (*op.lottery_options).ticket_price.asset_id, "Lottery asset type should be same");
const auto& lottery_balance_obj = lottery_obj.lottery_data->lottery_balance_id(db());
FC_ASSERT(lottery_balance_obj.jackpot.amount > 0, "Non zero progressive jackpot not allowed");
}
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }
@ -39,6 +59,26 @@ object_id_type nft_metadata_create_evaluator::do_apply( const nft_metadata_creat
obj.is_transferable = op.is_transferable; obj.is_transferable = op.is_transferable;
obj.is_sellable = op.is_sellable; obj.is_sellable = op.is_sellable;
obj.account_role = op.account_role; obj.account_role = op.account_role;
if (op.max_supply) {
obj.max_supply = *op.max_supply;
}
if (op.lottery_options) {
asset jackpot_sum(0,(*op.lottery_options).ticket_price.asset_id);
for(auto lottery_id: (*op.lottery_options).progressive_jackpots) {
const auto& lottery_obj = lottery_id(db());
const auto& lottery_balance_obj = lottery_obj.lottery_data->lottery_balance_id(db());
FC_ASSERT(lottery_balance_obj.jackpot.amount > 0, "Non zero progressive jackpot not allowed");
db().modify(lottery_balance_obj, [&] ( nft_lottery_balance_object& nlbo ) {
jackpot_sum += nlbo.jackpot;
nlbo.jackpot -= nlbo.jackpot;
});
}
const auto& new_lottery_balance_obj = db().create<nft_lottery_balance_object>([&](nft_lottery_balance_object& nlbo) {
nlbo.total_progressive_jackpot = jackpot_sum;
nlbo.jackpot = jackpot_sum;
});
obj.lottery_data = nft_lottery_data(*op.lottery_options, new_lottery_balance_obj.id);
}
}); });
return new_nft_metadata_object.id; return new_nft_metadata_object.id;
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }
@ -110,6 +150,7 @@ void_result nft_mint_evaluator::do_evaluate( const nft_mint_operation& op )
FC_ASSERT( itr_nft_md != idx_nft_md.end(), "NFT metadata not found" ); FC_ASSERT( itr_nft_md != idx_nft_md.end(), "NFT metadata not found" );
FC_ASSERT( itr_nft_md->owner == op.payer, "Only metadata owner can mint NFT" ); FC_ASSERT( itr_nft_md->owner == op.payer, "Only metadata owner can mint NFT" );
FC_ASSERT(itr_nft_md->get_token_current_supply(db()) < itr_nft_md->max_supply, "NFTs can't be minted more than max_supply");
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }

View file

@ -0,0 +1,145 @@
#include <graphene/chain/nft_lottery_evaluator.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/account_role_object.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene
{
namespace chain
{
void_result nft_lottery_token_purchase_evaluator::do_evaluate(const nft_lottery_token_purchase_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
op.buyer(d);
const auto &lottery_md_obj = op.lottery_id(d);
FC_ASSERT(lottery_md_obj.is_lottery(), "Not a lottery type");
if (lottery_md_obj.account_role)
{
const auto &ar_idx = d.get_index_type<account_role_index>().indices().get<by_id>();
auto ar_itr = ar_idx.find(*lottery_md_obj.account_role);
if (ar_itr != ar_idx.end())
{
FC_ASSERT(d.account_role_valid(*ar_itr, op.buyer, get_type()), "Account role not valid");
}
}
auto lottery_options = lottery_md_obj.lottery_data->lottery_options;
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 nft_lottery_token_purchase_evaluator::do_apply(const nft_lottery_token_purchase_operation &op)
{
try
{
transaction_evaluation_state nft_mint_context(&db());
nft_mint_context.skip_fee_schedule_check = true;
const auto &lottery_md_obj = op.lottery_id(db());
nft_id_type nft_id;
for (size_t i = 0; i < op.tickets_to_buy; i++)
{
nft_mint_operation mint_op;
mint_op.payer = lottery_md_obj.owner;
mint_op.nft_metadata_id = lottery_md_obj.id;
mint_op.owner = op.buyer;
nft_id = db().apply_operation(nft_mint_context, mint_op).get<object_id_type>();
}
db().adjust_balance(op.buyer, -op.amount);
db().modify(lottery_md_obj.lottery_data->lottery_balance_id(db()), [&](nft_lottery_balance_object &obj) {
obj.jackpot += op.amount;
});
return nft_id;
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result nft_lottery_reward_evaluator::do_evaluate(const nft_lottery_reward_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
op.winner(d);
const auto &lottery_md_obj = op.lottery_id(d);
FC_ASSERT(lottery_md_obj.is_lottery());
const auto &lottery_options = lottery_md_obj.lottery_data->lottery_options;
FC_ASSERT(lottery_options.is_active);
FC_ASSERT(lottery_md_obj.get_lottery_jackpot(d) >= op.amount);
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result nft_lottery_reward_evaluator::do_apply(const nft_lottery_reward_operation &op)
{
try
{
const auto &lottery_md_obj = op.lottery_id(db());
db().adjust_balance(op.winner, op.amount);
db().modify(lottery_md_obj.lottery_data->lottery_balance_id(db()), [&](nft_lottery_balance_object &obj) {
obj.jackpot -= op.amount;
});
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result nft_lottery_end_evaluator::do_evaluate(const nft_lottery_end_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
const auto &lottery_md_obj = op.lottery_id(d);
FC_ASSERT(lottery_md_obj.is_lottery());
const auto &lottery_options = lottery_md_obj.lottery_data->lottery_options;
FC_ASSERT(lottery_options.is_active);
FC_ASSERT(lottery_md_obj.get_lottery_jackpot(d).amount == 0);
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result nft_lottery_end_evaluator::do_apply(const nft_lottery_end_operation &op)
{
try
{
const auto &lottery_md_obj = op.lottery_id(db());
db().modify(lottery_md_obj, [&](nft_metadata_object &obj) {
obj.lottery_data->lottery_options.is_active = false;
});
db().modify(lottery_md_obj.lottery_data->lottery_balance_id(db()), [&](nft_lottery_balance_object &obj) {
obj.sweeps_tickets_sold = lottery_md_obj.get_token_current_supply(db());
});
if (lottery_md_obj.lottery_data->lottery_options.delete_tickets_after_draw)
{
const auto &nft_index_by_md = db().get_index_type<nft_index>().indices().get<by_metadata>();
auto delete_nft_itr = nft_index_by_md.lower_bound(op.lottery_id);
while (delete_nft_itr != nft_index_by_md.end() && delete_nft_itr->nft_metadata_id == op.lottery_id)
{
const nft_object &nft_obj = *delete_nft_itr;
++delete_nft_itr;
db().remove(nft_obj);
}
}
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
} // namespace chain
} // namespace graphene

View file

@ -0,0 +1,171 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/nft_object.hpp>
namespace graphene
{
namespace chain
{
time_point_sec nft_metadata_object::get_lottery_expiration() const
{
if (lottery_data)
return lottery_data->lottery_options.end_date;
return time_point_sec();
}
asset nft_metadata_object::get_lottery_jackpot(const database &db) const
{
if (lottery_data)
return lottery_data->lottery_balance_id(db).jackpot;
return asset();
}
share_type nft_metadata_object::get_token_current_supply(const database &db) const
{
share_type current_supply;
const auto &idx_lottery_by_md = db.get_index_type<nft_index>().indices().get<by_metadata>();
auto lottery_range = idx_lottery_by_md.equal_range(id);
current_supply = std::distance(lottery_range.first, lottery_range.second);
return current_supply;
}
vector<account_id_type> nft_metadata_object::get_holders(const database &db) const
{
const auto &idx_lottery_by_md = db.get_index_type<nft_index>().indices().get<by_metadata>();
auto lottery_range = idx_lottery_by_md.equal_range(id);
vector<account_id_type> holders;
holders.reserve(std::distance(lottery_range.first, lottery_range.second));
std::for_each(lottery_range.first, lottery_range.second,
[&](const nft_object &ticket) {
holders.emplace_back(ticket.owner);
});
return holders;
}
vector<uint64_t> nft_metadata_object::get_ticket_ids(const database &db) const
{
const auto &idx_lottery_by_md = db.get_index_type<nft_index>().indices().get<by_metadata>();
auto lottery_range = idx_lottery_by_md.equal_range(id);
vector<uint64_t> tickets;
tickets.reserve(std::distance(lottery_range.first, lottery_range.second));
std::for_each(lottery_range.first, lottery_range.second,
[&](const nft_object &ticket) {
tickets.emplace_back(ticket.id.instance());
});
return tickets;
}
void nft_metadata_object::distribute_benefactors_part(database &db)
{
transaction_evaluation_state eval(&db);
const auto &lottery_options = lottery_data->lottery_options;
share_type jackpot = lottery_options.ticket_price.amount * get_token_current_supply(db) + lottery_data->lottery_balance_id(db).total_progressive_jackpot.amount;
for (auto benefactor : lottery_options.benefactors)
{
nft_lottery_reward_operation reward_op;
reward_op.lottery_id = id;
reward_op.winner = benefactor.id;
reward_op.is_benefactor_reward = true;
reward_op.win_percentage = benefactor.share;
reward_op.amount = asset(jackpot.value * benefactor.share / GRAPHENE_100_PERCENT, lottery_options.ticket_price.asset_id);
db.apply_operation(eval, reward_op);
}
}
map<account_id_type, vector<uint16_t>> nft_metadata_object::distribute_winners_part(database &db)
{
transaction_evaluation_state eval(&db);
auto current_supply = get_token_current_supply(db);
auto &lottery_options = lottery_data->lottery_options;
auto holders = get_holders(db);
vector<uint64_t> ticket_ids = get_ticket_ids(db);
FC_ASSERT(current_supply.value == (int64_t)holders.size());
FC_ASSERT(get_lottery_jackpot(db).amount.value == current_supply.value * lottery_options.ticket_price.amount.value);
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_lottery_jackpot(db).amount.value;
auto selections = lottery_options.winning_tickets.size() <= holders.size() ? lottery_options.winning_tickets.size() : holders.size();
auto winner_numbers = db.get_random_numbers(0, holders.size(), selections, false);
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();
}
auto sweeps_distribution_percentage = db.get_global_properties().parameters.sweeps_distribution_percentage();
for (size_t c = 0; c < winner_numbers.size(); ++c)
{
auto winner_num = winner_numbers[c];
nft_lottery_reward_operation reward_op;
reward_op.lottery_id = id;
reward_op.is_benefactor_reward = false;
reward_op.winner = holders[winner_num];
if (ticket_ids.size() > winner_num)
{
reward_op.winner_ticket_id = ticket_ids[winner_num];
}
reward_op.win_percentage = tickets[c];
reward_op.amount = asset(jackpot * tickets[c] * (1. - sweeps_distribution_percentage / (double)GRAPHENE_100_PERCENT) / GRAPHENE_100_PERCENT, lottery_options.ticket_price.asset_id);
db.apply_operation(eval, reward_op);
structurized_participants[holders[winner_num]].push_back(tickets[c]);
}
return structurized_participants;
}
void nft_metadata_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 sweeps_params = db.get_global_properties().parameters;
uint64_t distribution_asset_supply = sweeps_params.sweeps_distribution_asset()(db).dynamic_data(db).current_supply.value;
const auto range = asset_bal_idx.equal_range(boost::make_tuple(sweeps_params.sweeps_distribution_asset()));
asset remaining_jackpot = get_lottery_jackpot(db);
uint64_t holders_sum = 0;
for (const account_balance_object &holder_balance : boost::make_iterator_range(range.first, range.second))
{
int64_t holder_part = remaining_jackpot.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 = remaining_jackpot.amount.value * SWEEPS_VESTING_BALANCE_MULTIPLIER - holders_sum;
db.adjust_sweeps_vesting_balance(sweeps_params.sweeps_vesting_accumulator_account(), balance_rest);
db.modify(lottery_data->lottery_balance_id(db), [&](nft_lottery_balance_object &obj) {
obj.jackpot -= remaining_jackpot;
});
}
void nft_metadata_object::end_lottery(database &db)
{
transaction_evaluation_state eval(&db);
const auto &lottery_options = lottery_data->lottery_options;
FC_ASSERT(is_lottery());
FC_ASSERT(lottery_options.is_active && (lottery_options.end_date <= db.head_block_time() || lottery_options.ending_on_soldout));
auto participants = distribute_winners_part(db);
if (participants.size() > 0)
{
distribute_benefactors_part(db);
distribute_sweeps_holders_part(db);
}
nft_lottery_end_operation end_op;
end_op.lottery_id = get_id();
db.apply_operation(eval, end_op);
}
} // namespace chain
} // namespace graphene

View file

@ -219,6 +219,18 @@ struct proposal_operation_hardfork_visitor
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_delete_operation not allowed yet!" ); FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_delete_operation not allowed yet!" );
} }
void operator()(const nft_lottery_token_purchase_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_lottery_token_purchase_operation not allowed yet!" );
}
void operator()(const nft_lottery_reward_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_lottery_reward_operation not allowed yet!" );
}
void operator()(const nft_lottery_end_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_lottery_end_operation not allowed yet!" );
}
void operator()(const son_create_operation &v) const { void operator()(const son_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_create_operation not allowed yet!" ); FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_create_operation not allowed yet!" );
} }

View file

@ -45,6 +45,10 @@ void nft_metadata_create_operation::validate() const
FC_ASSERT(fee.amount >= 0, "Fee must not be negative"); FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
FC_ASSERT(is_valid_nft_token_name(name), "Invalid NFT name provided"); FC_ASSERT(is_valid_nft_token_name(name), "Invalid NFT name provided");
FC_ASSERT(is_valid_nft_token_name(symbol), "Invalid NFT symbol provided"); FC_ASSERT(is_valid_nft_token_name(symbol), "Invalid NFT symbol provided");
if (lottery_options)
{
(*lottery_options).validate();
}
} }
void nft_metadata_update_operation::validate() const void nft_metadata_update_operation::validate() const

View file

@ -0,0 +1,38 @@
#include <graphene/chain/protocol/nft_ops.hpp>
#include <graphene/chain/protocol/nft_lottery.hpp>
#include <graphene/chain/protocol/operations.hpp>
namespace graphene
{
namespace chain
{
void nft_lottery_options::validate() const
{
FC_ASSERT(winning_tickets.size() <= 64);
FC_ASSERT(ticket_price.amount >= 1);
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");
FC_ASSERT(ending_on_soldout == true || end_date != time_point_sec(), "lottery may not end");
}
share_type nft_lottery_token_purchase_operation::calculate_fee(const fee_parameters_type &k) const
{
return k.fee;
}
void nft_lottery_token_purchase_operation::validate() const
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
FC_ASSERT(tickets_to_buy > 0);
}
} // namespace chain
} // namespace graphene

View file

@ -0,0 +1,24 @@
#include <graphene/chain/random_number_evaluator.hpp>
#include <graphene/chain/random_number_object.hpp>
namespace graphene { namespace chain {
void_result random_number_store_evaluator::do_evaluate( const random_number_store_operation& op )
{ try {
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
object_id_type random_number_store_evaluator::do_apply( const random_number_store_operation& op )
{ try {
const auto& new_random_number_object = db().create<random_number_object>( [&]( random_number_object& obj ) {
obj.account = op.account;
obj.timestamp = db().head_block_time();
obj.random_number = op.random_number;
obj.data = op.data;
});
return new_random_number_object.id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
} } // graphene::chain

View file

@ -73,7 +73,7 @@ enum authority_type
* the meta data about the receipt that helps the sender identify which receipt is * the meta data about the receipt that helps the sender identify which receipt is
* for the receiver and which is for the change address. * for the receiver and which is for the change address.
*/ */
struct blind_confirmation struct blind_confirmation
{ {
struct output struct output
{ {
@ -317,7 +317,7 @@ class wallet_api
*/ */
uint64_t get_account_count()const; uint64_t get_account_count()const;
/** Lists all accounts controlled by this wallet. /** Lists all accounts controlled by this wallet.
* This returns a list of the full account objects for all accounts whose private keys * This returns a list of the full account objects for all accounts whose private keys
* we possess. * we possess.
* @returns a list of account objects * @returns a list of account objects
*/ */
@ -329,14 +329,14 @@ class wallet_api
* start by setting \c lowerbound to the empty string \c "", and then each iteration, pass * start by setting \c lowerbound to the empty string \c "", and then each iteration, pass
* the last account name returned as the \c lowerbound for the next \c list_accounts() call. * the last account name returned as the \c lowerbound for the next \c list_accounts() call.
* *
* @param lowerbound the name of the first account to return. If the named account does not exist, * @param lowerbound the name of the first account to return. If the named account does not exist,
* the list will start at the account that comes after \c lowerbound * the list will start at the account that comes after \c lowerbound
* @param limit the maximum number of accounts to return (max: 1000) * @param limit the maximum number of accounts to return (max: 1000)
* @returns a list of accounts mapping account names to account ids * @returns a list of accounts mapping account names to account ids
*/ */
map<string,account_id_type> list_accounts(const string& lowerbound, uint32_t limit); map<string,account_id_type> list_accounts(const string& lowerbound, uint32_t limit);
/** List the balances of an account. /** List the balances of an account.
* Each account can have multiple balances, one for each type of asset owned by that * Each account can have multiple balances, one for each type of asset owned by that
* account. The returned list will only contain assets for which the account has a * account. The returned list will only contain assets for which the account has a
* nonzero balance * nonzero balance
* @param id the name or id of the account whose balances you want * @param id the name or id of the account whose balances you want
@ -344,7 +344,7 @@ class wallet_api
*/ */
vector<asset> list_account_balances(const string& id); vector<asset> list_account_balances(const string& id);
/** Lists all assets registered on the blockchain. /** Lists all assets registered on the blockchain.
* *
* To list all assets, pass the empty string \c "" for the lowerbound to start * To list all assets, pass the empty string \c "" for the lowerbound to start
* at the beginning of the list, and iterate as necessary. * at the beginning of the list, and iterate as necessary.
* *
@ -355,12 +355,12 @@ class wallet_api
vector<asset_object> list_assets(const string& lowerbound, uint32_t limit)const; vector<asset_object> list_assets(const string& lowerbound, uint32_t limit)const;
/** Returns assets count registered on the blockchain. /** Returns assets count registered on the blockchain.
* *
* @returns assets count * @returns assets count
*/ */
uint64_t get_asset_count()const; uint64_t get_asset_count()const;
vector<asset_object> get_lotteries( asset_id_type stop = asset_id_type(), vector<asset_object> get_lotteries( asset_id_type stop = asset_id_type(),
unsigned limit = 100, unsigned limit = 100,
asset_id_type start = asset_id_type() )const; asset_id_type start = asset_id_type() )const;
@ -396,7 +396,7 @@ class wallet_api
vector<limit_order_object> get_limit_orders(string a, string b, uint32_t limit)const; vector<limit_order_object> get_limit_orders(string a, string b, uint32_t limit)const;
vector<call_order_object> get_call_orders(string a, uint32_t limit)const; vector<call_order_object> get_call_orders(string a, uint32_t limit)const;
vector<force_settlement_object> get_settle_orders(string a, uint32_t limit)const; vector<force_settlement_object> get_settle_orders(string a, uint32_t limit)const;
/** Returns the block chain's slowly-changing settings. /** Returns the block chain's slowly-changing settings.
* This object contains all of the properties of the blockchain that are fixed * This object contains all of the properties of the blockchain that are fixed
* or that change only once per maintenance interval (daily) such as the * or that change only once per maintenance interval (daily) such as the
@ -452,8 +452,8 @@ class wallet_api
* Returns the blockchain object corresponding to the given id. * Returns the blockchain object corresponding to the given id.
* *
* This generic function can be used to retrieve any object from the blockchain * This generic function can be used to retrieve any object from the blockchain
* that is assigned an ID. Certain types of objects have specialized convenience * that is assigned an ID. Certain types of objects have specialized convenience
* functions to return their objects -- e.g., assets have \c get_asset(), accounts * functions to return their objects -- e.g., assets have \c get_asset(), accounts
* have \c get_account(), but this function will work for any object. * have \c get_account(), but this function will work for any object.
* *
* @param id the id of the object to return * @param id the id of the object to return
@ -461,7 +461,7 @@ class wallet_api
*/ */
variant get_object(object_id_type id) const; variant get_object(object_id_type id) const;
/** Returns the current wallet filename. /** Returns the current wallet filename.
* *
* This is the filename that will be used when automatically saving the wallet. * This is the filename that will be used when automatically saving the wallet.
* *
@ -537,21 +537,21 @@ class wallet_api
* @ingroup Wallet Management * @ingroup Wallet Management
*/ */
bool is_new()const; bool is_new()const;
/** Checks whether the wallet is locked (is unable to use its private keys). /** Checks whether the wallet is locked (is unable to use its private keys).
* *
* This state can be changed by calling \c lock() or \c unlock(). * This state can be changed by calling \c lock() or \c unlock().
* @return true if the wallet is locked * @return true if the wallet is locked
* @ingroup Wallet Management * @ingroup Wallet Management
*/ */
bool is_locked()const; bool is_locked()const;
/** Locks the wallet immediately. /** Locks the wallet immediately.
* @ingroup Wallet Management * @ingroup Wallet Management
*/ */
void lock(); void lock();
/** Unlocks the wallet. /** Unlocks the wallet.
* *
* The wallet remain unlocked until the \c lock is called * The wallet remain unlocked until the \c lock is called
* or the program exits. * or the program exits.
@ -559,7 +559,7 @@ class wallet_api
* @ingroup Wallet Management * @ingroup Wallet Management
*/ */
void unlock(string password); void unlock(string password);
/** Sets a new password on the wallet. /** Sets a new password on the wallet.
* *
* The wallet must be either 'new' or 'unlocked' to * The wallet must be either 'new' or 'unlocked' to
@ -572,7 +572,7 @@ class wallet_api
* *
* The keys are printed in WIF format. You can import these keys into another wallet * The keys are printed in WIF format. You can import these keys into another wallet
* using \c import_key() * using \c import_key()
* @returns a map containing the private keys, indexed by their public key * @returns a map containing the private keys, indexed by their public key
*/ */
map<public_key_type, string> dump_private_keys(); map<public_key_type, string> dump_private_keys();
@ -607,13 +607,13 @@ class wallet_api
bool load_wallet_file(string wallet_filename = ""); bool load_wallet_file(string wallet_filename = "");
/** Quitting from Peerplays wallet. /** Quitting from Peerplays wallet.
* *
* The current wallet will be closed. * The current wallet will be closed.
*/ */
void quit(); void quit();
/** Saves the current wallet to the given filename. /** Saves the current wallet to the given filename.
* *
* @warning This does not change the wallet filename that will be used for future * @warning This does not change the wallet filename that will be used for future
* writes, so think of this function as 'Save a Copy As...' instead of * writes, so think of this function as 'Save a Copy As...' instead of
* 'Save As...'. Use \c set_wallet_filename() to make the filename * 'Save As...'. Use \c set_wallet_filename() to make the filename
@ -682,7 +682,7 @@ class wallet_api
/** Imports the private key for an existing account. /** Imports the private key for an existing account.
* *
* The private key must match either an owner key or an active key for the * The private key must match either an owner key or an active key for the
* named account. * named account.
* *
* @see dump_private_keys() * @see dump_private_keys()
* *
@ -820,7 +820,7 @@ class wallet_api
* @param to the name or id of the account receiving the funds * @param to the name or id of the account receiving the funds
* @param amount the amount to send (in nominal units -- to send half of a BTS, specify 0.5) * @param amount the amount to send (in nominal units -- to send half of a BTS, specify 0.5)
* @param asset_symbol the symbol or id of the asset to send * @param asset_symbol the symbol or id of the asset to send
* @param memo a memo to attach to the transaction. The memo will be encrypted in the * @param memo a memo to attach to the transaction. The memo will be encrypted in the
* transaction and readable for the receiver. There is no length limit * transaction and readable for the receiver. There is no length limit
* other than the limit imposed by maximum transaction size, but transaction * other than the limit imposed by maximum transaction size, but transaction
* increase with transaction size * increase with transaction size
@ -895,7 +895,7 @@ class wallet_api
* who sent it. * who sent it.
* *
* @param opt_from - if not empty and the sender is a unknown public key, then the unknown public key will be given the label opt_from * @param opt_from - if not empty and the sender is a unknown public key, then the unknown public key will be given the label opt_from
* @param confirmation_receipt - a base58 encoded stealth confirmation * @param confirmation_receipt - a base58 encoded stealth confirmation
*/ */
blind_receipt receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ); blind_receipt receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo );
@ -903,18 +903,18 @@ class wallet_api
* Transfers a public balance from @from to one or more blinded balances using a * Transfers a public balance from @from to one or more blinded balances using a
* stealth transfer. * stealth transfer.
*/ */
blind_confirmation transfer_to_blind( string from_account_id_or_name, blind_confirmation transfer_to_blind( string from_account_id_or_name,
string asset_symbol, string asset_symbol,
/** map from key or label to amount */ /** map from key or label to amount */
vector<pair<string, string>> to_amounts, vector<pair<string, string>> to_amounts,
bool broadcast = false ); bool broadcast = false );
/** /**
* Transfers funds from a set of blinded balances to a public account balance. * Transfers funds from a set of blinded balances to a public account balance.
*/ */
blind_confirmation transfer_from_blind( blind_confirmation transfer_from_blind(
string from_blind_account_key_or_label, string from_blind_account_key_or_label,
string to_account_id_or_name, string to_account_id_or_name,
string amount, string amount,
string asset_symbol, string asset_symbol,
bool broadcast = false ); bool broadcast = false );
@ -930,14 +930,14 @@ class wallet_api
/** Place a limit order attempting to sell one asset for another. /** Place a limit order attempting to sell one asset for another.
* *
* Buying and selling are the same operation on Graphene; if you want to buy BTS * Buying and selling are the same operation on Graphene; if you want to buy BTS
* with USD, you should sell USD for BTS. * with USD, you should sell USD for BTS.
* *
* The blockchain will attempt to sell the \c symbol_to_sell for as * The blockchain will attempt to sell the \c symbol_to_sell for as
* much \c symbol_to_receive as possible, as long as the price is at * much \c symbol_to_receive as possible, as long as the price is at
* least \c min_to_receive / \c amount_to_sell. * least \c min_to_receive / \c amount_to_sell.
* *
* In addition to the transaction fees, market fees will apply as specified * In addition to the transaction fees, market fees will apply as specified
* by the issuer of both the selling asset and the receiving asset as * by the issuer of both the selling asset and the receiving asset as
* a percentage of the amount exchanged. * a percentage of the amount exchanged.
* *
@ -950,16 +950,16 @@ class wallet_api
* *
* @todo Allow order expiration to be set here. Document default/max expiration time * @todo Allow order expiration to be set here. Document default/max expiration time
* *
* @param seller_account the account providing the asset being sold, and which will * @param seller_account the account providing the asset being sold, and which will
* receive the proceeds of the sale. * receive the proceeds of the sale.
* @param amount_to_sell the amount of the asset being sold to sell (in nominal units) * @param amount_to_sell the amount of the asset being sold to sell (in nominal units)
* @param symbol_to_sell the name or id of the asset to sell * @param symbol_to_sell the name or id of the asset to sell
* @param min_to_receive the minimum amount you are willing to receive in return for * @param min_to_receive the minimum amount you are willing to receive in return for
* selling the entire amount_to_sell * selling the entire amount_to_sell
* @param symbol_to_receive the name or id of the asset you wish to receive * @param symbol_to_receive the name or id of the asset you wish to receive
* @param timeout_sec if the order does not fill immediately, this is the length of * @param timeout_sec if the order does not fill immediately, this is the length of
* time the order will remain on the order books before it is * time the order will remain on the order books before it is
* cancelled and the un-spent funds are returned to the seller's * cancelled and the un-spent funds are returned to the seller's
* account * account
* @param fill_or_kill if true, the order will only be included in the blockchain * @param fill_or_kill if true, the order will only be included in the blockchain
* if it is filled immediately; if false, an open order will be * if it is filled immediately; if false, an open order will be
@ -976,12 +976,12 @@ class wallet_api
uint32_t timeout_sec = 0, uint32_t timeout_sec = 0,
bool fill_or_kill = false, bool fill_or_kill = false,
bool broadcast = false); bool broadcast = false);
/** Place a limit order attempting to sell one asset for another. /** Place a limit order attempting to sell one asset for another.
* *
* This API call abstracts away some of the details of the sell_asset call to be more * This API call abstracts away some of the details of the sell_asset call to be more
* user friendly. All orders placed with sell never timeout and will not be killed if they * user friendly. All orders placed with sell never timeout and will not be killed if they
* cannot be filled immediately. If you wish for one of these parameters to be different, * cannot be filled immediately. If you wish for one of these parameters to be different,
* then sell_asset should be used instead. * then sell_asset should be used instead.
* *
* @param seller_account the account providing the asset being sold, and which will * @param seller_account the account providing the asset being sold, and which will
@ -991,7 +991,7 @@ class wallet_api
* @param rate The rate in base:quote at which you want to sell. * @param rate The rate in base:quote at which you want to sell.
* @param amount The amount of base you want to sell. * @param amount The amount of base you want to sell.
* @param broadcast true to broadcast the transaction on the network. * @param broadcast true to broadcast the transaction on the network.
* @returns The signed transaction selling the funds. * @returns The signed transaction selling the funds.
*/ */
signed_transaction sell( string seller_account, signed_transaction sell( string seller_account,
string base, string base,
@ -999,7 +999,7 @@ class wallet_api
double rate, double rate,
double amount, double amount,
bool broadcast ); bool broadcast );
/** Place a limit order attempting to buy one asset with another. /** Place a limit order attempting to buy one asset with another.
* *
* This API call abstracts away some of the details of the sell_asset call to be more * This API call abstracts away some of the details of the sell_asset call to be more
@ -1054,14 +1054,14 @@ class wallet_api
* Right now this function is difficult to use because you must provide raw JSON data * Right now this function is difficult to use because you must provide raw JSON data
* structures for the options objects, and those include prices and asset ids. * structures for the options objects, and those include prices and asset ids.
* *
* @param issuer the name or id of the account who will pay the fee and become the * @param issuer the name or id of the account who will pay the fee and become the
* issuer of the new asset. This can be updated later * issuer of the new asset. This can be updated later
* @param symbol the ticker symbol of the new asset * @param symbol the ticker symbol of the new asset
* @param precision the number of digits of precision to the right of the decimal point, * @param precision the number of digits of precision to the right of the decimal point,
* must be less than or equal to 12 * must be less than or equal to 12
* @param common asset options required for all new assets. * @param common asset options required for all new assets.
* Note that core_exchange_rate technically needs to store the asset ID of * Note that core_exchange_rate technically needs to store the asset ID of
* this new asset. Since this ID is not known at the time this operation is * this new asset. Since this ID is not known at the time this operation is
* created, create this price as though the new asset has instance ID 1, and * created, create this price as though the new asset has instance ID 1, and
* the chain will overwrite it with the new asset's ID. * the chain will overwrite it with the new asset's ID.
* @param bitasset_opts options specific to BitAssets. This may be null unless the * @param bitasset_opts options specific to BitAssets. This may be null unless the
@ -1081,7 +1081,7 @@ class wallet_api
asset_options common, asset_options common,
lottery_asset_options lottery_opts, lottery_asset_options lottery_opts,
bool broadcast = false); bool broadcast = false);
signed_transaction buy_ticket( asset_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy ); signed_transaction buy_ticket( asset_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy );
/** Issue new shares of an asset. /** Issue new shares of an asset.
@ -1099,8 +1099,8 @@ class wallet_api
bool broadcast = false); bool broadcast = false);
/** Update the core options on an asset. /** Update the core options on an asset.
* There are a number of options which all assets in the network use. These options are * There are a number of options which all assets in the network use. These options are
* enumerated in the asset_object::asset_options struct. This command is used to update * enumerated in the asset_object::asset_options struct. This command is used to update
* these options for an existing asset. * these options for an existing asset.
* *
* @note This operation cannot be used to update BitAsset-specific options. For these options, * @note This operation cannot be used to update BitAsset-specific options. For these options,
@ -1164,7 +1164,7 @@ class wallet_api
signed_transaction update_asset_feed_producers(string symbol, signed_transaction update_asset_feed_producers(string symbol,
flat_set<string> new_feed_producers, flat_set<string> new_feed_producers,
bool broadcast = false); bool broadcast = false);
/** Publishes a price feed for the named asset. /** Publishes a price feed for the named asset.
* *
* Price feed providers use this command to publish their price feeds for market-issued assets. A price feed is * Price feed providers use this command to publish their price feeds for market-issued assets. A price feed is
@ -1192,7 +1192,7 @@ class wallet_api
/** Pay into the fee pool for the given asset. /** Pay into the fee pool for the given asset.
* *
* User-issued assets can optionally have a pool of the core asset which is * User-issued assets can optionally have a pool of the core asset which is
* automatically used to pay transaction fees for any transaction using that * automatically used to pay transaction fees for any transaction using that
* asset (using the asset's core exchange rate). * asset (using the asset's core exchange rate).
* *
@ -1233,7 +1233,7 @@ class wallet_api
* used as backing for other bitassets, those bitassets will be force settled at their current * used as backing for other bitassets, those bitassets will be force settled at their current
* feed price. * feed price.
* *
* @note this operation is used only by the asset issuer, \c settle_asset() may be used by * @note this operation is used only by the asset issuer, \c settle_asset() may be used by
* any user owning the asset * any user owning the asset
* *
* @param symbol the name or id of the asset to force settlement on * @param symbol the name or id of the asset to force settlement on
@ -1301,7 +1301,7 @@ class wallet_api
* @returns the signed transaction registering a committee_member * @returns the signed transaction registering a committee_member
*/ */
signed_transaction create_committee_member(string owner_account, signed_transaction create_committee_member(string owner_account,
string url, string url,
bool broadcast = false); bool broadcast = false);
/** Lists all witnesses registered in the blockchain. /** Lists all witnesses registered in the blockchain.
@ -1312,7 +1312,7 @@ class wallet_api
* start by setting \c lowerbound to the empty string \c "", and then each iteration, pass * start by setting \c lowerbound to the empty string \c "", and then each iteration, pass
* the last witness name returned as the \c lowerbound for the next \c list_witnesss() call. * the last witness name returned as the \c lowerbound for the next \c list_witnesss() call.
* *
* @param lowerbound the name of the first witness to return. If the named witness does not exist, * @param lowerbound the name of the first witness to return. If the named witness does not exist,
* the list will start at the witness that comes after \c lowerbound * the list will start at the witness that comes after \c lowerbound
* @param limit the maximum number of witnesss to return (max: 1000) * @param limit the maximum number of witnesss to return (max: 1000)
* @returns a list of witnesss mapping witness names to witness ids * @returns a list of witnesss mapping witness names to witness ids
@ -1327,7 +1327,7 @@ class wallet_api
* start by setting \c lowerbound to the empty string \c "", and then each iteration, pass * start by setting \c lowerbound to the empty string \c "", and then each iteration, pass
* the last committee_member name returned as the \c lowerbound for the next \c list_committee_members() call. * the last committee_member name returned as the \c lowerbound for the next \c list_committee_members() call.
* *
* @param lowerbound the name of the first committee_member to return. If the named committee_member does not exist, * @param lowerbound the name of the first committee_member to return. If the named committee_member does not exist,
* the list will start at the committee_member that comes after \c lowerbound * the list will start at the committee_member that comes after \c lowerbound
* @param limit the maximum number of committee_members to return (max: 1000) * @param limit the maximum number of committee_members to return (max: 1000)
* @returns a list of committee_members mapping committee_member names to committee_member ids * @returns a list of committee_members mapping committee_member names to committee_member ids
@ -1647,7 +1647,7 @@ class wallet_api
/** Vote for a given committee_member. /** Vote for a given committee_member.
* *
* An account can publish a list of all committee_memberes they approve of. This * An account can publish a list of all committee_memberes they approve of. This
* command allows you to add or remove committee_memberes from this list. * command allows you to add or remove committee_memberes from this list.
* Each account's vote is weighted according to the number of shares of the * 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. * core asset owned by that account at the time the votes are tallied.
@ -1657,7 +1657,7 @@ class wallet_api
* *
* @param voting_account the name or id of the account who is voting with their shares * @param voting_account the name or id of the account who is voting with their shares
* @param committee_member the name or id of the committee_member' owner account * @param committee_member the name or id of the committee_member' owner account
* @param approve true if you wish to vote in favor of that committee_member, false to * @param approve true if you wish to vote in favor of that committee_member, false to
* remove your vote in favor of that committee_member * remove your vote in favor of that committee_member
* @param broadcast true if you wish to broadcast the transaction * @param broadcast true if you wish to broadcast the transaction
* @return the signed transaction changing your vote for the given committee_member * @return the signed transaction changing your vote for the given committee_member
@ -1724,7 +1724,7 @@ class wallet_api
/** Vote for a given witness. /** Vote for a given witness.
* *
* An account can publish a list of all witnesses they approve of. This * An account can publish a list of all witnesses they approve of. This
* command allows you to add or remove witnesses from this list. * command allows you to add or remove witnesses from this list.
* Each account's vote is weighted according to the number of shares of the * 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. * core asset owned by that account at the time the votes are tallied.
@ -1734,7 +1734,7 @@ class wallet_api
* *
* @param voting_account the name or id of the account who is voting with their shares * @param voting_account the name or id of the account who is voting with their shares
* @param witness the name or id of the witness' owner account * @param witness the name or id of the witness' owner account
* @param approve true if you wish to vote in favor of that witness, false to * @param approve true if you wish to vote in favor of that witness, false to
* remove your vote in favor of that witness * remove your vote in favor of that witness
* @param broadcast true if you wish to broadcast the transaction * @param broadcast true if you wish to broadcast the transaction
* @return the signed transaction changing your vote for the given witness * @return the signed transaction changing your vote for the given witness
@ -1746,12 +1746,12 @@ class wallet_api
/** Change your witness votes. /** Change your witness votes.
* *
* An account can publish a list of all witnesses they approve of. * 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 * 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. * 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 * 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 * in one call. When you are changing your vote on several witnesses, this
* may be easier than multiple `vote_for_witness` and * may be easier than multiple `vote_for_witness` and
* `set_desired_witness_and_committee_member_count` calls. * `set_desired_witness_and_committee_member_count` calls.
* *
* @note you cannot vote against a witness, you can only vote for the witness * @note you cannot vote against a witness, you can only vote for the witness
@ -1766,7 +1766,7 @@ class wallet_api
* you currently approve). This list can be empty. * you currently approve). This list can be empty.
* @param desired_number_of_witnesses the number of witnesses you believe the network * @param desired_number_of_witnesses the number of witnesses you believe the network
* should have. You must vote for at least this many * should have. You must vote for at least this many
* witnesses. You can set this to 0 to abstain from * witnesses. You can set this to 0 to abstain from
* voting on the number of witnesses. * voting on the number of witnesses.
* @param broadcast true if you wish to broadcast the transaction * @param broadcast true if you wish to broadcast the transaction
* @return the signed transaction changing your vote for the given witnesses * @return the signed transaction changing your vote for the given witnesses
@ -1797,23 +1797,23 @@ class wallet_api
signed_transaction set_voting_proxy(string account_to_modify, signed_transaction set_voting_proxy(string account_to_modify,
optional<string> voting_account, optional<string> voting_account,
bool broadcast = false); bool broadcast = false);
/** Set your vote for the number of witnesses and committee_members in the system. /** Set your vote for the number of witnesses and committee_members in the system.
* *
* Each account can voice their opinion on how many committee_members and how many * Each account can voice their opinion on how many committee_members and how many
* witnesses there should be in the active committee_member/active witness list. These * witnesses there should be in the active committee_member/active witness list. These
* are independent of each other. You must vote your approval of at least as many * are independent of each other. You must vote your approval of at least as many
* committee_members or witnesses as you claim there should be (you can't say that there should * committee_members or witnesses as you claim there should be (you can't say that there should
* be 20 committee_members but only vote for 10). * be 20 committee_members but only vote for 10).
* *
* There are maximum values for each set in the blockchain parameters (currently * There are maximum values for each set in the blockchain parameters (currently
* defaulting to 1001). * defaulting to 1001).
* *
* This setting can be changed at any time. If your account has a voting proxy * This setting can be changed at any time. If your account has a voting proxy
* set, your preferences will be ignored. * set, your preferences will be ignored.
* *
* @param account_to_modify the name or id of the account to update * @param account_to_modify the name or id of the account to update
* @param number_of_committee_members the number * @param number_of_committee_members the number
* *
* @param broadcast true if you wish to broadcast the transaction * @param broadcast true if you wish to broadcast the transaction
* @return the signed transaction changing your vote proxy settings * @return the signed transaction changing your vote proxy settings
@ -1866,16 +1866,16 @@ class wallet_api
/** Returns an uninitialized object representing a given blockchain operation. /** Returns an uninitialized object representing a given blockchain operation.
* *
* This returns a default-initialized object of the given type; it can be used * This returns a default-initialized object of the given type; it can be used
* during early development of the wallet when we don't yet have custom commands for * during early development of the wallet when we don't yet have custom commands for
* creating all of the operations the blockchain supports. * creating all of the operations the blockchain supports.
* *
* Any operation the blockchain supports can be created using the transaction builder's * Any operation the blockchain supports can be created using the transaction builder's
* \c add_operation_to_builder_transaction() , but to do that from the CLI you need to * \c add_operation_to_builder_transaction() , but to do that from the CLI you need to
* know what the JSON form of the operation looks like. This will give you a template * know what the JSON form of the operation looks like. This will give you a template
* you can fill in. It's better than nothing. * you can fill in. It's better than nothing.
* *
* @param operation_type the type of operation to return, must be one of the * @param operation_type the type of operation to return, must be one of the
* operations defined in `graphene/chain/operations.hpp` * operations defined in `graphene/chain/operations.hpp`
* (e.g., "global_parameters_update_operation") * (e.g., "global_parameters_update_operation")
* @return a default-constructed operation of the given type * @return a default-constructed operation of the given type
@ -1900,7 +1900,7 @@ class wallet_api
bool broadcast = false); bool broadcast = false);
/** Propose a fee change. /** Propose a fee change.
* *
* @param proposing_account The account paying the fee to propose the tx * @param proposing_account The account paying the fee to propose the tx
* @param expiration_time Timestamp specifying when the proposal will either take effect or expire. * @param expiration_time Timestamp specifying when the proposal will either take effect or expire.
* @param changed_values Map of operation type to new fee. Operations may be specified by name or ID. * @param changed_values Map of operation type to new fee. Operations may be specified by name or ID.
@ -1942,7 +1942,33 @@ class wallet_api
const approval_delta& delta, const approval_delta& delta,
bool broadcast /* = false */ bool broadcast /* = false */
); );
/** Get random numbers
* @brief Returns the random number
* @param minimum Lower bound of segment containing random number
* @param maximum Upper bound of segment containing random number
* @param selections Number of random numbers to return
* @param duplicates Allow duplicated numbers
* @param broadcast true if you wish to broadcast the transaction
* @return the signed version of the transaction
* @return Vector containing random numbers from segment [minimum, maximum)
*/
vector<uint64_t> get_random_number_ex(string account,
uint64_t minimum,
uint64_t maximum,
uint64_t selections,
bool duplicates,
bool broadcast);
/** Get random number
* @brief Returns the random number
* @param bound Upper bound of segment containing random number
* @return Random number from segment [0, bound)
*/
uint64_t get_random_number(string account,
uint64_t bound,
bool broadcast);
order_book get_order_book( const string& base, const string& quote, unsigned limit = 50); order_book get_order_book( const string& base, const string& quote, unsigned limit = 50);
asset get_total_matched_bet_amount_for_betting_market_group(betting_market_group_id_type group_id); asset get_total_matched_bet_amount_for_betting_market_group(betting_market_group_id_type group_id);
@ -1981,7 +2007,7 @@ class wallet_api
sport_id_type sport_id, sport_id_type sport_id,
fc::optional<internationalized_string_type> name, fc::optional<internationalized_string_type> name,
bool broadcast = false); bool broadcast = false);
signed_transaction propose_delete_sport( signed_transaction propose_delete_sport(
const string& proposing_account, const string& proposing_account,
fc::time_point_sec expiration_time, fc::time_point_sec expiration_time,
@ -2008,7 +2034,7 @@ class wallet_api
fc::time_point_sec expiration_time, fc::time_point_sec expiration_time,
event_group_id_type event_group, event_group_id_type event_group,
bool broadcast = false); bool broadcast = false);
signed_transaction propose_create_event( signed_transaction propose_create_event(
const string& proposing_account, const string& proposing_account,
fc::time_point_sec expiration_time, fc::time_point_sec expiration_time,
@ -2079,7 +2105,7 @@ class wallet_api
fc::optional<internationalized_string_type> payout_condition, fc::optional<internationalized_string_type> payout_condition,
bool broadcast = false); bool broadcast = false);
/** Place a bet /** Place a bet
* @param bettor the account placing the bet * @param bettor the account placing the bet
* @param betting_market_id the market on which to bet * @param betting_market_id the market on which to bet
* @param back_or_lay back or lay * @param back_or_lay back or lay
@ -2155,7 +2181,7 @@ class wallet_api
tournament_state state); tournament_state state);
/** Get specific information about a tournament /** Get specific information about a tournament
* @param tournament_id the ID of the tournament * @param tournament_id the ID of the tournament
*/ */
tournament_object get_tournament(tournament_id_type id); tournament_object get_tournament(tournament_id_type id);
@ -2201,6 +2227,7 @@ class wallet_api
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(custom_permission_id_type permission_id) const; vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(custom_permission_id_type permission_id) const;
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(string owner, string permission_name) const; vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(string owner, string permission_name) const;
vector<authority> get_active_custom_account_authorities_by_operation(string owner, int operation_type) const; vector<authority> get_active_custom_account_authorities_by_operation(string owner, int operation_type) const;
///////// /////////
// NFT // // NFT //
///////// /////////
@ -2226,6 +2253,8 @@ class wallet_api
bool is_transferable, bool is_transferable,
bool is_sellable, bool is_sellable,
optional<account_role_id_type> role_id, optional<account_role_id_type> role_id,
optional<share_type> max_supply,
optional<nft_lottery_options> lottery_options,
bool broadcast); bool broadcast);
/** /**
@ -2363,6 +2392,7 @@ class wallet_api
* @return Returns vector of NFT objects, empty vector if none * @return Returns vector of NFT objects, empty vector if none
*/ */
vector<nft_object> nft_get_all_tokens() const; vector<nft_object> nft_get_all_tokens() const;
signed_transaction nft_lottery_buy_ticket( nft_metadata_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy, bool broadcast );
signed_transaction create_offer(set<nft_id_type> item_ids, signed_transaction create_offer(set<nft_id_type> item_ids,
string issuer_accound_id_or_name, string issuer_accound_id_or_name,
@ -2503,7 +2533,7 @@ FC_REFLECT_DERIVED( graphene::wallet::signed_block_with_info, (graphene::chain::
FC_REFLECT_DERIVED( graphene::wallet::vesting_balance_object_with_info, (graphene::chain::vesting_balance_object), FC_REFLECT_DERIVED( graphene::wallet::vesting_balance_object_with_info, (graphene::chain::vesting_balance_object),
(allowed_withdraw)(allowed_withdraw_time) ) (allowed_withdraw)(allowed_withdraw_time) )
FC_REFLECT( graphene::wallet::operation_detail, FC_REFLECT( graphene::wallet::operation_detail,
(memo)(description)(op) ) (memo)(description)(op) )
FC_API( graphene::wallet::wallet_api, FC_API( graphene::wallet::wallet_api,
@ -2634,6 +2664,8 @@ FC_API( graphene::wallet::wallet_api,
(propose_fee_change) (propose_fee_change)
(propose_dividend_asset_update) (propose_dividend_asset_update)
(approve_proposal) (approve_proposal)
(get_random_number_ex)
(get_random_number)
(dbg_make_uia) (dbg_make_uia)
(dbg_make_mia) (dbg_make_mia)
(dbg_push_blocks) (dbg_push_blocks)
@ -2697,6 +2729,7 @@ FC_API( graphene::wallet::wallet_api,
(nft_get_approved) (nft_get_approved)
(nft_is_approved_for_all) (nft_is_approved_for_all)
(nft_get_all_tokens) (nft_get_all_tokens)
(nft_lottery_buy_ticket)
(create_offer) (create_offer)
(create_bid) (create_bid)
(cancel_offer) (cancel_offer)

View file

@ -615,7 +615,7 @@ public:
throw fc::canceled_exception(); throw fc::canceled_exception();
} }
bool copy_wallet_file( string destination_filename ) bool copy_wallet_file( string destination_filename )
{ {
fc::path src_path = get_wallet_filename(); fc::path src_path = get_wallet_filename();
@ -909,7 +909,7 @@ public:
const rock_paper_scissors_game_details& rps_details = game_obj.game_details.get<rock_paper_scissors_game_details>(); 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) for (unsigned i = 0; i < 2; ++i)
{ {
if (rps_details.commit_moves.at(i) && if (rps_details.commit_moves.at(i) &&
!rps_details.reveal_moves.at(i)) // if this player has committed but not revealed !rps_details.reveal_moves.at(i)) // if this player has committed but not revealed
{ {
const account_id_type& account_id = game_obj.players[i]; const account_id_type& account_id = game_obj.players[i];
@ -929,7 +929,7 @@ public:
if (iter != _wallet.committed_game_moves.end()) if (iter != _wallet.committed_game_moves.end())
{ {
const rock_paper_scissors_throw_reveal& reveal = iter->second; const rock_paper_scissors_throw_reveal& reveal = iter->second;
game_move_operation move_operation; game_move_operation move_operation;
move_operation.game_id = game_obj.id; move_operation.game_id = game_obj.id;
move_operation.player_account_id = account_id; move_operation.player_account_id = account_id;
@ -972,7 +972,7 @@ public:
} }
} FC_RETHROW_EXCEPTIONS(warn, "") } } FC_RETHROW_EXCEPTIONS(warn, "") }
// Cache all matches in the tournament, which will also register us for // Cache all matches in the tournament, which will also register us for
// updates on those matches // updates on those matches
void monitor_matches_in_tournament(const tournament_object& tournament_obj) void monitor_matches_in_tournament(const tournament_object& tournament_obj)
{ try { { try {
@ -1129,8 +1129,8 @@ public:
dlog( "validated successfully tmp wallet file ${fn}", ("fn", tmp_wallet_filename) ); dlog( "validated successfully tmp wallet file ${fn}", ("fn", tmp_wallet_filename) );
fc::rename( tmp_wallet_filename, wallet_filename ); fc::rename( tmp_wallet_filename, wallet_filename );
dlog( "renamed successfully tmp wallet file ${fn}", ("fn", tmp_wallet_filename) ); dlog( "renamed successfully tmp wallet file ${fn}", ("fn", tmp_wallet_filename) );
} }
else else
{ {
FC_THROW("tmp wallet file cannot be validated ${fn}", ("fn", tmp_wallet_filename) ); FC_THROW("tmp wallet file cannot be validated ${fn}", ("fn", tmp_wallet_filename) );
} }
@ -1625,7 +1625,7 @@ public:
return sign_transaction( tx, broadcast ); return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (issuer)(symbol)(common)(broadcast) ) } } FC_CAPTURE_AND_RETHROW( (issuer)(symbol)(common)(broadcast) ) }
signed_transaction buy_ticket( asset_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy ) signed_transaction buy_ticket( asset_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy )
{ try { { try {
auto asset_obj = get_asset( lottery ); auto asset_obj = get_asset( lottery );
@ -1636,7 +1636,7 @@ public:
top.buyer = buyer; top.buyer = buyer;
top.tickets_to_buy = tickets_to_buy; top.tickets_to_buy = tickets_to_buy;
top.amount = asset( asset_obj.lottery_options->ticket_price.amount * tickets_to_buy, asset_obj.lottery_options->ticket_price.asset_id ); top.amount = asset( asset_obj.lottery_options->ticket_price.amount * tickets_to_buy, asset_obj.lottery_options->ticket_price.asset_id );
signed_transaction tx; signed_transaction tx;
tx.operations.push_back( top ); tx.operations.push_back( top );
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
@ -1644,8 +1644,8 @@ public:
return sign_transaction( tx, true ); return sign_transaction( tx, true );
} FC_CAPTURE_AND_RETHROW( (lottery)(tickets_to_buy) ) } } FC_CAPTURE_AND_RETHROW( (lottery)(tickets_to_buy) ) }
signed_transaction update_asset(string symbol, signed_transaction update_asset(string symbol,
optional<string> new_issuer, optional<string> new_issuer,
asset_options new_options, asset_options new_options,
@ -2456,13 +2456,13 @@ public:
fc::optional<vesting_balance_id_type> vbid = maybe_id<vesting_balance_id_type>(witness_name); fc::optional<vesting_balance_id_type> vbid = maybe_id<vesting_balance_id_type>(witness_name);
if( !vbid ) if( !vbid )
{ {
if (is_witness(witness_name)) if (is_witness(witness_name))
{ {
witness_object wit = get_witness( witness_name ); witness_object wit = get_witness( witness_name );
FC_ASSERT( wit.pay_vb, "Account ${account} has no core Token ${TOKEN} vested and thus its not allowed to withdraw.", ("account", witness_name)("TOKEN", GRAPHENE_SYMBOL)); FC_ASSERT( wit.pay_vb, "Account ${account} has no core Token ${TOKEN} vested and thus its not allowed to withdraw.", ("account", witness_name)("TOKEN", GRAPHENE_SYMBOL));
vbid = wit.pay_vb; vbid = wit.pay_vb;
} }
else else
FC_THROW("Account ${account} has no core Token ${TOKEN} vested and thus its not allowed to withdraw.", ("account", witness_name)("TOKEN", GRAPHENE_SYMBOL)); FC_THROW("Account ${account} has no core Token ${TOKEN} vested and thus its not allowed to withdraw.", ("account", witness_name)("TOKEN", GRAPHENE_SYMBOL));
} }
@ -2505,14 +2505,14 @@ public:
if( !vbid ) if( !vbid )
{ {
vbos = _remote_db->get_vesting_balances( account_name ); vbos = _remote_db->get_vesting_balances( account_name );
if( vbos.size() == 0 ) if( vbos.size() == 0 )
FC_THROW("Account ${account} has no core TOKEN vested and thus its not allowed to withdraw.", ("account", account_name)); FC_THROW("Account ${account} has no core TOKEN vested and thus its not allowed to withdraw.", ("account", account_name));
} }
//whether it is a witness or user, keep it in a container and iterate over to process all vesting balances and types //whether it is a witness or user, keep it in a container and iterate over to process all vesting balances and types
if(!vbos.size()) if(!vbos.size())
vbos.emplace_back( get_object<vesting_balance_object>(*vbid) ); vbos.emplace_back( get_object<vesting_balance_object>(*vbid) );
for (const vesting_balance_object& vesting_balance_obj: vbos) { for (const vesting_balance_object& vesting_balance_obj: vbos) {
if(vesting_balance_obj.balance_type == vesting_balance_type::gpos) if(vesting_balance_obj.balance_type == vesting_balance_type::gpos)
{ {
@ -2542,7 +2542,7 @@ public:
bool broadcast /* = false */) bool broadcast /* = false */)
{ try { { try {
std::vector<vesting_balance_object_with_info> vbo_info = get_vesting_balances(voting_account); std::vector<vesting_balance_object_with_info> vbo_info = get_vesting_balances(voting_account);
time_point_sec now = time_point::now(); time_point_sec now = time_point::now();
if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass
{ {
@ -2569,7 +2569,7 @@ public:
const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod(); const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod();
const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start()); const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start());
const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod; const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod;
if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time)) if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time))
FC_THROW("Account ${account} was already voting for committee_member ${committee_member} in the current GPOS sub-period", ("account", voting_account)("committee_member", committee_member)); FC_THROW("Account ${account} was already voting for committee_member ${committee_member} in the current GPOS sub-period", ("account", voting_account)("committee_member", committee_member));
else else
@ -2695,7 +2695,7 @@ public:
bool broadcast /* = false */) bool broadcast /* = false */)
{ try { { try {
std::vector<vesting_balance_object_with_info> vbo_info = get_vesting_balances(voting_account); std::vector<vesting_balance_object_with_info> vbo_info = get_vesting_balances(voting_account);
time_point_sec now = time_point::now(); time_point_sec now = time_point::now();
if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass if(now >= HARDFORK_GPOS_TIME) //can be removed after GPOS HARDFORK time pass
{ {
@ -2706,7 +2706,7 @@ public:
} }
account_object voting_account_object = get_account(voting_account); account_object voting_account_object = get_account(voting_account);
fc::optional<witness_object> witness_obj = _remote_db->get_witness_by_account(witness); fc::optional<witness_object> witness_obj = _remote_db->get_witness_by_account(witness);
if (!witness_obj) if (!witness_obj)
FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness)); FC_THROW("Account ${witness} is not registered as a witness", ("witness", witness));
@ -2722,7 +2722,7 @@ public:
const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod(); const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod();
const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start()); const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start());
const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod; const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod;
if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time)) if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time))
FC_THROW("Account ${account} was already voting for witness ${witness} in the current GPOS sub-period", ("account", voting_account)("witness", witness)); FC_THROW("Account ${account} was already voting for witness ${witness} in the current GPOS sub-period", ("account", voting_account)("witness", witness));
else else
@ -2740,7 +2740,7 @@ public:
if (!votes_removed) if (!votes_removed)
FC_THROW("Account ${account} has not voted for witness ${witness}", ("account", voting_account)("witness", witness)); FC_THROW("Account ${account} has not voted for witness ${witness}", ("account", voting_account)("witness", witness));
} }
account_update_operation account_update_op; account_update_operation account_update_op;
account_update_op.account = voting_account_object.id; account_update_op.account = voting_account_object.id;
account_update_op.new_options = voting_account_object.options; account_update_op.new_options = voting_account_object.options;
@ -3349,7 +3349,7 @@ public:
{ {
unsigned row_offset = (1 << round) - 1; unsigned row_offset = (1 << round) - 1;
unsigned row_vertical_spacing = 1 << (round + 1); unsigned row_vertical_spacing = 1 << (round + 1);
if (row >= row_offset && if (row >= row_offset &&
(row - row_offset) % row_vertical_spacing == 0) (row - row_offset) % row_vertical_spacing == 0)
{ {
unsigned player_number_in_round = (row - row_offset) / row_vertical_spacing; unsigned player_number_in_round = (row - row_offset) / row_vertical_spacing;
@ -3363,7 +3363,7 @@ public:
if (round == num_rounds) if (round == num_rounds)
{ {
match_object match = get_object<match_object>(tournament_details.matches[num_matches - 1]); match_object match = get_object<match_object>(tournament_details.matches[num_matches - 1]);
if (match.get_state() == match_state::match_complete && if (match.get_state() == match_state::match_complete &&
!match.match_winners.empty()) !match.match_winners.empty())
{ {
assert(match.match_winners.size() == 1); assert(match.match_winners.size() == 1);
@ -3819,6 +3819,38 @@ public:
return _remote_db->get_active_custom_account_authorities_by_operation(get_account(owner).id, operation_type); return _remote_db->get_active_custom_account_authorities_by_operation(get_account(owner).id, operation_type);
} }
vector<uint64_t> get_random_number_ex(string account,
uint64_t minimum,
uint64_t maximum,
uint64_t selections,
bool duplicates,
bool broadcast)
{
vector<uint64_t> v = _remote_db->get_random_number_ex(minimum, maximum, selections, duplicates);
random_number_store_operation op;
op.account = get_account(account).id;
op.random_number = v;
op.data = "";
signed_transaction trx;
trx.operations.push_back(op);
set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees );
trx.validate();
sign_transaction( trx, broadcast );
return v;
}
uint64_t get_random_number(string account,
uint64_t bound,
bool broadcast)
{
vector<uint64_t> v = get_random_number_ex(account, 0, bound, 1, false, broadcast);
return v.at(0);
}
void dbg_make_uia(string creator, string symbol) void dbg_make_uia(string creator, string symbol)
{ {
asset_options opts; asset_options opts;
@ -4176,7 +4208,7 @@ std::string operation_printer::operator()(const bet_place_operation& op)const
auto asset = wallet.get_asset(op.amount_to_bet.asset_id); auto asset = wallet.get_asset(op.amount_to_bet.asset_id);
auto bettor = wallet.get_account(op.bettor_id); auto bettor = wallet.get_account(op.bettor_id);
out << bettor.name << " placed a " << fc::json::to_string(op.back_or_lay) << " bet for " out << bettor.name << " placed a " << fc::json::to_string(op.back_or_lay) << " bet for "
<< asset.amount_to_pretty_string(op.amount_to_bet) << " at odds " << ((double)op.backer_multiplier / GRAPHENE_BETTING_ODDS_PRECISION) << asset.amount_to_pretty_string(op.amount_to_bet) << " at odds " << ((double)op.backer_multiplier / GRAPHENE_BETTING_ODDS_PRECISION)
<< " on market " << fc::json::to_string(op.betting_market_id) << " on market " << fc::json::to_string(op.betting_market_id)
<< " fee: " << fee_asset.amount_to_pretty_string(op.fee); << " fee: " << fee_asset.amount_to_pretty_string(op.fee);
@ -4319,7 +4351,7 @@ vector<asset_object> wallet_api::get_account_lotteries( account_id_type issuer,
return my->_remote_db->get_account_lotteries( issuer, stop, limit, start ); return my->_remote_db->get_account_lotteries( issuer, stop, limit, start );
} }
asset wallet_api::get_lottery_balance( asset_id_type lottery_id )const asset wallet_api::get_lottery_balance( asset_id_type lottery_id )const
{ {
return my->_remote_db->get_lottery_balance( lottery_id ); return my->_remote_db->get_lottery_balance( lottery_id );
} }
@ -4327,7 +4359,7 @@ asset wallet_api::get_lottery_balance( asset_id_type lottery_id )const
vector<operation_detail> wallet_api::get_account_history(string name, int limit) const vector<operation_detail> wallet_api::get_account_history(string name, int limit) const
{ {
vector<operation_detail> result; vector<operation_detail> result;
while (limit > 0) while (limit > 0)
{ {
bool skip_first_row = false; bool skip_first_row = false;
@ -4378,9 +4410,9 @@ vector<operation_detail> wallet_api::get_account_history(string name, int limit)
vector<operation_detail> wallet_api::get_relative_account_history(string name, uint32_t stop, int limit, uint32_t start)const vector<operation_detail> wallet_api::get_relative_account_history(string name, uint32_t stop, int limit, uint32_t start)const
{ {
FC_ASSERT( start > 0 || limit <= 100 ); FC_ASSERT( start > 0 || limit <= 100 );
vector<operation_detail> result; vector<operation_detail> result;
while( limit > 0 ) while( limit > 0 )
@ -5352,6 +5384,23 @@ vector<authority> wallet_api::get_active_custom_account_authorities_by_operation
return my->get_active_custom_account_authorities_by_operation(owner, operation_type); return my->get_active_custom_account_authorities_by_operation(owner, operation_type);
} }
vector<uint64_t> wallet_api::get_random_number_ex(string account,
uint64_t minimum,
uint64_t maximum,
uint64_t selections,
bool duplicates,
bool broadcast)
{
return my->get_random_number_ex( account, minimum, maximum, selections, duplicates, broadcast );
}
uint64_t wallet_api::get_random_number(string account,
uint64_t bound,
bool broadcast)
{
return my->get_random_number( account, bound, broadcast );
}
global_property_object wallet_api::get_global_properties() const global_property_object wallet_api::get_global_properties() const
{ {
return my->get_global_properties(); return my->get_global_properties();
@ -6353,22 +6402,22 @@ signed_transaction wallet_api::propose_delete_sport(
{ {
FC_ASSERT( !is_locked() ); FC_ASSERT( !is_locked() );
const chain_parameters& current_params = get_global_properties().parameters; const chain_parameters& current_params = get_global_properties().parameters;
sport_delete_operation sport_delete_op; sport_delete_operation sport_delete_op;
sport_delete_op.sport_id = sport_id; sport_delete_op.sport_id = sport_id;
proposal_create_operation prop_op; proposal_create_operation prop_op;
prop_op.expiration_time = expiration_time; prop_op.expiration_time = expiration_time;
prop_op.review_period_seconds = current_params.committee_proposal_review_period; prop_op.review_period_seconds = current_params.committee_proposal_review_period;
prop_op.fee_paying_account = get_account(proposing_account).id; prop_op.fee_paying_account = get_account(proposing_account).id;
prop_op.proposed_ops.emplace_back( sport_delete_op ); prop_op.proposed_ops.emplace_back( sport_delete_op );
current_params.current_fees->set_fee( prop_op.proposed_ops.back().op ); current_params.current_fees->set_fee( prop_op.proposed_ops.back().op );
signed_transaction tx; signed_transaction tx;
tx.operations.push_back(prop_op); tx.operations.push_back(prop_op);
my->set_operation_fees(tx, current_params.current_fees); my->set_operation_fees(tx, current_params.current_fees);
tx.validate(); tx.validate();
return my->sign_transaction(tx, broadcast); return my->sign_transaction(tx, broadcast);
} }
@ -6431,7 +6480,7 @@ signed_transaction wallet_api::propose_update_event_group(
return my->sign_transaction(tx, broadcast); return my->sign_transaction(tx, broadcast);
} }
signed_transaction wallet_api::propose_delete_event_group( signed_transaction wallet_api::propose_delete_event_group(
const string& proposing_account, const string& proposing_account,
fc::time_point_sec expiration_time, fc::time_point_sec expiration_time,
@ -6440,22 +6489,22 @@ signed_transaction wallet_api::propose_delete_event_group(
{ {
FC_ASSERT( !is_locked() ); FC_ASSERT( !is_locked() );
const chain_parameters& current_params = get_global_properties().parameters; const chain_parameters& current_params = get_global_properties().parameters;
event_group_delete_operation event_group_delete_op; event_group_delete_operation event_group_delete_op;
event_group_delete_op.event_group_id = event_group; event_group_delete_op.event_group_id = event_group;
proposal_create_operation prop_op; proposal_create_operation prop_op;
prop_op.expiration_time = expiration_time; prop_op.expiration_time = expiration_time;
prop_op.review_period_seconds = current_params.committee_proposal_review_period; prop_op.review_period_seconds = current_params.committee_proposal_review_period;
prop_op.fee_paying_account = get_account(proposing_account).id; prop_op.fee_paying_account = get_account(proposing_account).id;
prop_op.proposed_ops.emplace_back( event_group_delete_op ); prop_op.proposed_ops.emplace_back( event_group_delete_op );
current_params.current_fees->set_fee( prop_op.proposed_ops.back().op ); current_params.current_fees->set_fee( prop_op.proposed_ops.back().op );
signed_transaction tx; signed_transaction tx;
tx.operations.push_back(prop_op); tx.operations.push_back(prop_op);
my->set_operation_fees(tx, current_params.current_fees); my->set_operation_fees(tx, current_params.current_fees);
tx.validate(); tx.validate();
return my->sign_transaction(tx, broadcast); return my->sign_transaction(tx, broadcast);
} }
@ -6840,10 +6889,10 @@ signed_transaction wallet_api::tournament_create( string creator, tournament_opt
return my->sign_transaction( tx, broadcast ); return my->sign_transaction( tx, broadcast );
} }
signed_transaction wallet_api::tournament_join( string payer_account, signed_transaction wallet_api::tournament_join( string payer_account,
string player_account, string player_account,
tournament_id_type tournament_id, tournament_id_type tournament_id,
string buy_in_amount, string buy_in_amount,
string buy_in_asset_symbol, string buy_in_asset_symbol,
bool broadcast ) bool broadcast )
{ {
@ -6925,7 +6974,7 @@ signed_transaction wallet_api::rps_throw(game_id_type game_id,
graphene::chain::game_object game_obj = my->get_object<graphene::chain::game_object>(game_id); 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::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::tournament_object tournament_obj = my->get_object<graphene::chain::tournament_object>(match_obj.tournament_id);
graphene::chain::rock_paper_scissors_game_options game_options = graphene::chain::rock_paper_scissors_game_options game_options =
tournament_obj.options.game_options.get<graphene::chain::rock_paper_scissors_game_options>(); tournament_obj.options.game_options.get<graphene::chain::rock_paper_scissors_game_options>();
if ((int)gesture >= game_options.number_of_gestures) if ((int)gesture >= game_options.number_of_gestures)
FC_THROW("Gesture ${gesture} not supported in this game", ("gesture", gesture)); FC_THROW("Gesture ${gesture} not supported in this game", ("gesture", gesture));
@ -6972,6 +7021,8 @@ signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_na
bool is_transferable, bool is_transferable,
bool is_sellable, bool is_sellable,
optional<account_role_id_type> role_id, optional<account_role_id_type> role_id,
optional<share_type> max_supply,
optional<nft_lottery_options> lottery_options,
bool broadcast) bool broadcast)
{ {
account_object owner_account = my->get_account(owner_account_id_or_name); account_object owner_account = my->get_account(owner_account_id_or_name);
@ -6995,6 +7046,8 @@ signed_transaction wallet_api::nft_metadata_create(string owner_account_id_or_na
op.is_transferable = is_transferable; op.is_transferable = is_transferable;
op.is_sellable = is_sellable; op.is_sellable = is_sellable;
op.account_role = role_id; op.account_role = role_id;
op.max_supply = max_supply;
op.lottery_options = lottery_options;
signed_transaction trx; signed_transaction trx;
trx.operations.push_back(op); trx.operations.push_back(op);
@ -7178,6 +7231,21 @@ vector<nft_object> wallet_api::nft_get_all_tokens() const
return my->_remote_db->nft_get_all_tokens(); return my->_remote_db->nft_get_all_tokens();
} }
signed_transaction wallet_api::nft_lottery_buy_ticket( nft_metadata_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy, bool broadcast )
{
nft_lottery_token_purchase_operation op;
op.lottery_id = lottery;
op.buyer = buyer;
op.tickets_to_buy = tickets_to_buy;
signed_transaction trx;
trx.operations.push_back(op);
my->set_operation_fees( trx, my->_remote_db->get_global_properties().parameters.current_fees );
trx.validate();
return my->sign_transaction( trx, broadcast );
}
signed_transaction wallet_api::create_offer(set<nft_id_type> item_ids, signed_transaction wallet_api::create_offer(set<nft_id_type> item_ids,
string issuer_accound_id_or_name, string issuer_accound_id_or_name,
asset minimum_price, asset minimum_price,

View file

@ -44,6 +44,7 @@
#include <graphene/chain/event_object.hpp> #include <graphene/chain/event_object.hpp>
#include <graphene/chain/tournament_object.hpp> #include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/offer_object.hpp> #include <graphene/chain/offer_object.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/utilities/tempdir.hpp> #include <graphene/utilities/tempdir.hpp>
@ -335,6 +336,14 @@ void database_fixture::verify_asset_supplies( const database& db )
} }
} }
for (const nft_metadata_object &o : db.get_index_type<nft_metadata_index>().indices())
{
if (o.lottery_data)
{
total_balances[o.get_lottery_jackpot(db).asset_id] += o.get_lottery_jackpot(db).amount;
}
}
uint64_t sweeps_vestings = 0; uint64_t sweeps_vestings = 0;
for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() ) for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() )
sweeps_vestings += svbo.balance; sweeps_vestings += svbo.balance;

View file

@ -0,0 +1,842 @@
/*
* Copyright (c) 2017 PBSA, 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 <graphene/chain/database.hpp>
#include <fc/crypto/digest.hpp>
#include <fc/crypto/elliptic.hpp>
#include <fc/reflect/variant.hpp>
#include "../common/database_fixture.hpp"
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/nft_object.hpp>
using namespace graphene::chain;
using namespace graphene::chain::test;
BOOST_FIXTURE_TEST_SUITE(nft_lottery_tests, database_fixture)
BOOST_AUTO_TEST_CASE(create_lottery_nft_md_test)
{
try
{
generate_blocks(HARDFORK_NFT_TIME);
generate_block();
generate_block();
set_expiration(db, trx);
// Lottery Options
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
nft_lottery_options lottery_options;
lottery_options.benefactors.push_back(nft_lottery_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 = {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.winning_tickets = { 75 * GRAPHENE_1_PERCENT };
lottery_options.is_active = test_nft_md_id.instance.value % 2 ? false : true;
lottery_options.ending_on_soldout = true;
{
BOOST_TEST_MESSAGE("Send nft_metadata_create_operation");
nft_metadata_create_operation op;
op.owner = account_id_type();
op.symbol = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
op.base_uri = "http://nft.example.com";
op.is_transferable = true;
op.name = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
op.max_supply = 200;
op.lottery_options = lottery_options;
trx.operations.push_back(std::move(op));
PUSH_TX(db, trx, ~0);
}
generate_block();
BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results");
const auto &obj = test_nft_md_id(db);
BOOST_CHECK(obj.owner == account_id_type());
BOOST_CHECK(obj.name == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
BOOST_CHECK(obj.symbol == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
BOOST_CHECK(obj.base_uri == "http://nft.example.com");
BOOST_CHECK(obj.max_supply == share_type(200));
BOOST_CHECK(obj.is_lottery());
BOOST_CHECK(obj.get_token_current_supply(db) == share_type(0));
BOOST_CHECK(obj.get_lottery_jackpot(db) == asset());
BOOST_CHECK(obj.lottery_data->lottery_balance_id(db).sweeps_tickets_sold == share_type(0));
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(lottery_idx_test)
{
try
{
// generate loterries with different end_dates and is_active_flag
for (int i = 0; i < 26; ++i)
{
generate_blocks(30);
graphene::chain::test::set_expiration(db, trx);
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
INVOKE(create_lottery_nft_md_test);
auto test_nft_md_obj = test_nft_md_id(db);
}
auto &test_nft_md_idx = db.get_index_type<nft_metadata_index>().indices().get<active_nft_lotteries>();
auto test_itr = test_nft_md_idx.begin();
bool met_not_active = false;
// check sorting
while (test_itr != test_nft_md_idx.end())
{
if (!met_not_active && (!test_itr->is_lottery() || !test_itr->lottery_data->lottery_options.is_active))
met_not_active = true;
FC_ASSERT(!met_not_active || met_not_active && (!test_itr->is_lottery() || !test_itr->lottery_data->lottery_options.is_active), "MET ACTIVE LOTTERY AFTER NOT ACTIVE");
++test_itr;
}
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(tickets_purchase_test)
{
try
{
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
nft_id_type test_nft_id = db.get_index<nft_object>().get_next_id();
INVOKE(create_lottery_nft_md_test);
auto &test_nft_md_obj = test_nft_md_id(db);
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = account_id_type();
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = 1;
tpo.amount = asset(100);
trx.operations.push_back(std::move(tpo));
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
generate_block();
trx.operations.clear();
auto &test_nft_ticket = test_nft_id(db);
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == tpo.tickets_to_buy);
BOOST_CHECK(test_nft_ticket.owner == tpo.buyer);
BOOST_CHECK(test_nft_ticket.nft_metadata_id == test_nft_md_obj.id);
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(tickets_purchase_fail_test)
{
try
{
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
INVOKE(create_lottery_nft_md_test);
auto &test_nft_md_obj = test_nft_md_id(db);
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = account_id_type();
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = 2;
tpo.amount = asset(100);
trx.operations.push_back(tpo);
BOOST_REQUIRE_THROW(PUSH_TX(db, trx, ~0), fc::exception); // amount/tickets_to_buy != price
trx.operations.clear();
tpo.amount = asset(205);
trx.operations.push_back(tpo);
BOOST_REQUIRE_THROW(PUSH_TX(db, trx, ~0), fc::exception); // amount/tickets_to_buy != price
tpo.amount = asset(200, asset_id_type(1));
trx.operations.push_back(tpo);
BOOST_REQUIRE_THROW(PUSH_TX(db, trx, ~0), fc::exception); // trying to buy in other asset
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(lottery_end_by_stage_test)
{
try
{
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
INVOKE(create_lottery_nft_md_test);
auto test_nft_md_obj = test_nft_md_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));
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = account_id_type(i);
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = i;
tpo.amount = asset(100 * (i));
trx.operations.push_back(std::move(tpo));
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
generate_block();
trx.operations.clear();
}
test_nft_md_obj = test_nft_md_id(db);
uint64_t benefactor_balance_before_end = db.get_balance(account_id_type(), asset_id_type()).amount.value;
uint64_t jackpot = test_nft_md_obj.get_lottery_jackpot(db).amount.value;
uint16_t winners_part = 0;
for (uint16_t win : test_nft_md_obj.lottery_data->lottery_options.winning_tickets)
winners_part += win;
uint16_t participants_percents_sum = 0;
auto participants = test_nft_md_obj.distribute_winners_part(db);
for (auto p : participants)
{
for (auto e : p.second)
{
participants_percents_sum += e;
}
}
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(participants_percents_sum == winners_part);
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).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_nft_md_obj.distribute_benefactors_part(db);
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == jackpot * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT * winners_part / (double)GRAPHENE_100_PERCENT);
test_nft_md_obj.distribute_sweeps_holders_part(db);
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == 0);
uint64_t benefactor_recieved = db.get_balance(account_id_type(), asset_id_type()).amount.value - benefactor_balance_before_end;
BOOST_CHECK(jackpot * test_nft_md_obj.lottery_data->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
{
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
INVOKE(create_lottery_nft_md_test);
db.modify(test_nft_md_id(db), [&](nft_metadata_object &obj) {
obj.lottery_data->lottery_options.is_active = true;
});
auto test_nft_md_obj = test_nft_md_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));
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = account_id_type(i);
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = i;
tpo.amount = asset(100 * (i));
trx.operations.push_back(std::move(tpo));
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
generate_block();
trx.operations.clear();
}
test_nft_md_obj = test_nft_md_id(db);
uint64_t benefactor_balance_before_end = db.get_balance(account_id_type(), asset_id_type()).amount.value;
uint64_t jackpot = test_nft_md_obj.get_lottery_jackpot(db).amount.value;
uint16_t winners_part = 0;
for (uint16_t win : test_nft_md_obj.lottery_data->lottery_options.winning_tickets)
winners_part += win;
uint16_t participants_percents_sum = 0;
auto participants = test_nft_md_obj.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
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).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_nft_md_obj.distribute_benefactors_part(db);
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value > jackpot * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT * winners_part / (double)GRAPHENE_100_PERCENT);
test_nft_md_obj.distribute_sweeps_holders_part(db);
test_nft_md_obj = test_nft_md_id(db);
// but at the end is always equals 0
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == 0);
uint64_t benefactor_recieved = db.get_balance(account_id_type(), asset_id_type()).amount.value - benefactor_balance_before_end;
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(jackpot * test_nft_md_obj.lottery_data->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
{
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
INVOKE(create_lottery_nft_md_test);
auto test_nft_md_obj = test_nft_md_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));
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = account_id_type(i);
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = i;
tpo.amount = asset(100 * (i));
trx.operations.push_back(std::move(tpo));
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
trx.operations.clear();
}
generate_block();
test_nft_md_obj = test_nft_md_id(db);
uint64_t creator_balance_before_end = db.get_balance(account_id_type(), asset_id_type()).amount.value;
uint64_t jackpot = test_nft_md_obj.get_lottery_jackpot(db).amount.value;
uint16_t winners_part = 0;
for (uint8_t win : test_nft_md_obj.lottery_data->lottery_options.winning_tickets)
winners_part += win;
while (db.head_block_time() < (test_nft_md_obj.lottery_data->lottery_options.end_date + fc::seconds(30)))
generate_block();
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == 0);
uint64_t creator_recieved = db.get_balance(account_id_type(), asset_id_type()).amount.value - creator_balance_before_end;
BOOST_CHECK(jackpot * test_nft_md_obj.lottery_data->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
{
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
INVOKE(lottery_end_test);
auto test_nft_md_obj = test_nft_md_id(db);
account_id_type benefactor = test_nft_md_obj.lottery_data->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
{
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
INVOKE(create_lottery_nft_md_test);
auto test_nft_md_obj = test_nft_md_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));
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = account_id_type(i);
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = 1;
tpo.amount = asset(100);
trx.operations.push_back(std::move(tpo));
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
trx.operations.clear();
}
generate_block();
test_nft_md_obj = test_nft_md_id(db);
auto holders = test_nft_md_obj.get_holders(db);
auto participants = test_nft_md_obj.distribute_winners_part(db);
test_nft_md_obj = test_nft_md_id(db);
test_nft_md_obj.distribute_benefactors_part(db);
test_nft_md_obj = test_nft_md_id(db);
test_nft_md_obj.distribute_sweeps_holders_part(db);
test_nft_md_obj = test_nft_md_id(db);
generate_block();
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
{
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
INVOKE(create_lottery_nft_md_test);
auto test_nft_md_obj = test_nft_md_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));
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = account_id_type(i);
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = 1;
tpo.amount = asset(100);
trx.operations.push_back(std::move(tpo));
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
trx.operations.clear();
}
generate_block();
test_nft_md_obj = test_nft_md_id(db);
auto holders = test_nft_md_obj.get_holders(db);
idump((test_nft_md_obj.get_lottery_jackpot(db)));
while (db.head_block_time() < (test_nft_md_obj.lottery_data->lottery_options.end_date + fc::seconds(30)))
generate_block();
test_nft_md_obj = test_nft_md_id(db);
idump((test_nft_md_obj.get_lottery_jackpot(db)));
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_nft_md_obj.lottery_data->lottery_options.ticket_price.asset_id);
break;
}
}
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(ending_by_participants_count_test)
{
try
{
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
INVOKE(create_lottery_nft_md_test);
auto test_nft_md_obj = test_nft_md_id(db);
FC_ASSERT(test_nft_md_obj.lottery_data->lottery_options.is_active);
account_id_type buyer(3);
transfer(account_id_type(), buyer, asset(10000000));
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = buyer;
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = 200;
tpo.amount = asset(200 * 100);
trx.operations.push_back(tpo);
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
trx.operations.clear();
generate_block();
test_nft_md_obj = test_nft_md_id(db);
FC_ASSERT(!test_nft_md_obj.lottery_data->lottery_options.is_active);
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(try_to_end_empty_lottery_test)
{
try
{
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
INVOKE(create_lottery_nft_md_test);
auto test_nft_md_obj = test_nft_md_id(db);
while (db.head_block_time() < (test_nft_md_obj.lottery_data->lottery_options.end_date + fc::seconds(30)))
generate_block();
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(!test_nft_md_obj.lottery_data->lottery_options.is_active);
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(lottery_winner_ticket_id_test)
{
try
{
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
INVOKE(create_lottery_nft_md_test);
auto test_nft_md_obj = test_nft_md_id(db);
for (int i = 1; i < 4; ++i)
{
transfer(account_id_type(), account_id_type(i), asset(2000000));
}
for (int i = 1; i < 4; ++i)
{
if (i == 4)
continue;
nft_lottery_token_purchase_operation tpo;
tpo.buyer = account_id_type(i);
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = 1;
tpo.amount = asset(100);
trx.operations.push_back(std::move(tpo));
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
trx.operations.clear();
}
for (int i = 1; i < 4; ++i)
{
if (i == 4)
continue;
nft_lottery_token_purchase_operation tpo;
tpo.buyer = account_id_type(i);
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = 1;
tpo.amount = asset(100);
trx.operations.push_back(std::move(tpo));
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
trx.operations.clear();
}
generate_block();
test_nft_md_obj = test_nft_md_id(db);
uint64_t creator_balance_before_end = db.get_balance(account_id_type(), asset_id_type()).amount.value;
uint64_t jackpot = test_nft_md_obj.get_lottery_jackpot(db).amount.value;
uint16_t winners_part = 0;
for (uint8_t win : test_nft_md_obj.lottery_data->lottery_options.winning_tickets)
winners_part += win;
while (db.head_block_time() < (test_nft_md_obj.lottery_data->lottery_options.end_date))
generate_block();
auto op_history = get_operation_history(account_id_type(1)); //Can observe operation 79 to verify winner ticket number
for (auto h : op_history)
{
idump((h));
}
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == 0);
uint64_t creator_recieved = db.get_balance(account_id_type(), asset_id_type()).amount.value - creator_balance_before_end;
BOOST_CHECK(jackpot * test_nft_md_obj.lottery_data->lottery_options.benefactors[0].share / GRAPHENE_100_PERCENT == creator_recieved);
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(create_lottery_nft_md_delete_tickets_on_draw_test)
{
try
{
generate_blocks(HARDFORK_NFT_TIME);
generate_block();
generate_block();
set_expiration(db, trx);
// Lottery Options
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
nft_lottery_options lottery_options;
lottery_options.benefactors.push_back(nft_lottery_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 = {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.winning_tickets = { 75 * GRAPHENE_1_PERCENT };
lottery_options.is_active = test_nft_md_id.instance.value % 2 ? false : true;
lottery_options.ending_on_soldout = true;
lottery_options.delete_tickets_after_draw = true;
{
BOOST_TEST_MESSAGE("Send nft_metadata_create_operation");
nft_metadata_create_operation op;
op.owner = account_id_type();
op.symbol = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
op.base_uri = "http://nft.example.com";
op.is_transferable = true;
op.name = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
op.max_supply = 200;
op.lottery_options = lottery_options;
trx.operations.push_back(std::move(op));
PUSH_TX(db, trx, ~0);
trx.operations.clear();
}
generate_block();
BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results");
auto test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(test_nft_md_obj.owner == account_id_type());
BOOST_CHECK(test_nft_md_obj.name == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
BOOST_CHECK(test_nft_md_obj.symbol == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
BOOST_CHECK(test_nft_md_obj.base_uri == "http://nft.example.com");
BOOST_CHECK(test_nft_md_obj.max_supply == share_type(200));
BOOST_CHECK(test_nft_md_obj.is_lottery());
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == share_type(0));
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db) == asset());
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_balance_id(db).sweeps_tickets_sold == share_type(0));
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_options.is_active);
account_id_type buyer(3);
transfer(account_id_type(), buyer, asset(10000000));
{
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = buyer;
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = 199;
tpo.amount = asset(199 * 100);
trx.operations.push_back(tpo);
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
trx.operations.clear();
}
generate_block();
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_options.is_active);
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == 199);
{
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = buyer;
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = 1;
tpo.amount = asset(1 * 100);
trx.operations.push_back(tpo);
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
trx.operations.clear();
}
generate_block();
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(!test_nft_md_obj.lottery_data->lottery_options.is_active);
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == 0);
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(create_lottery_nft_with_permission_test)
{
try
{
generate_blocks(HARDFORK_NFT_TIME);
generate_block();
generate_block();
set_expiration(db, trx);
// Lottery Options
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
nft_lottery_options lottery_options;
lottery_options.benefactors.push_back(nft_lottery_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 = {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.winning_tickets = { 75 * GRAPHENE_1_PERCENT };
lottery_options.is_active = test_nft_md_id.instance.value % 2 ? false : true;
lottery_options.ending_on_soldout = true;
lottery_options.delete_tickets_after_draw = true;
account_id_type buyer1(3);
account_id_type buyer2(6);
generate_block();
transfer(account_id_type(), buyer1, asset(1000000));
transfer(account_id_type(), buyer2, asset(1000000));
generate_block();
{
BOOST_TEST_MESSAGE("Send account_role_create_operation");
account_role_create_operation op;
op.owner = account_id_type();
op.name = "Test Account Role";
op.metadata = "{\"country\": \"earth\", \"race\": \"human\" }";
int ops[] = {operation::tag<nft_lottery_token_purchase_operation>::value};
op.allowed_operations.insert(ops, ops + 1);
op.whitelisted_accounts.emplace(buyer1);
op.valid_to = db.head_block_time() + 1000;
trx.operations.push_back(op);
PUSH_TX(db, trx, ~0);
trx.operations.clear();
}
{
BOOST_TEST_MESSAGE("Send nft_metadata_create_operation");
nft_metadata_create_operation op;
op.owner = account_id_type();
op.symbol = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
op.base_uri = "http://nft.example.com";
op.is_transferable = true;
op.name = "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value);
op.max_supply = 200;
op.lottery_options = lottery_options;
op.account_role = account_role_id_type(0);
trx.operations.push_back(std::move(op));
PUSH_TX(db, trx, ~0);
trx.operations.clear();
}
generate_block();
BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results");
auto test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(test_nft_md_obj.owner == account_id_type());
BOOST_CHECK(test_nft_md_obj.name == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
BOOST_CHECK(test_nft_md_obj.symbol == "NFTLOTTERY" + std::to_string(test_nft_md_id.instance.value));
BOOST_CHECK(test_nft_md_obj.base_uri == "http://nft.example.com");
BOOST_CHECK(test_nft_md_obj.max_supply == share_type(200));
BOOST_CHECK(test_nft_md_obj.is_lottery());
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == share_type(0));
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db) == asset());
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_balance_id(db).sweeps_tickets_sold == share_type(0));
BOOST_CHECK(test_nft_md_obj.account_role == account_role_id_type(0));
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_options.is_active);
{
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = buyer1;
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = 199;
tpo.amount = asset(199 * 100);
trx.operations.push_back(tpo);
set_expiration(db, trx);
PUSH_TX(db, trx, ~0);
trx.operations.clear();
}
generate_block();
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(test_nft_md_obj.lottery_data->lottery_options.is_active);
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == 199);
{
nft_lottery_token_purchase_operation tpo;
tpo.fee = asset();
tpo.buyer = buyer2;
tpo.lottery_id = test_nft_md_obj.id;
tpo.tickets_to_buy = 1;
tpo.amount = asset(1 * 100);
trx.operations.push_back(tpo);
set_expiration(db, trx);
BOOST_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception);
trx.operations.clear();
}
generate_block();
test_nft_md_obj = test_nft_md_id(db);
BOOST_CHECK(test_nft_md_obj.get_token_current_supply(db) == 199);
}
catch (fc::exception &e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_SUITE_END()