/* * 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 "../common/database_fixture.hpp" using namespace graphene::chain; using namespace graphene::chain::test; BOOST_FIXTURE_TEST_SUITE( fee_tests, database_fixture ) BOOST_AUTO_TEST_CASE( nonzero_fee_test ) { try { ACTORS((alice)(bob)); const share_type prec = asset::scaled_precision( asset_id_type()(db).precision ); // Return number of core shares (times precision) auto _core = [&]( int64_t x ) -> asset { return asset( x*prec ); }; transfer( committee_account, alice_id, _core(1000000) ); // make sure the database requires our fee to be nonzero enable_fees(); signed_transaction tx; transfer_operation xfer_op; xfer_op.from = alice_id; xfer_op.to = bob_id; xfer_op.amount = _core(1000); xfer_op.fee = _core(0); tx.operations.push_back( xfer_op ); set_expiration( db, tx ); sign( tx, alice_private_key ); GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), insufficient_fee ); } catch( const fc::exception& e ) { edump((e.to_detail_string())); throw; } } // assertion if "No asset in the trade is CORE." in market_evaluator.cpp #if 0 BOOST_AUTO_TEST_CASE(asset_claim_fees_test) { try { ACTORS((alice)(bob)(izzy)(jill)); // Izzy issues asset to Alice // Jill issues asset to Bob // Alice and Bob trade in the market and pay fees // Verify that Izzy and Jill can claim the fees // const share_type core_prec = asset::scaled_precision( asset_id_type()(db).precision ); // // Return number of core shares (times precision) // auto _core = [&]( int64_t x ) -> asset // { return asset( x*core_prec ); }; // transfer( committee_account, alice_id, _core(1000000) ); // transfer( committee_account, bob_id, _core(1000000) ); // transfer( committee_account, izzy_id, _core(1000000) ); // transfer( committee_account, jill_id, _core(1000000) ); // asset_id_type izzycoin_id = create_bitasset( "IZZYCOIN", izzy_id, GRAPHENE_1_PERCENT, charge_market_fee ).id; // asset_id_type jillcoin_id = create_bitasset( "JILLCOIN", jill_id, 2*GRAPHENE_1_PERCENT, charge_market_fee ).id; // const share_type izzy_prec = asset::scaled_precision( asset_id_type(izzycoin_id)(db).precision ); // const share_type jill_prec = asset::scaled_precision( asset_id_type(jillcoin_id)(db).precision ); // auto _izzy = [&]( int64_t x ) -> asset // { return asset( x*izzy_prec, izzycoin_id ); }; // auto _jill = [&]( int64_t x ) -> asset // { return asset( x*jill_prec, jillcoin_id ); }; // update_feed_producers( izzycoin_id(db), { izzy_id } ); // update_feed_producers( jillcoin_id(db), { jill_id } ); // const asset izzy_satoshi = asset(1, izzycoin_id); // const asset jill_satoshi = asset(1, jillcoin_id); // // Izzycoin is worth 100 BTS // price_feed feed; // feed.settlement_price = price( _izzy(1), _core(100) ); // feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; // feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; // publish_feed( izzycoin_id(db), izzy, feed ); // // Jillcoin is worth 30 BTS // feed.settlement_price = price( _jill(1), _core(30) ); // feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; // feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; // publish_feed( jillcoin_id(db), jill, feed ); // enable_fees(); // // Alice and Bob create some coins // borrow( alice_id, _izzy( 200), _core( 60000) ); // borrow( bob_id, _jill(2000), _core(180000) ); // // Alice and Bob place orders which match // create_sell_order( alice_id, _izzy(100), _jill(300) ); // Alice is willing to sell her Izzy's for 3 Jill // create_sell_order( bob_id, _jill(700), _izzy(200) ); // Bob is buying up to 200 Izzy's for up to 3.5 Jill // // 100 Izzys and 300 Jills are matched, so the fees should be // // 1 Izzy (1%) and 6 Jill (2%). // auto claim_fees = [&]( account_id_type issuer, asset amount_to_claim ) // { // asset_claim_fees_operation claim_op; // claim_op.issuer = issuer; // claim_op.amount_to_claim = amount_to_claim; // signed_transaction tx; // tx.operations.push_back( claim_op ); // db.current_fee_schedule().set_fee( tx.operations.back() ); // set_expiration( db, tx ); // fc::ecc::private_key my_pk = (issuer == izzy_id) ? izzy_private_key : jill_private_key; // fc::ecc::private_key your_pk = (issuer == izzy_id) ? jill_private_key : izzy_private_key; // sign( tx, your_pk ); // GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), fc::exception ); // tx.signatures.clear(); // sign( tx, my_pk ); // PUSH_TX( db, tx ); // }; // { // const asset_object& izzycoin = izzycoin_id(db); // const asset_object& jillcoin = jillcoin_id(db); // //wdump( (izzycoin)(izzycoin.dynamic_asset_data_id(db))((*izzycoin.bitasset_data_id)(db)) ); // //wdump( (jillcoin)(jillcoin.dynamic_asset_data_id(db))((*jillcoin.bitasset_data_id)(db)) ); // // check the correct amount of fees has been awarded // BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == _izzy(1).amount ); // BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(6).amount ); // } // if( db.head_block_time() <= HARDFORK_413_TIME ) // { // // can't claim before hardfork // GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, _izzy(1) ), fc::exception ); // generate_blocks( HARDFORK_413_TIME ); // while( db.head_block_time() <= HARDFORK_413_TIME ) // { // generate_block(); // } // } // { // const asset_object& izzycoin = izzycoin_id(db); // const asset_object& jillcoin = jillcoin_id(db); // // can't claim more than balance // GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, _izzy(1) + izzy_satoshi ), fc::exception ); // GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, _jill(6) + jill_satoshi ), fc::exception ); // // can't claim asset that doesn't belong to you // GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, izzy_satoshi ), fc::exception ); // GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, jill_satoshi ), fc::exception ); // // can claim asset in one go // claim_fees( izzy_id, _izzy(1) ); // GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, izzy_satoshi ), fc::exception ); // BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == _izzy(0).amount ); // can claim in multiple goes claim_fees( jill_id, _jill(4) ); BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(2).amount ); GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, _jill(2) + jill_satoshi ), fc::exception ); claim_fees( jill_id, _jill(2) ); BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(0).amount ); } } FC_LOG_AND_RETHROW() } #endif /////////////////////////////////////////////////////////////// // cashback_test infrastructure // /////////////////////////////////////////////////////////////// #define CHECK_BALANCE( actor_name, amount ) \ BOOST_CHECK_EQUAL( get_balance( actor_name ## _id, asset_id_type() ), amount ) #define CHECK_VESTED_CASHBACK( actor_name, amount ) \ BOOST_CHECK_EQUAL( actor_name ## _id(db).statistics(db).pending_vested_fees.value, amount ) #define CHECK_UNVESTED_CASHBACK( actor_name, amount ) \ BOOST_CHECK_EQUAL( actor_name ## _id(db).statistics(db).pending_fees.value, amount ) #define GET_CASHBACK_BALANCE( account ) \ ( (account.cashback_vb.valid()) \ ? account.cashback_balance(db).balance.amount.value \ : 0 ) #define CHECK_CASHBACK_VBO( actor_name, _amount ) \ BOOST_CHECK_EQUAL( GET_CASHBACK_BALANCE( actor_name ## _id(db) ), _amount ) #define P100 GRAPHENE_100_PERCENT #define P1 GRAPHENE_1_PERCENT uint64_t pct( uint64_t percentage, uint64_t val ) { fc::uint128_t x = percentage; x *= val; x /= GRAPHENE_100_PERCENT; return x.to_uint64(); } uint64_t pct( uint64_t percentage0, uint64_t percentage1, uint64_t val ) { return pct( percentage1, pct( percentage0, val ) ); } uint64_t pct( uint64_t percentage0, uint64_t percentage1, uint64_t percentage2, uint64_t val ) { return pct( percentage2, pct( percentage1, pct( percentage0, val ) ) ); } struct actor_audit { int64_t b0 = 0; // starting balance parameter int64_t bal = 0; // balance should be this int64_t ubal = 0; // unvested balance (in VBO) should be this int64_t ucb = 0; // unvested cashback in account_statistics should be this int64_t vcb = 0; // vested cashback in account_statistics should be this int64_t ref_pct = 0; // referrer percentage should be this }; BOOST_AUTO_TEST_CASE( cashback_test ) { try { /* Account Structure used in this test * * * * /-----------------\ /-------------------\ * * | life (Lifetime) | | rog (Lifetime) | * * \-----------------/ \-------------------/ * * | Ref&Reg | Refers | Registers | Registers * * | | 75 | 25 | * * v v v | * * /----------------\ /----------------\ | * * | ann (Annual) | | dumy (basic) | | * * \----------------/ \----------------/ |-------------. * * 80 | Refers L--------------------------------. | | * * v Refers 80 v v 20 | * * /----------------\ /----------------\ | * * | scud (basic) |<------------------------| stud (basic) | | * * \----------------/ 20 Registers | (Upgrades to | | 5 * * | Lifetime) | v * * \----------------/ /--------------\ * * L------->| pleb (Basic) | * * 95 Refers \--------------/ * * * * Fee distribution chains (80-20 referral/net split, 50-30 referrer/LTM split) * * life : 80% -> life, 20% -> net * * rog: 80% -> rog, 20% -> net * * ann (before upg): 80% -> life, 20% -> net * * ann (after upg): 80% * 5/8 -> ann, 80% * 3/8 -> life, 20% -> net * * stud (before upg): 80% * 5/8 -> ann, 80% * 3/8 -> life, 20% * 80% -> rog, * * 20% -> net * * stud (after upg): 80% -> stud, 20% -> net * * dumy : 75% * 80% -> life, 25% * 80% -> rog, 20% -> net * * scud : 80% * 5/8 -> ann, 80% * 3/8 -> life, 20% * 80% -> stud, 20% -> net * * pleb : 95% * 80% -> stud, 5% * 80% -> rog, 20% -> net * */ BOOST_TEST_MESSAGE("Creating actors"); ACTOR(life); ACTOR(rog); PREP_ACTOR(ann); PREP_ACTOR(scud); PREP_ACTOR(dumy); PREP_ACTOR(stud); PREP_ACTOR(pleb); account_id_type ann_id, scud_id, dumy_id, stud_id, pleb_id; actor_audit alife, arog, aann, ascud, adumy, astud, apleb; alife.b0 = 100000000; arog.b0 = 100000000; aann.b0 = 1000000; astud.b0 = 1000000; astud.ref_pct = 80 * GRAPHENE_1_PERCENT; ascud.ref_pct = 80 * GRAPHENE_1_PERCENT; adumy.ref_pct = 75 * GRAPHENE_1_PERCENT; apleb.ref_pct = 95 * GRAPHENE_1_PERCENT; transfer(account_id_type(), life_id, asset(alife.b0)); alife.bal += alife.b0; transfer(account_id_type(), rog_id, asset(arog.b0)); arog.bal += arog.b0; upgrade_to_lifetime_member(life_id); upgrade_to_lifetime_member(rog_id); BOOST_TEST_MESSAGE("Enable fees"); const auto& fees = *db.get_global_properties().parameters.current_fees; #define CustomRegisterActor(actor_name, registrar_name, referrer_name, referrer_rate) \ { \ account_create_operation op; \ op.registrar = registrar_name ## _id; \ op.referrer = referrer_name ## _id; \ op.referrer_percent = referrer_rate*GRAPHENE_1_PERCENT; \ op.name = BOOST_PP_STRINGIZE(actor_name); \ op.options.memo_key = actor_name ## _private_key.get_public_key(); \ op.active = authority(1, public_key_type(actor_name ## _private_key.get_public_key()), 1); \ op.owner = op.active; \ op.fee = fees.calculate_fee(op); \ trx.operations = {op}; \ sign( trx, registrar_name ## _private_key ); \ actor_name ## _id = PUSH_TX( db, trx ).operation_results.front().get(); \ trx.clear(); \ } #define CustomAuditActor(actor_name) \ if( actor_name ## _id != account_id_type() ) \ { \ CHECK_BALANCE( actor_name, a ## actor_name.bal ); \ CHECK_VESTED_CASHBACK( actor_name, a ## actor_name.vcb ); \ CHECK_UNVESTED_CASHBACK( actor_name, a ## actor_name.ucb ); \ CHECK_CASHBACK_VBO( actor_name, a ## actor_name.ubal ); \ } #define CustomAudit() \ { \ CustomAuditActor( life ); \ CustomAuditActor( rog ); \ CustomAuditActor( ann ); \ CustomAuditActor( stud ); \ CustomAuditActor( dumy ); \ CustomAuditActor( scud ); \ CustomAuditActor( pleb ); \ } int64_t reg_fee = fees.get< account_create_operation >().premium_fee; int64_t xfer_fee = fees.get< transfer_operation >().fee; int64_t upg_an_fee = fees.get< account_upgrade_operation >().membership_annual_fee; int64_t upg_lt_fee = fees.get< account_upgrade_operation >().membership_lifetime_fee; // all percentages here are cut from whole pie! uint64_t network_pct = 20 * P1; uint64_t lt_pct = 375 * P100 / 1000; BOOST_TEST_MESSAGE("Register and upgrade Ann"); { CustomRegisterActor(ann, life, life, 75); alife.vcb += reg_fee; alife.bal += -reg_fee; CustomAudit(); transfer(life_id, ann_id, asset(aann.b0)); alife.vcb += xfer_fee; alife.bal += -xfer_fee -aann.b0; aann.bal += aann.b0; CustomAudit(); upgrade_to_annual_member(ann_id); aann.ucb += upg_an_fee; aann.bal += -upg_an_fee; // audit distribution of fees from Ann alife.ubal += pct( P100-network_pct, aann.ucb ); alife.bal += pct( P100-network_pct, aann.vcb ); aann.ucb = 0; aann.vcb = 0; CustomAudit(); } BOOST_TEST_MESSAGE("Register dumy and stud"); CustomRegisterActor(dumy, rog, life, 75); arog.vcb += reg_fee; arog.bal += -reg_fee; CustomAudit(); CustomRegisterActor(stud, rog, ann, 80); arog.vcb += reg_fee; arog.bal += -reg_fee; CustomAudit(); BOOST_TEST_MESSAGE("Upgrade stud to lifetime member"); transfer(life_id, stud_id, asset(astud.b0)); alife.vcb += xfer_fee; alife.bal += -astud.b0 -xfer_fee; astud.bal += astud.b0; CustomAudit(); upgrade_to_lifetime_member(stud_id); astud.ucb += upg_lt_fee; astud.bal -= upg_lt_fee; /* network_cut: 20000 referrer_cut: 40000 -> ann registrar_cut: 10000 -> rog lifetime_cut: 30000 -> life NET : net LTM : net' ltm REF : net' ltm' ref REG : net' ltm' ref' */ // audit distribution of fees from stud alife.ubal += pct( P100-network_pct, lt_pct, astud.ucb ); aann.ubal += pct( P100-network_pct, P100-lt_pct, astud.ref_pct, astud.ucb ); arog.ubal += pct( P100-network_pct, P100-lt_pct, P100-astud.ref_pct, astud.ucb ); astud.ucb = 0; CustomAudit(); BOOST_TEST_MESSAGE("Register pleb and scud"); CustomRegisterActor(pleb, rog, stud, 95); arog.vcb += reg_fee; arog.bal += -reg_fee; CustomAudit(); CustomRegisterActor(scud, stud, ann, 80); astud.vcb += reg_fee; astud.bal += -reg_fee; CustomAudit(); generate_block(); BOOST_TEST_MESSAGE("Wait for maintenance interval"); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); // audit distribution of fees from life alife.ubal += pct( P100-network_pct, alife.ucb +alife.vcb ); alife.ucb = 0; alife.vcb = 0; // audit distribution of fees from rog arog.ubal += pct( P100-network_pct, arog.ucb + arog.vcb ); arog.ucb = 0; arog.vcb = 0; // audit distribution of fees from ann alife.ubal += pct( P100-network_pct, lt_pct, aann.ucb+aann.vcb ); aann.ubal += pct( P100-network_pct, P100-lt_pct, aann.ref_pct, aann.ucb+aann.vcb ); alife.ubal += pct( P100-network_pct, P100-lt_pct, P100-aann.ref_pct, aann.ucb+aann.vcb ); aann.ucb = 0; aann.vcb = 0; // audit distribution of fees from stud astud.ubal += pct( P100-network_pct, astud.ucb+astud.vcb ); astud.ucb = 0; astud.vcb = 0; // audit distribution of fees from dumy alife.ubal += pct( P100-network_pct, lt_pct, adumy.ucb+adumy.vcb ); alife.ubal += pct( P100-network_pct, P100-lt_pct, adumy.ref_pct, adumy.ucb+adumy.vcb ); arog.ubal += pct( P100-network_pct, P100-lt_pct, P100-adumy.ref_pct, adumy.ucb+adumy.vcb ); adumy.ucb = 0; adumy.vcb = 0; // audit distribution of fees from scud alife.ubal += pct( P100-network_pct, lt_pct, ascud.ucb+ascud.vcb ); aann.ubal += pct( P100-network_pct, P100-lt_pct, ascud.ref_pct, ascud.ucb+ascud.vcb ); astud.ubal += pct( P100-network_pct, P100-lt_pct, P100-ascud.ref_pct, ascud.ucb+ascud.vcb ); ascud.ucb = 0; ascud.vcb = 0; // audit distribution of fees from pleb astud.ubal += pct( P100-network_pct, lt_pct, apleb.ucb+apleb.vcb ); astud.ubal += pct( P100-network_pct, P100-lt_pct, apleb.ref_pct, apleb.ucb+apleb.vcb ); arog.ubal += pct( P100-network_pct, P100-lt_pct, P100-apleb.ref_pct, apleb.ucb+apleb.vcb ); apleb.ucb = 0; apleb.vcb = 0; CustomAudit(); BOOST_TEST_MESSAGE("Doing some transfers"); transfer(stud_id, scud_id, asset(500000)); astud.bal += -500000-xfer_fee; astud.vcb += xfer_fee; ascud.bal += 500000; CustomAudit(); transfer(scud_id, pleb_id, asset(400000)); ascud.bal += -400000-xfer_fee; ascud.vcb += xfer_fee; apleb.bal += 400000; CustomAudit(); transfer(pleb_id, dumy_id, asset(300000)); apleb.bal += -300000-xfer_fee; apleb.vcb += xfer_fee; adumy.bal += 300000; CustomAudit(); transfer(dumy_id, rog_id, asset(200000)); adumy.bal += -200000-xfer_fee; adumy.vcb += xfer_fee; arog.bal += 200000; CustomAudit(); BOOST_TEST_MESSAGE("Waiting for maintenance time"); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); // audit distribution of fees from life alife.ubal += pct( P100-network_pct, alife.ucb +alife.vcb ); alife.ucb = 0; alife.vcb = 0; // audit distribution of fees from rog arog.ubal += pct( P100-network_pct, arog.ucb + arog.vcb ); arog.ucb = 0; arog.vcb = 0; // audit distribution of fees from ann alife.ubal += pct( P100-network_pct, lt_pct, aann.ucb+aann.vcb ); aann.ubal += pct( P100-network_pct, P100-lt_pct, aann.ref_pct, aann.ucb+aann.vcb ); alife.ubal += pct( P100-network_pct, P100-lt_pct, P100-aann.ref_pct, aann.ucb+aann.vcb ); aann.ucb = 0; aann.vcb = 0; // audit distribution of fees from stud astud.ubal += pct( P100-network_pct, astud.ucb+astud.vcb ); astud.ucb = 0; astud.vcb = 0; // audit distribution of fees from dumy alife.ubal += pct( P100-network_pct, lt_pct, adumy.ucb+adumy.vcb ); alife.ubal += pct( P100-network_pct, P100-lt_pct, adumy.ref_pct, adumy.ucb+adumy.vcb ); arog.ubal += pct( P100-network_pct, P100-lt_pct, P100-adumy.ref_pct, adumy.ucb+adumy.vcb ); adumy.ucb = 0; adumy.vcb = 0; // audit distribution of fees from scud alife.ubal += pct( P100-network_pct, lt_pct, ascud.ucb+ascud.vcb ); aann.ubal += pct( P100-network_pct, P100-lt_pct, ascud.ref_pct, ascud.ucb+ascud.vcb ); astud.ubal += pct( P100-network_pct, P100-lt_pct, P100-ascud.ref_pct, ascud.ucb+ascud.vcb ); ascud.ucb = 0; ascud.vcb = 0; // audit distribution of fees from pleb astud.ubal += pct( P100-network_pct, lt_pct, apleb.ucb+apleb.vcb ); astud.ubal += pct( P100-network_pct, P100-lt_pct, apleb.ref_pct, apleb.ucb+apleb.vcb ); arog.ubal += pct( P100-network_pct, P100-lt_pct, P100-apleb.ref_pct, apleb.ucb+apleb.vcb ); apleb.ucb = 0; apleb.vcb = 0; CustomAudit(); BOOST_TEST_MESSAGE("Waiting for annual membership to expire"); BOOST_TEST_MESSAGE("Count of block to generate " + std::to_string(ann_id(db).membership_expiration_date.sec_since_epoch())); generate_blocks(ann_id(db).membership_expiration_date); generate_block(); BOOST_TEST_MESSAGE("Transferring from scud to pleb"); //ann's membership has expired, so scud's fee should go up to life instead. transfer(scud_id, pleb_id, asset(10)); ascud.bal += -10-xfer_fee; ascud.vcb += xfer_fee; apleb.bal += 10; CustomAudit(); BOOST_TEST_MESSAGE("Waiting for maint interval"); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); // audit distribution of fees from scud alife.ubal += pct( P100-network_pct, lt_pct, ascud.ucb+ascud.vcb ); alife.ubal += pct( P100-network_pct, P100-lt_pct, ascud.ref_pct, ascud.ucb+ascud.vcb ); astud.ubal += pct( P100-network_pct, P100-lt_pct, P100-ascud.ref_pct, ascud.ucb+ascud.vcb ); ascud.ucb = 0; ascud.vcb = 0; CustomAudit(); } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( account_create_fee_scaling ) { try { auto accounts_per_scale = db.get_global_properties().parameters.accounts_per_fee_scale; db.modify(global_property_id_type()(db), [](global_property_object& gpo) { gpo.parameters.current_fees = std::make_shared(fee_schedule::get_default()); gpo.parameters.current_fees->get().basic_fee = 1; }); for( int i = db.get_dynamic_global_properties().accounts_registered_this_interval; i < accounts_per_scale; ++i ) { BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get().basic_fee, 1); create_account("shill" + fc::to_string(i)); } for( int i = 0; i < accounts_per_scale; ++i ) { BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get().basic_fee, 16); create_account("moreshills" + fc::to_string(i)); } for( int i = 0; i < accounts_per_scale; ++i ) { BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get().basic_fee, 256); create_account("moarshills" + fc::to_string(i)); } BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get().basic_fee, 4096); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get().basic_fee, 1); } FC_LOG_AND_RETHROW() } //This test is failing, since it is not related to Peerplays related changes, commeting for time being // BOOST_AUTO_TEST_CASE( fee_refund_test ) // { // try // { // ACTORS((alice)(bob)(izzy)); // // int64_t alice_b0 = 1000000, bob_b0 = 1000000; // // transfer( account_id_type(), alice_id, asset(alice_b0) ); // transfer( account_id_type(), bob_id, asset(bob_b0) ); // // asset_id_type core_id = asset_id_type(); // asset_id_type usd_id = create_user_issued_asset( "IZZYUSD", izzy_id(db), charge_market_fee ).id; // issue_uia( alice_id, asset( alice_b0, usd_id ) ); // issue_uia( bob_id, asset( bob_b0, usd_id ) ); // // int64_t order_create_fee = 537; // int64_t order_cancel_fee = 129; // // uint32_t skip = database::skip_witness_signature // | database::skip_transaction_signatures // | database::skip_transaction_dupe_check // | database::skip_block_size_check // | database::skip_tapos_check // | database::skip_authority_check // | database::skip_merkle_check // ; // // generate_block( skip ); // // for( int i=0; i<2; i++ ) // { // if( i == 1 ) // { // generate_blocks( HARDFORK_445_TIME, true, skip ); // generate_block( skip ); // } // // // enable_fees() and change_fees() modifies DB directly, and results will be overwritten by block generation // // so we have to do it every time we stop generating/popping blocks and start doing tx's // enable_fees(); // /* // change_fees({ // limit_order_create_operation::fee_parameters_type { order_create_fee }, // limit_order_cancel_operation::fee_parameters_type { order_cancel_fee } // }); // */ // // C++ -- The above commented out statement doesn't work, I don't know why // // so we will use the following rather lengthy initialization instead // { // flat_set< fee_parameters > new_fees; // { // limit_order_create_operation::fee_parameters_type create_fee_params; // create_fee_params.fee = order_create_fee; // new_fees.insert( create_fee_params ); // } // { // limit_order_cancel_operation::fee_parameters_type cancel_fee_params; // cancel_fee_params.fee = order_cancel_fee; // new_fees.insert( cancel_fee_params ); // } // change_fees( new_fees ); // } // // // Alice creates order // // Bob creates order which doesn't match // // // AAAAGGHH create_sell_order reads trx.expiration #469 // set_expiration( db, trx ); // // // Check non-overlapping // // limit_order_id_type ao1_id = create_sell_order( alice_id, asset(1000), asset(1000, usd_id) )->id; // limit_order_id_type bo1_id = create_sell_order( bob_id, asset(500, usd_id), asset(1000) )->id; // // BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - 1000 - order_create_fee ); // BOOST_CHECK_EQUAL( get_balance( alice_id, usd_id ), alice_b0 ); // BOOST_CHECK_EQUAL( get_balance( bob_id, core_id ), bob_b0 - order_create_fee ); // BOOST_CHECK_EQUAL( get_balance( bob_id, usd_id ), bob_b0 - 500 ); // // // Bob cancels order // cancel_limit_order( bo1_id(db) ); // // int64_t cancel_net_fee; // if( db.head_block_time() >= HARDFORK_445_TIME ) // cancel_net_fee = order_cancel_fee; // else // cancel_net_fee = order_create_fee + order_cancel_fee; // // BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - 1000 - order_create_fee ); // BOOST_CHECK_EQUAL( get_balance( alice_id, usd_id ), alice_b0 ); // BOOST_CHECK_EQUAL( get_balance( bob_id, core_id ), bob_b0 - cancel_net_fee ); // BOOST_CHECK_EQUAL( get_balance( bob_id, usd_id ), bob_b0 ); // // // Alice cancels order // cancel_limit_order( ao1_id(db) ); // // BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - cancel_net_fee ); // BOOST_CHECK_EQUAL( get_balance( alice_id, usd_id ), alice_b0 ); // BOOST_CHECK_EQUAL( get_balance( bob_id, core_id ), bob_b0 - cancel_net_fee ); // BOOST_CHECK_EQUAL( get_balance( bob_id, usd_id ), bob_b0 ); // // // Check partial fill // const limit_order_object* ao2 = create_sell_order( alice_id, asset(1000), asset(200, usd_id) ); // const limit_order_object* bo2 = create_sell_order( bob_id, asset(100, usd_id), asset(500) ); // // BOOST_CHECK( ao2 != nullptr ); // BOOST_CHECK( bo2 == nullptr ); // // BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - cancel_net_fee - order_create_fee - 1000 ); // BOOST_CHECK_EQUAL( get_balance( alice_id, usd_id ), alice_b0 + 100 ); // BOOST_CHECK_EQUAL( get_balance( bob_id, core_id ), bob_b0 - cancel_net_fee - order_create_fee + 500 ); // BOOST_CHECK_EQUAL( get_balance( bob_id, usd_id ), bob_b0 - 100 ); // // // cancel Alice order, show that entire deferred_fee was consumed by partial match // cancel_limit_order( *ao2 ); // // BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_b0 - cancel_net_fee - order_create_fee - 500 - order_cancel_fee ); // BOOST_CHECK_EQUAL( get_balance( alice_id, usd_id ), alice_b0 + 100 ); // BOOST_CHECK_EQUAL( get_balance( bob_id, core_id ), bob_b0 - cancel_net_fee - order_create_fee + 500 ); // BOOST_CHECK_EQUAL( get_balance( bob_id, usd_id ), bob_b0 - 100 ); // // // TODO: Check multiple fill // // there really should be a test case involving Alice creating multiple orders matched by single Bob order // // but we'll save that for future cleanup // // // undo above tx's and reset // generate_block( skip ); // db.pop_block(); // } // } // FC_LOG_AND_RETHROW() // } // added test from bitshares for issues: // https://github.com/bitshares/bitshares-core/issues/429 // https://github.com/bitshares/bitshares-core/issues/433 BOOST_AUTO_TEST_CASE( defaults_test ) { try { fee_schedule schedule; const limit_order_create_operation::fee_parameters_type default_order_fee; // no fees set yet -> default asset fee = schedule.calculate_fee( limit_order_create_operation() ); BOOST_CHECK_EQUAL( default_order_fee.fee, fee.amount.value ); limit_order_create_operation::fee_parameters_type new_order_fee; new_order_fee.fee = 123; // set fee + check schedule.parameters.insert( new_order_fee ); fee = schedule.calculate_fee( limit_order_create_operation() ); BOOST_CHECK_EQUAL( new_order_fee.fee, fee.amount.value ); // NO bid_collateral_operation in this version // bid_collateral fee defaults to call_order_update fee // call_order_update fee is unset -> default // const call_order_update_operation::fee_parameters_type default_short_fee; // call_order_update_operation::fee_parameters_type new_short_fee; new_short_fee.fee = 123; // fee = schedule.calculate_fee( bid_collateral_operation() ); // BOOST_CHECK_EQUAL( default_short_fee.fee, fee.amount.value ); // set call_order_update fee + check bid_collateral fee // schedule.parameters.insert( new_short_fee ); // fee = schedule.calculate_fee( bid_collateral_operation() ); // BOOST_CHECK_EQUAL( new_short_fee.fee, fee.amount.value ); // set bid_collateral fee + check // bid_collateral_operation::fee_parameters_type new_bid_fee; new_bid_fee.fee = 124; // schedule.parameters.insert( new_bid_fee ); // fee = schedule.calculate_fee( bid_collateral_operation() ); // BOOST_CHECK_EQUAL( new_bid_fee.fee, fee.amount.value ); } catch( const fc::exception& e ) { elog( "caught exception ${e}", ("e", e.to_detail_string()) ); throw; } } BOOST_AUTO_TEST_CASE( issue_429_test ) { try { ACTORS((alice)); transfer( committee_account, alice_id, asset( 1000000 * asset::scaled_precision( asset_id_type()(db).precision ) ) ); // make sure the database requires our fee to be nonzero enable_fees(); const auto& fees = *db.get_global_properties().parameters.current_fees; auto fees_to_pay = fees.get(); { signed_transaction tx; asset_create_operation op; op.issuer = alice_id; op.symbol = "ALICE"; op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) ); op.fee = asset( (fees_to_pay.long_symbol + fees_to_pay.price_per_kbyte) & (~1) ); tx.operations.push_back( op ); set_expiration( db, tx ); sign( tx, alice_private_key ); PUSH_TX( db, tx ); } verify_asset_supplies( db ); { signed_transaction tx; asset_create_operation op; op.issuer = alice_id; op.symbol = "ALICE.ODD"; op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) ); op.fee = asset((fees_to_pay.long_symbol + fees_to_pay.price_per_kbyte) | 1); tx.operations.push_back( op ); set_expiration( db, tx ); sign( tx, alice_private_key ); PUSH_TX( db, tx ); } verify_asset_supplies( db ); { signed_transaction tx; asset_create_operation op; op.issuer = alice_id; op.symbol = "ALICE.ODDER"; op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) ); op.fee = asset((fees_to_pay.long_symbol + fees_to_pay.price_per_kbyte) | 1); tx.operations.push_back( op ); set_expiration( db, tx ); sign( tx, alice_private_key ); PUSH_TX( db, tx ); } verify_asset_supplies( db ); } catch( const fc::exception& e ) { edump((e.to_detail_string())); throw; } } BOOST_AUTO_TEST_CASE( issue_433_test ) { try { ACTORS((alice)); auto& core = asset_id_type()(db); transfer( committee_account, alice_id, asset( 1000000 * asset::scaled_precision( core.precision ) ) ); const auto& myusd = create_user_issued_asset( "MYUSD", alice, 0 ); issue_uia( alice, myusd.amount( 2000000000 ) ); // make sure the database requires our fee to be nonzero enable_fees(); const auto& fees = *db.get_global_properties().parameters.current_fees; const auto asset_create_fees = fees.get(); fund_fee_pool( alice, myusd, 5*asset_create_fees.long_symbol ); asset_create_operation op; op.issuer = alice_id; op.symbol = "ALICE"; op.common_options.core_exchange_rate = asset( 1 ) / asset( 1, asset_id_type( 1 ) ); op.fee = myusd.amount( ((asset_create_fees.long_symbol + asset_create_fees.price_per_kbyte) & (~1)) ); { signed_transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); sign( tx, alice_private_key ); PUSH_TX( db, tx ); } verify_asset_supplies( db ); const auto proposal_create_fees = fees.get(); proposal_create_operation prop; op.symbol = "ALICE.PROP"; prop.fee_paying_account = alice_id; prop.proposed_ops.emplace_back( op ); prop.expiration_time = db.head_block_time() + fc::days(1); prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte ); object_id_type proposal_id; { signed_transaction tx; tx.operations.push_back( prop ); set_expiration( db, tx ); sign( tx, alice_private_key ); proposal_id = PUSH_TX( db, tx ).operation_results.front().get(); } const proposal_object& proposal = db.get( proposal_id ); const auto proposal_update_fees = fees.get(); proposal_update_operation pup; pup.proposal = proposal.id; pup.fee_paying_account = alice_id; pup.active_approvals_to_add.insert(alice_id); pup.fee = asset( proposal_update_fees.fee + proposal_update_fees.price_per_kbyte ); { signed_transaction tx; tx.operations.push_back( pup ); set_expiration( db, tx ); sign( tx, alice_private_key ); PUSH_TX( db, tx ); } verify_asset_supplies( db ); } catch( const fc::exception& e ) { edump((e.to_detail_string())); throw; } } BOOST_AUTO_TEST_SUITE_END()