Merge pull request #30 from peerplays-network/revert-28-beatrice
Revert "Merge branch 'master' into beatrice"
This commit is contained in:
commit
c7c87c6380
28 changed files with 8992 additions and 215045 deletions
222008
genesis.json
222008
genesis.json
File diff suppressed because it is too large
Load diff
|
|
@ -169,14 +169,14 @@ namespace graphene { namespace app {
|
|||
void network_broadcast_api::broadcast_transaction(const signed_transaction& trx)
|
||||
{
|
||||
trx.validate();
|
||||
database_api( *(_app.chain_database() ) ).check_transaction_for_duplicated_operations(trx);
|
||||
_app.chain_database()->check_tansaction_for_duplicated_operations(trx);
|
||||
_app.chain_database()->push_transaction(trx);
|
||||
_app.p2p_node()->broadcast_transaction(trx);
|
||||
}
|
||||
|
||||
fc::variant network_broadcast_api::broadcast_transaction_synchronous(const signed_transaction& trx)
|
||||
{
|
||||
database_api( *(_app.chain_database() ) ).check_transaction_for_duplicated_operations(trx);
|
||||
_app.chain_database()->check_tansaction_for_duplicated_operations(trx);
|
||||
|
||||
fc::promise<fc::variant>::ptr prom( new fc::promise<fc::variant>() );
|
||||
broadcast_transaction_with_callback( [=]( const fc::variant& v ){
|
||||
|
|
|
|||
|
|
@ -162,40 +162,10 @@ namespace detail {
|
|||
{
|
||||
// t.me/peerplays #seednodes
|
||||
vector<string> seeds = {
|
||||
"seed.ppy.blckchnd.com:6112", // blckchnd
|
||||
"ppy.esteem.ws:7777", // good-karma
|
||||
"peerplays.bitcoiner.me:9777", // bitcoiner
|
||||
"peerplays.roelandp.nl:9777", // roelandp
|
||||
"ppyseed.bacchist.me:42420", // bacchist-witness
|
||||
"5.9.18.213:18828", // pfunk
|
||||
"31.171.244.121:7777", // taconator
|
||||
"seed.peerplaysdb.com:9777", // jesta
|
||||
"ppy-seed.xeldal.com:19777", // xeldal
|
||||
"seed.ppy.altcap.io:61388", // winner.winner.chicken.dinner
|
||||
"seed.peerplaysnodes.com:9777", // wackou
|
||||
"peerplays-seed.privex.io:7777", // someguy123/privex
|
||||
"51.15.78.16:9777", // agoric.systems
|
||||
"212.71.253.163:9777", // xtar
|
||||
"51.15.35.96:9777", // lafona
|
||||
"anyx.ca:9777", // anyx
|
||||
"82.223.108.91:7777", // hiltos-witness
|
||||
"seed.ppy.nuevax.com:19777", // nuevax
|
||||
"peerplays.butler.net:9777", // billbutler-witness
|
||||
"peerplays.bitcoiner.me:9777", // bitcoiner
|
||||
"ppyseed.bacchist.me:42420", // bacchist-witness
|
||||
"peerplays.bhuz.info:9777", // bhuz
|
||||
"node.peerblock.trade:9777", // bitcoinsig
|
||||
"peerplays.crypto.fans:9777", // sc-steemit
|
||||
"54.38.193.20:9777", // royal-flush
|
||||
"ppy001.bts-nodes.net:7777", // baxters-sports-witness
|
||||
"ppy002.bts-nodes.net:7777", // baxters-sports-witness
|
||||
"ppy003.bts-nodes.net:7777", // baxters-sports-witness
|
||||
"ppy004.bts-nodes.net:7777", // baxters-sports-witness
|
||||
"ppy.proxyhosts.info:7777", // baxters-sports-witness
|
||||
"ppyseed.spacemx.tech:9777", // spacecrypt-witness
|
||||
"peerplaysblockchain.net:9777", // houdini-witness
|
||||
"54.37.235.164:7777", // melea-trust
|
||||
"peerplays-seed.lukestokes.info:7777" // lukestokes-witness
|
||||
"ppy-beatrice-seed.blckchnd.com:6666",
|
||||
"159.69.223.206:7777",
|
||||
"51.38.237.243:9666",
|
||||
"pbsa-beatrice.blockchainprojectsbv.com:9195"
|
||||
};
|
||||
|
||||
for( const string& endpoint_string : seeds )
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/crypto/digest.hpp>
|
||||
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <boost/rational.hpp>
|
||||
|
|
@ -46,44 +45,6 @@
|
|||
|
||||
typedef std::map< std::pair<graphene::chain::asset_id_type, graphene::chain::asset_id_type>, std::vector<fc::variant> > market_queue_type;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct proposed_operations_digest_accumulator
|
||||
{
|
||||
typedef void result_type;
|
||||
|
||||
void operator()(const graphene::chain::proposal_create_operation& proposal)
|
||||
{
|
||||
for (auto& operation: proposal.proposed_ops)
|
||||
{
|
||||
if( operation.op.which() != graphene::chain::operation::tag<graphene::chain::betting_market_group_create_operation>::value
|
||||
&& operation.op.which() != graphene::chain::operation::tag<graphene::chain::betting_market_create_operation>::value )
|
||||
proposed_operations_digests.push_back(fc::digest(operation.op));
|
||||
}
|
||||
}
|
||||
|
||||
//empty template method is needed for all other operation types
|
||||
//we can ignore them, we are interested in only proposal_create_operation
|
||||
template<class T>
|
||||
void operator()(const T&)
|
||||
{}
|
||||
|
||||
std::vector<fc::sha256> proposed_operations_digests;
|
||||
};
|
||||
|
||||
std::vector<fc::sha256> gather_proposed_operations_digests(const graphene::chain::transaction& trx)
|
||||
{
|
||||
proposed_operations_digest_accumulator digest_accumulator;
|
||||
for (auto& operation: trx.operations)
|
||||
{
|
||||
operation.visit(digest_accumulator);
|
||||
}
|
||||
|
||||
return digest_accumulator.proposed_operations_digests;
|
||||
}
|
||||
}
|
||||
|
||||
namespace graphene { namespace app {
|
||||
|
||||
class database_api_impl;
|
||||
|
|
@ -109,7 +70,6 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
map<uint32_t, optional<block_header>> get_block_header_batch(const vector<uint32_t> block_nums)const;
|
||||
optional<signed_block> get_block(uint32_t block_num)const;
|
||||
processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const;
|
||||
void check_transaction_for_duplicated_operations(const signed_transaction& trx);
|
||||
|
||||
// Globals
|
||||
chain_property_object get_chain_properties()const;
|
||||
|
|
@ -469,39 +429,6 @@ processed_transaction database_api_impl::get_transaction(uint32_t block_num, uin
|
|||
return opt_block->transactions[trx_num];
|
||||
}
|
||||
|
||||
void database_api::check_transaction_for_duplicated_operations(const signed_transaction& trx)
|
||||
{
|
||||
my->check_transaction_for_duplicated_operations(trx);
|
||||
}
|
||||
|
||||
void database_api_impl::check_transaction_for_duplicated_operations(const signed_transaction& trx)
|
||||
{
|
||||
const auto& idx = _db.get_index_type<proposal_index>();
|
||||
const auto& pidx = dynamic_cast<const primary_index<proposal_index>&>(idx);
|
||||
const auto& raidx = pidx.get_secondary_index<graphene::chain::required_approval_index>();
|
||||
|
||||
auto acc_itr = raidx._account_to_proposals.find( GRAPHENE_WITNESS_ACCOUNT );
|
||||
if( acc_itr != raidx._account_to_proposals.end() )
|
||||
{
|
||||
auto& p_set = acc_itr->second;
|
||||
|
||||
std::set<fc::sha256> existed_operations_digests;
|
||||
for( auto p_itr = p_set.begin(); p_itr != p_set.end(); ++p_itr )
|
||||
{
|
||||
for( auto& operation : (*p_itr)(_db).proposed_transaction.operations )
|
||||
{
|
||||
existed_operations_digests.insert( fc::digest(operation) );
|
||||
}
|
||||
}
|
||||
|
||||
auto proposed_operations_digests = gather_proposed_operations_digests(trx);
|
||||
for (auto& digest : proposed_operations_digests)
|
||||
{
|
||||
FC_ASSERT(existed_operations_digests.count(digest) == 0, "Proposed operation is already pending for apsproval.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Globals //
|
||||
|
|
|
|||
|
|
@ -192,12 +192,6 @@ class database_api
|
|||
*/
|
||||
optional<signed_transaction> get_recent_transaction_by_id( const transaction_id_type& id )const;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
*/
|
||||
void check_transaction_for_duplicated_operations(const signed_transaction& trx);
|
||||
|
||||
/////////////
|
||||
// Globals //
|
||||
/////////////
|
||||
|
|
@ -651,7 +645,6 @@ class database_api
|
|||
*/
|
||||
vector<tournament_id_type> get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
|
||||
|
||||
|
||||
private:
|
||||
std::shared_ptr< database_api_impl > my;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ FC_REFLECT( graphene::app::full_account,
|
|||
(limit_orders)
|
||||
(call_orders)
|
||||
(settle_orders)
|
||||
(proposals)
|
||||
(assets)
|
||||
(withdraws)
|
||||
(proposals)
|
||||
|
|
|
|||
|
|
@ -279,17 +279,6 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
|
|||
void_result account_update_evaluator::do_apply( const account_update_operation& o )
|
||||
{ try {
|
||||
database& d = db();
|
||||
|
||||
if( o.new_options.valid() )
|
||||
{
|
||||
d.modify( acnt->statistics( d ), [&]( account_statistics_object& aso )
|
||||
{
|
||||
if((o.new_options->votes != acnt->options.votes ||
|
||||
o.new_options->voting_account != acnt->options.voting_account))
|
||||
aso.last_vote_time = d.head_block_time();
|
||||
} );
|
||||
}
|
||||
|
||||
bool sa_before, sa_after;
|
||||
d.modify( *acnt, [&](account_object& a){
|
||||
if( o.owner )
|
||||
|
|
@ -331,6 +320,7 @@ void_result account_update_evaluator::do_apply( const account_update_operation&
|
|||
sa.account = o.account;
|
||||
} );
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
|
|
|||
|
|
@ -57,37 +57,35 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o
|
|||
|
||||
if( d.head_block_time() > HARDFORK_385_TIME )
|
||||
{
|
||||
if( d.head_block_time() <= HARDFORK_409_TIME )
|
||||
|
||||
if( d.head_block_time() <= HARDFORK_409_TIME )
|
||||
{
|
||||
auto dotpos = op.symbol.find( '.' );
|
||||
if( dotpos != std::string::npos )
|
||||
{
|
||||
auto dotpos = op.symbol.find( '.' );
|
||||
if( dotpos != std::string::npos )
|
||||
{
|
||||
auto prefix = op.symbol.substr( 0, dotpos );
|
||||
auto asset_symbol_itr = asset_indx.find( op.symbol );
|
||||
FC_ASSERT( asset_symbol_itr != asset_indx.end(),
|
||||
"Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered",
|
||||
("s",op.symbol)("p",prefix) );
|
||||
FC_ASSERT( asset_symbol_itr->issuer == op.issuer,
|
||||
"Asset ${s} may only be created by issuer of ${p}, ${i}",
|
||||
("s",op.symbol)("p",prefix)("i", op.issuer(d).name) );
|
||||
}
|
||||
auto prefix = op.symbol.substr( 0, dotpos );
|
||||
auto asset_symbol_itr = asset_indx.find( op.symbol );
|
||||
FC_ASSERT( asset_symbol_itr != asset_indx.end(), "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered",
|
||||
("s",op.symbol)("p",prefix) );
|
||||
FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}",
|
||||
("s",op.symbol)("p",prefix)("i", op.issuer(d).name) );
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dotpos = op.symbol.rfind( '.' );
|
||||
if( dotpos != std::string::npos )
|
||||
{
|
||||
auto dotpos = op.symbol.rfind( '.' );
|
||||
if( dotpos != std::string::npos )
|
||||
{
|
||||
auto prefix = op.symbol.substr( 0, dotpos );
|
||||
auto asset_symbol_itr = asset_indx.find( prefix );
|
||||
FC_ASSERT( asset_symbol_itr != asset_indx.end(),
|
||||
"Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered",
|
||||
("s",op.symbol)("p",prefix) );
|
||||
FC_ASSERT( asset_symbol_itr->issuer == op.issuer,
|
||||
"Asset ${s} may only be created by issuer of ${p}, ${i}",
|
||||
("s",op.symbol)("p",prefix)("i", op.issuer(d).name) );
|
||||
}
|
||||
auto prefix = op.symbol.substr( 0, dotpos );
|
||||
auto asset_symbol_itr = asset_indx.find( prefix );
|
||||
FC_ASSERT( asset_symbol_itr != asset_indx.end(), "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered",
|
||||
("s",op.symbol)("p",prefix) );
|
||||
FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}",
|
||||
("s",op.symbol)("p",prefix)("i", op.issuer(d).name) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dotpos = op.symbol.find( '.' );
|
||||
|
|
|
|||
|
|
@ -149,6 +149,30 @@ std::vector<block_id_type> database::get_block_ids_on_fork(block_id_type head_of
|
|||
return result;
|
||||
}
|
||||
|
||||
void database::check_tansaction_for_duplicated_operations(const signed_transaction& trx)
|
||||
{
|
||||
const auto& proposal_index = get_index<proposal_object>();
|
||||
std::set<fc::sha256> existed_operations_digests;
|
||||
|
||||
proposal_index.inspect_all_objects( [&](const object& obj){
|
||||
const proposal_object& proposal = static_cast<const proposal_object&>(obj);
|
||||
auto proposed_operations_digests = gather_proposed_operations_digests( proposal.proposed_transaction );
|
||||
existed_operations_digests.insert( proposed_operations_digests.begin(), proposed_operations_digests.end() );
|
||||
});
|
||||
|
||||
for (auto& pending_transaction: _pending_tx)
|
||||
{
|
||||
auto proposed_operations_digests = gather_proposed_operations_digests(pending_transaction);
|
||||
existed_operations_digests.insert(proposed_operations_digests.begin(), proposed_operations_digests.end());
|
||||
}
|
||||
|
||||
auto proposed_operations_digests = gather_proposed_operations_digests(trx);
|
||||
for (auto& digest: proposed_operations_digests)
|
||||
{
|
||||
FC_ASSERT(existed_operations_digests.count(digest) == 0, "Proposed operation is already pending for approval.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push block "may fail" in which case every partial change is unwound. After
|
||||
* push block is successful the block is appended to the chain database on disk.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
#ifndef HARDFORK_1000_TIME
|
||||
#define HARDFORK_1000_TIME (fc::time_point_sec( 1550491200 ))
|
||||
#define HARDFORK_1000_TIME (fc::time_point_sec( 1540000000 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// added delete sport and delete event group operations
|
||||
#ifndef HARDFORK_1001_TIME
|
||||
#define HARDFORK_1001_TIME (fc::time_point_sec( 1550491200 ))
|
||||
#define HARDFORK_1001_TIME (fc::time_point_sec( 1540000000 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Placeholder HF for affiliate reward system
|
||||
#ifndef HARDFORK_999_TIME
|
||||
#define HARDFORK_999_TIME (fc::time_point_sec( 1550491200 ))
|
||||
#define HARDFORK_999_TIME (fc::time_point_sec( 1540000000 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -82,11 +82,6 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
share_type pending_vested_fees;
|
||||
|
||||
/**
|
||||
* Keep the date of the last voting activity for this account.
|
||||
*/
|
||||
time_point_sec last_vote_time;
|
||||
|
||||
/// @brief Split up and pay out @ref pending_fees and @ref pending_vested_fees
|
||||
void process_fees(const account_object& a, database& d) const;
|
||||
|
||||
|
|
@ -468,7 +463,6 @@ FC_REFLECT_DERIVED( graphene::chain::account_statistics_object,
|
|||
(total_core_in_orders)
|
||||
(lifetime_fees_paid)
|
||||
(pending_fees)(pending_vested_fees)
|
||||
(last_vote_time)
|
||||
)
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::pending_dividend_payout_balance_for_holder_object,
|
||||
|
|
|
|||
|
|
@ -717,8 +717,8 @@ inline Stream& operator>>( Stream& s, betting_market_group_object& betting_marke
|
|||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_rules_object, (graphene::db::object), (name)(description) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_group_object, (graphene::db::object), (description)(event_id)(rules_id)(asset_id)(total_matched_bets_amount)(never_in_play)(delay_before_settling)(settling_time) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_object, (graphene::db::object), (group_id)(description)(payout_condition)(resolution) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_group_object, (graphene::db::object), (description) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_object, (graphene::db::object), (group_id) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::bet_object, (graphene::db::object), (bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(back_or_lay)(end_of_delay) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_position_object, (graphene::db::object), (bettor_id)(betting_market_id)(pay_if_payout_condition)(pay_if_not_payout_condition)(pay_if_canceled)(pay_if_not_canceled)(fees_collected) )
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#define GRAPHENE_SYMBOL "PPY"
|
||||
#define GRAPHENE_ADDRESS_PREFIX "PPY"
|
||||
#define GRAPHENE_SYMBOL "TEST"
|
||||
#define GRAPHENE_ADDRESS_PREFIX "TEST"
|
||||
|
||||
#define GRAPHENE_MIN_ACCOUNT_NAME_LENGTH 1
|
||||
#define GRAPHENE_MAX_ACCOUNT_NAME_LENGTH 63
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
#define GRAPHENE_MIN_BLOCK_INTERVAL 1 /* seconds */
|
||||
#define GRAPHENE_MAX_BLOCK_INTERVAL 30 /* seconds */
|
||||
|
||||
#define GRAPHENE_DEFAULT_BLOCK_INTERVAL 5 /* seconds */
|
||||
#define GRAPHENE_DEFAULT_BLOCK_INTERVAL 3 /* seconds */
|
||||
#define GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE 2048
|
||||
#define GRAPHENE_DEFAULT_MAX_BLOCK_SIZE (GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE*GRAPHENE_DEFAULT_BLOCK_INTERVAL*200000)
|
||||
#define GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION (60*60*24) // seconds, aka: 1 day
|
||||
|
|
@ -179,7 +179,7 @@
|
|||
|
||||
#define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET (asset_id_type(743))
|
||||
|
||||
#define GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE (5*GRAPHENE_1_PERCENT)
|
||||
#define GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE (3*GRAPHENE_1_PERCENT)
|
||||
|
||||
/**
|
||||
* Betting-related constants.
|
||||
|
|
@ -208,7 +208,7 @@
|
|||
{ 300000, 10000}, /* <= 30: 1.00 */ \
|
||||
{ 500000, 20000}, /* <= 50: 2.00 */ \
|
||||
{ 1000000, 50000}, /* <= 100: 5.00 */ \
|
||||
{ 10000000, 100000} } /* <= 1000: 10.00 */
|
||||
{ 10000000, 100000} } /* <= 1000: 10.00 */
|
||||
#define GRAPHENE_DEFAULT_BETTING_PERCENT_FEE (2 * GRAPHENE_1_PERCENT)
|
||||
#define GRAPHENE_DEFAULT_LIVE_BETTING_DELAY_TIME 5 // seconds
|
||||
#define TOURNAMENT_MIN_ROUND_DELAY 0
|
||||
|
|
|
|||
|
|
@ -136,6 +136,8 @@ namespace graphene { namespace chain {
|
|||
void add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts );
|
||||
const flat_map<uint32_t,block_id_type> get_checkpoints()const { return _checkpoints; }
|
||||
bool before_last_checkpoint()const;
|
||||
|
||||
void check_tansaction_for_duplicated_operations(const signed_transaction& trx);
|
||||
|
||||
bool push_block( const signed_block& b, uint32_t skip = skip_nothing );
|
||||
processed_transaction push_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing );
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/protocol/event.hpp>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
|
||||
|
|
@ -158,5 +157,5 @@ typedef generic_index<event_object, event_object_multi_index_type> event_object_
|
|||
return s;
|
||||
}
|
||||
} } // graphene::chain
|
||||
FC_REFLECT(graphene::chain::event_object, (name)(season)(start_time)(event_group_id)(at_least_one_betting_market_group_settled)(scores))
|
||||
FC_REFLECT(graphene::chain::event_object, (name))
|
||||
|
||||
|
|
|
|||
|
|
@ -481,13 +481,13 @@ FC_REFLECT( graphene::chain::bet_place_operation,
|
|||
(fee)(bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(back_or_lay)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_matched_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::bet_matched_operation, (fee)(bettor_id)(bet_id)(amount_bet)(backer_multiplier)(guaranteed_winnings_returned) )
|
||||
FC_REFLECT( graphene::chain::bet_matched_operation, (bettor_id)(bet_id)(amount_bet)(backer_multiplier)(guaranteed_winnings_returned) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_cancel_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::bet_cancel_operation, (fee) (bettor_id) (bet_to_cancel) (extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_canceled_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::bet_canceled_operation, (fee)(bettor_id)(bet_id)(stake_returned) )
|
||||
FC_REFLECT( graphene::chain::bet_canceled_operation, (bettor_id)(bet_id)(stake_returned) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_adjusted_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::bet_adjusted_operation, (fee) (bettor_id)(bet_id)(stake_returned) )
|
||||
FC_REFLECT( graphene::chain::bet_adjusted_operation, (bettor_id)(bet_id)(stake_returned) )
|
||||
|
|
|
|||
|
|
@ -71,12 +71,6 @@ namespace graphene { namespace chain {
|
|||
FC_ASSERT( itr != parameters.end() );
|
||||
return itr->template get<typename Operation::fee_parameters_type>();
|
||||
}
|
||||
template<typename Operation>
|
||||
const bool exists()const
|
||||
{
|
||||
auto itr = parameters.find(typename Operation::fee_parameters_type());
|
||||
return itr != parameters.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @note must be sorted by fee_parameters.which() and have no duplicates
|
||||
|
|
|
|||
|
|
@ -46,34 +46,13 @@ struct proposal_operation_hardfork_visitor
|
|||
void operator()(const T &v) const {}
|
||||
|
||||
void operator()(const committee_member_update_global_parameters_operation &op) const {
|
||||
if( block_time < HARDFORK_1000_TIME ) { // TODO: remove after hf
|
||||
if( block_time < HARDFORK_1000_TIME ) // TODO: remove after hf
|
||||
FC_ASSERT( !op.new_parameters.extensions.value.min_bet_multiplier.valid()
|
||||
&& !op.new_parameters.extensions.value.max_bet_multiplier.valid()
|
||||
&& !op.new_parameters.extensions.value.betting_rake_fee_percentage.valid()
|
||||
&& !op.new_parameters.extensions.value.permitted_betting_odds_increments.valid()
|
||||
&& !op.new_parameters.extensions.value.live_betting_delay_time.valid(),
|
||||
"Parameter extensions are not allowed yet!" );
|
||||
FC_ASSERT( !op.new_parameters.current_fees->exists<sport_create_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<sport_update_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<sport_delete_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<event_group_create_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<event_group_update_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<event_group_delete_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<event_create_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<event_update_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<betting_market_rules_create_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<betting_market_rules_update_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<betting_market_group_create_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<betting_market_create_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<bet_place_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<betting_market_group_resolve_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<betting_market_group_cancel_unmatched_bets_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<bet_cancel_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<betting_market_group_update_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<betting_market_update_operation>()
|
||||
&& !op.new_parameters.current_fees->exists<event_update_status_operation>(),
|
||||
"Bookie-specific operations not available before HARDFORK_1000_TIME" );
|
||||
}
|
||||
&& !op.new_parameters.extensions.value.max_bet_multiplier.valid()
|
||||
&& !op.new_parameters.extensions.value.betting_rake_fee_percentage.valid()
|
||||
&& !op.new_parameters.extensions.value.permitted_betting_odds_increments.valid()
|
||||
&& !op.new_parameters.extensions.value.live_betting_delay_time.valid(),
|
||||
"Parameter extensions are not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const graphene::chain::tournament_payout_operation &o) const {
|
||||
|
|
@ -92,18 +71,10 @@ struct proposal_operation_hardfork_visitor
|
|||
FC_ASSERT( !aco.extensions.value.affiliate_distributions.valid(), "Affiliate reward distributions not allowed yet" );
|
||||
}
|
||||
|
||||
void operator()(const sport_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "sport_create_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const sport_update_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "sport_update_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const sport_delete_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "sport_delete_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const event_group_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_group_create_operation not allowed yet!" );
|
||||
}
|
||||
|
|
@ -112,10 +83,6 @@ struct proposal_operation_hardfork_visitor
|
|||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_group_update_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const event_group_delete_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_group_delete_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const event_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_create_operation not allowed yet!" );
|
||||
}
|
||||
|
|
@ -153,7 +120,7 @@ struct proposal_operation_hardfork_visitor
|
|||
}
|
||||
|
||||
void operator()(const bet_cancel_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "bet_cancel_operation not allowed yet!" );
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_group_resolve_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const betting_market_group_update_operation &v) const {
|
||||
|
|
|
|||
35
libraries/chain/protocol/competitor.cpp
Normal file
35
libraries/chain/protocol/competitor.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/protocol/competitor.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void competitor_create_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
|
|
@ -3011,7 +3011,7 @@ boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
|
|||
std::cout << "Random number generator seeded to " << time(NULL) << std::endl;
|
||||
|
||||
// betting operations don't take effect until HARDFORK 1000
|
||||
GRAPHENE_TESTING_GENESIS_TIMESTAMP = HARDFORK_1000_TIME.sec_since_epoch() + 10;
|
||||
GRAPHENE_TESTING_GENESIS_TIMESTAMP = HARDFORK_1000_TIME.sec_since_epoch() + 2;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,10 +170,8 @@ string database_fixture::generate_anon_acct_name()
|
|||
void database_fixture::verify_asset_supplies( const database& db )
|
||||
{
|
||||
//wlog("*** Begin asset supply verification ***");
|
||||
|
||||
// It seems peerplays by default DO have core fee pool in genesis so commenting this out
|
||||
//const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db);
|
||||
//BOOST_CHECK(core_asset_data.fee_pool == 0);
|
||||
const asset_dynamic_data_object& core_asset_data = db.get_core_asset().dynamic_asset_data_id(db);
|
||||
BOOST_CHECK(core_asset_data.fee_pool == 0);
|
||||
|
||||
const simple_index<account_statistics_object>& statistics_index = db.get_index_type<simple_index<account_statistics_object>>();
|
||||
const auto& balance_index = db.get_index_type<account_balance_index>().indices();
|
||||
|
|
|
|||
|
|
@ -1,659 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018 oxarbitrage 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 "../common/database_fixture.hpp"
|
||||
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::chain::test;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( dividend_tests, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( create_dividend_uia )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
BOOST_TEST_MESSAGE("Creating dividend holder asset");
|
||||
{
|
||||
asset_create_operation creator;
|
||||
creator.issuer = account_id_type();
|
||||
creator.fee = asset();
|
||||
creator.symbol = "DIVIDEND";
|
||||
creator.common_options.max_supply = 100000000;
|
||||
creator.precision = 2;
|
||||
creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/
|
||||
creator.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;
|
||||
creator.common_options.flags = charge_market_fee;
|
||||
creator.common_options.core_exchange_rate = price({asset(2),asset(1,asset_id_type(1))});
|
||||
trx.operations.push_back(std::move(creator));
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("Creating test accounts");
|
||||
create_account("alice");
|
||||
create_account("bob");
|
||||
create_account("carol");
|
||||
create_account("dave");
|
||||
create_account("frank");
|
||||
|
||||
BOOST_TEST_MESSAGE("Creating test asset");
|
||||
{
|
||||
asset_create_operation creator;
|
||||
creator.issuer = account_id_type();
|
||||
creator.fee = asset();
|
||||
creator.symbol = "TESTB"; //cant use TEST
|
||||
creator.common_options.max_supply = 100000000;
|
||||
creator.precision = 2;
|
||||
creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/
|
||||
creator.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;
|
||||
creator.common_options.flags = charge_market_fee;
|
||||
creator.common_options.core_exchange_rate = price({asset(2),asset(1,asset_id_type(1))});
|
||||
trx.operations.push_back(std::move(creator));
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Funding asset fee pool");
|
||||
{
|
||||
asset_fund_fee_pool_operation fund_op;
|
||||
fund_op.from_account = account_id_type();
|
||||
fund_op.asset_id = get_asset("TESTB").id;
|
||||
fund_op.amount = 500000000;
|
||||
trx.operations.push_back(std::move(fund_op));
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
|
||||
// our DIVIDEND asset should not yet be a divdend asset
|
||||
const auto& dividend_holder_asset_object = get_asset("DIVIDEND");
|
||||
BOOST_CHECK(!dividend_holder_asset_object.dividend_data_id);
|
||||
|
||||
BOOST_TEST_MESSAGE("Converting the new asset to a dividend holder asset");
|
||||
{
|
||||
asset_update_dividend_operation op;
|
||||
op.issuer = dividend_holder_asset_object.issuer;
|
||||
op.asset_to_update = dividend_holder_asset_object.id;
|
||||
op.new_options.next_payout_time = db.head_block_time() + fc::minutes(1);
|
||||
op.new_options.payout_interval = 60 * 60 * 24 * 3;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Verifying the dividend holder asset options");
|
||||
BOOST_REQUIRE(dividend_holder_asset_object.dividend_data_id);
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
{
|
||||
BOOST_REQUIRE(dividend_data.options.payout_interval);
|
||||
BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 60 * 60 * 24 * 3);
|
||||
}
|
||||
|
||||
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
|
||||
BOOST_CHECK_EQUAL(dividend_distribution_account.name, "dividend-dividend-distribution");
|
||||
|
||||
// db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
// {
|
||||
// _gpo.parameters.current_fees->get<asset_dividend_distribution_operation>().distribution_base_fee = 100;
|
||||
// _gpo.parameters.current_fees->get<asset_dividend_distribution_operation>().distribution_fee_per_holder = 100;
|
||||
// } );
|
||||
|
||||
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_update_dividend_interval )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
INVOKE( create_dividend_uia );
|
||||
|
||||
const auto& dividend_holder_asset_object = get_asset("DIVIDEND");
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
|
||||
auto advance_to_next_payout_time = [&]() {
|
||||
// Advance to the next upcoming payout time
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
// generate blocks up to the next scheduled time
|
||||
generate_blocks(next_payout_scheduled_time);
|
||||
// if the scheduled time fell on a maintenance interval, then we should have paid out.
|
||||
// if not, we need to advance to the next maintenance interval to trigger the payout
|
||||
if (dividend_data.options.next_payout_time)
|
||||
{
|
||||
// we know there was a next_payout_time set when we entered this, so if
|
||||
// it has been cleared, we must have already processed payouts, no need to
|
||||
// further advance time.
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
if (*dividend_data.options.next_payout_time == next_payout_scheduled_time)
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_TEST_MESSAGE("Updating the payout interval");
|
||||
{
|
||||
asset_update_dividend_operation op;
|
||||
op.issuer = dividend_holder_asset_object.issuer;
|
||||
op.asset_to_update = dividend_holder_asset_object.id;
|
||||
op.new_options.next_payout_time = fc::time_point::now() + fc::minutes(1);
|
||||
op.new_options.payout_interval = 60 * 60 * 24; // 1 days
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Verifying the updated dividend holder asset options");
|
||||
{
|
||||
BOOST_REQUIRE(dividend_data.options.payout_interval);
|
||||
BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 60 * 60 * 24);
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("Removing the payout interval");
|
||||
{
|
||||
asset_update_dividend_operation op;
|
||||
op.issuer = dividend_holder_asset_object.issuer;
|
||||
op.asset_to_update = dividend_holder_asset_object.id;
|
||||
op.new_options.next_payout_time = dividend_data.options.next_payout_time;
|
||||
op.new_options.payout_interval = fc::optional<uint32_t>();
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
BOOST_CHECK(!dividend_data.options.payout_interval);
|
||||
advance_to_next_payout_time();
|
||||
BOOST_REQUIRE_MESSAGE(!dividend_data.options.next_payout_time, "A new payout was scheduled, but none should have been");
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
INVOKE( create_dividend_uia );
|
||||
|
||||
const auto& dividend_holder_asset_object = get_asset("DIVIDEND");
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
|
||||
const account_object& alice = get_account("alice");
|
||||
const account_object& bob = get_account("bob");
|
||||
const account_object& carol = get_account("carol");
|
||||
const account_object& dave = get_account("dave");
|
||||
const account_object& frank = get_account("frank");
|
||||
const auto& test_asset_object = get_asset("TESTB");
|
||||
|
||||
auto issue_asset_to_account = [&](const asset_object& asset_to_issue, const account_object& destination_account, int64_t amount_to_issue)
|
||||
{
|
||||
asset_issue_operation op;
|
||||
op.issuer = asset_to_issue.issuer;
|
||||
op.asset_to_issue = asset(amount_to_issue, asset_to_issue.id);
|
||||
op.issue_to_account = destination_account.id;
|
||||
trx.operations.push_back( op );
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
};
|
||||
|
||||
auto verify_pending_balance = [&](const account_object& holder_account_obj, const asset_object& payout_asset_obj, int64_t expected_balance) {
|
||||
int64_t pending_balance = get_dividend_pending_payout_balance(dividend_holder_asset_object.id,
|
||||
holder_account_obj.id,
|
||||
payout_asset_obj.id);
|
||||
BOOST_CHECK_EQUAL(pending_balance, expected_balance);
|
||||
};
|
||||
|
||||
auto advance_to_next_payout_time = [&]() {
|
||||
// Advance to the next upcoming payout time
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
// generate blocks up to the next scheduled time
|
||||
generate_blocks(next_payout_scheduled_time);
|
||||
// if the scheduled time fell on a maintenance interval, then we should have paid out.
|
||||
// if not, we need to advance to the next maintenance interval to trigger the payout
|
||||
if (dividend_data.options.next_payout_time)
|
||||
{
|
||||
// we know there was a next_payout_time set when we entered this, so if
|
||||
// it has been cleared, we must have already processed payouts, no need to
|
||||
// further advance time.
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
if (*dividend_data.options.next_payout_time == next_payout_scheduled_time)
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
}
|
||||
};
|
||||
|
||||
// the first test will be testing pending balances, so we need to hit a
|
||||
// maintenance interval that isn't the payout interval. Payout is
|
||||
// every 3 days, maintenance interval is every 1 day.
|
||||
advance_to_next_payout_time();
|
||||
|
||||
// Set up the first test, issue alice, bob, and carol each 100 DIVIDEND.
|
||||
// Then deposit 300 TEST in the distribution account, and see that they
|
||||
// each are credited 100 TEST.
|
||||
issue_asset_to_account(dividend_holder_asset_object, alice, 100000);
|
||||
issue_asset_to_account(dividend_holder_asset_object, bob, 100000);
|
||||
issue_asset_to_account(dividend_holder_asset_object, carol, 100000);
|
||||
|
||||
BOOST_TEST_MESSAGE("Issuing 300 TEST to the dividend account");
|
||||
issue_asset_to_account(test_asset_object, dividend_distribution_account, 30000);
|
||||
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" );
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
|
||||
verify_pending_balance(alice, test_asset_object, 10000);
|
||||
verify_pending_balance(bob, test_asset_object, 10000);
|
||||
verify_pending_balance(carol, test_asset_object, 10000);
|
||||
|
||||
// For the second test, issue carol more than the other two, so it's
|
||||
// alice: 100 DIVIDND, bob: 100 DIVIDEND, carol: 200 DIVIDEND
|
||||
// Then deposit 400 TEST in the distribution account, and see that alice
|
||||
// and bob are credited with 100 TEST, and carol gets 200 TEST
|
||||
BOOST_TEST_MESSAGE("Issuing carol twice as much of the holder asset");
|
||||
issue_asset_to_account(dividend_holder_asset_object, carol, 100000); // one thousand at two digits of precision
|
||||
issue_asset_to_account(test_asset_object, dividend_distribution_account, 40000); // one thousand at two digits of precision
|
||||
BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" );
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
verify_pending_balance(alice, test_asset_object, 20000);
|
||||
verify_pending_balance(bob, test_asset_object, 20000);
|
||||
verify_pending_balance(carol, test_asset_object, 30000);
|
||||
|
||||
fc::time_point_sec old_next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
advance_to_next_payout_time();
|
||||
|
||||
|
||||
BOOST_REQUIRE_MESSAGE(dividend_data.options.next_payout_time, "No new payout was scheduled");
|
||||
BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time != *dividend_data.options.next_payout_time,
|
||||
"New payout was scheduled for the same time as the last payout");
|
||||
BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time + *dividend_data.options.payout_interval == *dividend_data.options.next_payout_time,
|
||||
"New payout was not scheduled for the expected time");
|
||||
|
||||
auto verify_dividend_payout_operations = [&](const account_object& destination_account, const asset& expected_payout)
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Verifying the virtual op was created");
|
||||
const account_transaction_history_index& hist_idx = db.get_index_type<account_transaction_history_index>();
|
||||
auto account_history_range = hist_idx.indices().get<by_seq>().equal_range(boost::make_tuple(destination_account.id));
|
||||
BOOST_REQUIRE(account_history_range.first != account_history_range.second);
|
||||
const operation_history_object& history_object = std::prev(account_history_range.second)->operation_id(db);
|
||||
const asset_dividend_distribution_operation& distribution_operation = history_object.op.get<asset_dividend_distribution_operation>();
|
||||
BOOST_CHECK(distribution_operation.account_id == destination_account.id);
|
||||
BOOST_CHECK(std::find(distribution_operation.amounts.begin(), distribution_operation.amounts.end(), expected_payout)
|
||||
!= distribution_operation.amounts.end());
|
||||
};
|
||||
|
||||
BOOST_TEST_MESSAGE("Verifying the payouts");
|
||||
BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 20000);
|
||||
verify_dividend_payout_operations(alice, asset(20000, test_asset_object.id));
|
||||
verify_pending_balance(alice, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 20000);
|
||||
verify_dividend_payout_operations(bob, asset(20000, test_asset_object.id));
|
||||
verify_pending_balance(bob, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 30000);
|
||||
verify_dividend_payout_operations(carol, asset(30000, test_asset_object.id));
|
||||
verify_pending_balance(carol, test_asset_object, 0);
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution_to_core_asset )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
BOOST_TEST_MESSAGE("Creating test accounts");
|
||||
create_account("alice");
|
||||
create_account("bob");
|
||||
create_account("carol");
|
||||
create_account("dave");
|
||||
create_account("frank");
|
||||
|
||||
BOOST_TEST_MESSAGE("Creating test asset");
|
||||
{
|
||||
asset_create_operation creator;
|
||||
creator.issuer = account_id_type();
|
||||
creator.fee = asset();
|
||||
creator.symbol = "TESTB";
|
||||
creator.common_options.max_supply = 100000000;
|
||||
creator.precision = 2;
|
||||
creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/
|
||||
creator.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;
|
||||
creator.common_options.flags = charge_market_fee;
|
||||
creator.common_options.core_exchange_rate = price({asset(2),asset(1,asset_id_type(1))});
|
||||
trx.operations.push_back(std::move(creator));
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
const auto& dividend_holder_asset_object = asset_id_type(0)(db);
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
|
||||
const account_object& alice = get_account("alice");
|
||||
const account_object& bob = get_account("bob");
|
||||
const account_object& carol = get_account("carol");
|
||||
const account_object& dave = get_account("dave");
|
||||
const account_object& frank = get_account("frank");
|
||||
const auto& test_asset_object = get_asset("TESTB");
|
||||
|
||||
auto issue_asset_to_account = [&](const asset_object& asset_to_issue, const account_object& destination_account, int64_t amount_to_issue)
|
||||
{
|
||||
asset_issue_operation op;
|
||||
op.issuer = asset_to_issue.issuer;
|
||||
op.asset_to_issue = asset(amount_to_issue, asset_to_issue.id);
|
||||
op.issue_to_account = destination_account.id;
|
||||
trx.operations.push_back( op );
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
};
|
||||
|
||||
auto verify_pending_balance = [&](const account_object& holder_account_obj, const asset_object& payout_asset_obj, int64_t expected_balance) {
|
||||
int64_t pending_balance = get_dividend_pending_payout_balance(dividend_holder_asset_object.id,
|
||||
holder_account_obj.id,
|
||||
payout_asset_obj.id);
|
||||
BOOST_CHECK_EQUAL(pending_balance, expected_balance);
|
||||
};
|
||||
|
||||
auto advance_to_next_payout_time = [&]() {
|
||||
// Advance to the next upcoming payout time
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
idump((next_payout_scheduled_time));
|
||||
// generate blocks up to the next scheduled time
|
||||
generate_blocks(next_payout_scheduled_time);
|
||||
// if the scheduled time fell on a maintenance interval, then we should have paid out.
|
||||
// if not, we need to advance to the next maintenance interval to trigger the payout
|
||||
if (dividend_data.options.next_payout_time)
|
||||
{
|
||||
// we know there was a next_payout_time set when we entered this, so if
|
||||
// it has been cleared, we must have already processed payouts, no need to
|
||||
// further advance time.
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
if (*dividend_data.options.next_payout_time == next_payout_scheduled_time)
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
}
|
||||
idump((db.head_block_time()));
|
||||
};
|
||||
|
||||
// the first test will be testing pending balances, so we need to hit a
|
||||
// maintenance interval that isn't the payout interval. Payout is
|
||||
// every 3 days, maintenance interval is every 1 day.
|
||||
advance_to_next_payout_time();
|
||||
|
||||
// Set up the first test, issue alice, bob, and carol, and dave each 1/4 of the total
|
||||
// supply of the core asset.
|
||||
// Then deposit 400 TEST in the distribution account, and see that they
|
||||
// each are credited 100 TEST.
|
||||
transfer( committee_account(db), alice, asset( 250000000000000 ) );
|
||||
transfer( committee_account(db), bob, asset( 250000000000000 ) );
|
||||
transfer( committee_account(db), carol, asset( 250000000000000 ) );
|
||||
transfer( committee_account(db), dave, asset( 250000000000000 ) );
|
||||
|
||||
BOOST_TEST_MESSAGE("Issuing 300 TEST to the dividend account");
|
||||
issue_asset_to_account(test_asset_object, dividend_distribution_account, 40000);
|
||||
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" );
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
|
||||
verify_pending_balance(alice, test_asset_object, 10000);
|
||||
verify_pending_balance(bob, test_asset_object, 10000);
|
||||
verify_pending_balance(carol, test_asset_object, 10000);
|
||||
verify_pending_balance(dave, test_asset_object, 10000);
|
||||
|
||||
// For the second test, issue dave more than the other two, so it's
|
||||
// alice: 1/5 CORE, bob: 1/5 CORE, carol: 1/5 CORE, dave: 2/5 CORE
|
||||
// Then deposit 500 TEST in the distribution account, and see that alice
|
||||
// bob, and carol are credited with 100 TEST, and dave gets 200 TEST
|
||||
BOOST_TEST_MESSAGE("Issuing dave twice as much of the holder asset");
|
||||
transfer( alice, dave, asset( 50000000000000 ) );
|
||||
transfer( bob, dave, asset( 50000000000000 ) );
|
||||
transfer( carol, dave, asset( 50000000000000 ) );
|
||||
issue_asset_to_account(test_asset_object, dividend_distribution_account, 50000); // 500 at two digits of precision
|
||||
BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" );
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
verify_pending_balance(alice, test_asset_object, 20000);
|
||||
verify_pending_balance(bob, test_asset_object, 20000);
|
||||
verify_pending_balance(carol, test_asset_object, 20000);
|
||||
verify_pending_balance(dave, test_asset_object, 30000);
|
||||
|
||||
fc::time_point_sec old_next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
advance_to_next_payout_time();
|
||||
|
||||
|
||||
BOOST_REQUIRE_MESSAGE(dividend_data.options.next_payout_time, "No new payout was scheduled");
|
||||
BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time != *dividend_data.options.next_payout_time,
|
||||
"New payout was scheduled for the same time as the last payout");
|
||||
BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time + *dividend_data.options.payout_interval == *dividend_data.options.next_payout_time,
|
||||
"New payout was not scheduled for the expected time");
|
||||
|
||||
auto verify_dividend_payout_operations = [&](const account_object& destination_account, const asset& expected_payout)
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Verifying the virtual op was created");
|
||||
const account_transaction_history_index& hist_idx = db.get_index_type<account_transaction_history_index>();
|
||||
auto account_history_range = hist_idx.indices().get<by_seq>().equal_range(boost::make_tuple(destination_account.id));
|
||||
BOOST_REQUIRE(account_history_range.first != account_history_range.second);
|
||||
const operation_history_object& history_object = std::prev(account_history_range.second)->operation_id(db);
|
||||
const asset_dividend_distribution_operation& distribution_operation = history_object.op.get<asset_dividend_distribution_operation>();
|
||||
BOOST_CHECK(distribution_operation.account_id == destination_account.id);
|
||||
BOOST_CHECK(std::find(distribution_operation.amounts.begin(), distribution_operation.amounts.end(), expected_payout)
|
||||
!= distribution_operation.amounts.end());
|
||||
};
|
||||
|
||||
BOOST_TEST_MESSAGE("Verifying the payouts");
|
||||
BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 20000);
|
||||
verify_dividend_payout_operations(alice, asset(20000, test_asset_object.id));
|
||||
verify_pending_balance(alice, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 20000);
|
||||
verify_dividend_payout_operations(bob, asset(20000, test_asset_object.id));
|
||||
verify_pending_balance(bob, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 20000);
|
||||
verify_dividend_payout_operations(carol, asset(20000, test_asset_object.id));
|
||||
verify_pending_balance(carol, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(dave, test_asset_object), 30000);
|
||||
verify_dividend_payout_operations(dave, asset(30000, test_asset_object.id));
|
||||
verify_pending_balance(dave, test_asset_object, 0);
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_dividend_distribution_interval )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
INVOKE( create_dividend_uia );
|
||||
|
||||
const auto& dividend_holder_asset_object = get_asset("DIVIDEND");
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
|
||||
const account_object& alice = get_account("alice");
|
||||
const account_object& bob = get_account("bob");
|
||||
const account_object& carol = get_account("carol");
|
||||
const account_object& dave = get_account("dave");
|
||||
const account_object& frank = get_account("frank");
|
||||
const auto& test_asset_object = get_asset("TESTB");
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_dividend_corner_cases )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
INVOKE( create_dividend_uia );
|
||||
|
||||
const auto& dividend_holder_asset_object = get_asset("DIVIDEND");
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
|
||||
const account_object& alice = get_account("alice");
|
||||
const account_object& bob = get_account("bob");
|
||||
const account_object& carol = get_account("carol");
|
||||
const account_object& dave = get_account("dave");
|
||||
const account_object& frank = get_account("frank");
|
||||
const auto& test_asset_object = get_asset("TESTB");
|
||||
|
||||
auto issue_asset_to_account = [&](const asset_object& asset_to_issue, const account_object& destination_account, int64_t amount_to_issue)
|
||||
{
|
||||
asset_issue_operation op;
|
||||
op.issuer = asset_to_issue.issuer;
|
||||
op.asset_to_issue = asset(amount_to_issue, asset_to_issue.id);
|
||||
op.issue_to_account = destination_account.id;
|
||||
trx.operations.push_back( op );
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
};
|
||||
|
||||
auto verify_pending_balance = [&](const account_object& holder_account_obj, const asset_object& payout_asset_obj, int64_t expected_balance) {
|
||||
int64_t pending_balance = get_dividend_pending_payout_balance(dividend_holder_asset_object.id,
|
||||
holder_account_obj.id,
|
||||
payout_asset_obj.id);
|
||||
BOOST_CHECK_EQUAL(pending_balance, expected_balance);
|
||||
};
|
||||
|
||||
auto reserve_asset_from_account = [&](const asset_object& asset_to_reserve, const account_object& from_account, int64_t amount_to_reserve)
|
||||
{
|
||||
asset_reserve_operation reserve_op;
|
||||
reserve_op.payer = from_account.id;
|
||||
reserve_op.amount_to_reserve = asset(amount_to_reserve, asset_to_reserve.id);
|
||||
trx.operations.push_back(reserve_op);
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
};
|
||||
auto advance_to_next_payout_time = [&]() {
|
||||
// Advance to the next upcoming payout time
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
// generate blocks up to the next scheduled time
|
||||
generate_blocks(next_payout_scheduled_time);
|
||||
// if the scheduled time fell on a maintenance interval, then we should have paid out.
|
||||
// if not, we need to advance to the next maintenance interval to trigger the payout
|
||||
if (dividend_data.options.next_payout_time)
|
||||
{
|
||||
// we know there was a next_payout_time set when we entered this, so if
|
||||
// it has been cleared, we must have already processed payouts, no need to
|
||||
// further advance time.
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
if (*dividend_data.options.next_payout_time == next_payout_scheduled_time)
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
}
|
||||
};
|
||||
|
||||
// the first test will be testing pending balances, so we need to hit a
|
||||
// maintenance interval that isn't the payout interval. Payout is
|
||||
// every 3 days, maintenance interval is every 1 day.
|
||||
advance_to_next_payout_time();
|
||||
|
||||
BOOST_TEST_MESSAGE("Testing a payout interval when there are no users holding the dividend asset");
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0);
|
||||
issue_asset_to_account(test_asset_object, dividend_distribution_account, 1000);
|
||||
BOOST_TEST_MESSAGE("Generating blocks until next maintenance interval");
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
BOOST_TEST_MESSAGE("Verify that no pending payments were scheduled");
|
||||
verify_pending_balance(alice, test_asset_object, 0);
|
||||
verify_pending_balance(bob, test_asset_object, 0);
|
||||
verify_pending_balance(carol, test_asset_object, 0);
|
||||
advance_to_next_payout_time();
|
||||
BOOST_TEST_MESSAGE("Verify that no actual payments took place");
|
||||
verify_pending_balance(alice, test_asset_object, 0);
|
||||
verify_pending_balance(bob, test_asset_object, 0);
|
||||
verify_pending_balance(carol, test_asset_object, 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, test_asset_object), 1000);
|
||||
|
||||
BOOST_TEST_MESSAGE("Now give alice a small balance and see that she takes it all");
|
||||
issue_asset_to_account(dividend_holder_asset_object, alice, 1);
|
||||
generate_block();
|
||||
BOOST_TEST_MESSAGE("Generating blocks until next maintenance interval");
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
BOOST_TEST_MESSAGE("Verify that no alice received her payment of the entire amount");
|
||||
verify_pending_balance(alice, test_asset_object, 1000);
|
||||
|
||||
// Test that we can pay out the dividend asset itself
|
||||
issue_asset_to_account(dividend_holder_asset_object, bob, 1);
|
||||
issue_asset_to_account(dividend_holder_asset_object, carol, 1);
|
||||
issue_asset_to_account(dividend_holder_asset_object, dividend_distribution_account, 300);
|
||||
generate_block();
|
||||
BOOST_CHECK_EQUAL(get_balance(alice, dividend_holder_asset_object), 1);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 1);
|
||||
BOOST_CHECK_EQUAL(get_balance(carol, dividend_holder_asset_object), 1);
|
||||
BOOST_TEST_MESSAGE("Generating blocks until next maintenance interval");
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
BOOST_TEST_MESSAGE("Verify that the dividend asset was shared out");
|
||||
verify_pending_balance(alice, dividend_holder_asset_object, 100);
|
||||
verify_pending_balance(bob, dividend_holder_asset_object, 100);
|
||||
verify_pending_balance(carol, dividend_holder_asset_object, 100);
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
@ -9,9 +9,7 @@
|
|||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/protocol/committee_member.hpp>
|
||||
#include <graphene/chain/protocol/sport.hpp>
|
||||
#include <fc/crypto/digest.hpp>
|
||||
#include <graphene/app/database_api.hpp>
|
||||
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
|
|
@ -30,27 +28,6 @@ namespace
|
|||
return transfer;
|
||||
}
|
||||
|
||||
sport_create_operation make_sport_create_operation(std::string s1, std::string s2)
|
||||
{
|
||||
sport_create_operation op;
|
||||
op.name = {{ s1, s2 }};
|
||||
return op;
|
||||
}
|
||||
|
||||
betting_market_group_create_operation make_betting_market_group_create(string s1, string s2)
|
||||
{
|
||||
betting_market_group_create_operation op;
|
||||
op.description = {{ s1, s2 }};
|
||||
return op;
|
||||
}
|
||||
|
||||
betting_market_create_operation make_betting_market_operation(string s1, string s2)
|
||||
{
|
||||
betting_market_create_operation op;
|
||||
op.description = {{ s1, s2 }};
|
||||
return op;
|
||||
}
|
||||
|
||||
committee_member_create_operation make_committee_member_create_operation(const asset& fee, const account_id_type& member, const string& url)
|
||||
{
|
||||
committee_member_create_operation member_create_operation;
|
||||
|
|
@ -71,7 +48,6 @@ namespace
|
|||
fixture.db.create<proposal_object>([&](proposal_object& proposal)
|
||||
{
|
||||
proposal.proposed_transaction = transaction;
|
||||
proposal.required_active_approvals = { GRAPHENE_WITNESS_ACCOUNT };
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -112,16 +88,18 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( check_transaction_for_duplicated_operations, database_fixture )
|
||||
BOOST_FIXTURE_TEST_SUITE( check_tansaction_for_duplicated_operations, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_exception_throwing_for_the_same_operation_proposed_for_witness_twice )
|
||||
BOOST_AUTO_TEST_CASE( test_exception_throwing_for_the_same_operation_proposed_twice )
|
||||
{
|
||||
try
|
||||
{
|
||||
create_proposal(*this, {make_sport_create_operation("SPORT1", "S1")});
|
||||
ACTORS((alice))
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_sport_create_operation("SPORT1", "S1")} );
|
||||
BOOST_CHECK_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx), fc::exception);
|
||||
create_proposal(*this, {make_transfer_operation(account_id_type(), alice_id, asset(500))});
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_transfer_operation(account_id_type(), alice_id, asset(500))});
|
||||
BOOST_CHECK_THROW(db.check_tansaction_for_duplicated_operations(trx), fc::exception);
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -134,8 +112,10 @@ BOOST_AUTO_TEST_CASE( check_passes_without_duplication )
|
|||
{
|
||||
try
|
||||
{
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_sport_create_operation("SPORT1", "S1")});
|
||||
BOOST_CHECK_NO_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx));
|
||||
ACTORS((alice))
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_transfer_operation(account_id_type(), alice_id, asset(500))});
|
||||
BOOST_CHECK_NO_THROW(db.check_tansaction_for_duplicated_operations(trx));
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -144,14 +124,16 @@ BOOST_AUTO_TEST_CASE( check_passes_without_duplication )
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_passes_for_the_same_operation_with_different_names )
|
||||
BOOST_AUTO_TEST_CASE( check_passes_for_the_same_operation_with_different_assets )
|
||||
{
|
||||
try
|
||||
{
|
||||
create_proposal(*this, {make_sport_create_operation("SPORT1", "S1")});
|
||||
ACTORS((alice))
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_sport_create_operation("SPORT2", "S2")});
|
||||
BOOST_CHECK_NO_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx));
|
||||
create_proposal(*this, {make_transfer_operation(account_id_type(), alice_id, asset(500))});
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_transfer_operation(account_id_type(), alice_id, asset(501))});
|
||||
BOOST_CHECK_NO_THROW(db.check_tansaction_for_duplicated_operations(trx));
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -164,11 +146,13 @@ BOOST_AUTO_TEST_CASE( check_fails_for_duplication_in_transaction_with_several_op
|
|||
{
|
||||
try
|
||||
{
|
||||
create_proposal(*this, {make_sport_create_operation("SPORT1", "S1")});
|
||||
ACTORS((alice))
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_sport_create_operation("SPORT2", "S2"),
|
||||
make_sport_create_operation("SPORT1", "S1") }); //duplicated one
|
||||
BOOST_CHECK_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx), fc::exception);
|
||||
create_proposal(*this, {make_transfer_operation(account_id_type(), alice_id, asset(500))});
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_transfer_operation(account_id_type(), alice_id, asset(501)),
|
||||
make_transfer_operation(account_id_type(), alice_id, asset(500))}); //duplicated one
|
||||
BOOST_CHECK_THROW(db.check_tansaction_for_duplicated_operations(trx), fc::exception);
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -181,12 +165,14 @@ BOOST_AUTO_TEST_CASE( check_fails_for_duplicated_operation_in_existed_proposal_w
|
|||
{
|
||||
try
|
||||
{
|
||||
create_proposal(*this, {make_sport_create_operation("SPORT1", "S1"),
|
||||
make_sport_create_operation("SPORT2", "S2") }); //duplicated one
|
||||
ACTORS((alice))
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_sport_create_operation("SPORT3", "S3"),
|
||||
make_sport_create_operation("SPORT2", "S2")}); //duplicated one
|
||||
BOOST_CHECK_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx), fc::exception);
|
||||
create_proposal(*this, {make_transfer_operation(account_id_type(), alice_id, asset(499)),
|
||||
make_transfer_operation(account_id_type(), alice_id, asset(500))}); //duplicated one
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_transfer_operation(account_id_type(), alice_id, asset(501)),
|
||||
make_transfer_operation(account_id_type(), alice_id, asset(500))}); //duplicated one
|
||||
BOOST_CHECK_THROW(db.check_tansaction_for_duplicated_operations(trx), fc::exception);
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -199,11 +185,13 @@ BOOST_AUTO_TEST_CASE( check_fails_for_duplicated_operation_in_existed_proposal_w
|
|||
{
|
||||
try
|
||||
{
|
||||
create_proposal(*this, {make_sport_create_operation("SPORT1", "S1"),
|
||||
make_sport_create_operation("SPORT2", "S2")}); //duplicated one
|
||||
ACTORS((alice))
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_sport_create_operation("SPORT2", "S2")}); //duplicated one
|
||||
BOOST_CHECK_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx), fc::exception);
|
||||
create_proposal(*this, {make_transfer_operation(account_id_type(), alice_id, asset(499)),
|
||||
make_transfer_operation(account_id_type(), alice_id, asset(500))}); //duplicated one
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_transfer_operation(account_id_type(), alice_id, asset(500))}); //duplicated one
|
||||
BOOST_CHECK_THROW(db.check_tansaction_for_duplicated_operations(trx), fc::exception);
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -216,12 +204,12 @@ BOOST_AUTO_TEST_CASE( check_passes_for_different_operations_types )
|
|||
{
|
||||
try
|
||||
{
|
||||
ACTOR( alice );
|
||||
ACTORS((alice))
|
||||
|
||||
create_proposal(*this, {make_transfer_operation(account_id_type(), alice_id, asset(500))});
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_committee_member_create_operation(asset(1000), account_id_type(), "test url")});
|
||||
BOOST_CHECK_NO_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx));
|
||||
BOOST_CHECK_NO_THROW(db.check_tansaction_for_duplicated_operations(trx));
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -237,7 +225,7 @@ BOOST_AUTO_TEST_CASE( check_fails_for_same_member_create_operations )
|
|||
create_proposal(*this, {make_committee_member_create_operation(asset(1000), account_id_type(), "test url")});
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_committee_member_create_operation(asset(1000), account_id_type(), "test url")});
|
||||
BOOST_CHECK_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx), fc::exception);
|
||||
BOOST_CHECK_THROW(db.check_tansaction_for_duplicated_operations(trx), fc::exception);
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -253,7 +241,7 @@ BOOST_AUTO_TEST_CASE( check_passes_for_different_member_create_operations )
|
|||
create_proposal(*this, {make_committee_member_create_operation(asset(1000), account_id_type(), "test url")});
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_committee_member_create_operation(asset(1001), account_id_type(), "test url")});
|
||||
BOOST_CHECK_NO_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx));
|
||||
BOOST_CHECK_NO_THROW(db.check_tansaction_for_duplicated_operations(trx));
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -277,7 +265,7 @@ BOOST_AUTO_TEST_CASE( check_failes_for_several_operations_of_mixed_type )
|
|||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_transfer_operation(account_id_type(), alice_id, asset(501)), //duplicate
|
||||
make_committee_member_create_operation(asset(1002), account_id_type(), "test url")});
|
||||
|
||||
BOOST_CHECK_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx), fc::exception);
|
||||
BOOST_CHECK_THROW(db.check_tansaction_for_duplicated_operations(trx), fc::exception);
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -290,15 +278,20 @@ BOOST_AUTO_TEST_CASE( check_failes_for_duplicates_in_pending_transactions_list )
|
|||
{
|
||||
try
|
||||
{
|
||||
ACTOR( alice );
|
||||
ACTORS((alice))
|
||||
|
||||
auto duplicate = make_sport_create_operation("SPORT1", "S1");
|
||||
fc::ecc::private_key committee_key = init_account_priv_key;
|
||||
|
||||
push_proposal( *this, GRAPHENE_WITNESS_ACCOUNT(db), {duplicate} );
|
||||
const account_object& moneyman = create_account("moneyman", init_account_pub_key);
|
||||
const asset_object& core = asset_id_type()(db);
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation( *this, {duplicate} );
|
||||
transfer(account_id_type()(db), moneyman, core.amount(1000000));
|
||||
|
||||
BOOST_CHECK_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx), fc::exception);
|
||||
auto duplicate = make_transfer_operation(alice.id, moneyman.get_id(), asset(100));
|
||||
push_proposal(*this, moneyman, {duplicate});
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {duplicate});
|
||||
BOOST_CHECK_THROW(db.check_tansaction_for_duplicated_operations(trx), fc::exception);
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -323,7 +316,7 @@ BOOST_AUTO_TEST_CASE( check_passes_for_no_duplicates_in_pending_transactions_lis
|
|||
push_proposal(*this, moneyman, {make_transfer_operation(alice.id, moneyman.get_id(), asset(100))});
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {make_transfer_operation(alice.id, moneyman.get_id(), asset(101))});
|
||||
BOOST_CHECK_NO_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx));
|
||||
BOOST_CHECK_NO_THROW(db.check_tansaction_for_duplicated_operations(trx));
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -345,13 +338,13 @@ BOOST_AUTO_TEST_CASE( check_fails_for_several_transactions_with_duplicates_in_pe
|
|||
|
||||
transfer(account_id_type()(db), moneyman, core.amount(1000000));
|
||||
|
||||
auto duplicate = make_sport_create_operation("SPORT1", "S1");
|
||||
push_proposal(*this, moneyman, {make_sport_create_operation("SPORT2", "S2"), duplicate} );
|
||||
auto duplicate = make_transfer_operation(alice.id, moneyman.get_id(), asset(100));
|
||||
push_proposal(*this, moneyman, {make_transfer_operation(alice.id, moneyman.get_id(), asset(101)),
|
||||
duplicate});
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this,
|
||||
{duplicate, make_sport_create_operation("SPORT3", "S3")} );
|
||||
|
||||
BOOST_CHECK_THROW(graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx), fc::exception);
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {duplicate,
|
||||
make_transfer_operation(alice.id, moneyman.get_id(), asset(102))});
|
||||
BOOST_CHECK_THROW(db.check_tansaction_for_duplicated_operations(trx), fc::exception);
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
|
|
@ -360,80 +353,61 @@ BOOST_AUTO_TEST_CASE( check_fails_for_several_transactions_with_duplicates_in_pe
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_passes_for_duplicated_betting_market_group_create )
|
||||
BOOST_AUTO_TEST_CASE( check_passes_for_duplicated_betting_market_or_group )
|
||||
{
|
||||
try
|
||||
{
|
||||
auto duplicate = make_betting_market_group_create( "BMGROUP1", "BMG1" );
|
||||
generate_blocks( HARDFORK_1000_TIME + fc::seconds(300) );
|
||||
|
||||
create_proposal(*this, {duplicate} );
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {duplicate} );
|
||||
|
||||
BOOST_CHECK_NO_THROW( graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx) );
|
||||
}
|
||||
catch( const fc::exception &e )
|
||||
{
|
||||
edump( ( e.to_detail_string() ) );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_passes_for_duplicated_betting_market_create )
|
||||
{
|
||||
try
|
||||
{
|
||||
auto duplicate = make_betting_market_operation( "BMARKET1", "BM1" );
|
||||
|
||||
create_proposal( *this, {duplicate} );
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {duplicate} );
|
||||
|
||||
BOOST_CHECK_NO_THROW( graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx) );
|
||||
}
|
||||
catch( const fc::exception &e )
|
||||
{
|
||||
edump( ( e.to_detail_string() ) );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_passes_for_duplicated_betting_market_and_betting_market_group_create )
|
||||
{
|
||||
try
|
||||
{
|
||||
auto duplicate_market = make_betting_market_operation( "BMARKET1", "BM1" );
|
||||
auto duplicate_group = make_betting_market_group_create( "BMGROUP1", "BMG1" );
|
||||
const sport_id_type sport_id = create_sport( {{"SN","SPORT_NAME"}} ).id;
|
||||
const event_group_id_type event_group_id = create_event_group( {{"EG", "EVENT_GROUP"}}, sport_id ).id;
|
||||
const betting_market_rules_id_type betting_market_rules_id =
|
||||
create_betting_market_rules( {{"EN", "Rules"}}, {{"EN", "Some rules"}} ).id;
|
||||
|
||||
create_proposal( *this, {duplicate_market, duplicate_group} );
|
||||
event_create_operation evcop1;
|
||||
evcop1.event_group_id = event_group_id;
|
||||
evcop1.name = {{"NO", "NAME_ONE"}};
|
||||
evcop1.season = {{"NO", "NAME_ONE"}};
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {duplicate_market, duplicate_group} );
|
||||
event_create_operation evcop2;
|
||||
evcop2.event_group_id = event_group_id;
|
||||
evcop2.name = {{"NT", "NAME_TWO"}};
|
||||
evcop2.season = {{"NT", "NAME_TWO"}};
|
||||
|
||||
BOOST_CHECK_NO_THROW( graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx) );
|
||||
betting_market_group_create_operation bmgcop;
|
||||
bmgcop.description = {{"NN", "NO_NAME"}};
|
||||
bmgcop.event_id = object_id_type(relative_protocol_ids, 0, 0);
|
||||
bmgcop.rules_id = betting_market_rules_id;
|
||||
bmgcop.asset_id = asset_id_type();
|
||||
|
||||
betting_market_create_operation bmcop;
|
||||
bmcop.group_id = object_id_type(relative_protocol_ids, 0, 1);
|
||||
bmcop.payout_condition.insert( internationalized_string_type::value_type( "CN", "CONDI_NAME" ) );
|
||||
|
||||
proposal_create_operation pcop1 = proposal_create_operation::committee_proposal(
|
||||
db.get_global_properties().parameters,
|
||||
db.head_block_time()
|
||||
);
|
||||
pcop1.review_period_seconds.reset();
|
||||
|
||||
proposal_create_operation pcop2 = pcop1;
|
||||
|
||||
pcop1.proposed_ops.emplace_back( evcop1 );
|
||||
pcop1.proposed_ops.emplace_back( bmgcop );
|
||||
pcop1.proposed_ops.emplace_back( bmcop );
|
||||
|
||||
pcop2.proposed_ops.emplace_back( evcop2 );
|
||||
pcop2.proposed_ops.emplace_back( bmgcop );
|
||||
pcop2.proposed_ops.emplace_back( bmcop );
|
||||
|
||||
create_proposal(*this, { pcop1, pcop2 });
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, { pcop1, pcop2 });
|
||||
BOOST_CHECK_NO_THROW( db.check_tansaction_for_duplicated_operations(trx) );
|
||||
}
|
||||
catch( const fc::exception &e )
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
edump( ( e.to_detail_string() ) );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_passes_for_duplicated_betting_market_in_one_operation )
|
||||
{
|
||||
try
|
||||
{
|
||||
auto duplicate = make_betting_market_operation( "BMARKET1", "BM1" );
|
||||
|
||||
create_proposal( *this, {duplicate, duplicate} );
|
||||
|
||||
auto trx = make_signed_transaction_with_proposed_operation(*this, {duplicate, duplicate} );
|
||||
|
||||
BOOST_CHECK_NO_THROW( graphene::app::database_api(db).check_transaction_for_duplicated_operations(trx) );
|
||||
}
|
||||
catch( const fc::exception &e )
|
||||
{
|
||||
edump( ( e.to_detail_string() ) );
|
||||
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1112,6 +1112,633 @@ BOOST_AUTO_TEST_CASE( uia_fees )
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( dividend_tests, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( create_dividend_uia )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
BOOST_TEST_MESSAGE("Creating dividend holder asset");
|
||||
{
|
||||
asset_create_operation creator;
|
||||
creator.issuer = account_id_type();
|
||||
creator.fee = asset();
|
||||
creator.symbol = "DIVIDEND";
|
||||
creator.common_options.max_supply = 100000000;
|
||||
creator.precision = 2;
|
||||
creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/
|
||||
creator.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;
|
||||
creator.common_options.flags = charge_market_fee;
|
||||
creator.common_options.core_exchange_rate = price({asset(2),asset(1,asset_id_type(1))});
|
||||
trx.operations.push_back(std::move(creator));
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("Creating test accounts");
|
||||
create_account("alice");
|
||||
create_account("bob");
|
||||
create_account("carol");
|
||||
create_account("dave");
|
||||
create_account("frank");
|
||||
|
||||
BOOST_TEST_MESSAGE("Creating test asset");
|
||||
{
|
||||
asset_create_operation creator;
|
||||
creator.issuer = account_id_type();
|
||||
creator.fee = asset();
|
||||
creator.symbol = "TEST";
|
||||
creator.common_options.max_supply = 100000000;
|
||||
creator.precision = 2;
|
||||
creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/
|
||||
creator.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;
|
||||
creator.common_options.flags = charge_market_fee;
|
||||
creator.common_options.core_exchange_rate = price({asset(2),asset(1,asset_id_type(1))});
|
||||
trx.operations.push_back(std::move(creator));
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Funding asset fee pool");
|
||||
{
|
||||
asset_fund_fee_pool_operation fund_op;
|
||||
fund_op.from_account = account_id_type();
|
||||
fund_op.asset_id = get_asset("TEST").id;
|
||||
fund_op.amount = 500000000;
|
||||
trx.operations.push_back(std::move(fund_op));
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
|
||||
// our DIVIDEND asset should not yet be a divdend asset
|
||||
const auto& dividend_holder_asset_object = get_asset("DIVIDEND");
|
||||
BOOST_CHECK(!dividend_holder_asset_object.dividend_data_id);
|
||||
|
||||
BOOST_TEST_MESSAGE("Converting the new asset to a dividend holder asset");
|
||||
{
|
||||
asset_update_dividend_operation op;
|
||||
op.issuer = dividend_holder_asset_object.issuer;
|
||||
op.asset_to_update = dividend_holder_asset_object.id;
|
||||
op.new_options.next_payout_time = db.head_block_time() + fc::minutes(1);
|
||||
op.new_options.payout_interval = 60 * 60 * 24 * 3;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Verifying the dividend holder asset options");
|
||||
BOOST_REQUIRE(dividend_holder_asset_object.dividend_data_id);
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
{
|
||||
BOOST_REQUIRE(dividend_data.options.payout_interval);
|
||||
BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 60 * 60 * 24 * 3);
|
||||
}
|
||||
|
||||
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
|
||||
BOOST_CHECK_EQUAL(dividend_distribution_account.name, "dividend-dividend-distribution");
|
||||
|
||||
// db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
// {
|
||||
// _gpo.parameters.current_fees->get<asset_dividend_distribution_operation>().distribution_base_fee = 100;
|
||||
// _gpo.parameters.current_fees->get<asset_dividend_distribution_operation>().distribution_fee_per_holder = 100;
|
||||
// } );
|
||||
|
||||
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_update_dividend_interval )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
INVOKE( create_dividend_uia );
|
||||
|
||||
const auto& dividend_holder_asset_object = get_asset("DIVIDEND");
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
|
||||
auto advance_to_next_payout_time = [&]() {
|
||||
// Advance to the next upcoming payout time
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
// generate blocks up to the next scheduled time
|
||||
generate_blocks(next_payout_scheduled_time);
|
||||
// if the scheduled time fell on a maintenance interval, then we should have paid out.
|
||||
// if not, we need to advance to the next maintenance interval to trigger the payout
|
||||
if (dividend_data.options.next_payout_time)
|
||||
{
|
||||
// we know there was a next_payout_time set when we entered this, so if
|
||||
// it has been cleared, we must have already processed payouts, no need to
|
||||
// further advance time.
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
if (*dividend_data.options.next_payout_time == next_payout_scheduled_time)
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_TEST_MESSAGE("Updating the payout interval");
|
||||
{
|
||||
asset_update_dividend_operation op;
|
||||
op.issuer = dividend_holder_asset_object.issuer;
|
||||
op.asset_to_update = dividend_holder_asset_object.id;
|
||||
op.new_options.next_payout_time = fc::time_point::now() + fc::minutes(1);
|
||||
op.new_options.payout_interval = 60 * 60 * 24; // 1 days
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Verifying the updated dividend holder asset options");
|
||||
{
|
||||
BOOST_REQUIRE(dividend_data.options.payout_interval);
|
||||
BOOST_CHECK_EQUAL(*dividend_data.options.payout_interval, 60 * 60 * 24);
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("Removing the payout interval");
|
||||
{
|
||||
asset_update_dividend_operation op;
|
||||
op.issuer = dividend_holder_asset_object.issuer;
|
||||
op.asset_to_update = dividend_holder_asset_object.id;
|
||||
op.new_options.next_payout_time = dividend_data.options.next_payout_time;
|
||||
op.new_options.payout_interval = fc::optional<uint32_t>();
|
||||
trx.operations.push_back(op);
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
BOOST_CHECK(!dividend_data.options.payout_interval);
|
||||
advance_to_next_payout_time();
|
||||
BOOST_REQUIRE_MESSAGE(!dividend_data.options.next_payout_time, "A new payout was scheduled, but none should have been");
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
INVOKE( create_dividend_uia );
|
||||
|
||||
const auto& dividend_holder_asset_object = get_asset("DIVIDEND");
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
|
||||
const account_object& alice = get_account("alice");
|
||||
const account_object& bob = get_account("bob");
|
||||
const account_object& carol = get_account("carol");
|
||||
const account_object& dave = get_account("dave");
|
||||
const account_object& frank = get_account("frank");
|
||||
const auto& test_asset_object = get_asset("TEST");
|
||||
|
||||
auto issue_asset_to_account = [&](const asset_object& asset_to_issue, const account_object& destination_account, int64_t amount_to_issue)
|
||||
{
|
||||
asset_issue_operation op;
|
||||
op.issuer = asset_to_issue.issuer;
|
||||
op.asset_to_issue = asset(amount_to_issue, asset_to_issue.id);
|
||||
op.issue_to_account = destination_account.id;
|
||||
trx.operations.push_back( op );
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
};
|
||||
|
||||
auto verify_pending_balance = [&](const account_object& holder_account_obj, const asset_object& payout_asset_obj, int64_t expected_balance) {
|
||||
int64_t pending_balance = get_dividend_pending_payout_balance(dividend_holder_asset_object.id,
|
||||
holder_account_obj.id,
|
||||
payout_asset_obj.id);
|
||||
BOOST_CHECK_EQUAL(pending_balance, expected_balance);
|
||||
};
|
||||
|
||||
auto advance_to_next_payout_time = [&]() {
|
||||
// Advance to the next upcoming payout time
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
// generate blocks up to the next scheduled time
|
||||
generate_blocks(next_payout_scheduled_time);
|
||||
// if the scheduled time fell on a maintenance interval, then we should have paid out.
|
||||
// if not, we need to advance to the next maintenance interval to trigger the payout
|
||||
if (dividend_data.options.next_payout_time)
|
||||
{
|
||||
// we know there was a next_payout_time set when we entered this, so if
|
||||
// it has been cleared, we must have already processed payouts, no need to
|
||||
// further advance time.
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
if (*dividend_data.options.next_payout_time == next_payout_scheduled_time)
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
}
|
||||
};
|
||||
|
||||
// the first test will be testing pending balances, so we need to hit a
|
||||
// maintenance interval that isn't the payout interval. Payout is
|
||||
// every 3 days, maintenance interval is every 1 day.
|
||||
advance_to_next_payout_time();
|
||||
|
||||
// Set up the first test, issue alice, bob, and carol each 100 DIVIDEND.
|
||||
// Then deposit 300 TEST in the distribution account, and see that they
|
||||
// each are credited 100 TEST.
|
||||
issue_asset_to_account(dividend_holder_asset_object, alice, 100000);
|
||||
issue_asset_to_account(dividend_holder_asset_object, bob, 100000);
|
||||
issue_asset_to_account(dividend_holder_asset_object, carol, 100000);
|
||||
|
||||
BOOST_TEST_MESSAGE("Issuing 300 TEST to the dividend account");
|
||||
issue_asset_to_account(test_asset_object, dividend_distribution_account, 30000);
|
||||
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" );
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
|
||||
verify_pending_balance(alice, test_asset_object, 10000);
|
||||
verify_pending_balance(bob, test_asset_object, 10000);
|
||||
verify_pending_balance(carol, test_asset_object, 10000);
|
||||
|
||||
// For the second test, issue carol more than the other two, so it's
|
||||
// alice: 100 DIVIDND, bob: 100 DIVIDEND, carol: 200 DIVIDEND
|
||||
// Then deposit 400 TEST in the distribution account, and see that alice
|
||||
// and bob are credited with 100 TEST, and carol gets 200 TEST
|
||||
BOOST_TEST_MESSAGE("Issuing carol twice as much of the holder asset");
|
||||
issue_asset_to_account(dividend_holder_asset_object, carol, 100000); // one thousand at two digits of precision
|
||||
issue_asset_to_account(test_asset_object, dividend_distribution_account, 40000); // one thousand at two digits of precision
|
||||
BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" );
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
verify_pending_balance(alice, test_asset_object, 20000);
|
||||
verify_pending_balance(bob, test_asset_object, 20000);
|
||||
verify_pending_balance(carol, test_asset_object, 30000);
|
||||
|
||||
fc::time_point_sec old_next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
advance_to_next_payout_time();
|
||||
|
||||
|
||||
BOOST_REQUIRE_MESSAGE(dividend_data.options.next_payout_time, "No new payout was scheduled");
|
||||
BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time != *dividend_data.options.next_payout_time,
|
||||
"New payout was scheduled for the same time as the last payout");
|
||||
BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time + *dividend_data.options.payout_interval == *dividend_data.options.next_payout_time,
|
||||
"New payout was not scheduled for the expected time");
|
||||
|
||||
auto verify_dividend_payout_operations = [&](const account_object& destination_account, const asset& expected_payout)
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Verifying the virtual op was created");
|
||||
const account_transaction_history_index& hist_idx = db.get_index_type<account_transaction_history_index>();
|
||||
auto account_history_range = hist_idx.indices().get<by_seq>().equal_range(boost::make_tuple(destination_account.id));
|
||||
BOOST_REQUIRE(account_history_range.first != account_history_range.second);
|
||||
const operation_history_object& history_object = std::prev(account_history_range.second)->operation_id(db);
|
||||
const asset_dividend_distribution_operation& distribution_operation = history_object.op.get<asset_dividend_distribution_operation>();
|
||||
BOOST_CHECK(distribution_operation.account_id == destination_account.id);
|
||||
BOOST_CHECK(std::find(distribution_operation.amounts.begin(), distribution_operation.amounts.end(), expected_payout)
|
||||
!= distribution_operation.amounts.end());
|
||||
};
|
||||
|
||||
BOOST_TEST_MESSAGE("Verifying the payouts");
|
||||
BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 20000);
|
||||
verify_dividend_payout_operations(alice, asset(20000, test_asset_object.id));
|
||||
verify_pending_balance(alice, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 20000);
|
||||
verify_dividend_payout_operations(bob, asset(20000, test_asset_object.id));
|
||||
verify_pending_balance(bob, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 30000);
|
||||
verify_dividend_payout_operations(carol, asset(30000, test_asset_object.id));
|
||||
verify_pending_balance(carol, test_asset_object, 0);
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution_to_core_asset )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
BOOST_TEST_MESSAGE("Creating test accounts");
|
||||
create_account("alice");
|
||||
create_account("bob");
|
||||
create_account("carol");
|
||||
create_account("dave");
|
||||
create_account("frank");
|
||||
|
||||
BOOST_TEST_MESSAGE("Creating test asset");
|
||||
{
|
||||
asset_create_operation creator;
|
||||
creator.issuer = account_id_type();
|
||||
creator.fee = asset();
|
||||
creator.symbol = "TEST";
|
||||
creator.common_options.max_supply = 100000000;
|
||||
creator.precision = 2;
|
||||
creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/
|
||||
creator.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;
|
||||
creator.common_options.flags = charge_market_fee;
|
||||
creator.common_options.core_exchange_rate = price({asset(2),asset(1,asset_id_type(1))});
|
||||
trx.operations.push_back(std::move(creator));
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
const auto& dividend_holder_asset_object = asset_id_type(0)(db);
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
|
||||
const account_object& alice = get_account("alice");
|
||||
const account_object& bob = get_account("bob");
|
||||
const account_object& carol = get_account("carol");
|
||||
const account_object& dave = get_account("dave");
|
||||
const account_object& frank = get_account("frank");
|
||||
const auto& test_asset_object = get_asset("TEST");
|
||||
|
||||
auto issue_asset_to_account = [&](const asset_object& asset_to_issue, const account_object& destination_account, int64_t amount_to_issue)
|
||||
{
|
||||
asset_issue_operation op;
|
||||
op.issuer = asset_to_issue.issuer;
|
||||
op.asset_to_issue = asset(amount_to_issue, asset_to_issue.id);
|
||||
op.issue_to_account = destination_account.id;
|
||||
trx.operations.push_back( op );
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
};
|
||||
|
||||
auto verify_pending_balance = [&](const account_object& holder_account_obj, const asset_object& payout_asset_obj, int64_t expected_balance) {
|
||||
int64_t pending_balance = get_dividend_pending_payout_balance(dividend_holder_asset_object.id,
|
||||
holder_account_obj.id,
|
||||
payout_asset_obj.id);
|
||||
BOOST_CHECK_EQUAL(pending_balance, expected_balance);
|
||||
};
|
||||
|
||||
auto advance_to_next_payout_time = [&]() {
|
||||
// Advance to the next upcoming payout time
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
idump((next_payout_scheduled_time));
|
||||
// generate blocks up to the next scheduled time
|
||||
generate_blocks(next_payout_scheduled_time);
|
||||
// if the scheduled time fell on a maintenance interval, then we should have paid out.
|
||||
// if not, we need to advance to the next maintenance interval to trigger the payout
|
||||
if (dividend_data.options.next_payout_time)
|
||||
{
|
||||
// we know there was a next_payout_time set when we entered this, so if
|
||||
// it has been cleared, we must have already processed payouts, no need to
|
||||
// further advance time.
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
if (*dividend_data.options.next_payout_time == next_payout_scheduled_time)
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
}
|
||||
idump((db.head_block_time()));
|
||||
};
|
||||
|
||||
// the first test will be testing pending balances, so we need to hit a
|
||||
// maintenance interval that isn't the payout interval. Payout is
|
||||
// every 3 days, maintenance interval is every 1 day.
|
||||
advance_to_next_payout_time();
|
||||
|
||||
// Set up the first test, issue alice, bob, and carol, and dave each 1/4 of the total
|
||||
// supply of the core asset.
|
||||
// Then deposit 400 TEST in the distribution account, and see that they
|
||||
// each are credited 100 TEST.
|
||||
transfer( committee_account(db), alice, asset( 250000000000000 ) );
|
||||
transfer( committee_account(db), bob, asset( 250000000000000 ) );
|
||||
transfer( committee_account(db), carol, asset( 250000000000000 ) );
|
||||
transfer( committee_account(db), dave, asset( 250000000000000 ) );
|
||||
|
||||
BOOST_TEST_MESSAGE("Issuing 300 TEST to the dividend account");
|
||||
issue_asset_to_account(test_asset_object, dividend_distribution_account, 40000);
|
||||
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" );
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
|
||||
verify_pending_balance(alice, test_asset_object, 10000);
|
||||
verify_pending_balance(bob, test_asset_object, 10000);
|
||||
verify_pending_balance(carol, test_asset_object, 10000);
|
||||
verify_pending_balance(dave, test_asset_object, 10000);
|
||||
|
||||
// For the second test, issue dave more than the other two, so it's
|
||||
// alice: 1/5 CORE, bob: 1/5 CORE, carol: 1/5 CORE, dave: 2/5 CORE
|
||||
// Then deposit 500 TEST in the distribution account, and see that alice
|
||||
// bob, and carol are credited with 100 TEST, and dave gets 200 TEST
|
||||
BOOST_TEST_MESSAGE("Issuing dave twice as much of the holder asset");
|
||||
transfer( alice, dave, asset( 50000000000000 ) );
|
||||
transfer( bob, dave, asset( 50000000000000 ) );
|
||||
transfer( carol, dave, asset( 50000000000000 ) );
|
||||
issue_asset_to_account(test_asset_object, dividend_distribution_account, 50000); // 500 at two digits of precision
|
||||
BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" );
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
verify_pending_balance(alice, test_asset_object, 20000);
|
||||
verify_pending_balance(bob, test_asset_object, 20000);
|
||||
verify_pending_balance(carol, test_asset_object, 20000);
|
||||
verify_pending_balance(dave, test_asset_object, 30000);
|
||||
|
||||
fc::time_point_sec old_next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
advance_to_next_payout_time();
|
||||
|
||||
|
||||
BOOST_REQUIRE_MESSAGE(dividend_data.options.next_payout_time, "No new payout was scheduled");
|
||||
BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time != *dividend_data.options.next_payout_time,
|
||||
"New payout was scheduled for the same time as the last payout");
|
||||
BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time + *dividend_data.options.payout_interval == *dividend_data.options.next_payout_time,
|
||||
"New payout was not scheduled for the expected time");
|
||||
|
||||
auto verify_dividend_payout_operations = [&](const account_object& destination_account, const asset& expected_payout)
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Verifying the virtual op was created");
|
||||
const account_transaction_history_index& hist_idx = db.get_index_type<account_transaction_history_index>();
|
||||
auto account_history_range = hist_idx.indices().get<by_seq>().equal_range(boost::make_tuple(destination_account.id));
|
||||
BOOST_REQUIRE(account_history_range.first != account_history_range.second);
|
||||
const operation_history_object& history_object = std::prev(account_history_range.second)->operation_id(db);
|
||||
const asset_dividend_distribution_operation& distribution_operation = history_object.op.get<asset_dividend_distribution_operation>();
|
||||
BOOST_CHECK(distribution_operation.account_id == destination_account.id);
|
||||
BOOST_CHECK(std::find(distribution_operation.amounts.begin(), distribution_operation.amounts.end(), expected_payout)
|
||||
!= distribution_operation.amounts.end());
|
||||
};
|
||||
|
||||
BOOST_TEST_MESSAGE("Verifying the payouts");
|
||||
BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 20000);
|
||||
verify_dividend_payout_operations(alice, asset(20000, test_asset_object.id));
|
||||
verify_pending_balance(alice, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 20000);
|
||||
verify_dividend_payout_operations(bob, asset(20000, test_asset_object.id));
|
||||
verify_pending_balance(bob, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 20000);
|
||||
verify_dividend_payout_operations(carol, asset(20000, test_asset_object.id));
|
||||
verify_pending_balance(carol, test_asset_object, 0);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(dave, test_asset_object), 30000);
|
||||
verify_dividend_payout_operations(dave, asset(30000, test_asset_object.id));
|
||||
verify_pending_balance(dave, test_asset_object, 0);
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_dividend_distribution_interval )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
INVOKE( create_dividend_uia );
|
||||
|
||||
const auto& dividend_holder_asset_object = get_asset("DIVIDEND");
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
|
||||
const account_object& alice = get_account("alice");
|
||||
const account_object& bob = get_account("bob");
|
||||
const account_object& carol = get_account("carol");
|
||||
const account_object& dave = get_account("dave");
|
||||
const account_object& frank = get_account("frank");
|
||||
const auto& test_asset_object = get_asset("TEST");
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_dividend_corner_cases )
|
||||
{
|
||||
using namespace graphene;
|
||||
try {
|
||||
INVOKE( create_dividend_uia );
|
||||
|
||||
const auto& dividend_holder_asset_object = get_asset("DIVIDEND");
|
||||
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
|
||||
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
|
||||
const account_object& alice = get_account("alice");
|
||||
const account_object& bob = get_account("bob");
|
||||
const account_object& carol = get_account("carol");
|
||||
const account_object& dave = get_account("dave");
|
||||
const account_object& frank = get_account("frank");
|
||||
const auto& test_asset_object = get_asset("TEST");
|
||||
|
||||
auto issue_asset_to_account = [&](const asset_object& asset_to_issue, const account_object& destination_account, int64_t amount_to_issue)
|
||||
{
|
||||
asset_issue_operation op;
|
||||
op.issuer = asset_to_issue.issuer;
|
||||
op.asset_to_issue = asset(amount_to_issue, asset_to_issue.id);
|
||||
op.issue_to_account = destination_account.id;
|
||||
trx.operations.push_back( op );
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
};
|
||||
|
||||
auto verify_pending_balance = [&](const account_object& holder_account_obj, const asset_object& payout_asset_obj, int64_t expected_balance) {
|
||||
int64_t pending_balance = get_dividend_pending_payout_balance(dividend_holder_asset_object.id,
|
||||
holder_account_obj.id,
|
||||
payout_asset_obj.id);
|
||||
BOOST_CHECK_EQUAL(pending_balance, expected_balance);
|
||||
};
|
||||
|
||||
auto reserve_asset_from_account = [&](const asset_object& asset_to_reserve, const account_object& from_account, int64_t amount_to_reserve)
|
||||
{
|
||||
asset_reserve_operation reserve_op;
|
||||
reserve_op.payer = from_account.id;
|
||||
reserve_op.amount_to_reserve = asset(amount_to_reserve, asset_to_reserve.id);
|
||||
trx.operations.push_back(reserve_op);
|
||||
set_expiration(db, trx);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.clear();
|
||||
};
|
||||
auto advance_to_next_payout_time = [&]() {
|
||||
// Advance to the next upcoming payout time
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time;
|
||||
// generate blocks up to the next scheduled time
|
||||
generate_blocks(next_payout_scheduled_time);
|
||||
// if the scheduled time fell on a maintenance interval, then we should have paid out.
|
||||
// if not, we need to advance to the next maintenance interval to trigger the payout
|
||||
if (dividend_data.options.next_payout_time)
|
||||
{
|
||||
// we know there was a next_payout_time set when we entered this, so if
|
||||
// it has been cleared, we must have already processed payouts, no need to
|
||||
// further advance time.
|
||||
BOOST_REQUIRE(dividend_data.options.next_payout_time);
|
||||
if (*dividend_data.options.next_payout_time == next_payout_scheduled_time)
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
}
|
||||
};
|
||||
|
||||
// the first test will be testing pending balances, so we need to hit a
|
||||
// maintenance interval that isn't the payout interval. Payout is
|
||||
// every 3 days, maintenance interval is every 1 day.
|
||||
advance_to_next_payout_time();
|
||||
|
||||
BOOST_TEST_MESSAGE("Testing a payout interval when there are no users holding the dividend asset");
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 0);
|
||||
issue_asset_to_account(test_asset_object, dividend_distribution_account, 1000);
|
||||
BOOST_TEST_MESSAGE("Generating blocks until next maintenance interval");
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
BOOST_TEST_MESSAGE("Verify that no pending payments were scheduled");
|
||||
verify_pending_balance(alice, test_asset_object, 0);
|
||||
verify_pending_balance(bob, test_asset_object, 0);
|
||||
verify_pending_balance(carol, test_asset_object, 0);
|
||||
advance_to_next_payout_time();
|
||||
BOOST_TEST_MESSAGE("Verify that no actual payments took place");
|
||||
verify_pending_balance(alice, test_asset_object, 0);
|
||||
verify_pending_balance(bob, test_asset_object, 0);
|
||||
verify_pending_balance(carol, test_asset_object, 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 0);
|
||||
BOOST_CHECK_EQUAL(get_balance(dividend_distribution_account, test_asset_object), 1000);
|
||||
|
||||
BOOST_TEST_MESSAGE("Now give alice a small balance and see that she takes it all");
|
||||
issue_asset_to_account(dividend_holder_asset_object, alice, 1);
|
||||
generate_block();
|
||||
BOOST_TEST_MESSAGE("Generating blocks until next maintenance interval");
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
BOOST_TEST_MESSAGE("Verify that no alice received her payment of the entire amount");
|
||||
verify_pending_balance(alice, test_asset_object, 1000);
|
||||
|
||||
// Test that we can pay out the dividend asset itself
|
||||
issue_asset_to_account(dividend_holder_asset_object, bob, 1);
|
||||
issue_asset_to_account(dividend_holder_asset_object, carol, 1);
|
||||
issue_asset_to_account(dividend_holder_asset_object, dividend_distribution_account, 300);
|
||||
generate_block();
|
||||
BOOST_CHECK_EQUAL(get_balance(alice, dividend_holder_asset_object), 1);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob, dividend_holder_asset_object), 1);
|
||||
BOOST_CHECK_EQUAL(get_balance(carol, dividend_holder_asset_object), 1);
|
||||
BOOST_TEST_MESSAGE("Generating blocks until next maintenance interval");
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block(); // get the maintenance skip slots out of the way
|
||||
BOOST_TEST_MESSAGE("Verify that the dividend asset was shared out");
|
||||
verify_pending_balance(alice, dividend_holder_asset_object, 100);
|
||||
verify_pending_balance(bob, dividend_holder_asset_object, 100);
|
||||
verify_pending_balance(carol, dividend_holder_asset_object, 100);
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END() // end dividend_tests suite
|
||||
|
||||
BOOST_AUTO_TEST_CASE( cancel_limit_order_test )
|
||||
{ try {
|
||||
INVOKE( issue_uia );
|
||||
|
|
|
|||
|
|
@ -1,166 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018 oxarbitrage, 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/app/database_api.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::chain::test;
|
||||
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(voting_tests, database_fixture)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(last_voting_date)
|
||||
{
|
||||
try
|
||||
{
|
||||
ACTORS((alice));
|
||||
|
||||
transfer(committee_account, alice_id, asset(100));
|
||||
|
||||
// we are going to vote for this witness
|
||||
auto witness1 = witness_id_type(1)(db);
|
||||
|
||||
auto stats_obj = alice_id(db).statistics(db);
|
||||
BOOST_CHECK_EQUAL(stats_obj.last_vote_time.sec_since_epoch(), 0);
|
||||
|
||||
// alice votes
|
||||
graphene::chain::account_update_operation op;
|
||||
op.account = alice_id;
|
||||
op.new_options = alice.options;
|
||||
op.new_options->votes.insert(witness1.vote_id);
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
|
||||
auto now = db.head_block_time().sec_since_epoch();
|
||||
|
||||
// last_vote_time is updated for alice
|
||||
stats_obj = alice_id(db).statistics(db);
|
||||
BOOST_CHECK_EQUAL(stats_obj.last_vote_time.sec_since_epoch(), now);
|
||||
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE(last_voting_date_proxy)
|
||||
{
|
||||
try
|
||||
{
|
||||
ACTORS((alice)(proxy)(bob));
|
||||
|
||||
transfer(committee_account, alice_id, asset(100));
|
||||
transfer(committee_account, bob_id, asset(200));
|
||||
transfer(committee_account, proxy_id, asset(300));
|
||||
|
||||
generate_block();
|
||||
|
||||
// witness to vote for
|
||||
auto witness1 = witness_id_type(1)(db);
|
||||
|
||||
// round1: alice changes proxy, this is voting activity
|
||||
{
|
||||
graphene::chain::account_update_operation op;
|
||||
op.account = alice_id;
|
||||
op.new_options = alice_id(db).options;
|
||||
op.new_options->voting_account = proxy_id;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
}
|
||||
// alice last_vote_time is updated
|
||||
auto alice_stats_obj = alice_id(db).statistics(db);
|
||||
auto round1 = db.head_block_time().sec_since_epoch();
|
||||
BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1);
|
||||
|
||||
generate_block();
|
||||
|
||||
// round 2: alice update account but no proxy or voting changes are done
|
||||
{
|
||||
graphene::chain::account_update_operation op;
|
||||
op.account = alice_id;
|
||||
op.new_options = alice_id(db).options;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
set_expiration( db, trx );
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
}
|
||||
// last_vote_time is not updated
|
||||
auto round2 = db.head_block_time().sec_since_epoch();
|
||||
alice_stats_obj = alice_id(db).statistics(db);
|
||||
BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1);
|
||||
|
||||
generate_block();
|
||||
|
||||
// round 3: bob votes
|
||||
{
|
||||
graphene::chain::account_update_operation op;
|
||||
op.account = bob_id;
|
||||
op.new_options = bob_id(db).options;
|
||||
op.new_options->votes.insert(witness1.vote_id);
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
set_expiration( db, trx );
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
|
||||
// last_vote_time for bob is updated as he voted
|
||||
auto round3 = db.head_block_time().sec_since_epoch();
|
||||
auto bob_stats_obj = bob_id(db).statistics(db);
|
||||
BOOST_CHECK_EQUAL(bob_stats_obj.last_vote_time.sec_since_epoch(), round3);
|
||||
|
||||
generate_block();
|
||||
|
||||
// round 4: proxy votes
|
||||
{
|
||||
graphene::chain::account_update_operation op;
|
||||
op.account = proxy_id;
|
||||
op.new_options = proxy_id(db).options;
|
||||
op.new_options->votes.insert(witness1.vote_id);
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, proxy_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
}
|
||||
|
||||
// proxy just voted so the last_vote_time is updated
|
||||
auto round4 = db.head_block_time().sec_since_epoch();
|
||||
auto proxy_stats_obj = proxy_id(db).statistics(db);
|
||||
BOOST_CHECK_EQUAL(proxy_stats_obj.last_vote_time.sec_since_epoch(), round4);
|
||||
|
||||
// alice haves proxy, proxy votes but last_vote_time is not updated for alice
|
||||
alice_stats_obj = alice_id(db).statistics(db);
|
||||
BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1);
|
||||
|
||||
// bob haves nothing to do with proxy so last_vote_time is not updated
|
||||
bob_stats_obj = bob_id(db).statistics(db);
|
||||
BOOST_CHECK_EQUAL(bob_stats_obj.last_vote_time.sec_since_epoch(), round3);
|
||||
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
Loading…
Reference in a new issue