Revert "Merge branch 'master' into beatrice"

This reverts commit cc8766d319, reversing
changes made to fd71c4b682.
This commit is contained in:
Roshan Syed 2019-04-12 12:53:09 -03:00
parent f0f140e1e6
commit b787f3f21b
28 changed files with 8992 additions and 215045 deletions

222006
genesis.json

File diff suppressed because it is too large Load diff

View file

@ -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 ){

View file

@ -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 )

View file

@ -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 //

View file

@ -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;
};

View file

@ -67,6 +67,7 @@ FC_REFLECT( graphene::app::full_account,
(limit_orders)
(call_orders)
(settle_orders)
(proposals)
(assets)
(withdraws)
(proposals)

View file

@ -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) ) }

View file

@ -57,6 +57,7 @@ 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 )
{
auto dotpos = op.symbol.find( '.' );
@ -64,11 +65,9 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o
{
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",
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}",
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) );
}
}
@ -79,14 +78,13 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o
{
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",
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}",
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
{

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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) )

View file

@ -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.

View file

@ -137,6 +137,8 @@ namespace graphene { namespace chain {
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 );
bool _push_block( const signed_block& b );

View file

@ -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>

View file

@ -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))

View file

@ -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) )

View file

@ -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

View file

@ -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" );
}
}
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 {

View 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

View file

@ -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;
}

View file

@ -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();

View file

@ -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()

View file

@ -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 )
{
generate_blocks( HARDFORK_1000_TIME + fc::seconds(300) );
try
{
auto duplicate = 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} );
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} );
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_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" );
create_proposal( *this, {duplicate_market, duplicate_group} );
auto trx = make_signed_transaction_with_proposed_operation(*this, {duplicate_market, duplicate_group} );
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_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;
}
}

View file

@ -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 );

View file

@ -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()