From 0bdec0d71e5339235b9d594baaf8ae5cfda0a38c Mon Sep 17 00:00:00 2001 From: gladcow Date: Fri, 13 Mar 2020 13:30:58 +0300 Subject: [PATCH] son_fixture --- tests/CMakeLists.txt | 2 +- tests/peerplays_sidechain/plugin_test.cpp | 40 + tests/peerplays_sidechain/son_fixture.cpp | 1405 +++++++++++++++++++++ tests/peerplays_sidechain/son_fixture.hpp | 326 +++++ 4 files changed, 1772 insertions(+), 1 deletion(-) create mode 100644 tests/peerplays_sidechain/plugin_test.cpp create mode 100644 tests/peerplays_sidechain/son_fixture.cpp create mode 100644 tests/peerplays_sidechain/son_fixture.hpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5162f692..4fe8da34 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -34,7 +34,7 @@ add_executable( betting_test ${BETTING_TESTS} ${COMMON_SOURCES} ) target_link_libraries( betting_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB PEERPLAYS_SIDECHAIN_TESTS "peerplays_sidechain/*.cpp") -add_executable( peerplays_sidechain_test ${PEERPLAYS_SIDECHAIN_TESTS} ${COMMON_SOURCES} ) +add_executable( peerplays_sidechain_test ${PEERPLAYS_SIDECHAIN_TESTS} ) target_link_libraries( peerplays_sidechain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB TOURNAMENT_TESTS "tournament/*.cpp") diff --git a/tests/peerplays_sidechain/plugin_test.cpp b/tests/peerplays_sidechain/plugin_test.cpp new file mode 100644 index 00000000..fccfbdd7 --- /dev/null +++ b/tests/peerplays_sidechain/plugin_test.cpp @@ -0,0 +1,40 @@ +#include + +#include "son_fixture.hpp" + +#include + +using namespace graphene; +using namespace graphene::chain; +using namespace graphene::chain::test; +using namespace graphene::peerplays_sidechain; + +BOOST_FIXTURE_TEST_SUITE( plugin, son_fixture ) + +BOOST_AUTO_TEST_CASE(init) +{ + generate_blocks(HARDFORK_SON_TIME + 1); + set_expiration(db, trx); + + // create sons + std::vector keys; + std::vector sons; + std::string test_url = "https://son_test"; + for(size_t i = 0; i < 16; i++) + { + fc::ecc::private_key privkey = generate_private_key(fc::to_string(i)); + keys.push_back(privkey); + fc::ecc::public_key pubkey = privkey.get_public_key(); + sons.push_back(create_son("son" + fc::to_string(i), test_url, privkey, pubkey)); + ilog("son created: ${i}", ("i", i)); + } + + BOOST_CHECK(sons.size() == 16); + + // start plugin + peerplays_sidechain_plugin plugin; + boost::program_options::variables_map cfg; + plugin.plugin_initialize(cfg); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/peerplays_sidechain/son_fixture.cpp b/tests/peerplays_sidechain/son_fixture.cpp new file mode 100644 index 00000000..effe158d --- /dev/null +++ b/tests/peerplays_sidechain/son_fixture.cpp @@ -0,0 +1,1405 @@ +/* + * Copyright (c) 2015 Cryptonomex, Inc., and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include "son_fixture.hpp" + +using namespace graphene::chain::test; + +uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP = 1431700002; + +namespace graphene { namespace chain { + +using std::cout; +using std::cerr; + +son_fixture::son_fixture() + : app(), db( *app.chain_database() ) +{ + try { + int argc = boost::unit_test::framework::master_test_suite().argc; + char** argv = boost::unit_test::framework::master_test_suite().argv; + for( int i=1; i(); + auto mhplugin = app.register_plugin(); + auto bookieplugin = app.register_plugin(); + auto affiliateplugin = app.register_plugin(); + init_account_pub_key = init_account_priv_key.get_public_key(); + + boost::program_options::variables_map options; + + genesis_state.initial_timestamp = time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP ); + //int back_to_the_past = 0; + //back_to_the_past = 7 * 24 * 60 * 60; // week + //genesis_state.initial_timestamp = time_point_sec( (fc::time_point::now().sec_since_epoch() - back_to_the_past) / GRAPHENE_DEFAULT_BLOCK_INTERVAL * GRAPHENE_DEFAULT_BLOCK_INTERVAL ); + genesis_state.initial_parameters.witness_schedule_algorithm = GRAPHENE_WITNESS_SHUFFLED_ALGORITHM; + + genesis_state.initial_active_witnesses = 10; + for( unsigned i = 0; i < genesis_state.initial_active_witnesses; ++i ) + { + auto name = "init"+fc::to_string(i); + genesis_state.initial_accounts.emplace_back(name, + init_account_priv_key.get_public_key(), + init_account_priv_key.get_public_key(), + true); + genesis_state.initial_committee_candidates.push_back({name}); + genesis_state.initial_witness_candidates.push_back({name, init_account_priv_key.get_public_key()}); + } + genesis_state.initial_parameters.current_fees->zero_all_fees(); + open_database(); + + // add account tracking for ahplugin for special test case with track-account enabled + if( !options.count("track-account") && boost::unit_test::framework::current_test_case().p_name.value == "track_account") { + std::vector track_account; + std::string track = "\"1.2.18\""; + track_account.push_back(track); + options.insert(std::make_pair("track-account", boost::program_options::variable_value(track_account, false))); + options.insert(std::make_pair("partial-operations", boost::program_options::variable_value(true, false))); + } + // account tracking 2 accounts + if( !options.count("track-account") && boost::unit_test::framework::current_test_case().p_name.value == "track_account2") { + std::vector track_account; + std::string track = "\"1.2.0\""; + track_account.push_back(track); + track = "\"1.2.17\""; + track_account.push_back(track); + options.insert(std::make_pair("track-account", boost::program_options::variable_value(track_account, false))); + } + + // app.initialize(); + ahplugin->plugin_set_app(&app); + ahplugin->plugin_initialize(options); + mhplugin->plugin_set_app(&app); + mhplugin->plugin_initialize(options); + bookieplugin->plugin_set_app(&app); + bookieplugin->plugin_initialize(options); + affiliateplugin->plugin_set_app(&app); + affiliateplugin->plugin_initialize(options); + + ahplugin->plugin_startup(); + mhplugin->plugin_startup(); + bookieplugin->plugin_startup(); + affiliateplugin->plugin_startup(); + // stats api requests affiliate_stats plugin from app, so add it to app plugin list + app.enable_plugin(affiliateplugin->plugin_name()); + + generate_block(); + + set_expiration( db, trx ); + } catch ( const fc::exception& e ) + { + edump( (e.to_detail_string()) ); + throw; + } + + return; +} + +son_fixture::~son_fixture() +{ try { + // If we're unwinding due to an exception, don't do any more checks. + // This way, boost test's last checkpoint tells us approximately where the error was. + if( !std::uncaught_exception() ) + { + verify_asset_supplies(db); + verify_account_history_plugin_index(); + BOOST_CHECK( db.get_node_properties().skip_flags == database::skip_nothing ); + } + + if( data_dir ) + db.close(); + return; +} FC_CAPTURE_AND_RETHROW() } + +fc::ecc::private_key son_fixture::generate_private_key(string seed) +{ + static const fc::ecc::private_key committee = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); + if( seed == "null_key" ) + return committee; + return fc::ecc::private_key::regenerate(fc::sha256::hash(seed)); +} + +string son_fixture::generate_anon_acct_name() +{ + // names of the form "anon-acct-x123" ; the "x" is necessary + // to workaround issue #46 + return "anon-acct-x" + std::to_string( anon_acct_count++ ); +} + +void son_fixture::verify_asset_supplies( const database& db ) +{ + //wlog("*** Begin asset supply verification ***"); + 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& statistics_index = db.get_index_type>(); + const auto& balance_index = db.get_index_type().indices(); + const auto& settle_index = db.get_index_type().indices(); + const auto& tournaments_index = db.get_index_type().indices(); + const auto& asst_index = db.get_index_type().indices(); + + map total_balances; + map total_debts; + share_type core_in_orders; + share_type reported_core_in_orders; + + for( const tournament_object& t : tournaments_index ) + if (t.get_state() != tournament_state::concluded && t.get_state() != tournament_state::registration_period_expired) + total_balances[t.options.buy_in.asset_id] += t.prize_pool; + + for( const asset_object& ai : asst_index) + if (ai.is_lottery()) { + asset balance = db.get_balance( ai.get_id() ); + total_balances[ balance.asset_id ] += balance.amount; + } + for( const account_balance_object& b : balance_index ) + total_balances[b.asset_type] += b.balance; + for( const force_settlement_object& s : settle_index ) + total_balances[s.balance.asset_id] += s.balance.amount; + for( const account_statistics_object& a : statistics_index ) + { + reported_core_in_orders += a.total_core_in_orders; + total_balances[asset_id_type()] += a.pending_fees + a.pending_vested_fees; + } + for( const limit_order_object& o : db.get_index_type().indices() ) + { + asset for_sale = o.amount_for_sale(); + if( for_sale.asset_id == asset_id_type() ) core_in_orders += for_sale.amount; + total_balances[for_sale.asset_id] += for_sale.amount; + total_balances[asset_id_type()] += o.deferred_fee; + } + for( const call_order_object& o : db.get_index_type().indices() ) + { + asset col = o.get_collateral(); + if( col.asset_id == asset_id_type() ) core_in_orders += col.amount; + total_balances[col.asset_id] += col.amount; + total_debts[o.get_debt().asset_id] += o.get_debt().amount; + } + for( const asset_object& asset_obj : db.get_index_type().indices() ) + { + const auto& dasset_obj = asset_obj.dynamic_asset_data_id(db); + total_balances[asset_obj.id] += dasset_obj.accumulated_fees; + total_balances[asset_id_type()] += dasset_obj.fee_pool; + if( asset_obj.is_market_issued() ) + { + const auto& bad = asset_obj.bitasset_data(db); + total_balances[bad.options.short_backing_asset] += bad.settlement_fund; + } + total_balances[asset_obj.id] += dasset_obj.confidential_supply.value; + } + for( const vesting_balance_object& vbo : db.get_index_type< vesting_balance_index >().indices() ) + total_balances[ vbo.balance.asset_id ] += vbo.balance.amount; + for( const fba_accumulator_object& fba : db.get_index_type< simple_index< fba_accumulator_object > >() ) + total_balances[ asset_id_type() ] += fba.accumulated_fba_fees; + + for (const bet_object& o : db.get_index_type().indices()) + { + total_balances[o.amount_to_bet.asset_id] += o.amount_to_bet.amount; + } + for (const betting_market_position_object& o : db.get_index_type().indices()) + { + const betting_market_object& betting_market = o.betting_market_id(db); + const betting_market_group_object& betting_market_group = betting_market.group_id(db); + total_balances[betting_market_group.asset_id] += o.pay_if_canceled; + total_balances[betting_market_group.asset_id] += o.fees_collected; + } + + + uint64_t sweeps_vestings = 0; + for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() ) + sweeps_vestings += svbo.balance; + + total_balances[db.get_global_properties().parameters.sweeps_distribution_asset()] += sweeps_vestings / SWEEPS_VESTING_BALANCE_MULTIPLIER; + total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget; + total_balances[asset_id_type()] += db.get_dynamic_global_properties().son_budget; + + for( const auto& item : total_debts ) + { + BOOST_CHECK_EQUAL(item.first(db).dynamic_asset_data_id(db).current_supply.value, item.second.value); + } + + for( const asset_object& asset_obj : db.get_index_type().indices() ) + { + BOOST_CHECK_EQUAL(total_balances[asset_obj.id].value, asset_obj.dynamic_asset_data_id(db).current_supply.value); + } + + BOOST_CHECK_EQUAL( core_in_orders.value , reported_core_in_orders.value ); +// wlog("*** End asset supply verification ***"); +} + +void son_fixture::verify_account_history_plugin_index( )const +{ + return; + if( skip_key_index_test ) + return; + + const std::shared_ptr pin = + app.get_plugin("account_history"); + if( pin->tracked_accounts().size() == 0 ) + { + /* + vector< pair< account_id_type, address > > tuples_from_db; + const auto& primary_account_idx = db.get_index_type().indices().get(); + flat_set< public_key_type > acct_addresses; + acct_addresses.reserve( 2 * GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP + 2 ); + + for( const account_object& acct : primary_account_idx ) + { + account_id_type account_id = acct.id; + acct_addresses.clear(); + for( const pair< account_id_type, weight_type >& auth : acct.owner.account_auths ) + { + if( auth.first.type() == key_object_type ) + acct_addresses.insert( auth.first ); + } + for( const pair< object_id_type, weight_type >& auth : acct.active.auths ) + { + if( auth.first.type() == key_object_type ) + acct_addresses.insert( auth.first ); + } + acct_addresses.insert( acct.options.get_memo_key()(db).key_address() ); + for( const address& addr : acct_addresses ) + tuples_from_db.emplace_back( account_id, addr ); + } + + vector< pair< account_id_type, address > > tuples_from_index; + tuples_from_index.reserve( tuples_from_db.size() ); + const auto& key_account_idx = + db.get_index_type() + .indices().get(); + + for( const graphene::account_history::key_account_object& key_account : key_account_idx ) + { + address addr = key_account.key; + for( const account_id_type& account_id : key_account.account_ids ) + tuples_from_index.emplace_back( account_id, addr ); + } + + // TODO: use function for common functionality + { + // due to hashed index, account_id's may not be in sorted order... + std::sort( tuples_from_db.begin(), tuples_from_db.end() ); + size_t size_before_uniq = tuples_from_db.size(); + auto last = std::unique( tuples_from_db.begin(), tuples_from_db.end() ); + tuples_from_db.erase( last, tuples_from_db.end() ); + // but they should be unique (multiple instances of the same + // address within an account should have been de-duplicated + // by the flat_set above) + BOOST_CHECK( tuples_from_db.size() == size_before_uniq ); + } + + { + // (address, account) should be de-duplicated by flat_set<> + // in key_account_object + std::sort( tuples_from_index.begin(), tuples_from_index.end() ); + auto last = std::unique( tuples_from_index.begin(), tuples_from_index.end() ); + size_t size_before_uniq = tuples_from_db.size(); + tuples_from_index.erase( last, tuples_from_index.end() ); + BOOST_CHECK( tuples_from_index.size() == size_before_uniq ); + } + + //BOOST_CHECK_EQUAL( tuples_from_db, tuples_from_index ); + bool is_equal = true; + is_equal &= (tuples_from_db.size() == tuples_from_index.size()); + for( size_t i=0,n=tuples_from_db.size(); ipath(), [this]{return genesis_state;}, "test"); + } +} + +signed_block son_fixture::generate_block(uint32_t skip, const fc::ecc::private_key& key, int miss_blocks) +{ + skip |= database::skip_undo_history_check; + // skip == ~0 will skip checks specified in database::validation_steps + auto block = db.generate_block(db.get_slot_time(miss_blocks + 1), + db.get_scheduled_witness(miss_blocks + 1), + key, skip); + db.clear_pending(); + return block; +} + +void son_fixture::generate_blocks( uint32_t block_count ) +{ + for( uint32_t i = 0; i < block_count; ++i ) + generate_block(); +} + +void son_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks, uint32_t skip) +{ + if( miss_intermediate_blocks ) + { + generate_block(skip); + auto slots_to_miss = db.get_slot_at_time(timestamp); + if( slots_to_miss <= 1 ) + return; + --slots_to_miss; + generate_block(skip, init_account_priv_key, slots_to_miss); + return; + } + while( db.head_block_time() < timestamp ) + generate_block(skip); +} + +bool son_fixture::generate_maintenance_block() { + try { + fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + uint32_t skip = ~database::skip_fork_db; + auto maint_time = db.get_dynamic_global_properties().next_maintenance_time; + auto slots_to_miss = db.get_slot_at_time(maint_time); + db.generate_block(db.get_slot_time(slots_to_miss), + db.get_scheduled_witness(slots_to_miss), + committee_key, + skip); + return true; + } catch (std::exception& e) + { + return false; + } +} + +account_create_operation son_fixture::make_account( + const std::string& name /* = "nathan" */, + public_key_type key /* = key_id_type() */ + ) +{ try { + account_create_operation create_account; + create_account.registrar = account_id_type(); + + create_account.name = name; + create_account.owner = authority(123, key, 123); + create_account.active = authority(321, key, 321); + create_account.options.memo_key = key; + create_account.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT; + + auto& active_committee_members = db.get_global_properties().active_committee_members; + if( active_committee_members.size() > 0 ) + { + set votes; + votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id); + votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id); + votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id); + votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id); + votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id); + create_account.options.votes = flat_set(votes.begin(), votes.end()); + } + create_account.options.num_committee = create_account.options.votes.size(); + + create_account.fee = db.current_fee_schedule().calculate_fee( create_account ); + return create_account; +} FC_CAPTURE_AND_RETHROW() } + +account_create_operation son_fixture::make_account( + const std::string& name, + const account_object& registrar, + const account_object& referrer, + uint8_t referrer_percent /* = 100 */, + public_key_type key /* = public_key_type() */ + ) +{ + try + { + account_create_operation create_account; + + create_account.registrar = registrar.id; + create_account.referrer = referrer.id; + create_account.referrer_percent = referrer_percent; + + create_account.name = name; + create_account.owner = authority(123, key, 123); + create_account.active = authority(321, key, 321); + create_account.options.memo_key = key; + create_account.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT; + + const vector& active_committee_members = db.get_global_properties().active_committee_members; + if( active_committee_members.size() > 0 ) + { + set votes; + votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id); + votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id); + votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id); + votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id); + votes.insert(active_committee_members[rand() % active_committee_members.size()](db).vote_id); + create_account.options.votes = flat_set(votes.begin(), votes.end()); + } + create_account.options.num_committee = create_account.options.votes.size(); + + create_account.fee = db.current_fee_schedule().calculate_fee( create_account ); + return create_account; + } + FC_CAPTURE_AND_RETHROW((name)(referrer_percent)) +} + +const asset_object& son_fixture::get_asset( const string& symbol )const +{ + const auto& idx = db.get_index_type().indices().get(); + const auto itr = idx.find(symbol); + FC_ASSERT( itr != idx.end() ); + return *itr; +} + +const account_object& son_fixture::get_account( const string& name )const +{ + const auto& idx = db.get_index_type().indices().get(); + const auto itr = idx.find(name); + FC_ASSERT( itr != idx.end() ); + return *itr; +} + +const asset_object& son_fixture::create_bitasset( + const string& name, + account_id_type issuer /* = GRAPHENE_WITNESS_ACCOUNT */, + uint16_t market_fee_percent /* = 100 */ /* 1% */, + uint16_t flags /* = charge_market_fee */ + ) +{ try { + asset_create_operation creator; + creator.issuer = issuer; + creator.fee = asset(); + creator.symbol = name; + creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + creator.precision = 2; + creator.common_options.market_fee_percent = market_fee_percent; + if( issuer == GRAPHENE_WITNESS_ACCOUNT ) + flags |= witness_fed_asset; + creator.common_options.issuer_permissions = flags; + creator.common_options.flags = flags & ~global_settle & ~witness_fed_asset; + creator.common_options.core_exchange_rate = price({asset(1,asset_id_type(1)),asset(1)}); + creator.bitasset_opts = bitasset_options(); + trx.operations.push_back(std::move(creator)); + trx.validate(); + processed_transaction ptx = db.push_transaction(trx, ~0); + trx.operations.clear(); + return db.get(ptx.operation_results[0].get()); +} FC_CAPTURE_AND_RETHROW( (name)(flags) ) } + +const asset_object& son_fixture::create_prediction_market( + const string& name, + account_id_type issuer /* = GRAPHENE_WITNESS_ACCOUNT */, + uint16_t market_fee_percent /* = 100 */ /* 1% */, + uint16_t flags /* = charge_market_fee */ + ) +{ try { + asset_create_operation creator; + creator.issuer = issuer; + creator.fee = asset(); + creator.symbol = name; + creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + creator.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; + creator.common_options.market_fee_percent = market_fee_percent; + creator.common_options.issuer_permissions = flags | global_settle; + creator.common_options.flags = flags & ~global_settle; + if( issuer == GRAPHENE_WITNESS_ACCOUNT ) + creator.common_options.flags |= witness_fed_asset; + creator.common_options.core_exchange_rate = price({asset(1,asset_id_type(1)),asset(1)}); + creator.bitasset_opts = bitasset_options(); + creator.is_prediction_market = true; + trx.operations.push_back(std::move(creator)); + trx.validate(); + processed_transaction ptx = db.push_transaction(trx, ~0); + trx.operations.clear(); + return db.get(ptx.operation_results[0].get()); +} FC_CAPTURE_AND_RETHROW( (name)(flags) ) } + +const asset_object& son_fixture::create_user_issued_asset( const string& name ) +{ + asset_create_operation creator; + creator.issuer = account_id_type(); + creator.fee = asset(); + creator.symbol = name; + creator.common_options.max_supply = 0; + creator.precision = 2; + creator.common_options.core_exchange_rate = price({asset(1,asset_id_type(1)),asset(1)}); + creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + creator.common_options.flags = charge_market_fee; + creator.common_options.issuer_permissions = charge_market_fee; + trx.operations.push_back(std::move(creator)); + trx.validate(); + processed_transaction ptx = db.push_transaction(trx, ~0); + trx.operations.clear(); + return db.get(ptx.operation_results[0].get()); +} + +const asset_object& son_fixture::create_user_issued_asset( const string& name, const account_object& issuer, uint16_t flags ) +{ + asset_create_operation creator; + creator.issuer = issuer.id; + creator.fee = asset(); + creator.symbol = name; + creator.common_options.max_supply = 0; + creator.precision = 2; + creator.common_options.core_exchange_rate = price({asset(1,asset_id_type(1)),asset(1)}); + creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + creator.common_options.flags = flags; + creator.common_options.issuer_permissions = flags; + trx.operations.clear(); + trx.operations.push_back(std::move(creator)); + set_expiration( db, trx ); + trx.validate(); + processed_transaction ptx = db.push_transaction(trx, ~0); + trx.operations.clear(); + return db.get(ptx.operation_results[0].get()); +} + +void son_fixture::issue_uia( const account_object& recipient, asset amount ) +{ + BOOST_TEST_MESSAGE( "Issuing UIA" ); + asset_issue_operation op; + op.issuer = amount.asset_id(db).issuer; + op.asset_to_issue = amount; + op.issue_to_account = recipient.id; + trx.operations.push_back(op); + db.push_transaction( trx, ~0 ); + trx.operations.clear(); +} + +void son_fixture::issue_uia( account_id_type recipient_id, asset amount ) +{ + issue_uia( recipient_id(db), amount ); +} + +void son_fixture::change_fees( + const flat_set< fee_parameters >& new_params, + uint32_t new_scale /* = 0 */ + ) +{ + const chain_parameters& current_chain_params = db.get_global_properties().parameters; + const fee_schedule& current_fees = *(current_chain_params.current_fees); + + flat_map< int, fee_parameters > fee_map; + fee_map.reserve( current_fees.parameters.size() ); + for( const fee_parameters& op_fee : current_fees.parameters ) + fee_map[ op_fee.which() ] = op_fee; + for( const fee_parameters& new_fee : new_params ) + fee_map[ new_fee.which() ] = new_fee; + + fee_schedule_type new_fees; + + for( const std::pair< int, fee_parameters >& item : fee_map ) + new_fees.parameters.insert( item.second ); + if( new_scale != 0 ) + new_fees.scale = new_scale; + + chain_parameters new_chain_params = current_chain_params; + new_chain_params.current_fees = new_fees; + + db.modify(db.get_global_properties(), [&](global_property_object& p) { + p.parameters = new_chain_params; + }); +} + +const account_object& son_fixture::create_account( + const string& name, + const public_key_type& key /* = public_key_type() */ + ) +{ + trx.operations.push_back(make_account(name, key)); + trx.validate(); + processed_transaction ptx = db.push_transaction(trx, ~0); + auto& result = db.get(ptx.operation_results[0].get()); + trx.operations.clear(); + return result; +} + +const account_object& son_fixture::create_account( + const string& name, + const account_object& registrar, + const account_object& referrer, + uint8_t referrer_percent /* = 100 */, + const public_key_type& key /*= public_key_type()*/ + ) +{ + try + { + trx.operations.resize(1); + trx.operations.back() = (make_account(name, registrar, referrer, referrer_percent, key)); + trx.validate(); + auto r = db.push_transaction(trx, ~0); + const auto& result = db.get(r.operation_results[0].get()); + trx.operations.clear(); + return result; + } + FC_CAPTURE_AND_RETHROW( (name)(registrar)(referrer) ) +} + +const account_object& son_fixture::create_account( + const string& name, + const private_key_type& key, + const account_id_type& registrar_id /* = account_id_type() */, + const account_id_type& referrer_id /* = account_id_type() */, + uint8_t referrer_percent /* = 100 */ + ) +{ + try + { + trx.operations.clear(); + + account_create_operation account_create_op; + + account_create_op.registrar = registrar_id; + account_create_op.name = name; + account_create_op.owner = authority(1234, public_key_type(key.get_public_key()), 1234); + account_create_op.active = authority(5678, public_key_type(key.get_public_key()), 5678); + account_create_op.options.memo_key = key.get_public_key(); + account_create_op.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT; + trx.operations.push_back( account_create_op ); + + trx.validate(); + + processed_transaction ptx = db.push_transaction(trx, ~0); + const account_object& result = db.get(ptx.operation_results[0].get()); + trx.operations.clear(); + return result; + } + FC_CAPTURE_AND_RETHROW( (name)(registrar_id)(referrer_id) ) +} + +const committee_member_object& son_fixture::create_committee_member( const account_object& owner ) +{ + committee_member_create_operation op; + op.committee_member_account = owner.id; + trx.operations.push_back(op); + trx.validate(); + processed_transaction ptx = db.push_transaction(trx, ~0); + trx.operations.clear(); + return db.get(ptx.operation_results[0].get()); +} + +const witness_object&son_fixture::create_witness(account_id_type owner, const fc::ecc::private_key& signing_private_key) +{ + return create_witness(owner(db), signing_private_key); +} + +const witness_object& son_fixture::create_witness( const account_object& owner, + const fc::ecc::private_key& signing_private_key ) +{ try { + witness_create_operation op; + op.witness_account = owner.id; + op.block_signing_key = signing_private_key.get_public_key(); + + secret_hash_type::encoder enc; + fc::raw::pack(enc, signing_private_key); + fc::raw::pack(enc, secret_hash_type()); + op.initial_secret = secret_hash_type::hash(enc.result()); + trx.operations.push_back(op); + trx.validate(); + processed_transaction ptx = db.push_transaction(trx, ~0); + trx.clear(); + return db.get(ptx.operation_results[0].get()); + } FC_CAPTURE_AND_RETHROW() } + +vesting_balance_id_type son_fixture::create_vesting(account_id_type user, unsigned int amount, vesting_balance_type vesting_type) +{ + vesting_balance_create_operation op; + op.creator = user; + op.owner = user; + op.amount = asset(amount*GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_type; + op.policy = dormant_vesting_policy_initializer {}; + trx.operations.push_back(op); + trx.validate(); + processed_transaction ptx = db.push_transaction(trx, ~0); + trx.clear(); + return ptx.operation_results[0].get(); +} + +son_id_type son_fixture::create_son(const std::string &name, const std::string &url, const fc::ecc::private_key &signing_key, const fc::ecc::public_key &btc_key) +{ + auto owner = create_account(name, signing_key.get_public_key()); + upgrade_to_lifetime_member(owner); + transfer( committee_account, owner.id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + auto deposit = create_vesting(owner.id, 50, vesting_balance_type::son); + auto payment = create_vesting(owner.id, 1, vesting_balance_type::normal); + + flat_map sidechain_public_keys; + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = + btc_key.to_base58(); + + son_create_operation op; + op.owner_account = owner.id; + op.url = url; + op.deposit = deposit; + op.pay_vb = payment; + op.signing_key = signing_key.get_public_key(); + op.sidechain_public_keys = sidechain_public_keys; + trx.operations.push_back(op); + sign(trx, signing_key); + processed_transaction ptx = db.push_transaction(trx, ~0); + trx.clear(); + return ptx.operation_results[0].get(); +} + +uint64_t son_fixture::fund( + const account_object& account, + const asset& amount /* = asset(500000) */ + ) +{ + transfer(account_id_type()(db), account, amount); + return get_balance(account, amount.asset_id(db)); +} + +void son_fixture::sign(signed_transaction& trx, const fc::ecc::private_key& key) +{ + trx.sign( key, db.get_chain_id() ); +} + +digest_type son_fixture::digest( const transaction& tx ) +{ + return tx.digest(); +} + +const limit_order_object*son_fixture::create_sell_order(account_id_type user, const asset& amount, const asset& recv) +{ + auto r = create_sell_order(user(db), amount, recv); + verify_asset_supplies(db); + return r; +} + +const limit_order_object* son_fixture::create_sell_order( const account_object& user, const asset& amount, const asset& recv ) +{ + limit_order_create_operation buy_order; + buy_order.seller = user.id; + buy_order.amount_to_sell = amount; + buy_order.min_to_receive = recv; + trx.operations.push_back(buy_order); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + trx.validate(); + auto processed = db.push_transaction(trx, ~0); + trx.operations.clear(); + verify_asset_supplies(db); + return db.find( processed.operation_results[0].get() ); +} + +asset son_fixture::cancel_limit_order( const limit_order_object& order ) +{ + limit_order_cancel_operation cancel_order; + cancel_order.fee_paying_account = order.seller; + cancel_order.order = order.id; + trx.operations.push_back(cancel_order); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + trx.validate(); + auto processed = db.push_transaction(trx, ~0); + trx.operations.clear(); + verify_asset_supplies(db); + return processed.operation_results[0].get(); +} + +void son_fixture::transfer( + account_id_type from, + account_id_type to, + const asset& amount, + const asset& fee /* = asset() */ + ) +{ + transfer(from(db), to(db), amount, fee); +} + +void son_fixture::transfer( + const account_object& from, + const account_object& to, + const asset& amount, + const asset& fee /* = asset() */ ) +{ + try + { + set_expiration( db, trx ); + transfer_operation trans; + trans.from = from.id; + trans.to = to.id; + trans.amount = amount; + trx.operations.push_back(trans); + + if( fee == asset() ) + { + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + } + trx.validate(); + db.push_transaction(trx, ~0); + verify_asset_supplies(db); + trx.operations.clear(); + } FC_CAPTURE_AND_RETHROW( (from.id)(to.id)(amount)(fee) ) +} + +void son_fixture::update_feed_producers( const asset_object& mia, flat_set producers ) +{ try { + set_expiration( db, trx ); + trx.operations.clear(); + asset_update_feed_producers_operation op; + op.asset_to_update = mia.id; + op.issuer = mia.issuer; + op.new_feed_producers = std::move(producers); + trx.operations = {std::move(op)}; + + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + trx.validate(); + db.push_transaction(trx, ~0); + trx.operations.clear(); + verify_asset_supplies(db); +} FC_CAPTURE_AND_RETHROW( (mia)(producers) ) } + +void son_fixture::publish_feed( const asset_object& mia, const account_object& by, const price_feed& f ) +{ + set_expiration( db, trx ); + trx.operations.clear(); + + asset_publish_feed_operation op; + op.publisher = by.id; + op.asset_id = mia.id; + op.feed = f; + if( op.feed.core_exchange_rate.is_null() ) + op.feed.core_exchange_rate = op.feed.settlement_price; + trx.operations.emplace_back( std::move(op) ); + + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + trx.validate(); + db.push_transaction(trx, ~0); + trx.operations.clear(); + verify_asset_supplies(db); +} + +void son_fixture::force_global_settle( const asset_object& what, const price& p ) +{ try { + set_expiration( db, trx ); + trx.operations.clear(); + asset_global_settle_operation sop; + sop.issuer = what.issuer; + sop.asset_to_settle = what.id; + sop.settle_price = p; + trx.operations.push_back(sop); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + trx.validate(); + db.push_transaction(trx, ~0); + trx.operations.clear(); + verify_asset_supplies(db); +} FC_CAPTURE_AND_RETHROW( (what)(p) ) } + +operation_result son_fixture::force_settle( const account_object& who, asset what ) +{ try { + set_expiration( db, trx ); + trx.operations.clear(); + asset_settle_operation sop; + sop.account = who.id; + sop.amount = what; + trx.operations.push_back(sop); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + trx.validate(); + processed_transaction ptx = db.push_transaction(trx, ~0); + const operation_result& op_result = ptx.operation_results.front(); + trx.operations.clear(); + verify_asset_supplies(db); + return op_result; +} FC_CAPTURE_AND_RETHROW( (who)(what) ) } + +const call_order_object* son_fixture::borrow(const account_object& who, asset what, asset collateral) +{ try { + set_expiration( db, trx ); + trx.operations.clear(); + call_order_update_operation update; + update.funding_account = who.id; + update.delta_collateral = collateral; + update.delta_debt = what; + trx.operations.push_back(update); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + trx.validate(); + db.push_transaction(trx, ~0); + trx.operations.clear(); + verify_asset_supplies(db); + + auto& call_idx = db.get_index_type().indices().get(); + auto itr = call_idx.find( boost::make_tuple(who.id, what.asset_id) ); + const call_order_object* call_obj = nullptr; + + if( itr != call_idx.end() ) + call_obj = &*itr; + return call_obj; +} FC_CAPTURE_AND_RETHROW( (who.name)(what)(collateral) ) } + +void son_fixture::cover(const account_object& who, asset what, asset collateral) +{ try { + set_expiration( db, trx ); + trx.operations.clear(); + call_order_update_operation update; + update.funding_account = who.id; + update.delta_collateral = -collateral; + update.delta_debt = -what; + trx.operations.push_back(update); + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + trx.validate(); + db.push_transaction(trx, ~0); + trx.operations.clear(); + verify_asset_supplies(db); +} FC_CAPTURE_AND_RETHROW( (who.name)(what)(collateral) ) } + +void son_fixture::fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount ) +{ + asset_fund_fee_pool_operation fund; + fund.from_account = from.id; + fund.asset_id = asset_to_fund.id; + fund.amount = amount; + trx.operations.push_back( fund ); + + for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); + trx.validate(); + db.push_transaction(trx, ~0); + trx.operations.clear(); + verify_asset_supplies(db); +} + +void son_fixture::enable_fees() +{ + db.modify(global_property_id_type()(db), [](global_property_object& gpo) + { + gpo.parameters.current_fees = fee_schedule::get_default(); + }); +} + +void son_fixture::upgrade_to_lifetime_member(account_id_type account) +{ + upgrade_to_lifetime_member(account(db)); +} + +void son_fixture::upgrade_to_lifetime_member( const account_object& account ) +{ + try + { + account_upgrade_operation op; + op.account_to_upgrade = account.get_id(); + op.upgrade_to_lifetime_member = true; + op.fee = db.get_global_properties().parameters.current_fees->calculate_fee(op); + trx.operations = {op}; + db.push_transaction(trx, ~0); + FC_ASSERT( op.account_to_upgrade(db).is_lifetime_member() ); + trx.clear(); + verify_asset_supplies(db); + } + FC_CAPTURE_AND_RETHROW((account)) +} + +void son_fixture::upgrade_to_annual_member(account_id_type account) +{ + upgrade_to_annual_member(account(db)); +} + +void son_fixture::upgrade_to_annual_member(const account_object& account) +{ + try { + account_upgrade_operation op; + op.account_to_upgrade = account.get_id(); + op.fee = db.get_global_properties().parameters.current_fees->calculate_fee(op); + trx.operations = {op}; + db.push_transaction(trx, ~0); + FC_ASSERT( op.account_to_upgrade(db).is_member(db.head_block_time()) ); + trx.clear(); + verify_asset_supplies(db); + } FC_CAPTURE_AND_RETHROW((account)) +} + +void son_fixture::print_market( const string& syma, const string& symb )const +{ + const auto& limit_idx = db.get_index_type(); + const auto& price_idx = limit_idx.indices().get(); + + cerr << std::fixed; + cerr.precision(5); + cerr << std::setw(10) << std::left << "NAME" << " "; + cerr << std::setw(16) << std::right << "FOR SALE" << " "; + cerr << std::setw(16) << std::right << "FOR WHAT" << " "; + cerr << std::setw(10) << std::right << "PRICE (S/W)" << " "; + cerr << std::setw(10) << std::right << "1/PRICE (W/S)" << "\n"; + cerr << string(70, '=') << std::endl; + auto cur = price_idx.begin(); + while( cur != price_idx.end() ) + { + cerr << std::setw( 10 ) << std::left << cur->seller(db).name << " "; + cerr << std::setw( 10 ) << std::right << cur->for_sale.value << " "; + cerr << std::setw( 5 ) << std::left << cur->amount_for_sale().asset_id(db).symbol << " "; + cerr << std::setw( 10 ) << std::right << cur->amount_to_receive().amount.value << " "; + cerr << std::setw( 5 ) << std::left << cur->amount_to_receive().asset_id(db).symbol << " "; + cerr << std::setw( 10 ) << std::right << cur->sell_price.to_real() << " "; + cerr << std::setw( 10 ) << std::right << (~cur->sell_price).to_real() << " "; + cerr << "\n"; + ++cur; + } +} + +string son_fixture::pretty( const asset& a )const +{ + std::stringstream ss; + ss << a.amount.value << " "; + ss << a.asset_id(db).symbol; + return ss.str(); +} + +void son_fixture::print_limit_order( const limit_order_object& cur )const +{ + std::cout << std::setw(10) << cur.seller(db).name << " "; + std::cout << std::setw(10) << "LIMIT" << " "; + std::cout << std::setw(16) << pretty( cur.amount_for_sale() ) << " "; + std::cout << std::setw(16) << pretty( cur.amount_to_receive() ) << " "; + std::cout << std::setw(16) << cur.sell_price.to_real() << " "; +} + +void son_fixture::print_call_orders()const +{ + cout << std::fixed; + cout.precision(5); + cout << std::setw(10) << std::left << "NAME" << " "; + cout << std::setw(10) << std::right << "TYPE" << " "; + cout << std::setw(16) << std::right << "DEBT" << " "; + cout << std::setw(16) << std::right << "COLLAT" << " "; + cout << std::setw(16) << std::right << "CALL PRICE(D/C)" << " "; + cout << std::setw(16) << std::right << "~CALL PRICE(C/D)" << " "; + cout << std::setw(16) << std::right << "SWAN(D/C)" << " "; + cout << std::setw(16) << std::right << "SWAN(C/D)" << "\n"; + cout << string(70, '='); + + for( const call_order_object& o : db.get_index_type().indices() ) + { + std::cout << "\n"; + cout << std::setw( 10 ) << std::left << o.borrower(db).name << " "; + cout << std::setw( 16 ) << std::right << pretty( o.get_debt() ) << " "; + cout << std::setw( 16 ) << std::right << pretty( o.get_collateral() ) << " "; + cout << std::setw( 16 ) << std::right << o.call_price.to_real() << " "; + cout << std::setw( 16 ) << std::right << (~o.call_price).to_real() << " "; + cout << std::setw( 16 ) << std::right << (o.get_debt()/o.get_collateral()).to_real() << " "; + cout << std::setw( 16 ) << std::right << (~(o.get_debt()/o.get_collateral())).to_real() << " "; + } + std::cout << "\n"; +} + +void son_fixture::print_joint_market( const string& syma, const string& symb )const +{ + cout << std::fixed; + cout.precision(5); + + cout << std::setw(10) << std::left << "NAME" << " "; + cout << std::setw(10) << std::right << "TYPE" << " "; + cout << std::setw(16) << std::right << "FOR SALE" << " "; + cout << std::setw(16) << std::right << "FOR WHAT" << " "; + cout << std::setw(16) << std::right << "PRICE (S/W)" << "\n"; + cout << string(70, '='); + + const auto& limit_idx = db.get_index_type(); + const auto& limit_price_idx = limit_idx.indices().get(); + + auto limit_itr = limit_price_idx.begin(); + while( limit_itr != limit_price_idx.end() ) + { + std::cout << std::endl; + print_limit_order( *limit_itr ); + ++limit_itr; + } +} + +int64_t son_fixture::get_balance( account_id_type account, asset_id_type a )const +{ + return db.get_balance(account, a).amount.value; +} + +int64_t son_fixture::get_balance( const account_object& account, const asset_object& a )const +{ + return db.get_balance(account.get_id(), a.get_id()).amount.value; +} + +int64_t son_fixture::get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type, + account_id_type dividend_holder_account_id, + asset_id_type dividend_payout_asset_type) const +{ + const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index = + db.get_index_type(); + auto pending_payout_iter = + pending_payout_balance_index.indices().get().find(boost::make_tuple(dividend_holder_asset_type, dividend_payout_asset_type, dividend_holder_account_id)); + if (pending_payout_iter == pending_payout_balance_index.indices().get().end()) + return 0; + else + return pending_payout_iter->pending_balance.value; +} + +vector< operation_history_object > son_fixture::get_operation_history( account_id_type account_id )const +{ + vector< operation_history_object > result; + const auto& stats = account_id(db).statistics(db); + if(stats.most_recent_op == account_transaction_history_id_type()) + return result; + + const account_transaction_history_object* node = &stats.most_recent_op(db); + while( true ) + { + result.push_back( node->operation_id(db) ); + if(node->next == account_transaction_history_id_type()) + break; + node = db.find(node->next); + } + return result; +} + +void son_fixture::process_operation_by_witnesses(operation op) +{ + const flat_set& active_witnesses = db.get_global_properties().active_witnesses; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = (*active_witnesses.begin())(db).witness_account; + proposal_op.proposed_ops.emplace_back(op); + proposal_op.expiration_time = db.head_block_time() + fc::days(1); + + signed_transaction tx; + tx.operations.push_back(proposal_op); + set_expiration(db, tx); + sign(tx, init_account_priv_key); + + processed_transaction processed_tx = db.push_transaction(tx); + proposal_id_type proposal_id = processed_tx.operation_results[0].get(); + + for (const witness_id_type& witness_id : active_witnesses) + { + const witness_object& witness = witness_id(db); + const account_object& witness_account = witness.witness_account(db); + + proposal_update_operation pup; + pup.proposal = proposal_id; + pup.fee_paying_account = witness_account.id; + pup.active_approvals_to_add.insert(witness_account.id); + + signed_transaction tx; + tx.operations.push_back( pup ); + set_expiration( db, tx ); + sign(tx, init_account_priv_key); + + db.push_transaction(tx, ~0); + const auto& proposal_idx = db.get_index_type().indices().get(); + if (proposal_idx.find(proposal_id) == proposal_idx.end()) + break; + } +} + +void son_fixture::process_operation_by_committee(operation op) +{ + const vector& active_committee_members = db.get_global_properties().active_committee_members; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = (*active_committee_members.begin())(db).committee_member_account; + proposal_op.proposed_ops.emplace_back(op); + proposal_op.expiration_time = db.head_block_time() + fc::days(1); + + signed_transaction tx; + tx.operations.push_back(proposal_op); + set_expiration(db, tx); + sign(tx, init_account_priv_key); + + processed_transaction processed_tx = db.push_transaction(tx); + proposal_id_type proposal_id = processed_tx.operation_results[0].get(); + + for (const committee_member_id_type& committee_member_id : active_committee_members) + { + const committee_member_object& committee_member = committee_member_id(db); + const account_object& committee_member_account = committee_member.committee_member_account(db); + + proposal_update_operation pup; + pup.proposal = proposal_id; + pup.fee_paying_account = committee_member_account.id; + pup.active_approvals_to_add.insert(committee_member_account.id); + + signed_transaction tx; + tx.operations.push_back( pup ); + set_expiration( db, tx ); + sign(tx, init_account_priv_key); + + db.push_transaction(tx, ~0); + const auto& proposal_idx = db.get_index_type().indices().get(); + if (proposal_idx.find(proposal_id) == proposal_idx.end()) + break; + } +} + +void son_fixture::force_operation_by_witnesses(operation op) +{ + const chain_parameters& params = db.get_global_properties().parameters; + signed_transaction trx; + trx.operations = {op}; + for( auto& op : trx.operations ) + db.current_fee_schedule().set_fee(op); + trx.validate(); + trx.set_expiration(db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3)); + sign(trx, init_account_priv_key); + PUSH_TX(db, trx); +} + +void son_fixture::set_is_proposed_trx(operation op) +{ + const flat_set& active_witnesses = db.get_global_properties().active_witnesses; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = (*active_witnesses.begin())(db).witness_account; + proposal_op.proposed_ops.emplace_back(op); + proposal_op.expiration_time = db.head_block_time() + fc::days(1); + + signed_transaction tx; + tx.operations.push_back(proposal_op); + set_expiration(db, tx); + sign(tx, init_account_priv_key); + + processed_transaction processed_tx = db.push_transaction(tx); + proposal_id_type proposal_id = processed_tx.operation_results[0].get(); + + for (const witness_id_type& witness_id : active_witnesses) + { + const witness_object& witness = witness_id(db); + const account_object& witness_account = witness.witness_account(db); + + proposal_update_operation pup; + pup.proposal = proposal_id; + pup.fee_paying_account = witness_account.id; + pup.active_approvals_to_add.insert(witness_account.id); + + db.push_proposal(pup.proposal(db)); + break; + } +} + +proposal_id_type son_fixture::propose_operation(operation op) +{ + const flat_set& active_witnesses = db.get_global_properties().active_witnesses; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = (*active_witnesses.begin())(db).witness_account; + proposal_op.proposed_ops.emplace_back(op); + proposal_op.expiration_time = db.head_block_time() + fc::days(1); + + signed_transaction tx; + tx.operations.push_back(proposal_op); + set_expiration(db, tx); + sign(tx, init_account_priv_key); + + processed_transaction processed_tx = db.push_transaction(tx); + proposal_id_type proposal_id = processed_tx.operation_results[0].get(); + + return proposal_id; +} + +void son_fixture::process_proposal_by_witnesses(const std::vector& witnesses, proposal_id_type proposal_id, bool remove) +{ + const auto& proposal_idx = db.get_index_type().indices().get(); + + for (const witness_id_type& witness_id : witnesses) + { + if (proposal_idx.find(proposal_id) == proposal_idx.end()) + break; + + const witness_object& witness = witness_id(db); + const account_object& witness_account = witness.witness_account(db); + + proposal_update_operation pup; + pup.proposal = proposal_id; + pup.fee_paying_account = witness_account.id; + if (remove) + pup.active_approvals_to_remove.insert(witness_account.id); + else + pup.active_approvals_to_add.insert(witness_account.id); + + signed_transaction tx; + tx.operations.push_back( pup ); + set_expiration( db, tx ); + sign(tx, init_account_priv_key); + + db.push_transaction(tx, ~0); + } +} + + +namespace test { + +void set_expiration( const database& db, transaction& tx ) +{ + const chain_parameters& params = db.get_global_properties().parameters; + tx.set_reference_block(db.head_block_id()); + tx.set_expiration( db.head_block_time() + fc::seconds( params.block_interval * (params.maintenance_skip_slots + 1) * 3 ) ); +} + +bool _push_block( database& db, const signed_block& b, uint32_t skip_flags /* = 0 */ ) +{ + return db.push_block( b, skip_flags); +} + +processed_transaction _push_transaction( database& db, const signed_transaction& tx, uint32_t skip_flags /* = 0 */ ) +{ try { + auto pt = db.push_transaction( tx, skip_flags ); + son_fixture::verify_asset_supplies(db); + return pt; +} FC_CAPTURE_AND_RETHROW((tx)) } + +} // graphene::chain::test + +} } // graphene::chain diff --git a/tests/peerplays_sidechain/son_fixture.hpp b/tests/peerplays_sidechain/son_fixture.hpp new file mode 100644 index 00000000..477201f5 --- /dev/null +++ b/tests/peerplays_sidechain/son_fixture.hpp @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2015 Cryptonomex, Inc., and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once + +#include +#include +#include +#include + +#include + +#include + +#include + +using namespace graphene::db; + +extern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP; + +#define PUSH_TX \ + graphene::chain::test::_push_transaction + +#define PUSH_BLOCK \ + graphene::chain::test::_push_block + +// See below +#define REQUIRE_OP_VALIDATION_SUCCESS( op, field, value ) \ +{ \ + const auto temp = op.field; \ + op.field = value; \ + op.validate(); \ + op.field = temp; \ +} +#define REQUIRE_OP_EVALUATION_SUCCESS( op, field, value ) \ +{ \ + const auto temp = op.field; \ + op.field = value; \ + trx.operations.back() = op; \ + op.field = temp; \ + db.push_transaction( trx, ~0 ); \ +} + +#define GRAPHENE_REQUIRE_THROW( expr, exc_type ) \ +{ \ + std::string req_throw_info = fc::json::to_string( \ + fc::mutable_variant_object() \ + ("source_file", __FILE__) \ + ("source_lineno", __LINE__) \ + ("expr", #expr) \ + ("exc_type", #exc_type) \ + ); \ + if( fc::enable_record_assert_trip ) \ + std::cout << "GRAPHENE_REQUIRE_THROW begin " \ + << req_throw_info << std::endl; \ + BOOST_REQUIRE_THROW( expr, exc_type ); \ + if( fc::enable_record_assert_trip ) \ + std::cout << "GRAPHENE_REQUIRE_THROW end " \ + << req_throw_info << std::endl; \ +} + +#define GRAPHENE_CHECK_THROW( expr, exc_type ) \ +{ \ + std::string req_throw_info = fc::json::to_string( \ + fc::mutable_variant_object() \ + ("source_file", __FILE__) \ + ("source_lineno", __LINE__) \ + ("expr", #expr) \ + ("exc_type", #exc_type) \ + ); \ + if( fc::enable_record_assert_trip ) \ + std::cout << "GRAPHENE_CHECK_THROW begin " \ + << req_throw_info << std::endl; \ + BOOST_CHECK_THROW( expr, exc_type ); \ + if( fc::enable_record_assert_trip ) \ + std::cout << "GRAPHENE_CHECK_THROW end " \ + << req_throw_info << std::endl; \ +} + +#define REQUIRE_OP_VALIDATION_FAILURE_2( op, field, value, exc_type ) \ +{ \ + const auto temp = op.field; \ + op.field = value; \ + GRAPHENE_REQUIRE_THROW( op.validate(), exc_type ); \ + op.field = temp; \ +} +#define REQUIRE_OP_VALIDATION_FAILURE( op, field, value ) \ + REQUIRE_OP_VALIDATION_FAILURE_2( op, field, value, fc::exception ) + +#define REQUIRE_THROW_WITH_VALUE_2(op, field, value, exc_type) \ +{ \ + auto bak = op.field; \ + op.field = value; \ + trx.operations.back() = op; \ + op.field = bak; \ + GRAPHENE_REQUIRE_THROW(db.push_transaction(trx, ~0), exc_type); \ +} + +#define REQUIRE_THROW_WITH_VALUE( op, field, value ) \ + REQUIRE_THROW_WITH_VALUE_2( op, field, value, fc::exception ) + +///This simply resets v back to its default-constructed value. Requires v to have a working assingment operator and +/// default constructor. +#define RESET(v) v = decltype(v)() +///This allows me to build consecutive test cases. It's pretty ugly, but it works well enough for unit tests. +/// i.e. This allows a test on update_account to begin with the database at the end state of create_account. +#define INVOKE(test) ((struct test*)this)->test_method(); trx.clear() + +#define PREP_ACTOR(name) \ + fc::ecc::private_key name ## _private_key = generate_private_key(BOOST_PP_STRINGIZE(name)); \ + public_key_type name ## _public_key = name ## _private_key.get_public_key(); + +#define ACTOR(name) \ + PREP_ACTOR(name) \ + const auto& name = create_account(BOOST_PP_STRINGIZE(name), name ## _public_key); \ + account_id_type name ## _id = name.id; (void)name ## _id; + +#define GET_ACTOR(name) \ + fc::ecc::private_key name ## _private_key = generate_private_key(BOOST_PP_STRINGIZE(name)); \ + const account_object& name = get_account(BOOST_PP_STRINGIZE(name)); \ + account_id_type name ## _id = name.id; \ + (void)name ##_id + +#define ACTORS_IMPL(r, data, elem) ACTOR(elem) +#define ACTORS(names) BOOST_PP_SEQ_FOR_EACH(ACTORS_IMPL, ~, names) + +namespace graphene { namespace chain { + + namespace keywords { + BOOST_PARAMETER_NAME(event_id) + BOOST_PARAMETER_NAME(event_group_id) + BOOST_PARAMETER_NAME(name) + BOOST_PARAMETER_NAME(season) + BOOST_PARAMETER_NAME(status) + BOOST_PARAMETER_NAME(force) + BOOST_PARAMETER_NAME(betting_market_group_id) + BOOST_PARAMETER_NAME(description) + BOOST_PARAMETER_NAME(rules_id) + } + +struct son_fixture { + // the reason we use an app is to exercise the indexes of built-in + // plugins + graphene::app::application app; + genesis_state_type genesis_state; + chain::database &db; + signed_transaction trx; + public_key_type committee_key; + account_id_type committee_account; + fc::ecc::private_key private_key = fc::ecc::private_key::generate(); + fc::ecc::private_key init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); + public_key_type init_account_pub_key; + + optional data_dir; + bool skip_key_index_test = false; + uint32_t anon_acct_count; + + son_fixture(); + ~son_fixture(); + + static fc::ecc::private_key generate_private_key(string seed); + string generate_anon_acct_name(); + static void verify_asset_supplies( const database& db ); + void verify_account_history_plugin_index( )const; + void open_database(); + signed_block generate_block(uint32_t skip = ~0, + const fc::ecc::private_key& key = generate_private_key("null_key"), + int miss_blocks = 0); + + /** + * @brief Generates block_count blocks + * @param block_count number of blocks to generate + */ + void generate_blocks(uint32_t block_count); + + /** + * @brief Generates blocks until the head block time matches or exceeds timestamp + * @param timestamp target time to generate blocks until + */ + void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true, uint32_t skip = ~0); + + /////////// + /// @brief Skip intermediate blocks, and generate a maintenance block + /// @returns true on success + /////////// + bool generate_maintenance_block(); + + account_create_operation make_account( + const std::string& name = "nathan", + public_key_type = public_key_type() + ); + + account_create_operation make_account( + const std::string& name, + const account_object& registrar, + const account_object& referrer, + uint8_t referrer_percent = 100, + public_key_type key = public_key_type() + ); + + void force_global_settle(const asset_object& what, const price& p); + operation_result force_settle(account_id_type who, asset what) + { return force_settle(who(db), what); } + operation_result force_settle(const account_object& who, asset what); + void update_feed_producers(asset_id_type mia, flat_set producers) + { update_feed_producers(mia(db), producers); } + void update_feed_producers(const asset_object& mia, flat_set producers); + void publish_feed(asset_id_type mia, account_id_type by, const price_feed& f) + { publish_feed(mia(db), by(db), f); } + void publish_feed(const asset_object& mia, const account_object& by, const price_feed& f); + const call_order_object* borrow(account_id_type who, asset what, asset collateral) + { return borrow(who(db), what, collateral); } + const call_order_object* borrow(const account_object& who, asset what, asset collateral); + void cover(account_id_type who, asset what, asset collateral_freed) + { cover(who(db), what, collateral_freed); } + void cover(const account_object& who, asset what, asset collateral_freed); + + const asset_object& get_asset( const string& symbol )const; + const account_object& get_account( const string& name )const; + const asset_object& create_bitasset(const string& name, + account_id_type issuer = GRAPHENE_WITNESS_ACCOUNT, + uint16_t market_fee_percent = 100 /*1%*/, + uint16_t flags = charge_market_fee); + const asset_object& create_prediction_market(const string& name, + account_id_type issuer = GRAPHENE_WITNESS_ACCOUNT, + uint16_t market_fee_percent = 100 /*1%*/, + uint16_t flags = charge_market_fee); + const asset_object& create_user_issued_asset( const string& name ); + const asset_object& create_user_issued_asset( const string& name, + const account_object& issuer, + uint16_t flags ); + void issue_uia( const account_object& recipient, asset amount ); + void issue_uia( account_id_type recipient_id, asset amount ); + + const account_object& create_account( + const string& name, + const public_key_type& key = public_key_type() + ); + + const account_object& create_account( + const string& name, + const account_object& registrar, + const account_object& referrer, + uint8_t referrer_percent = 100, + const public_key_type& key = public_key_type() + ); + + const account_object& create_account( + const string& name, + const private_key_type& key, + const account_id_type& registrar_id = account_id_type(), + const account_id_type& referrer_id = account_id_type(), + uint8_t referrer_percent = 100 + ); + + const committee_member_object& create_committee_member( const account_object& owner ); + const witness_object& create_witness(account_id_type owner, + const fc::ecc::private_key& signing_private_key = generate_private_key("null_key")); + const witness_object& create_witness(const account_object& owner, + const fc::ecc::private_key& signing_private_key = generate_private_key("null_key")); + vesting_balance_id_type create_vesting(account_id_type user, unsigned int amount, vesting_balance_type vesting_type); + son_id_type create_son(const std::string& name, const std::string& url, const fc::ecc::private_key& signing_key, const fc::ecc::public_key& btc_key); + uint64_t fund( const account_object& account, const asset& amount = asset(500000) ); + digest_type digest( const transaction& tx ); + void sign( signed_transaction& trx, const fc::ecc::private_key& key ); + const limit_order_object* create_sell_order( account_id_type user, const asset& amount, const asset& recv ); + const limit_order_object* create_sell_order( const account_object& user, const asset& amount, const asset& recv ); + asset cancel_limit_order( const limit_order_object& order ); + void transfer( account_id_type from, account_id_type to, const asset& amount, const asset& fee = asset() ); + void transfer( const account_object& from, const account_object& to, const asset& amount, const asset& fee = asset() ); + void fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount ); + void enable_fees(); + void change_fees( const flat_set< fee_parameters >& new_params, uint32_t new_scale = 0 ); + void upgrade_to_lifetime_member( account_id_type account ); + void upgrade_to_lifetime_member( const account_object& account ); + void upgrade_to_annual_member( account_id_type account ); + void upgrade_to_annual_member( const account_object& account ); + void print_market( const string& syma, const string& symb )const; + string pretty( const asset& a )const; + void print_limit_order( const limit_order_object& cur )const; + void print_call_orders( )const; + void print_joint_market( const string& syma, const string& symb )const; + int64_t get_balance( account_id_type account, asset_id_type a )const; + int64_t get_balance( const account_object& account, const asset_object& a )const; + int64_t get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type, + account_id_type dividend_holder_account_id, + asset_id_type dividend_payout_asset_type) const; + vector< operation_history_object > get_operation_history( account_id_type account_id )const; + void process_operation_by_witnesses(operation op); + void process_operation_by_committee(operation op); + void force_operation_by_witnesses(operation op); + void set_is_proposed_trx(operation op); + + proposal_id_type propose_operation(operation op); + void process_proposal_by_witnesses(const std::vector& witnesses, proposal_id_type proposal_id, bool remove = false); +}; + +namespace test { +/// set a reasonable expiration time for the transaction +void set_expiration( const database& db, transaction& tx ); + +bool _push_block( database& db, const signed_block& b, uint32_t skip_flags = 0 ); +processed_transaction _push_transaction( database& db, const signed_transaction& tx, uint32_t skip_flags = 0 ); +} + +} }