From 09970f675ec56a9ed06e796bb1368ee334f26c41 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 10 Apr 2020 10:04:49 -0500 Subject: [PATCH 01/47] Fee max of 30% --- libraries/protocol/chain_parameters.cpp | 4 ++-- tests/tests/bsip86_tests.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/protocol/chain_parameters.cpp b/libraries/protocol/chain_parameters.cpp index 00bf0a3e55..590ce8cbc0 100644 --- a/libraries/protocol/chain_parameters.cpp +++ b/libraries/protocol/chain_parameters.cpp @@ -103,8 +103,8 @@ namespace graphene { namespace protocol { "Committee proposal review period must be less than the maximum proposal lifetime" ); if( extensions.value.market_fee_network_percent.valid() ) { - FC_ASSERT( *extensions.value.market_fee_network_percent <= GRAPHENE_100_PERCENT, - "The market_fee_network_percent parameter can not exceed 100%" ); + FC_ASSERT( *extensions.value.market_fee_network_percent <= 3000, // GRAPHENE_100_PERCENT is 10000 + "The market_fee_network_percent parameter can not exceed 30%" ); } } diff --git a/tests/tests/bsip86_tests.cpp b/tests/tests/bsip86_tests.cpp index bb90e6de37..739720e1ba 100644 --- a/tests/tests/bsip86_tests.cpp +++ b/tests/tests/bsip86_tests.cpp @@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE( hardfork_time_test ) cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; committee_member_update_global_parameters_operation cmuop; - cmuop.new_parameters.extensions.value.market_fee_network_percent = 10001; // 100.01% + cmuop.new_parameters.extensions.value.market_fee_network_percent = 3001; // 30.01% cop.proposed_ops.emplace_back(cmuop); trx.operations.push_back(cop); From c5c763e40959f289d6b44bd294abee32701fd990 Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 17 Apr 2020 15:24:15 -0400 Subject: [PATCH 02/47] Implement BSIP85 Maker order creation fee discount --- libraries/chain/db_market.cpp | 69 ++++++++++++++++--- libraries/chain/hardfork.d/BSIP_85.hf | 6 ++ libraries/chain/proposal_evaluator.cpp | 4 ++ libraries/protocol/chain_parameters.cpp | 11 +++ .../graphene/protocol/chain_parameters.hpp | 5 ++ 5 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 libraries/chain/hardfork.d/BSIP_85.hf diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 6b50a323d1..7f98e458d6 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -812,23 +812,72 @@ bool database::fill_limit_order( const limit_order_object& order, const asset& p assert( pays.asset_id != receives.asset_id ); push_applied_operation( fill_order_operation( order.id, order.seller, pays, receives, issuer_fees, fill_price, is_maker ) ); + // BSIP85: Maker order creation fee discount, https://github.com/bitshares/bsips/blob/master/bsip-0085.md + // if the order creation fee was paid in BTS, + // return round_down(deferred_fee * maker_fee_discount_percent) to the owner, + // then process the remaining deferred fee as before; + // if the order creation fee was paid in another asset, + // return round_down(deferred_paid_fee * maker_fee_discount_percent) to the owner, + // return round_down(deferred_fee * maker_fee_discount_percent) to the fee pool of the asset, + // then process the remaining deferred fee and deferred paid fee as before. + const uint16_t maker_discount_percent = get_global_properties().parameters.get_maker_fee_discount_percent(); + + // Save local copies for calculation + share_type deferred_fee = order.deferred_fee; + share_type deferred_paid_fee = order.deferred_paid_fee.amount; + // conditional because cheap integer comparison may allow us to avoid two expensive modify() and object lookups - if( order.deferred_fee > 0 ) + if( order.deferred_paid_fee.amount > 0 ) // implies head_block_time() > HARDFORK_CORE_604_TIME { - modify( seller.statistics(*this), [&]( account_statistics_object& statistics ) + share_type fee_pool_refund = 0; + if( is_maker && maker_discount_percent > 0 ) { - statistics.pay_fee( order.deferred_fee, get_global_properties().parameters.cashback_vesting_threshold ); - } ); - } + share_type refund = detail::calculate_percent( deferred_paid_fee, maker_discount_percent ); + if( refund > 0 ) + { + FC_ASSERT( refund <= deferred_paid_fee, "Internal error" ); + adjust_balance( order.seller, asset(refund, order.deferred_paid_fee.asset_id) ); + deferred_paid_fee -= refund; + + // deferred_fee might be positive too + FC_ASSERT( deferred_fee > 0, "Internal error" ); + fee_pool_refund = detail::calculate_percent( deferred_fee, maker_discount_percent ); + FC_ASSERT( fee_pool_refund <= deferred_fee, "Internal error" ); + deferred_fee -= fee_pool_refund; + } + } - if( order.deferred_paid_fee.amount > 0 ) // implies head_block_time() > HARDFORK_CORE_604_TIME - { const auto& fee_asset_dyn_data = order.deferred_paid_fee.asset_id(*this).dynamic_asset_data_id(*this); - modify( fee_asset_dyn_data, [&](asset_dynamic_data_object& addo) { - addo.accumulated_fees += order.deferred_paid_fee.amount; + modify( fee_asset_dyn_data, [deferred_paid_fee,fee_pool_refund](asset_dynamic_data_object& addo) { + addo.accumulated_fees += deferred_paid_fee; + addo.fee_pool += fee_pool_refund; }); } + if( order.deferred_fee > 0 ) + { + if( order.deferred_paid_fee.amount <= 0 // paid in CORE, or before HF 604 + && is_maker && maker_discount_percent > 0 ) + { + share_type refund = detail::calculate_percent( deferred_fee, maker_discount_percent ); + if( refund > 0 ) + { + FC_ASSERT( refund <= deferred_fee, "Internal error" ); + adjust_balance( order.seller, asset(refund, asset_id_type()) ); + deferred_fee -= refund; + } + } + // else do nothing here, because we have already processed it above, or no need to process + + if( deferred_fee > 0 ) + { + modify( seller.statistics(*this), [deferred_fee,this]( account_statistics_object& statistics ) + { + statistics.pay_fee( deferred_fee, get_global_properties().parameters.cashback_vesting_threshold ); + } ); + } + } + if( pays == order.amount_for_sale() ) { remove( order ); @@ -836,7 +885,7 @@ bool database::fill_limit_order( const limit_order_object& order, const asset& p } else { - modify( order, [&]( limit_order_object& b ) { + modify( order, [&pays]( limit_order_object& b ) { b.for_sale -= pays.amount; b.deferred_fee = 0; b.deferred_paid_fee.amount = 0; diff --git a/libraries/chain/hardfork.d/BSIP_85.hf b/libraries/chain/hardfork.d/BSIP_85.hf new file mode 100644 index 0000000000..7fb612a385 --- /dev/null +++ b/libraries/chain/hardfork.d/BSIP_85.hf @@ -0,0 +1,6 @@ +// BSIP 85 (Maker order creation fee discount) hardfork check +#ifndef HARDFORK_BSIP_85_TIME +// Jan 1 2030, midnight; this is a dummy date until a hardfork date is scheduled +#define HARDFORK_BSIP_85_TIME (fc::time_point_sec( 1893456000 )) +#define HARDFORK_BSIP_85_PASSED(now) (now >= HARDFORK_BSIP_85_TIME) +#endif diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 5a4a2b69bc..2d94cef5e2 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -71,6 +71,10 @@ struct proposal_operation_hardfork_visitor FC_ASSERT(!op.new_parameters.current_fees->exists(), "Unable to define fees for custom authority operations prior to hardfork BSIP 40"); } + if (!HARDFORK_BSIP_85_PASSED(block_time)) { + FC_ASSERT(!op.new_parameters.extensions.value.maker_fee_discount_percent.valid(), + "Unable to set maker_fee_discount_percent before hardfork BSIP 85"); + } if (!HARDFORK_BSIP_86_PASSED(block_time)) { FC_ASSERT(!op.new_parameters.extensions.value.market_fee_network_percent.valid(), "Unable to set market_fee_network_percent before hardfork BSIP 86"); diff --git a/libraries/protocol/chain_parameters.cpp b/libraries/protocol/chain_parameters.cpp index 00bf0a3e55..860bf04754 100644 --- a/libraries/protocol/chain_parameters.cpp +++ b/libraries/protocol/chain_parameters.cpp @@ -106,6 +106,11 @@ namespace graphene { namespace protocol { FC_ASSERT( *extensions.value.market_fee_network_percent <= GRAPHENE_100_PERCENT, "The market_fee_network_percent parameter can not exceed 100%" ); } + if( extensions.value.maker_fee_discount_percent.valid() ) + { + FC_ASSERT( *extensions.value.maker_fee_discount_percent <= GRAPHENE_100_PERCENT, + "The maker_fee_discount_percent parameter can not exceed 100%" ); + } } uint16_t chain_parameters::get_market_fee_network_percent() const @@ -114,6 +119,12 @@ namespace graphene { namespace protocol { *extensions.value.market_fee_network_percent : 0; } + uint16_t chain_parameters::get_maker_fee_discount_percent() const + { + return extensions.value.maker_fee_discount_percent.valid() ? + *extensions.value.maker_fee_discount_percent : 0; + } + }} GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::chain_parameters ) diff --git a/libraries/protocol/include/graphene/protocol/chain_parameters.hpp b/libraries/protocol/include/graphene/protocol/chain_parameters.hpp index 249c99ae6e..7bde1f6b07 100644 --- a/libraries/protocol/include/graphene/protocol/chain_parameters.hpp +++ b/libraries/protocol/include/graphene/protocol/chain_parameters.hpp @@ -84,6 +84,7 @@ namespace graphene { namespace protocol { optional< htlc_options > updatable_htlc_options; optional< custom_authority_options_type > custom_authority_options; optional< uint16_t > market_fee_network_percent; + optional< uint16_t > maker_fee_discount_percent; }; extension extensions; @@ -99,6 +100,9 @@ namespace graphene { namespace protocol { /// If @ref market_fee_network_percent is valid, return the value it contains, otherwise return 0 uint16_t get_market_fee_network_percent() const; + /// If @ref maker_fee_discount_percent is valid, return the value it contains, otherwise return 0 + uint16_t get_maker_fee_discount_percent() const; + private: static void safe_copy(chain_parameters& to, const chain_parameters& from); }; @@ -121,6 +125,7 @@ FC_REFLECT( graphene::protocol::chain_parameters::ext, (updatable_htlc_options) (custom_authority_options) (market_fee_network_percent) + (maker_fee_discount_percent) ) FC_REFLECT( graphene::protocol::chain_parameters, From 85f651cf82b3b3a6920a266d5a38875d771ab700 Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 17 Apr 2020 17:36:31 -0400 Subject: [PATCH 03/47] Add a comment --- libraries/chain/db_market.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 7f98e458d6..97a9ffb924 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -833,6 +833,9 @@ bool database::fill_limit_order( const limit_order_object& order, const asset& p if( is_maker && maker_discount_percent > 0 ) { share_type refund = detail::calculate_percent( deferred_paid_fee, maker_discount_percent ); + // Note: it's possible that the deferred_paid_fee is very small, + // which can result in a zero refund due to rounding issue, + // in this case, no refund to the fee pool if( refund > 0 ) { FC_ASSERT( refund <= deferred_paid_fee, "Internal error" ); From 43ce6fdd1c345e3f9db50fa2b6dc68a994be1dc5 Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 17 Apr 2020 17:42:03 -0400 Subject: [PATCH 04/47] Add test cases for bsip 85 --- tests/tests/bsip85_tests.cpp | 256 +++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 tests/tests/bsip85_tests.cpp diff --git a/tests/tests/bsip85_tests.cpp b/tests/tests/bsip85_tests.cpp new file mode 100644 index 0000000000..88eaab71e0 --- /dev/null +++ b/tests/tests/bsip85_tests.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2020 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 "../common/database_fixture.hpp" + +#include +#include +#include + +#include + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE( bsip85_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( hardfork_time_test ) +{ try { + + { + // The maker fee discount percent is 0 by default + BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 ); + + // Try to set new committee parameter before hardfork + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db.get_global_properties().parameters, db.head_block_time() ); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation cmuop; + cmuop.new_parameters.extensions.value.maker_fee_discount_percent = 1; + cop.proposed_ops.emplace_back( cmuop ); + trx.operations.push_back( cop ); + + // It should fail + GRAPHENE_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception ); + trx.clear(); + + // The percent should still be 0 + BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 ); + } + + // Pass the hardfork + generate_blocks( HARDFORK_BSIP_85_TIME ); + set_expiration( db, trx ); + + { + // The maker fee discount percent is still 0 + BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 ); + + // Try to set new committee parameter after hardfork + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db.get_global_properties().parameters, db.head_block_time() ); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation cmuop; + cmuop.new_parameters.extensions.value.maker_fee_discount_percent = 10001; // 100.01% + cop.proposed_ops.emplace_back(cmuop); + trx.operations.push_back(cop); + + // Should fail since the value is too big + GRAPHENE_CHECK_THROW( PUSH_TX(db, trx, ~0), fc::exception ); + // The maker fee discount percent is still 0 + BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 ); + + trx.operations.clear(); + cop.proposed_ops.clear(); + cmuop.new_parameters.extensions.value.maker_fee_discount_percent = 1123; // 11.23% + cop.proposed_ops.emplace_back(cmuop); + trx.operations.push_back(cop); + + // Should succeed + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.operations.clear(); + proposal_id_type prop_id = ptx.operation_results[0].get(); + + // The maker fee discount percent is still 0 + BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 ); + + // Approve the proposal + proposal_update_operation uop; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = { get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id() }; + trx.operations.push_back(uop); + PUSH_TX(db, trx, ~0); + + // The maker fee discount percent is still 0 + BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 0 ); + + generate_blocks( prop_id( db ).expiration_time + 5 ); + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time ); + generate_block(); + + // The maker fee discount percent should have changed + BOOST_CHECK_EQUAL( db.get_global_properties().parameters.get_maker_fee_discount_percent(), 1123 ); + + } +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( bsip85_maker_fee_discount_test ) +{ + try + { + ACTORS((alice)(bob)(izzy)); + + int64_t alice_b0 = 1000000, bob_b0 = 1000000; + int64_t pool_0 = 1000000, accum_0 = 0; + + 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(); + int64_t cer_core_amount = 1801; + int64_t cer_usd_amount = 31; + price tmp_cer( asset( cer_core_amount ), asset( cer_usd_amount, asset_id_type(1) ) ); + const auto& usd_obj = create_user_issued_asset( "IZZYUSD", izzy_id(db), charge_market_fee, tmp_cer ); + asset_id_type usd_id = usd_obj.id; + issue_uia( alice_id, asset( alice_b0, usd_id ) ); + issue_uia( bob_id, asset( bob_b0, usd_id ) ); + + fund_fee_pool( committee_account( db ), usd_obj, pool_0 ); + + // If pay fee in CORE + int64_t order_create_fee = 547; + int64_t order_maker_refund = 61; // 547 * 11.23% = 61.4281 + + // If pay fee in USD + int64_t usd_create_fee = order_create_fee * cer_usd_amount / cer_core_amount; + if( usd_create_fee * cer_core_amount != order_create_fee * cer_usd_amount ) usd_create_fee += 1; + int64_t usd_maker_refund = usd_create_fee * 1123 / 10000; + // amount paid by fee pool + int64_t core_create_fee = usd_create_fee * cer_core_amount / cer_usd_amount; + int64_t core_maker_refund = usd_maker_refund == 0 ? 0 : core_create_fee * 1123 / 10000; + + fee_parameters::flat_set_type 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 ); + + // Pass BSIP 85 HF time + // Note: no test case for the behavior before the HF since it's covered by other test cases + INVOKE( hardfork_time_test ); + set_expiration( db, trx ); + + // 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( new_fees ); + + { + // prepare params + time_point_sec max_exp = time_point_sec::maximum(); + price cer = usd_id( db ).options.core_exchange_rate; + const auto* usd_stat = &usd_id( db ).dynamic_asset_data_id( db ); + + // balance data + int64_t alice_bc = alice_b0, bob_bc = bob_b0; // core balance + int64_t alice_bu = alice_b0, bob_bu = bob_b0; // usd balance + int64_t pool_b = pool_0, accum_b = accum_0; + + // Check order fill + BOOST_TEST_MESSAGE( "Creating ao1, then be filled by bo1" ); + // pays fee in core + const limit_order_object* ao1 = create_sell_order( alice_id, asset(1000), asset(200, usd_id) ); + const limit_order_id_type ao1id = ao1->id; + // pays fee in usd + const limit_order_object* bo1 = create_sell_order( bob_id, asset(200, usd_id), asset(1000), max_exp, cer ); + + BOOST_CHECK( db.find( ao1id ) == nullptr ); + BOOST_CHECK( bo1 == nullptr ); + + // data after order created + alice_bc -= 1000; // amount for sale + alice_bc -= order_create_fee; // fee + bob_bu -= 200; // amount for sale + bob_bu -= usd_create_fee; // fee + pool_b -= core_create_fee; // fee pool + accum_b += 0; + + // data after order filled + alice_bu += 200; // bob pays + alice_bc += order_maker_refund; // maker fee refund + bob_bc += 1000; // alice pays + accum_b += usd_create_fee; // bo1 paid fee, was taker, no refund + pool_b += 0; // no change + + BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc ); + BOOST_CHECK_EQUAL( get_balance( alice_id, usd_id ), alice_bu ); + BOOST_CHECK_EQUAL( get_balance( bob_id, core_id ), bob_bc ); + BOOST_CHECK_EQUAL( get_balance( bob_id, usd_id ), bob_bu ); + BOOST_CHECK_EQUAL( usd_stat->fee_pool.value, pool_b ); + BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value, accum_b ); + + // Check partial fill + BOOST_TEST_MESSAGE( "Creating ao2, then be partially filled by bo2" ); + // pays fee in usd + const limit_order_object* ao2 = create_sell_order( alice_id, asset(1000), asset(200, usd_id), max_exp, cer ); + const limit_order_id_type ao2id = ao2->id; + // pays fee in core + const limit_order_object* bo2 = create_sell_order( bob_id, asset(100, usd_id), asset(500) ); + + BOOST_CHECK( db.find( ao2id ) != nullptr ); + BOOST_CHECK( bo2 == nullptr ); + + // data after order created + alice_bc -= 1000; // amount to sell + alice_bu -= usd_create_fee; // fee + pool_b -= core_create_fee; // fee pool + accum_b += 0; + bob_bc -= order_create_fee; // fee + bob_bu -= 100; // amount to sell + + // data after order filled + alice_bu += 100; // bob pays + alice_bu += usd_maker_refund; // maker fee refund + bob_bc += 500; + accum_b += usd_create_fee - usd_maker_refund; // ao2 paid fee deduct maker refund + pool_b += core_maker_refund; // ao2 maker refund + + BOOST_CHECK_EQUAL( get_balance( alice_id, core_id ), alice_bc ); + BOOST_CHECK_EQUAL( get_balance( alice_id, usd_id ), alice_bu ); + BOOST_CHECK_EQUAL( get_balance( bob_id, core_id ), bob_bc ); + BOOST_CHECK_EQUAL( get_balance( bob_id, usd_id ), bob_bu ); + BOOST_CHECK_EQUAL( usd_stat->fee_pool.value, pool_b ); + BOOST_CHECK_EQUAL( usd_stat->accumulated_fees.value, accum_b ); + + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_SUITE_END() + From 2f2ec4d4d4e2c7f111da02fef167c00ba92598db Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Mon, 30 Mar 2020 11:24:57 -0400 Subject: [PATCH 05/47] BSIP81: Define taker fee --- libraries/protocol/asset_ops.cpp | 1 + libraries/protocol/include/graphene/protocol/asset_ops.hpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/libraries/protocol/asset_ops.cpp b/libraries/protocol/asset_ops.cpp index 202c79f4fc..c8d38eb612 100644 --- a/libraries/protocol/asset_ops.cpp +++ b/libraries/protocol/asset_ops.cpp @@ -217,6 +217,7 @@ void asset_options::validate()const FC_ASSERT( max_supply > 0 ); FC_ASSERT( max_supply <= GRAPHENE_MAX_SHARE_SUPPLY ); FC_ASSERT( market_fee_percent <= GRAPHENE_100_PERCENT ); + FC_ASSERT( taker_fee_percent <= GRAPHENE_100_PERCENT ); FC_ASSERT( max_market_fee >= 0 && max_market_fee <= GRAPHENE_MAX_SHARE_SUPPLY ); // There must be no high bits in permissions whose meaning is not known. FC_ASSERT( !(issuer_permissions & ~ASSET_ISSUER_PERMISSION_MASK) ); diff --git a/libraries/protocol/include/graphene/protocol/asset_ops.hpp b/libraries/protocol/include/graphene/protocol/asset_ops.hpp index 78c204ac06..15219e4937 100644 --- a/libraries/protocol/include/graphene/protocol/asset_ops.hpp +++ b/libraries/protocol/include/graphene/protocol/asset_ops.hpp @@ -49,7 +49,11 @@ namespace graphene { namespace protocol { /// When this asset is traded on the markets, this percentage of the total traded will be exacted and paid /// to the issuer. This is a fixed point value, representing hundredths of a percent, i.e. a value of 100 /// in this field means a 1% fee is charged on market trades of this asset. + // BSIP81: Asset owners may specify different market fee rate for maker orders and taker orders + // After BSIP81 activation, market_fee_percent is the maker fee uint16_t market_fee_percent = 0; + // After BSIP81 activation, taker_fee_percent is the taker fee + uint16_t taker_fee_percent = 0; /// Market fees calculated as @ref market_fee_percent of the traded volume are capped to this value share_type max_market_fee = GRAPHENE_MAX_SHARE_SUPPLY; @@ -523,6 +527,7 @@ FC_REFLECT( graphene::protocol::asset_claim_pool_operation::fee_parameters_type, FC_REFLECT( graphene::protocol::asset_options, (max_supply) (market_fee_percent) + (taker_fee_percent) (max_market_fee) (issuer_permissions) (flags) From b252cf680e35362c478659a01cd6a18e138d9342 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Mon, 30 Mar 2020 11:25:34 -0400 Subject: [PATCH 06/47] BSIP81: Use maker or taker fee when calculating market fee --- libraries/chain/db_market.cpp | 25 ++++++++++++++----- .../chain/include/graphene/chain/database.hpp | 5 ++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 6b50a323d1..2145371d48 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -805,7 +805,7 @@ bool database::fill_limit_order( const limit_order_object& order, const asset& p const account_object& seller = order.seller(*this); const asset_object& recv_asset = receives.asset_id(*this); - auto issuer_fees = pay_market_fees(&seller, recv_asset, receives); + auto issuer_fees = pay_market_fees(&seller, recv_asset, receives, is_maker); pay_order( seller, receives - issuer_fees, pays ); @@ -925,7 +925,7 @@ bool database::fill_settle_order( const force_settlement_object& settle, const a if( head_block_time() >= HARDFORK_CORE_1780_TIME ) settle_owner_ptr = &settle.owner(*this); - auto issuer_fees = pay_market_fees( settle_owner_ptr, get(receives.asset_id), receives ); + auto issuer_fees = pay_market_fees( settle_owner_ptr, get(receives.asset_id), receives, is_maker ); if( pays < settle.balance ) { @@ -1174,7 +1174,7 @@ void database::pay_order( const account_object& receiver, const asset& receives, adjust_balance(receiver.get_id(), receives); } -asset database::calculate_market_fee( const asset_object& trade_asset, const asset& trade_amount ) +asset database::calculate_market_fee( const asset_object& trade_asset, const asset& trade_amount, const bool& is_maker) { assert( trade_asset.id == trade_amount.asset_id ); @@ -1183,7 +1183,19 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass if( trade_asset.options.market_fee_percent == 0 ) return trade_asset.amount(0); - auto value = detail::calculate_percent(trade_amount.amount, trade_asset.options.market_fee_percent); + // BSIP81: Asset owners may specify different market fee rate for maker orders and taker orders + uint16_t fee_percent; + auto maint_time = get_dynamic_global_properties().next_maintenance_time; + bool before_core_hardfork_bsip81 = ( maint_time <= HARDFORK_BSIP_81_TIME ); // before simple maker-taker fee + if( before_core_hardfork_bsip81 ) { + // Before BSIP81, the fee is the market fee + fee_percent = trade_asset.options.market_fee_percent; + } else { + // After BSIP81, the fee depends on if maker or taker + fee_percent = is_maker ? trade_asset.options.market_fee_percent : trade_asset.options.taker_fee_percent; + } + + auto value = detail::calculate_percent(trade_amount.amount, fee_percent); asset percent_fee = trade_asset.amount(value); if( percent_fee.amount > trade_asset.options.max_market_fee ) @@ -1192,9 +1204,10 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass return percent_fee; } -asset database::pay_market_fees(const account_object* seller, const asset_object& recv_asset, const asset& receives ) +asset database::pay_market_fees(const account_object* seller, const asset_object& recv_asset, const asset& receives, + const bool& is_maker) { - const auto market_fees = calculate_market_fee( recv_asset, receives ); + const auto market_fees = calculate_market_fee( recv_asset, receives, is_maker ); auto issuer_fees = market_fees; FC_ASSERT( issuer_fees <= receives, "Market fee shouldn't be greater than receives"); //Don't dirty undo state if not actually collecting any fees diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 7103ed753c..e4c5b880a8 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -439,8 +439,9 @@ namespace graphene { namespace chain { // helpers to fill_order void pay_order( const account_object& receiver, const asset& receives, const asset& pays ); - asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount); - asset pay_market_fees(const account_object* seller, const asset_object& recv_asset, const asset& receives ); + asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount, const bool& is_maker); + asset pay_market_fees(const account_object* seller, const asset_object& recv_asset, const asset& receives, + const bool& is_maker); ///@} From 3e49240e7ae7d64c21977b9604a68d99ee936e54 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 18 Apr 2020 14:15:35 +0000 Subject: [PATCH 07/47] Add get_limit_orders_by_account API --- libraries/app/application.cpp | 5 +++ libraries/app/database_api.cpp | 43 +++++++++++++++++-- libraries/app/database_api_impl.hxx | 3 ++ .../app/include/graphene/app/application.hpp | 1 + .../app/include/graphene/app/database_api.hpp | 20 +++++++++ .../include/graphene/chain/market_object.hpp | 9 ++++ 6 files changed, 78 insertions(+), 3 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 672bed4dc8..16de823ddd 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -343,6 +343,9 @@ void application_impl::set_api_limit() { if(_options->count("api-limit-get-limit-orders")){ _app_options.api_limit_get_limit_orders = _options->at("api-limit-get-limit-orders").as(); } + if(_options->count("api-limit-get-limit-orders-by-account")){ + _app_options.api_limit_get_limit_orders_by_account = _options->at("api-limit-get-limit-orders-by-account").as(); + } if(_options->count("api-limit-get-order-book")){ _app_options.api_limit_get_order_book = _options->at("api-limit-get-order-book").as(); } @@ -1079,6 +1082,8 @@ void application::set_program_options(boost::program_options::options_descriptio "For database_api_impl::list_assets and get_assets_by_issuer to set max limit value") ("api-limit-get-limit-orders",boost::program_options::value()->default_value(300), "For database_api_impl::get_limit_orders to set max limit value") + ("api-limit-get-limit-orders-by-account",boost::program_options::value()->default_value(101), + "For database_api_impl::get_limit_orders_by_account to set max limit value") ("api-limit-get-order-book",boost::program_options::value()->default_value(50), "For database_api_impl::get_order_book to set max limit value") ("api-limit-lookup-accounts",boost::program_options::value()->default_value(1000), diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 5cc9dcede7..1f94848545 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -989,6 +989,43 @@ vector database_api_impl::get_limit_orders( const std::strin return get_limit_orders(asset_a_id, asset_b_id, limit); } +vector database_api::get_limit_orders_by_account( const string& account_name_or_id, + optional limit, optional start_id ) +{ + return my->get_limit_orders_by_account( account_name_or_id, limit, start_id ); +} + +vector database_api_impl::get_limit_orders_by_account( const string& account_name_or_id, + optional olimit, optional ostart_id ) +{ + uint32_t limit = olimit.valid() ? *olimit : 101; + FC_ASSERT( limit <= _app_options->api_limit_get_limit_orders_by_account, + "limit can not be greater than ${limit}", + ("limit", _app_options->api_limit_get_limit_orders_by_account) ); + + vector results; + + const account_object* account = get_account_from_string(account_name_or_id); + if (account == nullptr) + return results; + + limit_order_id_type start_id = ostart_id.valid() ? *ostart_id : limit_order_id_type(); + + const auto& index_by_account = _db.get_index_type().indices().get(); + auto lower_itr = index_by_account.lower_bound( std::make_tuple( account->id, start_id ) ); + auto upper_itr = index_by_account.upper_bound( account->id ); + + results.reserve( limit ); + uint32_t count = 0; + for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count) + { + const limit_order_object &order = *lower_itr; + results.emplace_back(order); + } + + return results; +} + vector database_api::get_account_limit_orders( const string& account_name_or_id, const string &base, const string "e, uint32_t limit, optional ostart_id, optional ostart_price ) @@ -1021,9 +1058,9 @@ vector database_api_impl::get_account_limit_orders( FC_ASSERT(ostart_price->quote.asset_id == quote_id, "Quote asset inconsistent with start price"); } - const auto& index_by_account = _db.get_index_type().indices().get(); - limit_order_multi_index_type::index::type::const_iterator lower_itr; - limit_order_multi_index_type::index::type::const_iterator upper_itr; + const auto& index_by_account = _db.get_index_type().indices().get(); + limit_order_multi_index_type::index::type::const_iterator lower_itr; + limit_order_multi_index_type::index::type::const_iterator upper_itr; // if both order_id and price are invalid, query the first page if ( !ostart_id.valid() && !ostart_price.valid() ) diff --git a/libraries/app/database_api_impl.hxx b/libraries/app/database_api_impl.hxx index e2e7c376a4..44a724d3df 100644 --- a/libraries/app/database_api_impl.hxx +++ b/libraries/app/database_api_impl.hxx @@ -101,6 +101,9 @@ class database_api_impl : public std::enable_shared_from_this // Markets / feeds vector get_limit_orders( const std::string& a, const std::string& b, uint32_t limit)const; + vector get_limit_orders_by_account( const string& account_name_or_id, + optional limit, + optional start_id ); vector get_account_limit_orders( const string& account_name_or_id, const string &base, const string "e, uint32_t limit, diff --git a/libraries/app/include/graphene/app/application.hpp b/libraries/app/include/graphene/app/application.hpp index d0f81960db..96e2d431ec 100644 --- a/libraries/app/include/graphene/app/application.hpp +++ b/libraries/app/include/graphene/app/application.hpp @@ -57,6 +57,7 @@ namespace graphene { namespace app { uint64_t api_limit_get_settle_orders = 300; uint64_t api_limit_get_assets = 101; uint64_t api_limit_get_limit_orders = 300; + uint64_t api_limit_get_limit_orders_by_account = 101; uint64_t api_limit_get_order_book = 50; uint64_t api_limit_list_htlcs = 100; uint64_t api_limit_lookup_accounts = 1000; diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 1960e64824..a2bea87646 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -431,6 +431,25 @@ class database_api */ vector get_limit_orders(std::string a, std::string b, uint32_t limit)const; + /** + * @brief Fetch open limit orders in all markets relevant to the specified account, ordered by ID + * + * @param account_name_or_id The name or ID of an account to retrieve + * @param limit The limitation of items each query can fetch, not greater than a configured value + * @param start_id Start order id, fetch orders whose IDs are greater than or equal to this order + * + * @return List of limit orders of the specified account + * + * @note + * 1. if @p account_name_or_id cannot be tied to an account, empty result will be returned + * 2. @p limit can be omitted or be null, if so the default value 101 will be used + * 3. @p start_id can be omitted or be null, if so the api will return the "first page" of orders + * 4. can only omit one or more arguments in the end of the list, but not one or more in the middle + */ + vector get_limit_orders_by_account( const string& account_name_or_id, + optional limit = 101, + optional start_id = optional() ); + /** * @brief Fetch all orders relevant to the specified account and specified market, result orders * are sorted descendingly by price @@ -952,6 +971,7 @@ FC_API(graphene::app::database_api, // Markets / feeds (get_order_book) (get_limit_orders) + (get_limit_orders_by_account) (get_account_limit_orders) (get_call_orders) (get_call_orders_by_account) diff --git a/libraries/chain/include/graphene/chain/market_object.hpp b/libraries/chain/include/graphene/chain/market_object.hpp index 0861f0eccc..365e2c281c 100644 --- a/libraries/chain/include/graphene/chain/market_object.hpp +++ b/libraries/chain/include/graphene/chain/market_object.hpp @@ -70,6 +70,7 @@ class limit_order_object : public abstract_object struct by_price; struct by_expiration; struct by_account; +struct by_account_price; typedef multi_index_container< limit_order_object, indexed_by< @@ -87,7 +88,15 @@ typedef multi_index_container< >, composite_key_compare< std::greater, std::less > >, + // index used by APIs ordered_unique< tag, + composite_key< limit_order_object, + member, + member + > + >, + // index used by APIs + ordered_unique< tag, composite_key< limit_order_object, member, member, From a60feec1ebef5944b4cc793d044a2ddddef184ed Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Mon, 30 Mar 2020 14:25:42 -0400 Subject: [PATCH 08/47] BSIP81: Define hardfork time --- libraries/chain/hardfork.d/BSIP_81.hf | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 libraries/chain/hardfork.d/BSIP_81.hf diff --git a/libraries/chain/hardfork.d/BSIP_81.hf b/libraries/chain/hardfork.d/BSIP_81.hf new file mode 100644 index 0000000000..c70c6ccd10 --- /dev/null +++ b/libraries/chain/hardfork.d/BSIP_81.hf @@ -0,0 +1,6 @@ +// BSIP 81 (Simple Maker-Taker Market Fees) hardfork check +#ifndef HARDFORK_BSIP_81_TIME +// Jan 1 2030, midnight this is a dummy date until a hardfork date is scheduled +#define HARDFORK_BSIP_81_TIME (fc::time_point_sec( 1893456000 )) +#define HARDFORK_BSIP_81_PASSED(now) (now >= HARDFORK_BSIP_81_TIME) +#endif \ No newline at end of file From 58767d9dc6d2cf3e6be75ebc62a9f526a82e6bec Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Mon, 30 Mar 2020 15:31:57 -0400 Subject: [PATCH 09/47] BSIP81: Hardfork guards on asset operations --- libraries/chain/asset_evaluator.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 5e6460a089..bd0e7d40ed 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -50,7 +50,7 @@ namespace detail { void_result asset_create_evaluator::do_evaluate( const asset_create_operation& op ) { try { - database& d = db(); + const database& d = db(); const auto& chain_parameters = d.get_global_properties().parameters; FC_ASSERT( op.common_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); @@ -69,7 +69,8 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o FC_ASSERT( asset_symbol_itr == asset_indx.end() ); // This must remain due to "BOND.CNY" being allowed before this HF - if( d.head_block_time() > HARDFORK_385_TIME ) + const time_point_sec now = d.head_block_time(); + if( now > HARDFORK_385_TIME ) { auto dotpos = op.symbol.rfind( '.' ); if( dotpos != std::string::npos ) @@ -107,6 +108,11 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o FC_ASSERT( op.precision == op.bitasset_opts->short_backing_asset(d).precision ); } + if(now <= HARDFORK_BSIP_81_TIME) { + // Taker fees should be zero until activation of BSIP81 + FC_ASSERT(op.common_options.taker_fee_percent == 0, "Simple maker-taker fees are not yet activated"); + } + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -267,7 +273,8 @@ static void validate_new_issuer( const database& d, const asset_object& a, accou void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) { try { - database& d = db(); + const database& d = db(); + const time_point_sec now = d.head_block_time(); const asset_object& a = o.asset_to_update(d); auto a_copy = a; @@ -276,7 +283,7 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) if( o.new_issuer ) { - FC_ASSERT( d.head_block_time() < HARDFORK_CORE_199_TIME, + FC_ASSERT( now < HARDFORK_CORE_199_TIME, "Since Hardfork #199, updating issuer requires the use of asset_update_issuer_operation."); validate_new_issuer( d, a, *o.new_issuer ); } @@ -308,6 +315,11 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) for( auto id : o.new_options.blacklist_authorities ) d.get_object(id); + if(now <= HARDFORK_BSIP_81_TIME) { + // Taker fees should be zero until activation of BSIP81 + FC_ASSERT(o.new_options.taker_fee_percent == 0, "Simple maker-taker fees are not yet activated"); + } + return void_result(); } FC_CAPTURE_AND_RETHROW((o)) } From e9c8f5903193ef97693ae0b523c9a53692672597 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Mon, 30 Mar 2020 17:20:41 -0400 Subject: [PATCH 10/47] BSIP81: Assign default taker fees at hardfork activation time --- libraries/chain/db_maint.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 1bdcc67d1a..03d942e3e5 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -1079,6 +1079,23 @@ void process_hf_868_890( database& db, bool skip_check_call_orders ) } // for each market issued asset } +/**** + * @brief a one-time data process to assign default taker fees for BSIP81 + */ +void process_hf_bsip81(database &db) { + // for each asset + const auto &asset_idx = db.get_index_type().indices().get(); + for (auto asset_itr = asset_idx.cbegin(); asset_itr != asset_idx.cend(); ++asset_itr) { + const asset_object &asset = *asset_itr; + const uint16_t raw_market_fee_percent = asset.options.market_fee_percent; + wlog("Setting default taker fee of ${asset} to ${raw_market_fee_percent}.", + ("asset", asset.symbol) ("raw_market_fee_percent", raw_market_fee_percent)); + db.modify(asset, [raw_market_fee_percent](asset_object &obj) { + obj.options.taker_fee_percent = raw_market_fee_percent; + }); + } +} + /** * @brief Remove any custom active authorities whose expiration dates are in the past * @param db A mutable database reference @@ -1240,6 +1257,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g if( (dgpo.next_maintenance_time <= HARDFORK_CORE_1270_TIME) && (next_maintenance_time > HARDFORK_CORE_1270_TIME) ) to_update_and_match_call_orders_for_hf_1270 = true; + // To set the default taker fee to equal the market fees, for hard fork BSIP81 + if( (dgpo.next_maintenance_time <= HARDFORK_BSIP_81_TIME) && (next_maintenance_time > HARDFORK_BSIP_81_TIME) ) + process_hf_bsip81(*this); + // make sure current_supply is less than or equal to max_supply if ( dgpo.next_maintenance_time <= HARDFORK_CORE_1465_TIME && next_maintenance_time > HARDFORK_CORE_1465_TIME ) process_hf_1465(*this); From ebff7f1da1da83201bfd56d09df683d729fb0691 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Wed, 1 Apr 2020 17:59:40 -0400 Subject: [PATCH 11/47] BSIP81: Test the setting of taker fees --- tests/tests/simple_maker_taker_fee_tests.cpp | 202 +++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 tests/tests/simple_maker_taker_fee_tests.cpp diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp new file mode 100644 index 0000000000..8ffff26f7c --- /dev/null +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -0,0 +1,202 @@ +#include +#include +#include + +#include + +#include "../common/database_fixture.hpp" + + +using namespace graphene::chain; +using namespace graphene::chain::test; + +/** + * BSIP81: Asset owners may specify different market fee rate for maker orders and taker orders + */ +BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, database_fixture) + + /** + * Test of setting taker fee before HF and after HF for a UIA + */ + BOOST_AUTO_TEST_CASE(setting_taker_fees_uia) { + try { + // Initialize for the current time + trx.clear(); + set_expiration(db, trx); + + // Initialize actors + ACTORS((jill)(izzy)); + account_id_type issuer_id = jill.id; + fc::ecc::private_key issuer_private_key = jill_private_key; + + upgrade_to_lifetime_member(izzy); + + // Initialize tokens + price price(asset(1, asset_id_type(1)), asset(1)); + uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT; + const asset_object jillcoin = create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, + market_fee_percent); + + ////// + // Before HF, test inability to set taker fees + ////// + asset_update_operation uop; + uop.issuer = issuer_id; + uop.asset_to_update = jillcoin.get_id(); + uop.new_options = jillcoin.options; + uint16_t new_taker_fee_percent = uop.new_options.market_fee_percent / 2; + uop.new_options.taker_fee_percent = new_taker_fee_percent; + + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, issuer_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // An exception should be thrown indicating the reason + // TOOD: Check the specific exception? + + // Check the taker fee + asset_object updated_asset = jillcoin.get_id()(db); + uint16_t expected_taker_fee_percent = 0; // Before the HF it should be set to 0 + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + + ////// + // Advance to activate hardfork + ////// + generate_blocks(HARDFORK_BSIP_81_TIME); + generate_block(); + trx.clear(); + set_expiration(db, trx); + + + ////// + // After HF, test default values of taker fee after HF + // After the HF its default value should be the market fee percent + // which is effectively the new maker fee percent + ////// + updated_asset = jillcoin.get_id()(db); + expected_taker_fee_percent = updated_asset.options.market_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + + ////// + // After HF, test invalid taker fees + ////// + uop.new_options.taker_fee_percent = GRAPHENE_100_PERCENT + 1; + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, issuer_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // An exception should be thrown indicating the reason + // TOOD: Check the specific exception? + + + ////// + // After HF, test that new values can be set + ////// + uop.new_options.taker_fee_percent = new_taker_fee_percent; + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, issuer_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee + updated_asset = jillcoin.get_id()(db); + expected_taker_fee_percent = new_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + } FC_LOG_AND_RETHROW() + } + + + /** + * Test of setting taker fee before HF and after HF for a smart asset + */ + BOOST_AUTO_TEST_CASE(setting_taker_fees_smart_asset) { + try { + // Initialize for the current time + trx.clear(); + set_expiration(db, trx); + + // Initialize actors + ACTORS((smartissuer)(feedproducer)); + + // Initialize tokens + const auto &bitsmart = create_bitasset("SMARTBIT", smartissuer.id); + + + generate_blocks(HARDFORK_615_TIME); // get around Graphene issue #615 feed expiration bug + generate_block(); + + ////// + // Before HF, test inability to set taker fees + ////// + asset_update_operation uop; + uop.issuer = smartissuer.id; + uop.asset_to_update = bitsmart.get_id(); + uop.new_options = bitsmart.options; + uint16_t new_taker_fee_percent = uop.new_options.market_fee_percent / 2; + uop.new_options.taker_fee_percent = new_taker_fee_percent; + + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, smartissuer_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // An exception should be thrown indicating the reason + // TOOD: Check the specific exception? + + // Check the taker fee + asset_object updated_asset = bitsmart.get_id()(db); + uint16_t expected_taker_fee_percent = 0; // Before the HF it should be set to 0 + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + + ////// + // Advance to activate hardfork + ////// + generate_blocks(HARDFORK_BSIP_81_TIME); + generate_block(); + trx.clear(); + set_expiration(db, trx); + + + ////// + // After HF, test default values of taker fee after HF + // After the HF its default value should be the market fee percent + // which is effectively the new maker fee percent + ////// + updated_asset = bitsmart.get_id()(db); + expected_taker_fee_percent = updated_asset.options.market_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + + ////// + // After HF, test invalid taker fees + ////// + uop.new_options.taker_fee_percent = GRAPHENE_100_PERCENT + 1; + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, smartissuer_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // An exception should be thrown indicating the reason + // TOOD: Check the specific exception? + + + ////// + // After HF, test that new values can be set + ////// + uop.new_options.taker_fee_percent = new_taker_fee_percent; + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, smartissuer_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee + updated_asset = bitsmart.get_id()(db); + expected_taker_fee_percent = new_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + } FC_LOG_AND_RETHROW() + } + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 501cb1e93487b1ff345743597d9bf5dcb6739ad3 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Wed, 1 Apr 2020 19:24:03 -0400 Subject: [PATCH 12/47] BSIP81: Test default values of taker fees after hardfork activation --- tests/tests/simple_maker_taker_fee_tests.cpp | 212 ++++++++++++++++++- 1 file changed, 211 insertions(+), 1 deletion(-) diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index 8ffff26f7c..a7aab7068d 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -122,7 +122,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, database_fixture) ACTORS((smartissuer)(feedproducer)); // Initialize tokens - const auto &bitsmart = create_bitasset("SMARTBIT", smartissuer.id); + const asset_object &bitsmart = create_bitasset("SMARTBIT", smartissuer.id); generate_blocks(HARDFORK_615_TIME); // get around Graphene issue #615 feed expiration bug @@ -199,4 +199,214 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, database_fixture) } FC_LOG_AND_RETHROW() } + + /** + * Test the default taker fee values of multiple different assets after HF + */ + BOOST_AUTO_TEST_CASE(default_taker_fees) { + try { + // Initialize for the current time + trx.clear(); + set_expiration(db, trx); + + // Initialize actors + ACTORS((alice)(bob)(charlie)(smartissuer)); + + // Initialize tokens with custom market fees + price price(asset(1, asset_id_type(1)), asset(1)); + + const uint16_t alice1coin_market_fee_percent = 1 * GRAPHENE_1_PERCENT; + const asset_object alice1coin = create_user_issued_asset("ALICE1COIN", alice, charge_market_fee, price, 2, + alice1coin_market_fee_percent); + + const uint16_t alice2coin_market_fee_percent = 2 * GRAPHENE_1_PERCENT; + const asset_object alice2coin = create_user_issued_asset("ALICE2COIN", alice, charge_market_fee, price, 2, + alice2coin_market_fee_percent); + + const uint16_t bob1coin_market_fee_percent = 3 * GRAPHENE_1_PERCENT; + const asset_object bob1coin = create_user_issued_asset("BOB1COIN", alice, charge_market_fee, price, 2, + bob1coin_market_fee_percent); + + const uint16_t bob2coin_market_fee_percent = 4 * GRAPHENE_1_PERCENT; + const asset_object bob2coin = create_user_issued_asset("BOB2COIN", alice, charge_market_fee, price, 2, + bob2coin_market_fee_percent); + + const uint16_t charlie1coin_market_fee_percent = 4 * GRAPHENE_1_PERCENT; + const asset_object charlie1coin = create_user_issued_asset("CHARLIE1COIN", alice, charge_market_fee, price, 2, + charlie1coin_market_fee_percent); + + const uint16_t charlie2coin_market_fee_percent = 5 * GRAPHENE_1_PERCENT; + const asset_object charlie2coin = create_user_issued_asset("CHARLIE2COIN", alice, charge_market_fee, price, 2, + charlie2coin_market_fee_percent); + + const uint16_t bitsmart1coin_market_fee_percent = 7 * GRAPHENE_1_PERCENT; + create_bitasset("SMARTBIT1", smartissuer.id, bitsmart1coin_market_fee_percent); + generate_blocks(1); // The smart asset's ID will be updated after a block is generated + const asset_object &bitsmart1 = *db.get_index_type().indices().get().find("SMARTBIT1"); + + const uint16_t bitsmart2coin_market_fee_percent = 8 * GRAPHENE_1_PERCENT; + create_bitasset("SMARTBIT2", smartissuer.id, bitsmart2coin_market_fee_percent); + generate_blocks(1); // The smart asset's ID will be updated after a block is generated + const asset_object &bitsmart2 = *db.get_index_type().indices().get().find("SMARTBIT2"); + + + ////// + // Before HF, test the market/maker fees for each asset + ////// + asset_object updated_asset; + uint16_t expected_fee_percent; + + updated_asset = alice1coin.get_id()(db); + expected_fee_percent = alice1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = alice2coin.get_id()(db); + expected_fee_percent = alice2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = bob1coin.get_id()(db); + expected_fee_percent = bob1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = bob2coin.get_id()(db); + expected_fee_percent = bob2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = charlie1coin.get_id()(db); + expected_fee_percent = charlie1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = charlie2coin.get_id()(db); + expected_fee_percent = charlie2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = bitsmart1.get_id()(db); + expected_fee_percent = bitsmart1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = bitsmart2.get_id()(db); + expected_fee_percent = bitsmart2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + + ////// + // Before HF, test that taker fees are set to zero + ////// + // Check the taker fee + updated_asset = alice1coin.get_id()(db); + expected_fee_percent = 0; // Before the HF it should be set to 0 + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = alice2coin.get_id()(db); + expected_fee_percent = 0; // Before the HF it should be set to 0 + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = bob1coin.get_id()(db); + expected_fee_percent = 0; // Before the HF it should be set to 0 + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = bob2coin.get_id()(db); + expected_fee_percent = 0; // Before the HF it should be set to 0 + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = charlie1coin.get_id()(db); + expected_fee_percent = 0; // Before the HF it should be set to 0 + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = charlie2coin.get_id()(db); + expected_fee_percent = 0; // Before the HF it should be set to 0 + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = bitsmart1.get_id()(db); + expected_fee_percent = 0; // Before the HF it should be set to 0 + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = bitsmart2.get_id()(db); + expected_fee_percent = 0; // Before the HF it should be set to 0 + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + + ////// + // Advance to activate hardfork + ////// + generate_blocks(HARDFORK_BSIP_81_TIME); + generate_block(); + trx.clear(); + set_expiration(db, trx); + + + ////// + // After HF, test the maker fees for each asset are unchanged + ////// + updated_asset = alice1coin.get_id()(db); + expected_fee_percent = alice1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = alice2coin.get_id()(db); + expected_fee_percent = alice2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = bob1coin.get_id()(db); + expected_fee_percent = bob1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = bob2coin.get_id()(db); + expected_fee_percent = bob2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = charlie1coin.get_id()(db); + expected_fee_percent = charlie1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = charlie2coin.get_id()(db); + expected_fee_percent = charlie2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = bitsmart1.get_id()(db); + expected_fee_percent = bitsmart1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + updated_asset = bitsmart2.get_id()(db); + expected_fee_percent = bitsmart2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.market_fee_percent); + + + ////// + // After HF, test the taker fees for each asset are set, by default, to the maker fees + ////// + updated_asset = alice1coin.get_id()(db); + expected_fee_percent = alice1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = alice2coin.get_id()(db); + expected_fee_percent = alice2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = bob1coin.get_id()(db); + expected_fee_percent = bob1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = bob2coin.get_id()(db); + expected_fee_percent = bob2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = charlie1coin.get_id()(db); + expected_fee_percent = charlie1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = charlie2coin.get_id()(db); + expected_fee_percent = charlie2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = bitsmart1.get_id()(db); + expected_fee_percent = bitsmart1coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + updated_asset = bitsmart2.get_id()(db); + expected_fee_percent = bitsmart2coin_market_fee_percent; + BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + + } FC_LOG_AND_RETHROW() + } + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From fe590d19feb30a8f30c387b1f8440087e4cfffcb Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Sat, 4 Apr 2020 12:58:49 -0400 Subject: [PATCH 13/47] BSIP81: Test taker fees when complete filling of UIA orders --- tests/tests/simple_maker_taker_fee_tests.cpp | 195 ++++++++++++++++++- 1 file changed, 194 insertions(+), 1 deletion(-) diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index a7aab7068d..4ba2cc9808 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../common/database_fixture.hpp" @@ -10,10 +11,48 @@ using namespace graphene::chain; using namespace graphene::chain::test; +struct simple_maker_taker_database_fixture : database_fixture { + simple_maker_taker_database_fixture() + : database_fixture() { + } + + const limit_order_create_operation + create_sell_operation(account_id_type user, const asset &amount, const asset &recv) { + const time_point_sec order_expiration = time_point_sec::maximum(); + const price &fee_core_exchange_rate = price::unit_price(); + limit_order_create_operation op = create_sell_operation(user, amount, recv, order_expiration, + fee_core_exchange_rate); + return op; + } + + const limit_order_create_operation + create_sell_operation(account_id_type user, const asset &amount, const asset &recv, + const time_point_sec order_expiration, + const price &fee_core_exchange_rate) { + limit_order_create_operation op = create_sell_operation(user(db), amount, recv, order_expiration, + fee_core_exchange_rate); + return op; + } + + const limit_order_create_operation + create_sell_operation(const account_object &user, const asset &amount, const asset &recv, + const time_point_sec order_expiration, + const price &fee_core_exchange_rate) { + limit_order_create_operation sell_order; + sell_order.seller = user.id; + sell_order.amount_to_sell = amount; + sell_order.min_to_receive = recv; + sell_order.expiration = order_expiration; + + return sell_order; + } +}; + + /** * BSIP81: Asset owners may specify different market fee rate for maker orders and taker orders */ -BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, database_fixture) +BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_database_fixture) /** * Test of setting taker fee before HF and after HF for a UIA @@ -409,4 +448,158 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, database_fixture) } FC_LOG_AND_RETHROW() } + + /** + * Test of different maker and taker fees charged when filling limit orders after HF for a UIA + */ + BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_uia) { + try { + // Initialize for the current time + trx.clear(); + set_expiration(db, trx); + + // Initialize actors + ACTORS((jill)(izzy)(alice)(bob)); + + // Initialize tokens + price price(asset(1, asset_id_type(1)), asset(1)); + + const uint16_t JILL_PRECISION = 100; + const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT; + const asset_object jillcoin = create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, + jill_market_fee_percent); + + const uint16_t IZZY_PRECISION = 1000; + const uint16_t izzy_market_fee_percent = 5 * GRAPHENE_1_PERCENT; + const asset_object izzycoin = create_user_issued_asset("ICOIN", izzy, charge_market_fee, price, 3, + izzy_market_fee_percent); + + + ////// + // Advance to activate hardfork + ////// + generate_blocks(HARDFORK_BSIP_81_TIME); + generate_block(); + trx.clear(); + set_expiration(db, trx); + + + ////// + // After HF, test that new values can be set + ////// + // Define the new taker fees + uint16_t jill_maker_fee_percent = jill_market_fee_percent; + uint16_t jill_taker_fee_percent = jill_maker_fee_percent / 2; + + uint16_t izzy_maker_fee_percent = izzy_market_fee_percent; + uint16_t izzy_taker_fee_percent = izzy_maker_fee_percent / 2; + + // Set the new taker fee for JILLCOIN + asset_update_operation uop; + uop.issuer = jill.id; + uop.asset_to_update = jillcoin.get_id(); + uop.new_options = jillcoin.options; + uop.new_options.taker_fee_percent = jill_taker_fee_percent; + + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, jill_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee for JILLCOIN + asset_object updated_asset = jillcoin.get_id()(db); + uint16_t expected_taker_fee_percent = jill_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + // Set the new taker fee for IZZYCOIN + uop.issuer = izzy.id; + uop.asset_to_update = izzycoin.get_id(); + uop.new_options = izzycoin.options; + uop.new_options.taker_fee_percent = izzy_taker_fee_percent; + + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, izzy_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee for IZZYCOIN + updated_asset = izzycoin.get_id()(db); + expected_taker_fee_percent = izzy_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + + ////// + // After HF, create limit orders that will perfectly match + ////// + BOOST_TEST_MESSAGE("Issuing 10 jillcoin to alice"); + issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION)); + BOOST_TEST_MESSAGE("Checking alice's balance"); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION); + + BOOST_TEST_MESSAGE("Issuing 300 izzycoin to bob"); + issue_uia(bob, izzycoin.amount(300 * IZZY_PRECISION)); + BOOST_TEST_MESSAGE("Checking bob's balance"); + BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 300 * IZZY_PRECISION); + + // Alice and Bob place orders which match, and are completely filled by each other + // Alice is willing to sell 10 JILLCOIN for at least 300 IZZYCOIN + limit_order_create_operation alice_sell_op = create_sell_operation(alice.id, + jillcoin.amount(10 * JILL_PRECISION), + izzycoin.amount(300 * + IZZY_PRECISION)); + trx.clear(); + trx.operations.push_back(alice_sell_op); + asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type alice_order_id = ptx.operation_results[0].get(); + + const limit_order_object* alice_order_before = db.find(alice_order_id); + BOOST_CHECK(alice_order_before != nullptr); + + // Bob is willing to sell 300 IZZYCOIN for at least 10 JILLCOIN + limit_order_create_operation bob_sell_op = create_sell_operation(bob.id, izzycoin.amount(300 * IZZY_PRECISION), + jillcoin.amount( + 10 * + JILL_PRECISION)); + trx.clear(); + trx.operations.push_back(bob_sell_op); + asset bob_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, bob_private_key); + ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type bob_order_id = ptx.operation_results[0].get(); + + // Check that the orders were filled by ensuring that they are no longer on the order books + const limit_order_object* alice_order = db.find(alice_order_id); + BOOST_CHECK(alice_order == nullptr); + const limit_order_object* bob_order = db.find(bob_order_id); + BOOST_CHECK(bob_order == nullptr); + + + // Check the new balances of the maker + // Alice was the maker; she is receiving IZZYCOIN + asset expected_izzy_fee = jillcoin.amount( + 300 * IZZY_PRECISION * izzy_maker_fee_percent / GRAPHENE_100_PERCENT); + BOOST_REQUIRE_EQUAL(get_balance(alice, izzycoin), + (300 * IZZY_PRECISION) - alice_sell_fee.amount.value - expected_izzy_fee.amount.value); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0); + + // Check the new balance of the taker + // Bob was the taker; he is receiving JILLCOIN + asset expected_jill_fee = jillcoin.amount( + 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT); + BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), + (10 * JILL_PRECISION) - bob_sell_fee.amount.value - expected_jill_fee.amount.value); + BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 0); + + // Check the asset issuer's accumulated fees + BOOST_CHECK(izzycoin.dynamic_asset_data_id(db).accumulated_fees == expected_izzy_fee.amount); + BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee.amount); + + } FC_LOG_AND_RETHROW() + } + + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From c7ed43bc3560030fea97655a8a296e7bd3168749 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Thu, 9 Apr 2020 11:43:50 -0400 Subject: [PATCH 14/47] BSIP81: Hardfork guards on asset operations in proposals --- libraries/chain/asset_evaluator.cpp | 9 + libraries/chain/hardfork.d/BSIP_81.hf | 1 - libraries/chain/proposal_evaluator.cpp | 12 +- tests/tests/simple_maker_taker_fee_tests.cpp | 200 ++++++++++++++++++- 4 files changed, 212 insertions(+), 10 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index bd0e7d40ed..41fbfd4bf1 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -45,6 +45,15 @@ namespace detail { "Asset extension reward percent must be less than 100% till HARDFORK_1774_TIME!"); } } + + // TODO review and remove code below and links to it after HARDFORK_BSIP_81_TIME + void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options) + { + if (block_time < HARDFORK_BSIP_81_TIME) { + // Taker fees should be zero until activation of BSIP81 + FC_ASSERT(options.taker_fee_percent == 0, "Taker fee must be 0% until HARDFORK_BSIP_81_TIME"); + } + } } void_result asset_create_evaluator::do_evaluate( const asset_create_operation& op ) diff --git a/libraries/chain/hardfork.d/BSIP_81.hf b/libraries/chain/hardfork.d/BSIP_81.hf index c70c6ccd10..8d9f212d9c 100644 --- a/libraries/chain/hardfork.d/BSIP_81.hf +++ b/libraries/chain/hardfork.d/BSIP_81.hf @@ -2,5 +2,4 @@ #ifndef HARDFORK_BSIP_81_TIME // Jan 1 2030, midnight this is a dummy date until a hardfork date is scheduled #define HARDFORK_BSIP_81_TIME (fc::time_point_sec( 1893456000 )) -#define HARDFORK_BSIP_81_PASSED(now) (now >= HARDFORK_BSIP_81_TIME) #endif \ No newline at end of file diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 5a4a2b69bc..15c2091252 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -30,6 +30,7 @@ namespace graphene { namespace chain { namespace detail { void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options); + void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options); } struct proposal_operation_hardfork_visitor @@ -45,13 +46,19 @@ struct proposal_operation_hardfork_visitor template void operator()(const T &v) const {} - // hf_1774 void operator()(const graphene::chain::asset_create_operation &v) const { + // hf_1774 detail::check_asset_options_hf_1774(block_time, v.common_options); + + // HARDFORK_BSIP_81 + detail::check_asset_options_hf_bsip81(block_time, v.common_options); } - // hf_1774 void operator()(const graphene::chain::asset_update_operation &v) const { + // hf_1774 detail::check_asset_options_hf_1774(block_time, v.new_options); + + // HARDFORK_BSIP_81 + detail::check_asset_options_hf_bsip81(block_time, v.new_options); } void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const { @@ -94,6 +101,7 @@ struct proposal_operation_hardfork_visitor void operator()(const graphene::chain::custom_authority_delete_operation&) const { FC_ASSERT( HARDFORK_BSIP_40_PASSED(block_time), "Not allowed until hardfork BSIP 40" ); } + // loop and self visit in proposals void operator()(const graphene::chain::proposal_create_operation &v) const { bool already_contains_proposal_update = false; diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index 4ba2cc9808..5e82542d35 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -46,6 +46,28 @@ struct simple_maker_taker_database_fixture : database_fixture { return sell_order; } + + const asset_create_operation create_user_issued_asset_operation(const string &name, const account_object &issuer, + uint16_t flags, const price &core_exchange_rate, + uint8_t precision, uint16_t maker_fee_percent, + uint16_t taker_fee_percent) { + asset_create_operation creator; + creator.issuer = issuer.id; + creator.fee = asset(); + creator.symbol = name; + creator.common_options.max_supply = 0; + creator.precision = precision; + + creator.common_options.core_exchange_rate = core_exchange_rate; + creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + creator.common_options.flags = flags; + creator.common_options.issuer_permissions = flags; + creator.common_options.market_fee_percent = maker_fee_percent; + creator.common_options.taker_fee_percent = taker_fee_percent; + + return creator; + + } }; @@ -68,8 +90,6 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa account_id_type issuer_id = jill.id; fc::ecc::private_key issuer_private_key = jill_private_key; - upgrade_to_lifetime_member(izzy); - // Initialize tokens price price(asset(1, asset_id_type(1)), asset(1)); uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT; @@ -89,8 +109,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa trx.operations.push_back(uop); db.current_fee_schedule().set_fee(trx.operations.back()); sign(trx, issuer_private_key); - GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // An exception should be thrown indicating the reason - // TOOD: Check the specific exception? + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + // TODO: Check the specific exception? // Check the taker fee asset_object updated_asset = jillcoin.get_id()(db); @@ -98,6 +118,62 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + ////// + // Before HF, test inability to set taker fees with an asset update operation inside of a proposal + ////// + { + trx.clear(); + set_expiration(db, trx); + + uint64_t alternate_taker_fee_percent = new_taker_fee_percent * 2; + uop.new_options.taker_fee_percent = alternate_taker_fee_percent; + + proposal_create_operation cop; + cop.review_period_seconds = 86400; + uint32_t buffer_seconds = 60 * 60; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds; + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.proposed_ops.emplace_back(uop); + + trx.operations.push_back(cop); + // sign(trx, issuer_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); + + // Check the taker fee is not changed because the proposal has not been approved + updated_asset = jillcoin.get_id()(db); + expected_taker_fee_percent = 0; // Before the HF it should be set to 0 + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + } + + + ////// + // Before HF, test inability to set taker fees with an asset create operation inside of a proposal + ////// + { + trx.clear(); + set_expiration(db, trx); + + uint64_t maker_fee_percent = 10 * GRAPHENE_1_PERCENT; + uint64_t taker_fee_percent = 2 * GRAPHENE_1_PERCENT; + asset_create_operation ac_op = create_user_issued_asset_operation("JCOIN2", jill, charge_market_fee, price, + 2, + maker_fee_percent, taker_fee_percent); + + proposal_create_operation cop; + cop.review_period_seconds = 86400; + uint32_t buffer_seconds = 60 * 60; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds; + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.proposed_ops.emplace_back(ac_op); + + trx.operations.push_back(cop); + // sign(trx, issuer_private_key); + + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // The proposal should be rejected + + } + + ////// // Advance to activate hardfork ////// @@ -126,7 +202,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa db.current_fee_schedule().set_fee(trx.operations.back()); sign(trx, issuer_private_key); GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // An exception should be thrown indicating the reason - // TOOD: Check the specific exception? + // TODO: Check the specific exception? ////// @@ -144,6 +220,116 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa expected_taker_fee_percent = new_taker_fee_percent; BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + ////// + // After HF, test ability to set taker fees with an asset update operation inside of a proposal + ////// + { + trx.clear(); + set_expiration(db, trx); + + uint64_t alternate_taker_fee_percent = new_taker_fee_percent * 2; + uop.new_options.taker_fee_percent = alternate_taker_fee_percent; + + proposal_create_operation cop; + cop.review_period_seconds = 86400; + uint32_t buffer_seconds = 60 * 60; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds; + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.proposed_ops.emplace_back(uop); + + trx.operations.push_back(cop); + // sign(trx, issuer_private_key); + processed_transaction processed = PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee is not changed because the proposal has not been approved + updated_asset = jillcoin.get_id()(db); + expected_taker_fee_percent = new_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + + // Approve the proposal + trx.clear(); + proposal_id_type pid = processed.operation_results[0].get(); + + proposal_update_operation pup; + pup.fee_paying_account = jill.id; + pup.proposal = pid; + pup.active_approvals_to_add.insert(jill.id); + trx.operations.push_back(pup); + set_expiration(db, trx); + sign(trx, jill_private_key); + + PUSH_TX(db, trx); // No exception should be thrown + + // Advance to after proposal expires + generate_blocks(cop.expiration_time); + + // Check the taker fee is not changed because the proposal has not been approved + updated_asset = jillcoin.get_id()(db); + expected_taker_fee_percent = alternate_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + } + + + ////// + // After HF, test ability to set taker fees with an asset create operation inside of a proposal + ////// + { + trx.clear(); + set_expiration(db, trx); + + uint64_t maker_fee_percent = 10 * GRAPHENE_1_PERCENT; + uint64_t taker_fee_percent = 2 * GRAPHENE_1_PERCENT; + asset_create_operation ac_op = create_user_issued_asset_operation("JCOIN2", jill, charge_market_fee, price, + 2, + maker_fee_percent, taker_fee_percent); + + proposal_create_operation cop; + cop.review_period_seconds = 86400; + uint32_t buffer_seconds = 60 * 60; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + buffer_seconds; + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.proposed_ops.emplace_back(ac_op); + + trx.operations.push_back(cop); + // sign(trx, issuer_private_key); + + processed_transaction processed = PUSH_TX(db, trx); // No exception should be thrown + + // Check the asset does not exist because the proposal has not been approved + const auto& asset_idx = db.get_index_type().indices().get(); + const auto itr = asset_idx.find("JCOIN2"); + BOOST_CHECK(itr == asset_idx.end()); + + // Approve the proposal + trx.clear(); + proposal_id_type pid = processed.operation_results[0].get(); + + proposal_update_operation pup; + pup.fee_paying_account = jill.id; + pup.proposal = pid; + pup.active_approvals_to_add.insert(jill.id); + trx.operations.push_back(pup); + set_expiration(db, trx); + sign(trx, jill_private_key); + + PUSH_TX(db, trx); // No exception should be thrown + + // Advance to after proposal expires + generate_blocks(cop.expiration_time); + + // Check the taker fee is not changed because the proposal has not been approved + BOOST_CHECK(asset_idx.find("JCOIN2") != asset_idx.end()); + updated_asset = *asset_idx.find("JCOIN2"); + expected_taker_fee_percent = taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + uint16_t expected_maker_fee_percent = maker_fee_percent; + BOOST_CHECK_EQUAL(expected_maker_fee_percent, updated_asset.options.market_fee_percent); + + } + } FC_LOG_AND_RETHROW() } @@ -181,7 +367,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa db.current_fee_schedule().set_fee(trx.operations.back()); sign(trx, smartissuer_private_key); GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // An exception should be thrown indicating the reason - // TOOD: Check the specific exception? + // TODO: Check the specific exception? // Check the taker fee asset_object updated_asset = bitsmart.get_id()(db); @@ -217,7 +403,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa db.current_fee_schedule().set_fee(trx.operations.back()); sign(trx, smartissuer_private_key); GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), fc::exception); // An exception should be thrown indicating the reason - // TOOD: Check the specific exception? + // TODO: Check the specific exception? ////// From 36fbf2d515fe3fc576d8b576d40ef359db0fa8a0 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Thu, 9 Apr 2020 17:06:24 -0400 Subject: [PATCH 15/47] BSIP81: Test taker fees when complete filling of UIA orders --- tests/tests/simple_maker_taker_fee_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index 5e82542d35..2982357394 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -766,7 +766,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the new balances of the maker // Alice was the maker; she is receiving IZZYCOIN - asset expected_izzy_fee = jillcoin.amount( + asset expected_izzy_fee = izzycoin.amount( 300 * IZZY_PRECISION * izzy_maker_fee_percent / GRAPHENE_100_PERCENT); BOOST_REQUIRE_EQUAL(get_balance(alice, izzycoin), (300 * IZZY_PRECISION) - alice_sell_fee.amount.value - expected_izzy_fee.amount.value); From 9d73860bc4fb7b9767d36b4ddaae9dd0783075d9 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Thu, 9 Apr 2020 17:06:47 -0400 Subject: [PATCH 16/47] BSIP81: Test taker fees when complete filling of a smart asset order --- tests/tests/simple_maker_taker_fee_tests.cpp | 172 +++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index 2982357394..5d5b5057a8 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -788,4 +788,176 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa } + /** + * Test of different maker and taker fees charged when filling limit orders after HF for a smart asset + */ + BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_smart_asset) { + try { + // Initialize for the current time + trx.clear(); + set_expiration(db, trx); + + // Initialize actors + ACTORS((jill)(izzy)(alice)(bob)); + ACTORS((smartissuer)(feedproducer)); + + // Initialize tokens + price price(asset(1, asset_id_type(1)), asset(1)); + const uint16_t JILL_PRECISION = 100; + const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT; + const asset_object jillcoin = create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, + jill_market_fee_percent); + + const uint16_t SMARTBIT_PRECISION = 10000; + const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT; + const asset_object &smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, + charge_market_fee, 4); + const auto &core = asset_id_type()(db); + + update_feed_producers(smartbit, {feedproducer.id}); + + price_feed current_feed; + current_feed.settlement_price = smartbit.amount(100) / core.amount(100); + current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default + publish_feed(smartbit, feedproducer, current_feed); + + FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price); + + + ////// + // Advance to activate hardfork + ////// + generate_blocks(HARDFORK_BSIP_81_TIME); + generate_block(); + trx.clear(); + set_expiration(db, trx); + + + ////// + // After HF, test that new values can be set + ////// + // Define the new taker fees + uint16_t jill_maker_fee_percent = jill_market_fee_percent; + uint16_t jill_taker_fee_percent = jill_maker_fee_percent / 2; + + uint16_t smartbit_maker_fee_percent = 1 * GRAPHENE_1_PERCENT; + uint16_t smartbit_taker_fee_percent = 3 * GRAPHENE_1_PERCENT; + + // Set the new taker fee for JILLCOIN + asset_update_operation uop; + uop.issuer = jill.id; + uop.asset_to_update = jillcoin.get_id(); + uop.new_options = jillcoin.options; + uop.new_options.taker_fee_percent = jill_taker_fee_percent; + + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, jill_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee for JILLCOIN + asset_object updated_asset = jillcoin.get_id()(db); + uint16_t expected_taker_fee_percent = jill_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + + // Set the new taker fee for SMARTBIT + uop = asset_update_operation(); + uop.issuer = smartissuer.id; + uop.asset_to_update = smartbit.get_id(); + uop.new_options = smartbit.options; + uop.new_options.market_fee_percent = smartbit_maker_fee_percent; + uop.new_options.taker_fee_percent = smartbit_taker_fee_percent; + + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, smartissuer_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee for SMARTBIT + updated_asset = smartbit.get_id()(db); + expected_taker_fee_percent = smartbit_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + // Check the maker fee for SMARTBIT + updated_asset = smartbit.get_id()(db); + expected_taker_fee_percent = smartbit_maker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.market_fee_percent); + + + ////// + // After HF, create limit orders that will perfectly match + ////// + BOOST_TEST_MESSAGE("Issuing 10 jillcoin to alice"); + issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION)); + BOOST_TEST_MESSAGE("Checking alice's balance"); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION); + + BOOST_TEST_MESSAGE("Issuing 300 SMARTBIT to bob"); + transfer(committee_account, bob.id, asset(10000000)); + publish_feed(smartbit, feedproducer, current_feed); // Publish a recent feed + borrow(bob, smartbit.amount(300 * SMARTBIT_PRECISION), asset(2 * 300 * SMARTBIT_PRECISION)); + BOOST_TEST_MESSAGE("Checking bob's balance"); + BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 300 * SMARTBIT_PRECISION); + + // Alice and Bob place orders which match, and are completely filled by each other + // Alice is willing to sell 10 JILLCOIN for at least 300 SMARTBIT + limit_order_create_operation alice_sell_op = create_sell_operation(alice.id, + jillcoin.amount(10 * JILL_PRECISION), + smartbit.amount(300 * SMARTBIT_PRECISION)); + trx.clear(); + trx.operations.push_back(alice_sell_op); + asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type alice_order_id = ptx.operation_results[0].get(); + + const limit_order_object *alice_order_before = db.find(alice_order_id); + BOOST_CHECK(alice_order_before != nullptr); + + + // Bob is willing to sell 300 SMARTBIT for at least 10 JILLCOIN + limit_order_create_operation bob_sell_op + = create_sell_operation(bob.id, smartbit.amount(300 * SMARTBIT_PRECISION), + jillcoin.amount(10 * JILL_PRECISION)); + trx.clear(); + trx.operations.push_back(bob_sell_op); + asset bob_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, bob_private_key); + ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type bob_order_id = ptx.operation_results[0].get(); + + // Check that the orders were filled by ensuring that they are no longer on the order books + const limit_order_object *alice_order = db.find(alice_order_id); + BOOST_CHECK(alice_order == nullptr); + const limit_order_object *bob_order = db.find(bob_order_id); + BOOST_CHECK(bob_order == nullptr); + + + // Check the new balances of the maker + // Alice was the maker; she is receiving SMARTBIT + asset expected_smartbit_fee = smartbit.amount( + 300 * SMARTBIT_PRECISION * smartbit_maker_fee_percent / GRAPHENE_100_PERCENT); + BOOST_REQUIRE_EQUAL(get_balance(alice, smartbit), + (300 * SMARTBIT_PRECISION) - alice_sell_fee.amount.value - + expected_smartbit_fee.amount.value); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0); + + // Check the new balance of the taker + // Bob was the taker; he is receiving JILLCOIN + asset expected_jill_fee = jillcoin.amount( + 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT); + BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), + (10 * JILL_PRECISION) - bob_sell_fee.amount.value - expected_jill_fee.amount.value); + BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 0); + + // Check the asset issuer's accumulated fees + BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == expected_smartbit_fee.amount); + BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee.amount); + + } FC_LOG_AND_RETHROW() + } + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 9b9ed84d858d6e1ee5c59b0760afc9445b9ebef5 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Fri, 10 Apr 2020 09:52:02 -0400 Subject: [PATCH 17/47] BSIP81: Test maker and taker fees for partially filled limit orders --- tests/tests/simple_maker_taker_fee_tests.cpp | 247 +++++++++++++++++++ 1 file changed, 247 insertions(+) diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index 5d5b5057a8..883a940313 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -960,4 +960,251 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa } FC_LOG_AND_RETHROW() } + + /** + * Test of different maker and taker fees charged when filling limit orders after HF for a smart asset + * + * 1. (Order 1) An order will be placed to offer JCOIN + * + * 2. (Order 2) A matching-order will be placed to offer SMARTBIT. + * Order 2 is large enough that it should be partially filled, and Order 1 will be completely filled. + * Order 1 should be charged a maker fee, and Order 2 should be charged a taker fee. + * Order 2 should remain on the book. + * + * 3. (Order 3) A matching order will be placed to offer JCOIN. + * Order 3 should be charged a taker fee, and Order 2 should be charged a maker fee. + * + * Summary: Order 2 should be charged a taker fee when matching Order 1, and Order 2 should be charged a maker fee when matching Order 3. + */ + BOOST_AUTO_TEST_CASE(partial_maker_partial_taker_fills) { + try { + // Initialize for the current time + trx.clear(); + set_expiration(db, trx); + + // Initialize actors + ACTORS((jill)(izzy)(alice)(bob)(charlie)); + ACTORS((smartissuer)(feedproducer)); + + // Initialize tokens + price price(asset(1, asset_id_type(1)), asset(1)); + const uint16_t JILL_PRECISION = 100; + const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT; + const asset_object jillcoin = create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, + jill_market_fee_percent); + + const uint16_t SMARTBIT_PRECISION = 10000; + const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT; + const asset_object &smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, + charge_market_fee, 4); + const auto &core = asset_id_type()(db); + + update_feed_producers(smartbit, {feedproducer.id}); + + price_feed current_feed; + current_feed.settlement_price = smartbit.amount(100) / core.amount(100); + current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default + publish_feed(smartbit, feedproducer, current_feed); + + FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price); + + + ////// + // Advance to activate hardfork + ////// + generate_blocks(HARDFORK_BSIP_81_TIME); + generate_block(); + trx.clear(); + set_expiration(db, trx); + + + ////// + // After HF, test that new values can be set + ////// + // Define the new taker fees + uint16_t jill_maker_fee_percent = jill_market_fee_percent; + uint16_t jill_taker_fee_percent = jill_maker_fee_percent / 2; + + uint16_t smartbit_maker_fee_percent = 1 * GRAPHENE_1_PERCENT; + uint16_t smartbit_taker_fee_percent = 3 * GRAPHENE_1_PERCENT; + + // Set the new taker fee for JILLCOIN + asset_update_operation uop; + uop.issuer = jill.id; + uop.asset_to_update = jillcoin.get_id(); + uop.new_options = jillcoin.options; + uop.new_options.taker_fee_percent = jill_taker_fee_percent; + + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, jill_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee for JILLCOIN + asset_object updated_asset = jillcoin.get_id()(db); + uint16_t expected_taker_fee_percent = jill_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + + // Set the new taker fee for SMARTBIT + uop = asset_update_operation(); + uop.issuer = smartissuer.id; + uop.asset_to_update = smartbit.get_id(); + uop.new_options = smartbit.options; + uop.new_options.market_fee_percent = smartbit_maker_fee_percent; + uop.new_options.taker_fee_percent = smartbit_taker_fee_percent; + + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, smartissuer_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee for SMARTBIT + updated_asset = smartbit.get_id()(db); + expected_taker_fee_percent = smartbit_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + // Check the maker fee for SMARTBIT + updated_asset = smartbit.get_id()(db); + expected_taker_fee_percent = smartbit_maker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.market_fee_percent); + + + ////// + // Create Orders 1 and 2 that will match. + // Order 1 will be completely filled, and Order 2 will be partially filled. + ////// + // Initialize token balance of actors + BOOST_TEST_MESSAGE("Issuing 10 JCOIN to alice"); + issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION)); + BOOST_TEST_MESSAGE("Checking alice's balance"); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION); + + BOOST_TEST_MESSAGE("Issuing 600 SMARTBIT to bob"); + transfer(committee_account, bob.id, asset(2 * 1000 * SMARTBIT_PRECISION)); + publish_feed(smartbit, feedproducer, current_feed); // Publish a recent feed + borrow(bob, smartbit.amount(600 * SMARTBIT_PRECISION), asset(2 * 600 * SMARTBIT_PRECISION)); + BOOST_TEST_MESSAGE("Checking bob's balance"); + BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 600 * SMARTBIT_PRECISION); + + // Alice and Bob place orders which match, and are completely filled by each other + // Alice is willing to sell 10 JILLCOIN for at least 300 SMARTBIT + limit_order_create_operation order_1_op = create_sell_operation(alice.id, + jillcoin.amount(10 * JILL_PRECISION), + smartbit.amount(300 * SMARTBIT_PRECISION)); + trx.clear(); + trx.operations.push_back(order_1_op); + asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type order_1_id = ptx.operation_results[0].get(); + + const limit_order_object *order_1_before = db.find(order_1_id); + BOOST_CHECK(order_1_before != nullptr); + + + // Bob is willing to sell 600 SMARTBIT for at least 20 JILLCOIN + limit_order_create_operation order_2_op + = create_sell_operation(bob.id, smartbit.amount(600 * SMARTBIT_PRECISION), + jillcoin.amount(20 * JILL_PRECISION)); + trx.clear(); + trx.operations.push_back(order_2_op); + asset order_2_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, bob_private_key); + ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type order_2_id = ptx.operation_results[0].get(); + + // Check that order 1 was completely filled by ensuring that they it is no longer on the order book + const limit_order_object *order_1 = db.find(order_1_id); + BOOST_CHECK(order_1 == nullptr); + // Check that order 2 was partially filled by ensuring that they it is still on the order book + const limit_order_object *order_2 = db.find(order_2_id); + BOOST_CHECK(order_2 != nullptr); + + + // Check the new balances of the maker + // Alice was the maker; she is receiving SMARTBIT + asset expected_smartbit_fee = smartbit.amount( + 300 * SMARTBIT_PRECISION * smartbit_maker_fee_percent / GRAPHENE_100_PERCENT); + int64_t expected_alice_balance_after_order_2 = + (300 * SMARTBIT_PRECISION) - alice_sell_fee.amount.value - expected_smartbit_fee.amount.value; + BOOST_REQUIRE_EQUAL(get_balance(alice, smartbit), expected_alice_balance_after_order_2); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0); + + // Check the new balance of the taker + // Bob was the taker; he is receiving JILLCOIN + asset expected_jill_fee = jillcoin.amount( + 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT); + int64_t expected_bob_balance_after_order_2 = + (10 * JILL_PRECISION) - order_2_sell_fee.amount.value - expected_jill_fee.amount.value; + BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), expected_bob_balance_after_order_2); + BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 0); + + // Check the asset issuer's accumulated fees + share_type expected_smartbit_fee_after_order_2 = expected_smartbit_fee.amount; + share_type expected_jill_fee_after_order_2 = expected_jill_fee.amount; + BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == expected_smartbit_fee_after_order_2); + BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee_after_order_2); + + + ////// + // Create Order 3 that will the remainder of match Order 2 + ////// + // Initialize token balance of actors + BOOST_TEST_MESSAGE("Issuing 5 JCOIN to charlie"); + trx.clear(); + issue_uia(charlie, jillcoin.amount(5 * JILL_PRECISION)); + BOOST_TEST_MESSAGE("Checking charlie's balance"); + BOOST_REQUIRE_EQUAL(get_balance(charlie, jillcoin), 5 * JILL_PRECISION); + + // Charlie is is willing to sell 5 JILLCOIN for at least 150 SMARTBIT + limit_order_create_operation order_3_op = create_sell_operation(charlie.id, + jillcoin.amount(5 * JILL_PRECISION), + smartbit.amount(150 * SMARTBIT_PRECISION)); + trx.clear(); + trx.operations.push_back(order_3_op); + asset charlie_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, charlie_private_key); + ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type order_3_id = ptx.operation_results[0].get(); + + // Order 3 should be completely filled + const limit_order_object *order_3 = db.find(order_3_id); + BOOST_CHECK(order_3 == nullptr); + + // Order 2 should be partially filled and still present on the order books + const limit_order_object *order_2_after = db.find(order_2_id); + BOOST_CHECK(order_2_after != nullptr); + + // Check the new balance of the taker + // Charlie was the taker; he is receiving SMARTBIT + expected_smartbit_fee = smartbit.amount( + 150 * SMARTBIT_PRECISION * smartbit_taker_fee_percent / GRAPHENE_100_PERCENT); + int64_t expected_charlie_balance_after_order_3 = + (150 * SMARTBIT_PRECISION) - charlie_sell_fee.amount.value - expected_smartbit_fee.amount.value; + BOOST_REQUIRE_EQUAL(get_balance(charlie, smartbit), expected_charlie_balance_after_order_3); + BOOST_REQUIRE_EQUAL(get_balance(charlie, jillcoin), 0); + + // Check the new balance of the maker + // Bob was the maker; he is receiving JILLCOIN + asset expected_jill_order_3_fee = jillcoin.amount( + 5 * JILL_PRECISION * jill_maker_fee_percent / GRAPHENE_100_PERCENT); + int64_t expected_bob_balance_after_order_3 = + expected_bob_balance_after_order_2 + + (5 * JILL_PRECISION) - expected_jill_order_3_fee.amount.value; + BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), expected_bob_balance_after_order_3); + BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 0); + + // Check the asset issuer's accumulated fees + share_type expected_smartbit_fee_after_order_3 = + expected_smartbit_fee_after_order_2 + expected_smartbit_fee.amount; + share_type expected_jill_fee_after_order_3 = expected_jill_fee_after_order_2 + expected_jill_fee.amount; + BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == expected_smartbit_fee_after_order_3); + BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee_after_order_3); + + } FC_LOG_AND_RETHROW() + } + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From bab6d230016255621c275dbdff8dd94d00d919da Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Mon, 13 Apr 2020 16:51:18 -0400 Subject: [PATCH 18/47] BSIP81: Comments --- libraries/chain/asset_evaluator.cpp | 5 +++-- libraries/chain/db_maint.cpp | 6 ++++-- libraries/protocol/asset_ops.cpp | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 41fbfd4bf1..52cf3a98a5 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -77,8 +77,9 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o auto asset_symbol_itr = asset_indx.find( op.symbol ); FC_ASSERT( asset_symbol_itr == asset_indx.end() ); - // This must remain due to "BOND.CNY" being allowed before this HF + // Define now from the current block time const time_point_sec now = d.head_block_time(); + // This must remain due to "BOND.CNY" being allowed before this HF if( now > HARDFORK_385_TIME ) { auto dotpos = op.symbol.rfind( '.' ); @@ -117,8 +118,8 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o FC_ASSERT( op.precision == op.bitasset_opts->short_backing_asset(d).precision ); } + // Taker fees should be zero until activation of BSIP81 if(now <= HARDFORK_BSIP_81_TIME) { - // Taker fees should be zero until activation of BSIP81 FC_ASSERT(op.common_options.taker_fee_percent == 0, "Simple maker-taker fees are not yet activated"); } diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 03d942e3e5..16195be278 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -1080,10 +1080,12 @@ void process_hf_868_890( database& db, bool skip_check_call_orders ) } /**** - * @brief a one-time data process to assign default taker fees for BSIP81 + * @brief A one-time data process to assign the initial taker fee percentage for BSIP81 + * Every asset issuer may subsequently change the taker fee */ void process_hf_bsip81(database &db) { - // for each asset + // Iterate through every each to set the default taker fee to equal the maker fee + // Without this processing code the taker fee will default to zero const auto &asset_idx = db.get_index_type().indices().get(); for (auto asset_itr = asset_idx.cbegin(); asset_itr != asset_idx.cend(); ++asset_itr) { const asset_object &asset = *asset_itr; diff --git a/libraries/protocol/asset_ops.cpp b/libraries/protocol/asset_ops.cpp index c8d38eb612..942c37c6eb 100644 --- a/libraries/protocol/asset_ops.cpp +++ b/libraries/protocol/asset_ops.cpp @@ -216,7 +216,9 @@ void asset_options::validate()const { FC_ASSERT( max_supply > 0 ); FC_ASSERT( max_supply <= GRAPHENE_MAX_SHARE_SUPPLY ); + // The non-negative maker fee must be less than 100% FC_ASSERT( market_fee_percent <= GRAPHENE_100_PERCENT ); + // The non-negative taker fee must be less than 100% FC_ASSERT( taker_fee_percent <= GRAPHENE_100_PERCENT ); FC_ASSERT( max_market_fee >= 0 && max_market_fee <= GRAPHENE_MAX_SHARE_SUPPLY ); // There must be no high bits in permissions whose meaning is not known. From 86a723391c804a6e6882c78e9bf17934329da4be Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Mon, 13 Apr 2020 17:38:34 -0400 Subject: [PATCH 19/47] BSIP81: Remove unused public function from evaluator --- libraries/chain/include/graphene/chain/market_evaluator.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/chain/include/graphene/chain/market_evaluator.hpp b/libraries/chain/include/graphene/chain/market_evaluator.hpp index 60a37e5bd3..ca27ff5f9b 100644 --- a/libraries/chain/include/graphene/chain/market_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/market_evaluator.hpp @@ -42,8 +42,6 @@ namespace graphene { namespace chain { void_result do_evaluate( const limit_order_create_operation& o ); object_id_type do_apply( const limit_order_create_operation& o ); - asset calculate_market_fee( const asset_object* aobj, const asset& trade_amount ); - /** override the default behavior defined by generic_evalautor */ virtual void convert_fee() override; From 8e221bec1d386e702341fde38e171e4298d47a70 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Mon, 13 Apr 2020 18:33:18 -0400 Subject: [PATCH 20/47] BSIP81: Fix bug of zero taker fees whenever maker fee is set to 0% --- libraries/chain/db_market.cpp | 11 +- tests/tests/simple_maker_taker_fee_tests.cpp | 316 +++++++++++++++++++ 2 files changed, 325 insertions(+), 2 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 2145371d48..655248b67a 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -1180,13 +1180,20 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass if( !trade_asset.charges_market_fees() ) return trade_asset.amount(0); - if( trade_asset.options.market_fee_percent == 0 ) + // Optimization: The fee is zero if the order is a maker, and the maker fee percent is 0% + if( is_maker && trade_asset.options.market_fee_percent == 0 ) return trade_asset.amount(0); // BSIP81: Asset owners may specify different market fee rate for maker orders and taker orders - uint16_t fee_percent; auto maint_time = get_dynamic_global_properties().next_maintenance_time; bool before_core_hardfork_bsip81 = ( maint_time <= HARDFORK_BSIP_81_TIME ); // before simple maker-taker fee + + // Optimization: The fee is zero if the order is a taker, and the taker fee percent is 0% + // This optimization should only be used after BSIP81 activation + if(!before_core_hardfork_bsip81 && !is_maker && trade_asset.options.taker_fee_percent == 0) + return trade_asset.amount(0); + + uint16_t fee_percent; if( before_core_hardfork_bsip81 ) { // Before BSIP81, the fee is the market fee fee_percent = trade_asset.options.market_fee_percent; diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index 883a940313..50cb8fae31 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -788,6 +788,322 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa } + /** + * Test of different maker and taker fees charged when filling limit orders after HF for a UIA + * + * Test the filling of a taker fee when the **maker** fee percent is set to 0. This tests some optimizations + * in database::calculate_market_fee(). + */ + BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_uia_2) { + try { + // Initialize for the current time + trx.clear(); + set_expiration(db, trx); + + // Initialize actors + ACTORS((jill)(izzy)(alice)(bob)); + + // Initialize tokens + price price(asset(1, asset_id_type(1)), asset(1)); + + const uint16_t JILL_PRECISION = 100; + const uint16_t jill_market_fee_percent = 0 * GRAPHENE_1_PERCENT; + const asset_object jillcoin = create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, + jill_market_fee_percent); + + const uint16_t IZZY_PRECISION = 1000; + const uint16_t izzy_market_fee_percent = 0 * GRAPHENE_1_PERCENT; + const asset_object izzycoin = create_user_issued_asset("ICOIN", izzy, charge_market_fee, price, 3, + izzy_market_fee_percent); + + + ////// + // Advance to activate hardfork + ////// + generate_blocks(HARDFORK_BSIP_81_TIME); + generate_block(); + trx.clear(); + set_expiration(db, trx); + + + ////// + // After HF, test that new values can be set + ////// + // Define the new taker fees + uint16_t jill_maker_fee_percent = jill_market_fee_percent; + uint16_t jill_taker_fee_percent = 1 * GRAPHENE_1_PERCENT; + + uint16_t izzy_maker_fee_percent = izzy_market_fee_percent; + uint16_t izzy_taker_fee_percent = 3 * GRAPHENE_1_PERCENT; + + // Set the new taker fee for JILLCOIN + asset_update_operation uop; + uop.issuer = jill.id; + uop.asset_to_update = jillcoin.get_id(); + uop.new_options.market_fee_percent = jill_maker_fee_percent; + uop.new_options = jillcoin.options; + uop.new_options.taker_fee_percent = jill_taker_fee_percent; + + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, jill_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee for JILLCOIN + asset_object updated_asset = jillcoin.get_id()(db); + uint16_t expected_taker_fee_percent = jill_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + // Set the new taker fee for IZZYCOIN + uop.issuer = izzy.id; + uop.asset_to_update = izzycoin.get_id(); + uop.new_options.market_fee_percent = izzy_maker_fee_percent; + uop.new_options = izzycoin.options; + uop.new_options.taker_fee_percent = izzy_taker_fee_percent; + + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, izzy_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee for IZZYCOIN + updated_asset = izzycoin.get_id()(db); + expected_taker_fee_percent = izzy_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + + ////// + // After HF, create limit orders that will perfectly match + ////// + BOOST_TEST_MESSAGE("Issuing 10 jillcoin to alice"); + issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION)); + BOOST_TEST_MESSAGE("Checking alice's balance"); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION); + + BOOST_TEST_MESSAGE("Issuing 300 izzycoin to bob"); + issue_uia(bob, izzycoin.amount(300 * IZZY_PRECISION)); + BOOST_TEST_MESSAGE("Checking bob's balance"); + BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 300 * IZZY_PRECISION); + + // Alice and Bob place orders which match, and are completely filled by each other + // Alice is willing to sell 10 JILLCOIN for at least 300 IZZYCOIN + limit_order_create_operation alice_sell_op = create_sell_operation(alice.id, + jillcoin.amount(10 * JILL_PRECISION), + izzycoin.amount(300 * + IZZY_PRECISION)); + trx.clear(); + trx.operations.push_back(alice_sell_op); + asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type alice_order_id = ptx.operation_results[0].get(); + + const limit_order_object* alice_order_before = db.find(alice_order_id); + BOOST_CHECK(alice_order_before != nullptr); + + // Bob is willing to sell 300 IZZYCOIN for at least 10 JILLCOIN + limit_order_create_operation bob_sell_op = create_sell_operation(bob.id, izzycoin.amount(300 * IZZY_PRECISION), + jillcoin.amount( + 10 * + JILL_PRECISION)); + trx.clear(); + trx.operations.push_back(bob_sell_op); + asset bob_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, bob_private_key); + ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type bob_order_id = ptx.operation_results[0].get(); + + // Check that the orders were filled by ensuring that they are no longer on the order books + const limit_order_object* alice_order = db.find(alice_order_id); + BOOST_CHECK(alice_order == nullptr); + const limit_order_object* bob_order = db.find(bob_order_id); + BOOST_CHECK(bob_order == nullptr); + + + // Check the new balances of the maker + // Alice was the maker; she is receiving IZZYCOIN + asset expected_izzy_fee = izzycoin.amount( + 300 * IZZY_PRECISION * izzy_maker_fee_percent / GRAPHENE_100_PERCENT); + BOOST_REQUIRE_EQUAL(get_balance(alice, izzycoin), + (300 * IZZY_PRECISION) - alice_sell_fee.amount.value - expected_izzy_fee.amount.value); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0); + + // Check the new balance of the taker + // Bob was the taker; he is receiving JILLCOIN + asset expected_jill_fee = jillcoin.amount( + 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT); + BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), + (10 * JILL_PRECISION) - bob_sell_fee.amount.value - expected_jill_fee.amount.value); + BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 0); + + // Check the asset issuer's accumulated fees + BOOST_CHECK(izzycoin.dynamic_asset_data_id(db).accumulated_fees == expected_izzy_fee.amount); + BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee.amount); + + } FC_LOG_AND_RETHROW() + } + + + /** + * Test of different maker and taker fees charged when filling limit orders after HF for a UIA + * + * Test the filling of a taker fee when the **taker** fee percent is set to 0. This tests some optimizations + * in database::calculate_market_fee(). + */ + BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_uia_3) { + try { + // Initialize for the current time + trx.clear(); + set_expiration(db, trx); + + // Initialize actors + ACTORS((jill)(izzy)(alice)(bob)); + + // Initialize tokens + price price(asset(1, asset_id_type(1)), asset(1)); + + const uint16_t JILL_PRECISION = 100; + const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT; + const asset_object jillcoin = create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, + jill_market_fee_percent); + + const uint16_t IZZY_PRECISION = 1000; + const uint16_t izzy_market_fee_percent = 5 * GRAPHENE_1_PERCENT; + const asset_object izzycoin = create_user_issued_asset("ICOIN", izzy, charge_market_fee, price, 3, + izzy_market_fee_percent); + + + ////// + // Advance to activate hardfork + ////// + generate_blocks(HARDFORK_BSIP_81_TIME); + generate_block(); + trx.clear(); + set_expiration(db, trx); + + + ////// + // After HF, test that new values can be set + ////// + // Define the new taker fees + uint16_t jill_maker_fee_percent = jill_market_fee_percent; + uint16_t jill_taker_fee_percent = 0 * GRAPHENE_1_PERCENT; + + uint16_t izzy_maker_fee_percent = izzy_market_fee_percent; + uint16_t izzy_taker_fee_percent = 0 * GRAPHENE_1_PERCENT; + + // Set the new taker fee for JILLCOIN + asset_update_operation uop; + uop.issuer = jill.id; + uop.asset_to_update = jillcoin.get_id(); + uop.new_options.market_fee_percent = jill_maker_fee_percent; + uop.new_options = jillcoin.options; + uop.new_options.taker_fee_percent = jill_taker_fee_percent; + + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, jill_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee for JILLCOIN + asset_object updated_asset = jillcoin.get_id()(db); + uint16_t expected_taker_fee_percent = jill_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + // Set the new taker fee for IZZYCOIN + uop.issuer = izzy.id; + uop.asset_to_update = izzycoin.get_id(); + uop.new_options.market_fee_percent = izzy_maker_fee_percent; + uop.new_options = izzycoin.options; + uop.new_options.taker_fee_percent = izzy_taker_fee_percent; + + trx.clear(); + trx.operations.push_back(uop); + db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, izzy_private_key); + PUSH_TX(db, trx); // No exception should be thrown + + // Check the taker fee for IZZYCOIN + updated_asset = izzycoin.get_id()(db); + expected_taker_fee_percent = izzy_taker_fee_percent; + BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + + + ////// + // After HF, create limit orders that will perfectly match + ////// + BOOST_TEST_MESSAGE("Issuing 10 jillcoin to alice"); + issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION)); + BOOST_TEST_MESSAGE("Checking alice's balance"); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION); + + BOOST_TEST_MESSAGE("Issuing 300 izzycoin to bob"); + issue_uia(bob, izzycoin.amount(300 * IZZY_PRECISION)); + BOOST_TEST_MESSAGE("Checking bob's balance"); + BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 300 * IZZY_PRECISION); + + // Alice and Bob place orders which match, and are completely filled by each other + // Alice is willing to sell 10 JILLCOIN for at least 300 IZZYCOIN + limit_order_create_operation alice_sell_op = create_sell_operation(alice.id, + jillcoin.amount(10 * JILL_PRECISION), + izzycoin.amount(300 * + IZZY_PRECISION)); + trx.clear(); + trx.operations.push_back(alice_sell_op); + asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type alice_order_id = ptx.operation_results[0].get(); + + const limit_order_object* alice_order_before = db.find(alice_order_id); + BOOST_CHECK(alice_order_before != nullptr); + + // Bob is willing to sell 300 IZZYCOIN for at least 10 JILLCOIN + limit_order_create_operation bob_sell_op = create_sell_operation(bob.id, izzycoin.amount(300 * IZZY_PRECISION), + jillcoin.amount( + 10 * + JILL_PRECISION)); + trx.clear(); + trx.operations.push_back(bob_sell_op); + asset bob_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, bob_private_key); + ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type bob_order_id = ptx.operation_results[0].get(); + + // Check that the orders were filled by ensuring that they are no longer on the order books + const limit_order_object* alice_order = db.find(alice_order_id); + BOOST_CHECK(alice_order == nullptr); + const limit_order_object* bob_order = db.find(bob_order_id); + BOOST_CHECK(bob_order == nullptr); + + + // Check the new balances of the maker + // Alice was the maker; she is receiving IZZYCOIN + asset expected_izzy_fee = izzycoin.amount( + 300 * IZZY_PRECISION * izzy_maker_fee_percent / GRAPHENE_100_PERCENT); + BOOST_REQUIRE_EQUAL(get_balance(alice, izzycoin), + (300 * IZZY_PRECISION) - alice_sell_fee.amount.value - expected_izzy_fee.amount.value); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0); + + // Check the new balance of the taker + // Bob was the taker; he is receiving JILLCOIN + asset expected_jill_fee = jillcoin.amount( + 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT); + BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), + (10 * JILL_PRECISION) - bob_sell_fee.amount.value - expected_jill_fee.amount.value); + BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 0); + + // Check the asset issuer's accumulated fees + BOOST_CHECK(izzycoin.dynamic_asset_data_id(db).accumulated_fees == expected_izzy_fee.amount); + BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee.amount); + + } FC_LOG_AND_RETHROW() + } + + /** * Test of different maker and taker fees charged when filling limit orders after HF for a smart asset */ From 4f339c6803ae45efce1a5dbd564a4e1ad29c2df7 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Mon, 13 Apr 2020 20:44:21 -0400 Subject: [PATCH 21/47] BSIP81: Refactor taker_fee_percent into asset_options.extensions --- libraries/chain/asset_evaluator.cpp | 12 +- libraries/chain/db_maint.cpp | 2 +- libraries/chain/db_market.cpp | 11 +- libraries/protocol/asset_ops.cpp | 5 +- .../include/graphene/protocol/asset_ops.hpp | 7 +- tests/tests/simple_maker_taker_fee_tests.cpp | 168 ++++++++++-------- 6 files changed, 120 insertions(+), 85 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 52cf3a98a5..4653159f91 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -51,7 +51,9 @@ namespace detail { { if (block_time < HARDFORK_BSIP_81_TIME) { // Taker fees should be zero until activation of BSIP81 - FC_ASSERT(options.taker_fee_percent == 0, "Taker fee must be 0% until HARDFORK_BSIP_81_TIME"); + FC_ASSERT(!options.extensions.value.taker_fee_percent.valid() || + *options.extensions.value.taker_fee_percent == 0, + "Taker fee must be 0% until HARDFORK_BSIP_81_TIME"); } } } @@ -120,7 +122,9 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o // Taker fees should be zero until activation of BSIP81 if(now <= HARDFORK_BSIP_81_TIME) { - FC_ASSERT(op.common_options.taker_fee_percent == 0, "Simple maker-taker fees are not yet activated"); + FC_ASSERT(!op.common_options.extensions.value.taker_fee_percent.valid() + || *op.common_options.extensions.value.taker_fee_percent == 0, + "Simple maker-taker fees are not yet activated"); } return void_result(); @@ -327,7 +331,9 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) if(now <= HARDFORK_BSIP_81_TIME) { // Taker fees should be zero until activation of BSIP81 - FC_ASSERT(o.new_options.taker_fee_percent == 0, "Simple maker-taker fees are not yet activated"); + FC_ASSERT(!o.new_options.extensions.value.taker_fee_percent.valid() + || *o.new_options.extensions.value.taker_fee_percent == 0, + "Simple maker-taker fees are not yet activated"); } return void_result(); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 16195be278..9999a161eb 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -1093,7 +1093,7 @@ void process_hf_bsip81(database &db) { wlog("Setting default taker fee of ${asset} to ${raw_market_fee_percent}.", ("asset", asset.symbol) ("raw_market_fee_percent", raw_market_fee_percent)); db.modify(asset, [raw_market_fee_percent](asset_object &obj) { - obj.options.taker_fee_percent = raw_market_fee_percent; + obj.options.extensions.value.taker_fee_percent = raw_market_fee_percent; }); } } diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 655248b67a..d37cd05670 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -1190,7 +1190,9 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass // Optimization: The fee is zero if the order is a taker, and the taker fee percent is 0% // This optimization should only be used after BSIP81 activation - if(!before_core_hardfork_bsip81 && !is_maker && trade_asset.options.taker_fee_percent == 0) + if(!before_core_hardfork_bsip81 && !is_maker + && trade_asset.options.extensions.value.taker_fee_percent.valid() + && *trade_asset.options.extensions.value.taker_fee_percent == 0) return trade_asset.amount(0); uint16_t fee_percent; @@ -1199,7 +1201,12 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass fee_percent = trade_asset.options.market_fee_percent; } else { // After BSIP81, the fee depends on if maker or taker - fee_percent = is_maker ? trade_asset.options.market_fee_percent : trade_asset.options.taker_fee_percent; + if (is_maker) { + fee_percent = trade_asset.options.market_fee_percent; + } else { + fee_percent = trade_asset.options.extensions.value.taker_fee_percent.valid() + ? *trade_asset.options.extensions.value.taker_fee_percent : 0; + } } auto value = detail::calculate_percent(trade_amount.amount, fee_percent); diff --git a/libraries/protocol/asset_ops.cpp b/libraries/protocol/asset_ops.cpp index 942c37c6eb..6ada68530f 100644 --- a/libraries/protocol/asset_ops.cpp +++ b/libraries/protocol/asset_ops.cpp @@ -218,8 +218,11 @@ void asset_options::validate()const FC_ASSERT( max_supply <= GRAPHENE_MAX_SHARE_SUPPLY ); // The non-negative maker fee must be less than 100% FC_ASSERT( market_fee_percent <= GRAPHENE_100_PERCENT ); + // The non-negative taker fee must be less than 100% - FC_ASSERT( taker_fee_percent <= GRAPHENE_100_PERCENT ); + if( extensions.value.taker_fee_percent.valid() ) + FC_ASSERT( *extensions.value.taker_fee_percent <= GRAPHENE_100_PERCENT ); + FC_ASSERT( max_market_fee >= 0 && max_market_fee <= GRAPHENE_MAX_SHARE_SUPPLY ); // There must be no high bits in permissions whose meaning is not known. FC_ASSERT( !(issuer_permissions & ~ASSET_ISSUER_PERMISSION_MASK) ); diff --git a/libraries/protocol/include/graphene/protocol/asset_ops.hpp b/libraries/protocol/include/graphene/protocol/asset_ops.hpp index 15219e4937..858c25b512 100644 --- a/libraries/protocol/include/graphene/protocol/asset_ops.hpp +++ b/libraries/protocol/include/graphene/protocol/asset_ops.hpp @@ -32,6 +32,8 @@ namespace graphene { namespace protocol { { fc::optional reward_percent; fc::optional> whitelist_market_fee_sharing; + // After BSIP81 activation, taker_fee_percent is the taker fee + fc::optional taker_fee_percent; }; typedef extension additional_asset_options_t; @@ -52,8 +54,6 @@ namespace graphene { namespace protocol { // BSIP81: Asset owners may specify different market fee rate for maker orders and taker orders // After BSIP81 activation, market_fee_percent is the maker fee uint16_t market_fee_percent = 0; - // After BSIP81 activation, taker_fee_percent is the taker fee - uint16_t taker_fee_percent = 0; /// Market fees calculated as @ref market_fee_percent of the traded volume are capped to this value share_type max_market_fee = GRAPHENE_MAX_SHARE_SUPPLY; @@ -527,7 +527,6 @@ FC_REFLECT( graphene::protocol::asset_claim_pool_operation::fee_parameters_type, FC_REFLECT( graphene::protocol::asset_options, (max_supply) (market_fee_percent) - (taker_fee_percent) (max_market_fee) (issuer_permissions) (flags) @@ -549,7 +548,7 @@ FC_REFLECT( graphene::protocol::bitasset_options, (extensions) ) -FC_REFLECT( graphene::protocol::additional_asset_options, (reward_percent)(whitelist_market_fee_sharing) ) +FC_REFLECT( graphene::protocol::additional_asset_options, (reward_percent)(whitelist_market_fee_sharing)(taker_fee_percent) ) FC_REFLECT( graphene::protocol::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) ) FC_REFLECT( graphene::protocol::asset_global_settle_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::protocol::asset_settle_operation::fee_parameters_type, (fee) ) diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index 50cb8fae31..b4c91bbe4e 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -63,7 +63,7 @@ struct simple_maker_taker_database_fixture : database_fixture { creator.common_options.flags = flags; creator.common_options.issuer_permissions = flags; creator.common_options.market_fee_percent = maker_fee_percent; - creator.common_options.taker_fee_percent = taker_fee_percent; + creator.common_options.extensions.value.taker_fee_percent = taker_fee_percent; return creator; @@ -104,7 +104,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa uop.asset_to_update = jillcoin.get_id(); uop.new_options = jillcoin.options; uint16_t new_taker_fee_percent = uop.new_options.market_fee_percent / 2; - uop.new_options.taker_fee_percent = new_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = new_taker_fee_percent; trx.operations.push_back(uop); db.current_fee_schedule().set_fee(trx.operations.back()); @@ -114,8 +114,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee asset_object updated_asset = jillcoin.get_id()(db); - uint16_t expected_taker_fee_percent = 0; // Before the HF it should be set to 0 - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); ////// @@ -126,7 +125,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa set_expiration(db, trx); uint64_t alternate_taker_fee_percent = new_taker_fee_percent * 2; - uop.new_options.taker_fee_percent = alternate_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = alternate_taker_fee_percent; proposal_create_operation cop; cop.review_period_seconds = 86400; @@ -141,8 +140,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee is not changed because the proposal has not been approved updated_asset = jillcoin.get_id()(db); - expected_taker_fee_percent = 0; // Before the HF it should be set to 0 - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); } @@ -189,14 +187,15 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // which is effectively the new maker fee percent ////// updated_asset = jillcoin.get_id()(db); - expected_taker_fee_percent = updated_asset.options.market_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + uint16_t expected_taker_fee_percent = updated_asset.options.market_fee_percent; + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); ////// // After HF, test invalid taker fees ////// - uop.new_options.taker_fee_percent = GRAPHENE_100_PERCENT + 1; + uop.new_options.extensions.value.taker_fee_percent = GRAPHENE_100_PERCENT + 1; trx.clear(); trx.operations.push_back(uop); db.current_fee_schedule().set_fee(trx.operations.back()); @@ -208,7 +207,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa ////// // After HF, test that new values can be set ////// - uop.new_options.taker_fee_percent = new_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = new_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); db.current_fee_schedule().set_fee(trx.operations.back()); @@ -218,7 +217,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee updated_asset = jillcoin.get_id()(db); expected_taker_fee_percent = new_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); ////// @@ -229,7 +229,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa set_expiration(db, trx); uint64_t alternate_taker_fee_percent = new_taker_fee_percent * 2; - uop.new_options.taker_fee_percent = alternate_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = alternate_taker_fee_percent; proposal_create_operation cop; cop.review_period_seconds = 86400; @@ -245,7 +245,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee is not changed because the proposal has not been approved updated_asset = jillcoin.get_id()(db); expected_taker_fee_percent = new_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); // Approve the proposal @@ -268,7 +269,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee is not changed because the proposal has not been approved updated_asset = jillcoin.get_id()(db); expected_taker_fee_percent = alternate_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); } @@ -324,7 +326,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa BOOST_CHECK(asset_idx.find("JCOIN2") != asset_idx.end()); updated_asset = *asset_idx.find("JCOIN2"); expected_taker_fee_percent = taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); uint16_t expected_maker_fee_percent = maker_fee_percent; BOOST_CHECK_EQUAL(expected_maker_fee_percent, updated_asset.options.market_fee_percent); @@ -347,7 +350,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa ACTORS((smartissuer)(feedproducer)); // Initialize tokens - const asset_object &bitsmart = create_bitasset("SMARTBIT", smartissuer.id); +// const asset_object &bitsmart = create_bitasset("SMARTBIT", smartissuer.id); + const asset_object bitsmart = create_bitasset("SMARTBIT", smartissuer.id); generate_blocks(HARDFORK_615_TIME); // get around Graphene issue #615 feed expiration bug @@ -361,7 +365,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa uop.asset_to_update = bitsmart.get_id(); uop.new_options = bitsmart.options; uint16_t new_taker_fee_percent = uop.new_options.market_fee_percent / 2; - uop.new_options.taker_fee_percent = new_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = new_taker_fee_percent; trx.operations.push_back(uop); db.current_fee_schedule().set_fee(trx.operations.back()); @@ -371,8 +375,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee asset_object updated_asset = bitsmart.get_id()(db); - uint16_t expected_taker_fee_percent = 0; // Before the HF it should be set to 0 - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); ////// @@ -390,14 +393,15 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // which is effectively the new maker fee percent ////// updated_asset = bitsmart.get_id()(db); - expected_taker_fee_percent = updated_asset.options.market_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + uint16_t expected_taker_fee_percent = updated_asset.options.market_fee_percent; + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); ////// // After HF, test invalid taker fees ////// - uop.new_options.taker_fee_percent = GRAPHENE_100_PERCENT + 1; + uop.new_options.extensions.value.taker_fee_percent = GRAPHENE_100_PERCENT + 1; trx.clear(); trx.operations.push_back(uop); db.current_fee_schedule().set_fee(trx.operations.back()); @@ -409,7 +413,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa ////// // After HF, test that new values can be set ////// - uop.new_options.taker_fee_percent = new_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = new_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); db.current_fee_schedule().set_fee(trx.operations.back()); @@ -419,7 +423,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee updated_asset = bitsmart.get_id()(db); expected_taker_fee_percent = new_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); } FC_LOG_AND_RETHROW() } @@ -467,12 +472,14 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa const uint16_t bitsmart1coin_market_fee_percent = 7 * GRAPHENE_1_PERCENT; create_bitasset("SMARTBIT1", smartissuer.id, bitsmart1coin_market_fee_percent); generate_blocks(1); // The smart asset's ID will be updated after a block is generated - const asset_object &bitsmart1 = *db.get_index_type().indices().get().find("SMARTBIT1"); +// const asset_object &bitsmart1 = *db.get_index_type().indices().get().find("SMARTBIT1"); + const asset_object bitsmart1 = *db.get_index_type().indices().get().find("SMARTBIT1"); const uint16_t bitsmart2coin_market_fee_percent = 8 * GRAPHENE_1_PERCENT; create_bitasset("SMARTBIT2", smartissuer.id, bitsmart2coin_market_fee_percent); generate_blocks(1); // The smart asset's ID will be updated after a block is generated - const asset_object &bitsmart2 = *db.get_index_type().indices().get().find("SMARTBIT2"); +// const asset_object &bitsmart2 = *db.get_index_type().indices().get().find("SMARTBIT2"); + const asset_object bitsmart2 = *db.get_index_type().indices().get().find("SMARTBIT2"); ////// @@ -515,40 +522,32 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa ////// - // Before HF, test that taker fees are set to zero + // Before HF, test that taker fees are not set ////// // Check the taker fee updated_asset = alice1coin.get_id()(db); - expected_fee_percent = 0; // Before the HF it should be set to 0 - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = alice2coin.get_id()(db); - expected_fee_percent = 0; // Before the HF it should be set to 0 - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = bob1coin.get_id()(db); - expected_fee_percent = 0; // Before the HF it should be set to 0 - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = bob2coin.get_id()(db); - expected_fee_percent = 0; // Before the HF it should be set to 0 - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = charlie1coin.get_id()(db); - expected_fee_percent = 0; // Before the HF it should be set to 0 - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = charlie2coin.get_id()(db); - expected_fee_percent = 0; // Before the HF it should be set to 0 - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = bitsmart1.get_id()(db); - expected_fee_percent = 0; // Before the HF it should be set to 0 - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = bitsmart2.get_id()(db); - expected_fee_percent = 0; // Before the HF it should be set to 0 - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); ////// @@ -601,35 +600,42 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa ////// updated_asset = alice1coin.get_id()(db); expected_fee_percent = alice1coin_market_fee_percent; - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); updated_asset = alice2coin.get_id()(db); expected_fee_percent = alice2coin_market_fee_percent; - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); updated_asset = bob1coin.get_id()(db); expected_fee_percent = bob1coin_market_fee_percent; - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); updated_asset = bob2coin.get_id()(db); expected_fee_percent = bob2coin_market_fee_percent; - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); updated_asset = charlie1coin.get_id()(db); expected_fee_percent = charlie1coin_market_fee_percent; - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); updated_asset = charlie2coin.get_id()(db); expected_fee_percent = charlie2coin_market_fee_percent; - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); updated_asset = bitsmart1.get_id()(db); expected_fee_percent = bitsmart1coin_market_fee_percent; - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); updated_asset = bitsmart2.get_id()(db); expected_fee_percent = bitsmart2coin_market_fee_percent; - BOOST_CHECK_EQUAL(expected_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); } FC_LOG_AND_RETHROW() } @@ -685,7 +691,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa uop.issuer = jill.id; uop.asset_to_update = jillcoin.get_id(); uop.new_options = jillcoin.options; - uop.new_options.taker_fee_percent = jill_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = jill_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); @@ -696,13 +702,14 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee for JILLCOIN asset_object updated_asset = jillcoin.get_id()(db); uint16_t expected_taker_fee_percent = jill_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); // Set the new taker fee for IZZYCOIN uop.issuer = izzy.id; uop.asset_to_update = izzycoin.get_id(); uop.new_options = izzycoin.options; - uop.new_options.taker_fee_percent = izzy_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = izzy_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); @@ -713,7 +720,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee for IZZYCOIN updated_asset = izzycoin.get_id()(db); expected_taker_fee_percent = izzy_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); ////// @@ -842,7 +850,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa uop.asset_to_update = jillcoin.get_id(); uop.new_options.market_fee_percent = jill_maker_fee_percent; uop.new_options = jillcoin.options; - uop.new_options.taker_fee_percent = jill_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = jill_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); @@ -853,14 +861,15 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee for JILLCOIN asset_object updated_asset = jillcoin.get_id()(db); uint16_t expected_taker_fee_percent = jill_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); // Set the new taker fee for IZZYCOIN uop.issuer = izzy.id; uop.asset_to_update = izzycoin.get_id(); uop.new_options.market_fee_percent = izzy_maker_fee_percent; uop.new_options = izzycoin.options; - uop.new_options.taker_fee_percent = izzy_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = izzy_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); @@ -871,7 +880,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee for IZZYCOIN updated_asset = izzycoin.get_id()(db); expected_taker_fee_percent = izzy_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); ////// @@ -1000,7 +1010,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa uop.asset_to_update = jillcoin.get_id(); uop.new_options.market_fee_percent = jill_maker_fee_percent; uop.new_options = jillcoin.options; - uop.new_options.taker_fee_percent = jill_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = jill_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); @@ -1011,14 +1021,15 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee for JILLCOIN asset_object updated_asset = jillcoin.get_id()(db); uint16_t expected_taker_fee_percent = jill_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); // Set the new taker fee for IZZYCOIN uop.issuer = izzy.id; uop.asset_to_update = izzycoin.get_id(); uop.new_options.market_fee_percent = izzy_maker_fee_percent; uop.new_options = izzycoin.options; - uop.new_options.taker_fee_percent = izzy_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = izzy_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); @@ -1029,7 +1040,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee for IZZYCOIN updated_asset = izzycoin.get_id()(db); expected_taker_fee_percent = izzy_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); ////// @@ -1126,7 +1138,9 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa const uint16_t SMARTBIT_PRECISION = 10000; const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT; - const asset_object &smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, +// const asset_object &smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, +// charge_market_fee, 4); + const asset_object smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, charge_market_fee, 4); const auto &core = asset_id_type()(db); @@ -1164,7 +1178,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa uop.issuer = jill.id; uop.asset_to_update = jillcoin.get_id(); uop.new_options = jillcoin.options; - uop.new_options.taker_fee_percent = jill_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = jill_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); @@ -1175,7 +1189,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee for JILLCOIN asset_object updated_asset = jillcoin.get_id()(db); uint16_t expected_taker_fee_percent = jill_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); // Set the new taker fee for SMARTBIT @@ -1184,7 +1199,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa uop.asset_to_update = smartbit.get_id(); uop.new_options = smartbit.options; uop.new_options.market_fee_percent = smartbit_maker_fee_percent; - uop.new_options.taker_fee_percent = smartbit_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = smartbit_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); @@ -1195,7 +1210,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee for SMARTBIT updated_asset = smartbit.get_id()(db); expected_taker_fee_percent = smartbit_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); // Check the maker fee for SMARTBIT updated_asset = smartbit.get_id()(db); @@ -1311,7 +1327,9 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa const uint16_t SMARTBIT_PRECISION = 10000; const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT; - const asset_object &smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, +// const asset_object &smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, +// charge_market_fee, 4); + const asset_object smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, charge_market_fee, 4); const auto &core = asset_id_type()(db); @@ -1349,7 +1367,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa uop.issuer = jill.id; uop.asset_to_update = jillcoin.get_id(); uop.new_options = jillcoin.options; - uop.new_options.taker_fee_percent = jill_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = jill_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); @@ -1360,7 +1378,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee for JILLCOIN asset_object updated_asset = jillcoin.get_id()(db); uint16_t expected_taker_fee_percent = jill_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); // Set the new taker fee for SMARTBIT @@ -1369,7 +1388,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa uop.asset_to_update = smartbit.get_id(); uop.new_options = smartbit.options; uop.new_options.market_fee_percent = smartbit_maker_fee_percent; - uop.new_options.taker_fee_percent = smartbit_taker_fee_percent; + uop.new_options.extensions.value.taker_fee_percent = smartbit_taker_fee_percent; trx.clear(); trx.operations.push_back(uop); @@ -1380,7 +1399,8 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the taker fee for SMARTBIT updated_asset = smartbit.get_id()(db); expected_taker_fee_percent = smartbit_taker_fee_percent; - BOOST_CHECK_EQUAL(expected_taker_fee_percent, updated_asset.options.taker_fee_percent); + BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); + BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); // Check the maker fee for SMARTBIT updated_asset = smartbit.get_id()(db); From 2be77d929860f54ca696371f0a6abad63d2eb3c7 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Tue, 14 Apr 2020 19:13:53 -0400 Subject: [PATCH 22/47] BSIP81: Refactor unit tests to obtain the final asset objects from the database --- tests/tests/simple_maker_taker_fee_tests.cpp | 82 +++++++++++++------- 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index b4c91bbe4e..7dc002b98b 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -350,13 +350,15 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa ACTORS((smartissuer)(feedproducer)); // Initialize tokens -// const asset_object &bitsmart = create_bitasset("SMARTBIT", smartissuer.id); - const asset_object bitsmart = create_bitasset("SMARTBIT", smartissuer.id); - + create_bitasset("SMARTBIT", smartissuer.id); + // Obtain asset object after a block is generated to obtain the final object that is commited to the database + generate_block(); + const asset_object &bitsmart = get_asset("SMARTBIT"); generate_blocks(HARDFORK_615_TIME); // get around Graphene issue #615 feed expiration bug generate_block(); + ////// // Before HF, test inability to set taker fees ////// @@ -446,40 +448,46 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa price price(asset(1, asset_id_type(1)), asset(1)); const uint16_t alice1coin_market_fee_percent = 1 * GRAPHENE_1_PERCENT; - const asset_object alice1coin = create_user_issued_asset("ALICE1COIN", alice, charge_market_fee, price, 2, + create_user_issued_asset("ALICE1COIN", alice, charge_market_fee, price, 2, alice1coin_market_fee_percent); const uint16_t alice2coin_market_fee_percent = 2 * GRAPHENE_1_PERCENT; - const asset_object alice2coin = create_user_issued_asset("ALICE2COIN", alice, charge_market_fee, price, 2, + create_user_issued_asset("ALICE2COIN", alice, charge_market_fee, price, 2, alice2coin_market_fee_percent); const uint16_t bob1coin_market_fee_percent = 3 * GRAPHENE_1_PERCENT; - const asset_object bob1coin = create_user_issued_asset("BOB1COIN", alice, charge_market_fee, price, 2, + create_user_issued_asset("BOB1COIN", alice, charge_market_fee, price, 2, bob1coin_market_fee_percent); const uint16_t bob2coin_market_fee_percent = 4 * GRAPHENE_1_PERCENT; - const asset_object bob2coin = create_user_issued_asset("BOB2COIN", alice, charge_market_fee, price, 2, + create_user_issued_asset("BOB2COIN", alice, charge_market_fee, price, 2, bob2coin_market_fee_percent); const uint16_t charlie1coin_market_fee_percent = 4 * GRAPHENE_1_PERCENT; - const asset_object charlie1coin = create_user_issued_asset("CHARLIE1COIN", alice, charge_market_fee, price, 2, + create_user_issued_asset("CHARLIE1COIN", alice, charge_market_fee, price, 2, charlie1coin_market_fee_percent); const uint16_t charlie2coin_market_fee_percent = 5 * GRAPHENE_1_PERCENT; - const asset_object charlie2coin = create_user_issued_asset("CHARLIE2COIN", alice, charge_market_fee, price, 2, + create_user_issued_asset("CHARLIE2COIN", alice, charge_market_fee, price, 2, charlie2coin_market_fee_percent); const uint16_t bitsmart1coin_market_fee_percent = 7 * GRAPHENE_1_PERCENT; create_bitasset("SMARTBIT1", smartissuer.id, bitsmart1coin_market_fee_percent); - generate_blocks(1); // The smart asset's ID will be updated after a block is generated -// const asset_object &bitsmart1 = *db.get_index_type().indices().get().find("SMARTBIT1"); - const asset_object bitsmart1 = *db.get_index_type().indices().get().find("SMARTBIT1"); + const uint16_t bitsmart2coin_market_fee_percent = 8 * GRAPHENE_1_PERCENT; create_bitasset("SMARTBIT2", smartissuer.id, bitsmart2coin_market_fee_percent); - generate_blocks(1); // The smart asset's ID will be updated after a block is generated -// const asset_object &bitsmart2 = *db.get_index_type().indices().get().find("SMARTBIT2"); - const asset_object bitsmart2 = *db.get_index_type().indices().get().find("SMARTBIT2"); + + // Obtain asset object after a block is generated to obtain the final object that is commited to the database + generate_block(); + const asset_object& alice1coin = get_asset("ALICE1COIN"); + const asset_object& alice2coin = get_asset("ALICE2COIN"); + const asset_object& bob1coin = get_asset("BOB1COIN"); + const asset_object& bob2coin = get_asset("BOB2COIN"); + const asset_object& charlie1coin = get_asset("CHARLIE1COIN"); + const asset_object& charlie2coin = get_asset("CHARLIE2COIN"); + const asset_object& bitsmart1 = get_asset("SMARTBIT1"); + const asset_object& bitsmart2 = get_asset("SMARTBIT2"); ////// @@ -658,14 +666,19 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa const uint16_t JILL_PRECISION = 100; const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT; - const asset_object jillcoin = create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, + create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, jill_market_fee_percent); const uint16_t IZZY_PRECISION = 1000; const uint16_t izzy_market_fee_percent = 5 * GRAPHENE_1_PERCENT; - const asset_object izzycoin = create_user_issued_asset("ICOIN", izzy, charge_market_fee, price, 3, + create_user_issued_asset("ICOIN", izzy, charge_market_fee, price, 3, izzy_market_fee_percent); + // Obtain asset object after a block is generated to obtain the final object that is commited to the database + generate_block(); + const asset_object& jillcoin = get_asset("JCOIN"); + const asset_object& izzycoin = get_asset("ICOIN"); + ////// // Advance to activate hardfork @@ -816,14 +829,19 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa const uint16_t JILL_PRECISION = 100; const uint16_t jill_market_fee_percent = 0 * GRAPHENE_1_PERCENT; - const asset_object jillcoin = create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, + create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, jill_market_fee_percent); const uint16_t IZZY_PRECISION = 1000; const uint16_t izzy_market_fee_percent = 0 * GRAPHENE_1_PERCENT; - const asset_object izzycoin = create_user_issued_asset("ICOIN", izzy, charge_market_fee, price, 3, + create_user_issued_asset("ICOIN", izzy, charge_market_fee, price, 3, izzy_market_fee_percent); + // Obtain asset object after a block is generated to obtain the final object that is commited to the database + generate_block(); + const asset_object& jillcoin = get_asset("JCOIN"); + const asset_object& izzycoin = get_asset("ICOIN"); + ////// // Advance to activate hardfork @@ -976,14 +994,19 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa const uint16_t JILL_PRECISION = 100; const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT; - const asset_object jillcoin = create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, + create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, jill_market_fee_percent); const uint16_t IZZY_PRECISION = 1000; const uint16_t izzy_market_fee_percent = 5 * GRAPHENE_1_PERCENT; - const asset_object izzycoin = create_user_issued_asset("ICOIN", izzy, charge_market_fee, price, 3, + create_user_issued_asset("ICOIN", izzy, charge_market_fee, price, 3, izzy_market_fee_percent); + // Obtain asset object after a block is generated to obtain the final object that is commited to the database + generate_block(); + const asset_object& jillcoin = get_asset("JCOIN"); + const asset_object& izzycoin = get_asset("ICOIN"); + ////// // Advance to activate hardfork @@ -1138,10 +1161,13 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa const uint16_t SMARTBIT_PRECISION = 10000; const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT; -// const asset_object &smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, -// charge_market_fee, 4); - const asset_object smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, + create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, charge_market_fee, 4); + + // Obtain asset object after a block is generated to obtain the final object that is commited to the database + generate_block(); + const asset_object &smartbit = get_asset("SMARTBIT"); + const auto &core = asset_id_type()(db); update_feed_producers(smartbit, {feedproducer.id}); @@ -1327,10 +1353,12 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa const uint16_t SMARTBIT_PRECISION = 10000; const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT; -// const asset_object &smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, -// charge_market_fee, 4); - const asset_object smartbit = create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, + create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, charge_market_fee, 4); + // Obtain asset object after a block is generated to obtain the final object that is commited to the database + generate_block(); + const asset_object &smartbit = get_asset("SMARTBIT"); + const auto &core = asset_id_type()(db); update_feed_producers(smartbit, {feedproducer.id}); From 759c91ad7dd3449dce86fdb4dc8c1ac6d52a1d84 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Wed, 15 Apr 2020 15:37:39 -0400 Subject: [PATCH 23/47] BSIP81: Comments --- libraries/chain/hardfork.d/BSIP_81.hf | 2 +- libraries/protocol/asset_ops.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/chain/hardfork.d/BSIP_81.hf b/libraries/chain/hardfork.d/BSIP_81.hf index 8d9f212d9c..5f4851bdc9 100644 --- a/libraries/chain/hardfork.d/BSIP_81.hf +++ b/libraries/chain/hardfork.d/BSIP_81.hf @@ -2,4 +2,4 @@ #ifndef HARDFORK_BSIP_81_TIME // Jan 1 2030, midnight this is a dummy date until a hardfork date is scheduled #define HARDFORK_BSIP_81_TIME (fc::time_point_sec( 1893456000 )) -#endif \ No newline at end of file +#endif diff --git a/libraries/protocol/asset_ops.cpp b/libraries/protocol/asset_ops.cpp index 6ada68530f..90c1e2917a 100644 --- a/libraries/protocol/asset_ops.cpp +++ b/libraries/protocol/asset_ops.cpp @@ -216,10 +216,10 @@ void asset_options::validate()const { FC_ASSERT( max_supply > 0 ); FC_ASSERT( max_supply <= GRAPHENE_MAX_SHARE_SUPPLY ); - // The non-negative maker fee must be less than 100% + // The non-negative maker fee must be less than or equal to 100% FC_ASSERT( market_fee_percent <= GRAPHENE_100_PERCENT ); - // The non-negative taker fee must be less than 100% + // The non-negative taker fee must be less than or equal to 100% if( extensions.value.taker_fee_percent.valid() ) FC_ASSERT( *extensions.value.taker_fee_percent <= GRAPHENE_100_PERCENT ); From 6d750c658b9e142d19034e4c421f0dc30de46fa8 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Wed, 15 Apr 2020 15:44:32 -0400 Subject: [PATCH 24/47] BSIP81: Taker orders should be charged the taker fee if defined, otherwise the maker fee --- libraries/chain/asset_evaluator.cpp | 21 ++++------- libraries/chain/db_maint.cpp | 22 ------------ libraries/chain/db_market.cpp | 23 ++++-------- tests/tests/simple_maker_taker_fee_tests.cpp | 37 +++++++------------- 4 files changed, 25 insertions(+), 78 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 4653159f91..c834a1021f 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -51,9 +51,8 @@ namespace detail { { if (block_time < HARDFORK_BSIP_81_TIME) { // Taker fees should be zero until activation of BSIP81 - FC_ASSERT(!options.extensions.value.taker_fee_percent.valid() || - *options.extensions.value.taker_fee_percent == 0, - "Taker fee must be 0% until HARDFORK_BSIP_81_TIME"); + FC_ASSERT(!options.extensions.value.taker_fee_percent.valid(), + "Taker fee percent should not be defined before HARDFORK_BSIP_81_TIME"); } } } @@ -120,12 +119,8 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o FC_ASSERT( op.precision == op.bitasset_opts->short_backing_asset(d).precision ); } - // Taker fees should be zero until activation of BSIP81 - if(now <= HARDFORK_BSIP_81_TIME) { - FC_ASSERT(!op.common_options.extensions.value.taker_fee_percent.valid() - || *op.common_options.extensions.value.taker_fee_percent == 0, - "Simple maker-taker fees are not yet activated"); - } + // Check the taker fee percent + detail::check_asset_options_hf_bsip81(now, op.common_options); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -329,12 +324,8 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) for( auto id : o.new_options.blacklist_authorities ) d.get_object(id); - if(now <= HARDFORK_BSIP_81_TIME) { - // Taker fees should be zero until activation of BSIP81 - FC_ASSERT(!o.new_options.extensions.value.taker_fee_percent.valid() - || *o.new_options.extensions.value.taker_fee_percent == 0, - "Simple maker-taker fees are not yet activated"); - } + // Check the taker fee percent + detail::check_asset_options_hf_bsip81(now, o.new_options); return void_result(); } FC_CAPTURE_AND_RETHROW((o)) } diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 9999a161eb..5aa2eb6e1a 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -1079,24 +1079,6 @@ void process_hf_868_890( database& db, bool skip_check_call_orders ) } // for each market issued asset } -/**** - * @brief A one-time data process to assign the initial taker fee percentage for BSIP81 - * Every asset issuer may subsequently change the taker fee - */ -void process_hf_bsip81(database &db) { - // Iterate through every each to set the default taker fee to equal the maker fee - // Without this processing code the taker fee will default to zero - const auto &asset_idx = db.get_index_type().indices().get(); - for (auto asset_itr = asset_idx.cbegin(); asset_itr != asset_idx.cend(); ++asset_itr) { - const asset_object &asset = *asset_itr; - const uint16_t raw_market_fee_percent = asset.options.market_fee_percent; - wlog("Setting default taker fee of ${asset} to ${raw_market_fee_percent}.", - ("asset", asset.symbol) ("raw_market_fee_percent", raw_market_fee_percent)); - db.modify(asset, [raw_market_fee_percent](asset_object &obj) { - obj.options.extensions.value.taker_fee_percent = raw_market_fee_percent; - }); - } -} /** * @brief Remove any custom active authorities whose expiration dates are in the past @@ -1259,10 +1241,6 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g if( (dgpo.next_maintenance_time <= HARDFORK_CORE_1270_TIME) && (next_maintenance_time > HARDFORK_CORE_1270_TIME) ) to_update_and_match_call_orders_for_hf_1270 = true; - // To set the default taker fee to equal the market fees, for hard fork BSIP81 - if( (dgpo.next_maintenance_time <= HARDFORK_BSIP_81_TIME) && (next_maintenance_time > HARDFORK_BSIP_81_TIME) ) - process_hf_bsip81(*this); - // make sure current_supply is less than or equal to max_supply if ( dgpo.next_maintenance_time <= HARDFORK_CORE_1465_TIME && next_maintenance_time > HARDFORK_CORE_1465_TIME ) process_hf_1465(*this); diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index d37cd05670..a29b2a2161 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -1184,29 +1184,18 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass if( is_maker && trade_asset.options.market_fee_percent == 0 ) return trade_asset.amount(0); - // BSIP81: Asset owners may specify different market fee rate for maker orders and taker orders - auto maint_time = get_dynamic_global_properties().next_maintenance_time; - bool before_core_hardfork_bsip81 = ( maint_time <= HARDFORK_BSIP_81_TIME ); // before simple maker-taker fee - // Optimization: The fee is zero if the order is a taker, and the taker fee percent is 0% - // This optimization should only be used after BSIP81 activation - if(!before_core_hardfork_bsip81 && !is_maker - && trade_asset.options.extensions.value.taker_fee_percent.valid() - && *trade_asset.options.extensions.value.taker_fee_percent == 0) + const optional& taker_fee_percent = trade_asset.options.extensions.value.taker_fee_percent; + if(!is_maker && taker_fee_percent.valid() && *taker_fee_percent == 0) return trade_asset.amount(0); uint16_t fee_percent; - if( before_core_hardfork_bsip81 ) { - // Before BSIP81, the fee is the market fee + if (is_maker) { + // Maker orders are charged the maker fee percent fee_percent = trade_asset.options.market_fee_percent; } else { - // After BSIP81, the fee depends on if maker or taker - if (is_maker) { - fee_percent = trade_asset.options.market_fee_percent; - } else { - fee_percent = trade_asset.options.extensions.value.taker_fee_percent.valid() - ? *trade_asset.options.extensions.value.taker_fee_percent : 0; - } + // Taker orders are charged the taker fee percent if they are valid. Otherwise, the maker fee percent. + fee_percent = taker_fee_percent.valid() ? *taker_fee_percent : trade_asset.options.market_fee_percent; } auto value = detail::calculate_percent(trade_amount.amount, fee_percent); diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index 7dc002b98b..3e806bb2fe 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -183,13 +183,11 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa ////// // After HF, test default values of taker fee after HF - // After the HF its default value should be the market fee percent - // which is effectively the new maker fee percent + // After the HF its default value should still not be set ////// updated_asset = jillcoin.get_id()(db); uint16_t expected_taker_fee_percent = updated_asset.options.market_fee_percent; - BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); - BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); ////// @@ -391,13 +389,11 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa ////// // After HF, test default values of taker fee after HF - // After the HF its default value should be the market fee percent - // which is effectively the new maker fee percent + // After the HF its default value should still not be set ////// updated_asset = bitsmart.get_id()(db); uint16_t expected_taker_fee_percent = updated_asset.options.market_fee_percent; - BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); - BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); ////// @@ -604,46 +600,39 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa ////// - // After HF, test the taker fees for each asset are set, by default, to the maker fees + // After HF, test the taker fees for each asset are not set ////// updated_asset = alice1coin.get_id()(db); expected_fee_percent = alice1coin_market_fee_percent; - BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); - BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = alice2coin.get_id()(db); expected_fee_percent = alice2coin_market_fee_percent; - BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); - BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = bob1coin.get_id()(db); expected_fee_percent = bob1coin_market_fee_percent; - BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); - BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = bob2coin.get_id()(db); expected_fee_percent = bob2coin_market_fee_percent; - BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); - BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = charlie1coin.get_id()(db); expected_fee_percent = charlie1coin_market_fee_percent; - BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); - BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = charlie2coin.get_id()(db); expected_fee_percent = charlie2coin_market_fee_percent; - BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = bitsmart1.get_id()(db); expected_fee_percent = bitsmart1coin_market_fee_percent; - BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); - BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); updated_asset = bitsmart2.get_id()(db); expected_fee_percent = bitsmart2coin_market_fee_percent; - BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); - BOOST_CHECK_EQUAL(expected_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); } FC_LOG_AND_RETHROW() } From 3408aac0b3b39be992a332f8439573e12f0a4bef Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Sat, 18 Apr 2020 10:23:22 -0400 Subject: [PATCH 25/47] BSIP81: Merge resolution with Issue 1780 --- libraries/chain/asset_evaluator.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index c834a1021f..dcff3f69fa 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -780,7 +780,8 @@ operation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator:: // performance loss. Needs testing. if( d.head_block_time() >= HARDFORK_CORE_1780_TIME ) { - auto issuer_fees = d.pay_market_fees( fee_paying_account, settled_amount.asset_id(d), settled_amount ); + const bool is_maker = false; // Settlement orders are takers + auto issuer_fees = d.pay_market_fees( fee_paying_account, settled_amount.asset_id(d), settled_amount , is_maker ); settled_amount -= issuer_fees; } From 322113d9f477757e8fbd69e1255871cea6803017 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 18 Apr 2020 14:41:33 +0000 Subject: [PATCH 26/47] Move api-limit related test cases to a new file --- tests/common/database_fixture.cpp | 3 +- tests/tests/api_limit_tests.cpp | 506 +++++++++++++++++++++++++++++ tests/tests/database_api_tests.cpp | 466 -------------------------- 3 files changed, 508 insertions(+), 467 deletions(-) create mode 100644 tests/tests/api_limit_tests.cpp diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 64e4f0ba7a..0853135a89 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -332,7 +332,8 @@ database_fixture::database_fixture(const fc::time_point_sec &initial_timestamp) } else if( current_test_name == "asset_in_collateral" || current_test_name == "htlc_database_api" - || current_suite_name == "database_api_tests" ) + || current_suite_name == "database_api_tests" + || current_suite_name == "api_limit_tests" ) { auto ahiplugin = app.register_plugin(); ahiplugin->plugin_set_app(&app); diff --git a/tests/tests/api_limit_tests.cpp b/tests/tests/api_limit_tests.cpp new file mode 100644 index 0000000000..f9878b556e --- /dev/null +++ b/tests/tests/api_limit_tests.cpp @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2018 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 "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE(api_limit_tests, database_fixture) + +BOOST_AUTO_TEST_CASE( api_limit_get_key_references ){ + try{ + const int num_keys = 210; + const int num_keys1 = 2; + vector< private_key_type > numbered_private_keys; + vector< public_key_type > numbered_key_id; + numbered_private_keys.reserve( num_keys ); + + graphene::app::database_api db_api1( db, &( app.get_options() )); + BOOST_CHECK_THROW( db_api1.get_key_references(numbered_key_id), fc::exception ); + + graphene::app::application_options opt = app.get_options(); + opt.has_api_helper_indexes_plugin = true; + graphene::app::database_api db_api( db, &opt ); + + for( int i=0; i > final_result=db_api.get_key_references(numbered_key_id); + BOOST_REQUIRE_EQUAL( final_result.size(), 2u ); + numbered_private_keys.reserve( num_keys ); + for( int i=num_keys1; iapp.get_options())); + + const account_object& alice = create_account("alice"); + const account_object& bob = create_account("bob"); + const account_object& carl = create_account("carl"); + const account_object& dan = create_account("dan"); + const account_object& fred = create_account("fred"); + const account_object& henry = create_account("henry"); + const account_object& kevin = create_account("kevin"); + const account_object& laura = create_account("laura"); + const account_object& lucy = create_account("lucy"); + const account_object& martin = create_account("martin"); + const account_object& patty = create_account("patty"); + + vector accounts; + accounts.push_back(alice.name); + accounts.push_back(bob.name); + accounts.push_back(carl.name); + accounts.push_back(dan.name); + accounts.push_back(fred.name); + accounts.push_back(henry.name); + accounts.push_back(kevin.name); + accounts.push_back(laura.name); + accounts.push_back(lucy.name); + accounts.push_back(martin.name); + accounts.push_back(patty.name); + + GRAPHENE_CHECK_THROW(db_api.get_full_accounts(accounts, false), fc::exception); + + accounts.erase(accounts.begin()); + auto full_accounts = db_api.get_full_accounts(accounts, false); + BOOST_CHECK(full_accounts.size() == 10); + + // not an account + accounts.erase(accounts.begin()); + accounts.push_back("nosuchaccount"); + + // non existing accounts will be ignored in the results + full_accounts = db_api.get_full_accounts(accounts, false); + BOOST_CHECK(full_accounts.size() == 9); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( api_limit_get_limit_orders ){ + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + //account_id_type() do 3 ops + create_bitasset("USD", account_id_type()); + create_account("dan"); + create_account("bob"); + asset_id_type bit_jmj_id = create_bitasset("JMJBIT").id; + generate_block(); + fc::usleep(fc::milliseconds(100)); + GRAPHENE_CHECK_THROW(db_api.get_limit_orders(std::string(static_cast(asset_id_type())), + std::string(static_cast(bit_jmj_id)), 370), fc::exception); + vector limit_orders =db_api.get_limit_orders(std::string( + static_cast(asset_id_type())), + std::string(static_cast(bit_jmj_id)), 340); + BOOST_REQUIRE_EQUAL( limit_orders.size(), 0u); + + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE( api_limit_get_call_orders ){ + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + //account_id_type() do 3 ops + auto nathan_private_key = generate_private_key("nathan"); + account_id_type nathan_id = create_account("nathan", nathan_private_key.get_public_key()).id; + transfer(account_id_type(), nathan_id, asset(100)); + asset_id_type bitusd_id = create_bitasset( + "USDBIT", nathan_id, 100, disable_force_settle).id; + generate_block(); + fc::usleep(fc::milliseconds(100)); + BOOST_CHECK( bitusd_id(db).is_market_issued() ); + GRAPHENE_CHECK_THROW(db_api.get_call_orders(std::string(static_cast(bitusd_id)), + 370), fc::exception); + vector< call_order_object> call_order =db_api.get_call_orders(std::string( + static_cast(bitusd_id)), 340); + BOOST_REQUIRE_EQUAL( call_order.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE( api_limit_get_settle_orders ){ + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + //account_id_type() do 3 ops + auto nathan_private_key = generate_private_key("nathan"); + account_id_type nathan_id = create_account("nathan", nathan_private_key.get_public_key()).id; + transfer(account_id_type(), nathan_id, asset(100)); + asset_id_type bitusd_id = create_bitasset( + "USDBIT", nathan_id, 100, disable_force_settle).id; + generate_block(); + fc::usleep(fc::milliseconds(100)); + GRAPHENE_CHECK_THROW(db_api.get_settle_orders( + std::string(static_cast(bitusd_id)), 370), fc::exception); + vector result =db_api.get_settle_orders( + std::string(static_cast(bitusd_id)), 340); + BOOST_REQUIRE_EQUAL( result.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE( api_limit_get_order_book ){ + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + auto nathan_private_key = generate_private_key("nathan"); + auto dan_private_key = generate_private_key("dan"); + account_id_type nathan_id = create_account("nathan", nathan_private_key.get_public_key()).id; + account_id_type dan_id = create_account("dan", dan_private_key.get_public_key()).id; + transfer(account_id_type(), nathan_id, asset(100)); + transfer(account_id_type(), dan_id, asset(100)); + asset_id_type bitusd_id = create_bitasset( + "USDBIT", nathan_id, 100, disable_force_settle).id; + asset_id_type bitdan_id = create_bitasset( + "DANBIT", dan_id, 100, disable_force_settle).id; + generate_block(); + fc::usleep(fc::milliseconds(100)); + GRAPHENE_CHECK_THROW(db_api.get_order_book(std::string(static_cast(bitusd_id)), + std::string(static_cast(bitdan_id)),89), fc::exception); + graphene::app::order_book result =db_api.get_order_book(std::string( + static_cast(bitusd_id)), std::string(static_cast(bitdan_id)),78); + BOOST_REQUIRE_EQUAL( result.bids.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( api_limit_lookup_accounts ) { + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + ACTOR(bob); + GRAPHENE_CHECK_THROW(db_api.lookup_accounts("bob",220), fc::exception); + map result =db_api.lookup_accounts("bob",190); + BOOST_REQUIRE_EQUAL( result.size(), 17u); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( api_limit_lookup_witness_accounts ) { + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + ACTORS((bob)) ; + GRAPHENE_CHECK_THROW(db_api.lookup_witness_accounts("bob",220), fc::exception); + map result =db_api.lookup_witness_accounts("bob",190); + BOOST_REQUIRE_EQUAL( result.size(), 10u); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE( api_limit_get_full_accounts2 ) { + + try { + graphene::app::database_api db_api(db, &(this->app.get_options())); + vector accounts; + for (int i=0; i<201; i++) { + std::string acct_name = "mytempacct" + std::to_string(i); + const account_object& account_name=create_account(acct_name); + accounts.push_back(account_name.name); + } + GRAPHENE_CHECK_THROW(db_api.get_full_accounts(accounts, false), fc::exception); + accounts.erase(accounts.begin()); + auto full_accounts = db_api.get_full_accounts(accounts, false); + BOOST_REQUIRE_EQUAL(full_accounts.size(), 200u); + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_withdraw_permissions_by_recipient){ + try{ + graphene::app::database_api db_api( db, &app.get_options()); + ACTORS((bob)) ; + withdraw_permission_id_type withdraw_permission; + GRAPHENE_CHECK_THROW(db_api.get_withdraw_permissions_by_recipient( + "bob",withdraw_permission, 251), fc::exception); + vector result =db_api.get_withdraw_permissions_by_recipient( + "bob",withdraw_permission,250); + BOOST_REQUIRE_EQUAL( result.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_withdraw_permissions_by_giver){ + try{ + graphene::app::database_api db_api( db, &app.get_options()); + ACTORS((bob)) ; + withdraw_permission_id_type withdraw_permission; + GRAPHENE_CHECK_THROW(db_api.get_withdraw_permissions_by_giver( + "bob",withdraw_permission, 251), fc::exception); + vector result =db_api.get_withdraw_permissions_by_giver( + "bob",withdraw_permission,250); + BOOST_REQUIRE_EQUAL( result.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_trade_history_by_sequence){ + try{ + app.enable_plugin("market_history"); + graphene::app::application_options opt=app.get_options(); + opt.has_market_history_plugin = true; + graphene::app::database_api db_api( db, &opt); + const auto& bitusd = create_bitasset("USDBIT"); + asset_id_type asset_1, asset_2; + asset_1 = bitusd.id; + asset_2 = asset_id_type(); + GRAPHENE_CHECK_THROW(db_api.get_trade_history_by_sequence( + std::string( static_cast(asset_1)), + std::string( static_cast(asset_2)), + 0,fc::time_point_sec(), 251), fc::exception); + vector result =db_api.get_trade_history_by_sequence( + std::string( static_cast(asset_1)), + std::string( static_cast(asset_2)), + 0,fc::time_point_sec(),250); + BOOST_REQUIRE_EQUAL( result.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE(api_limit_get_trade_history){ + try{ + app.enable_plugin("market_history"); + graphene::app::application_options opt=app.get_options(); + opt.has_market_history_plugin = true; + graphene::app::database_api db_api( db, &opt); + const auto& bitusd = create_bitasset("USDBIT"); + asset_id_type asset_1, asset_2; + asset_1 = bitusd.id; + asset_2 = asset_id_type(); + GRAPHENE_CHECK_THROW(db_api.get_trade_history( + std::string( static_cast(asset_1)), + std::string( static_cast(asset_2)), + fc::time_point_sec(),fc::time_point_sec(), + 251), fc::exception); + vector result =db_api.get_trade_history( + std::string( static_cast(asset_1)), + std::string( static_cast(asset_2)), + fc::time_point_sec(),fc::time_point_sec(),250); + BOOST_REQUIRE_EQUAL( result.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_top_markets){ + try{ + app.enable_plugin("market_history"); + graphene::app::application_options opt=app.get_options(); + opt.has_market_history_plugin = true; + graphene::app::database_api db_api( db, &opt); + const auto& bitusd = create_bitasset("USDBIT"); + asset_id_type asset_1, asset_2; + asset_1 = bitusd.id; + asset_2 = asset_id_type(); + GRAPHENE_CHECK_THROW(db_api.get_top_markets(251), fc::exception); + vector result =db_api.get_top_markets(250); + BOOST_REQUIRE_EQUAL( result.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_collateral_bids) { + try { + graphene::app::database_api db_api( db, &( app.get_options() )); + + int64_t init_balance = 10000; + ///account_id_type borrower, borrower2, feedproducer; + asset_id_type swan, back; + ACTORS((borrower) (borrower2) (feedproducer)) ; + const auto& bitusd = create_bitasset("USDBIT", feedproducer_id); + swan = bitusd.id; + back = asset_id_type(); + update_feed_producers(swan(db), {feedproducer_id}); + transfer(committee_account, borrower_id, asset(init_balance)); + transfer(committee_account, borrower2_id, asset(init_balance)); + + generate_blocks( HARDFORK_CORE_216_TIME ); + generate_block(); + + price_feed feed; + feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default + feed.settlement_price = swan(db).amount(1) / back(db).amount(1); + publish_feed(swan(db), feedproducer_id(db), feed); + // start out with 2:1 collateral + borrow(borrower_id(db), swan(db).amount(10), back(db).amount(2*10)); + borrow(borrower2_id(db), swan(db).amount(10), back(db).amount(4*10)); + //feed 1: 2 + feed.settlement_price = swan(db).amount(1) / back(db).amount(2); + publish_feed(swan(db), feedproducer_id(db), feed); + + // this sell order is designed to trigger a black swan + + create_sell_order( borrower2_id(db), swan(db).amount(1), back(db).amount(3) ); + BOOST_CHECK( swan(db).bitasset_data(db).has_settlement() ); + //making 3 collateral bids + for (int i=0; i<3; i++) { + + std::string acct_name = "mytempacct" + std::to_string(i); + account_id_type account_id=create_account(acct_name).id; + transfer(committee_account, account_id, asset(init_balance)); + bid_collateral(account_id(db), back(db).amount(10), swan(db).amount(1)); + } + auto swan_symbol = swan(db).symbol; + + + //validating normal case; total_bids =3 ; result_bids=3 + vector result_bids = db_api.get_collateral_bids(swan_symbol, 250, 0); + BOOST_CHECK_EQUAL( 3u, result_bids.size() ); + + //verify skip /// inefficient code test + //skip < total_bids; skip=1; total_bids =3 ; result_bids=2 + result_bids = db_api.get_collateral_bids(swan_symbol, 250, 1); + BOOST_CHECK_EQUAL( 2u, result_bids.size() ); + //skip= total_bids; skip=3; total_bids =3 ; result_bids=0 + result_bids = db_api.get_collateral_bids(swan_symbol, 250, 3); + BOOST_CHECK_EQUAL( 0u, result_bids.size() ); + //skip> total_bids; skip=4; total_bids =3 ; result_bids=0 + result_bids = db_api.get_collateral_bids(swan_symbol, 250, 4); + BOOST_CHECK_EQUAL( 0u, result_bids.size() ); + + //verify limit // inefficient code test + //limit= api_limit + for (int i=3; i<255; i++) { + std::string acct_name = "mytempacct" + std::to_string(i); + account_id_type account_id=create_account(acct_name).id; + transfer(committee_account, account_id, asset(init_balance)); + bid_collateral(account_id(db), back(db).amount(10), swan(db).amount(1)); + } + result_bids=db_api.get_collateral_bids(swan_symbol, 250, 0); + BOOST_CHECK_EQUAL( 250u, result_bids.size() ); + //limit> api_limit throw error + GRAPHENE_CHECK_THROW(db_api.get_collateral_bids(swan_symbol, 253, 3), fc::exception); + + } + catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_account_limit_orders) { + try { + graphene::app::database_api db_api( db, &( app.get_options() )); + ACTORS((seller)); + const auto &bitcny = create_bitasset("CNY"); + const auto &core = asset_id_type()(db); + + int64_t init_balance(10000000); + transfer(committee_account, seller_id, asset(init_balance)); + + /// Create versatile orders + for (size_t i = 0; i < 250; ++i) { + BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250+i))); + } + + + std::vector results=db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, "CNY",250); + BOOST_REQUIRE_EQUAL( results.size(), 250u); + GRAPHENE_CHECK_THROW( db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, "CNY",251), fc::exception); + + } + catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE( api_limit_lookup_vote_ids ) { + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + ACTORS( (connie)(whitney)(wolverine) ); + fund(connie); + upgrade_to_lifetime_member(connie); + fund(whitney); + upgrade_to_lifetime_member(whitney); + fund(wolverine); + upgrade_to_lifetime_member(wolverine); + const auto& committee = create_committee_member( connie ); + const auto& witness = create_witness( whitney ); + const auto& worker = create_worker( wolverine_id ); + std::vector votes; + votes.push_back( committee.vote_id ); + votes.push_back( witness.vote_id ); + const auto results = db_api.lookup_vote_ids( votes ); + BOOST_REQUIRE_EQUAL( results.size(), 2u); + votes.push_back( worker.vote_for ); + GRAPHENE_CHECK_THROW(db_api.lookup_vote_ids(votes), fc::exception); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( api_limit_lookup_committee_member_accounts ) { + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + ACTORS((bob)); + GRAPHENE_CHECK_THROW(db_api.lookup_committee_member_accounts("bob",220), fc::exception); + std::map result =db_api.lookup_committee_member_accounts("bob",190); + BOOST_REQUIRE_EQUAL( result.size(), 10u); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index 98febcf2af..730886aca0 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -1363,94 +1363,6 @@ BOOST_AUTO_TEST_CASE( verify_authority_multiple_accounts ) throw; } } -BOOST_AUTO_TEST_CASE( api_limit_get_key_references ){ - try{ - const int num_keys = 210; - const int num_keys1 = 2; - vector< private_key_type > numbered_private_keys; - vector< public_key_type > numbered_key_id; - numbered_private_keys.reserve( num_keys ); - - graphene::app::database_api db_api1( db, &( app.get_options() )); - BOOST_CHECK_THROW( db_api1.get_key_references(numbered_key_id), fc::exception ); - - graphene::app::application_options opt = app.get_options(); - opt.has_api_helper_indexes_plugin = true; - graphene::app::database_api db_api( db, &opt ); - - for( int i=0; i > final_result=db_api.get_key_references(numbered_key_id); - BOOST_REQUIRE_EQUAL( final_result.size(), 2u ); - numbered_private_keys.reserve( num_keys ); - for( int i=num_keys1; iapp.get_options())); - - const account_object& alice = create_account("alice"); - const account_object& bob = create_account("bob"); - const account_object& carl = create_account("carl"); - const account_object& dan = create_account("dan"); - const account_object& fred = create_account("fred"); - const account_object& henry = create_account("henry"); - const account_object& kevin = create_account("kevin"); - const account_object& laura = create_account("laura"); - const account_object& lucy = create_account("lucy"); - const account_object& martin = create_account("martin"); - const account_object& patty = create_account("patty"); - - vector accounts; - accounts.push_back(alice.name); - accounts.push_back(bob.name); - accounts.push_back(carl.name); - accounts.push_back(dan.name); - accounts.push_back(fred.name); - accounts.push_back(henry.name); - accounts.push_back(kevin.name); - accounts.push_back(laura.name); - accounts.push_back(lucy.name); - accounts.push_back(martin.name); - accounts.push_back(patty.name); - - GRAPHENE_CHECK_THROW(db_api.get_full_accounts(accounts, false), fc::exception); - - accounts.erase(accounts.begin()); - auto full_accounts = db_api.get_full_accounts(accounts, false); - BOOST_CHECK(full_accounts.size() == 10); - - // not an account - accounts.erase(accounts.begin()); - accounts.push_back("nosuchaccount"); - - // non existing accounts will be ignored in the results - full_accounts = db_api.get_full_accounts(accounts, false); - BOOST_CHECK(full_accounts.size() == 9); - - } catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} BOOST_AUTO_TEST_CASE( get_assets_by_issuer ) { try { @@ -1568,97 +1480,6 @@ BOOST_AUTO_TEST_CASE( get_settle_orders_by_account ) { } } -BOOST_AUTO_TEST_CASE( api_limit_get_limit_orders ){ - try{ - graphene::app::database_api db_api( db, &( app.get_options() )); - //account_id_type() do 3 ops - create_bitasset("USD", account_id_type()); - create_account("dan"); - create_account("bob"); - asset_id_type bit_jmj_id = create_bitasset("JMJBIT").id; - generate_block(); - fc::usleep(fc::milliseconds(100)); - GRAPHENE_CHECK_THROW(db_api.get_limit_orders(std::string(static_cast(asset_id_type())), - std::string(static_cast(bit_jmj_id)), 370), fc::exception); - vector limit_orders =db_api.get_limit_orders(std::string( - static_cast(asset_id_type())), - std::string(static_cast(bit_jmj_id)), 340); - BOOST_REQUIRE_EQUAL( limit_orders.size(), 0u); - - }catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_CASE( api_limit_get_call_orders ){ - try{ - graphene::app::database_api db_api( db, &( app.get_options() )); - //account_id_type() do 3 ops - auto nathan_private_key = generate_private_key("nathan"); - account_id_type nathan_id = create_account("nathan", nathan_private_key.get_public_key()).id; - transfer(account_id_type(), nathan_id, asset(100)); - asset_id_type bitusd_id = create_bitasset( - "USDBIT", nathan_id, 100, disable_force_settle).id; - generate_block(); - fc::usleep(fc::milliseconds(100)); - BOOST_CHECK( bitusd_id(db).is_market_issued() ); - GRAPHENE_CHECK_THROW(db_api.get_call_orders(std::string(static_cast(bitusd_id)), - 370), fc::exception); - vector< call_order_object> call_order =db_api.get_call_orders(std::string( - static_cast(bitusd_id)), 340); - BOOST_REQUIRE_EQUAL( call_order.size(), 0u); - }catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_CASE( api_limit_get_settle_orders ){ - try{ - graphene::app::database_api db_api( db, &( app.get_options() )); - //account_id_type() do 3 ops - auto nathan_private_key = generate_private_key("nathan"); - account_id_type nathan_id = create_account("nathan", nathan_private_key.get_public_key()).id; - transfer(account_id_type(), nathan_id, asset(100)); - asset_id_type bitusd_id = create_bitasset( - "USDBIT", nathan_id, 100, disable_force_settle).id; - generate_block(); - fc::usleep(fc::milliseconds(100)); - GRAPHENE_CHECK_THROW(db_api.get_settle_orders( - std::string(static_cast(bitusd_id)), 370), fc::exception); - vector result =db_api.get_settle_orders( - std::string(static_cast(bitusd_id)), 340); - BOOST_REQUIRE_EQUAL( result.size(), 0u); - }catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_CASE( api_limit_get_order_book ){ - try{ - graphene::app::database_api db_api( db, &( app.get_options() )); - auto nathan_private_key = generate_private_key("nathan"); - auto dan_private_key = generate_private_key("dan"); - account_id_type nathan_id = create_account("nathan", nathan_private_key.get_public_key()).id; - account_id_type dan_id = create_account("dan", dan_private_key.get_public_key()).id; - transfer(account_id_type(), nathan_id, asset(100)); - transfer(account_id_type(), dan_id, asset(100)); - asset_id_type bitusd_id = create_bitasset( - "USDBIT", nathan_id, 100, disable_force_settle).id; - asset_id_type bitdan_id = create_bitasset( - "DANBIT", dan_id, 100, disable_force_settle).id; - generate_block(); - fc::usleep(fc::milliseconds(100)); - GRAPHENE_CHECK_THROW(db_api.get_order_book(std::string(static_cast(bitusd_id)), - std::string(static_cast(bitdan_id)),89), fc::exception); - graphene::app::order_book result =db_api.get_order_book(std::string( - static_cast(bitusd_id)), std::string(static_cast(bitdan_id)),78); - BOOST_REQUIRE_EQUAL( result.bids.size(), 0u); - }catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - BOOST_AUTO_TEST_CASE( asset_in_collateral ) { try { ACTORS( (dan)(nathan) ); @@ -1795,292 +1616,5 @@ BOOST_AUTO_TEST_CASE( asset_in_collateral ) BOOST_CHECK_EQUAL( 1000, assets[1].total_backing_collateral->value ); } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE( api_limit_lookup_accounts ) { - try{ - graphene::app::database_api db_api( db, &( app.get_options() )); - ACTOR(bob); - GRAPHENE_CHECK_THROW(db_api.lookup_accounts("bob",220), fc::exception); - map result =db_api.lookup_accounts("bob",190); - BOOST_REQUIRE_EQUAL( result.size(), 17u); - - } catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( api_limit_lookup_witness_accounts ) { - try{ - graphene::app::database_api db_api( db, &( app.get_options() )); - ACTORS((bob)) ; - GRAPHENE_CHECK_THROW(db_api.lookup_witness_accounts("bob",220), fc::exception); - map result =db_api.lookup_witness_accounts("bob",190); - BOOST_REQUIRE_EQUAL( result.size(), 10u); - } catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_CASE( api_limit_get_full_accounts2 ) { - - try { - graphene::app::database_api db_api(db, &(this->app.get_options())); - vector accounts; - for (int i=0; i<201; i++) { - std::string acct_name = "mytempacct" + std::to_string(i); - const account_object& account_name=create_account(acct_name); - accounts.push_back(account_name.name); - } - GRAPHENE_CHECK_THROW(db_api.get_full_accounts(accounts, false), fc::exception); - accounts.erase(accounts.begin()); - auto full_accounts = db_api.get_full_accounts(accounts, false); - BOOST_REQUIRE_EQUAL(full_accounts.size(), 200u); - } catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_CASE(api_limit_get_withdraw_permissions_by_recipient){ - try{ - graphene::app::database_api db_api( db, &app.get_options()); - ACTORS((bob)) ; - withdraw_permission_id_type withdraw_permission; - GRAPHENE_CHECK_THROW(db_api.get_withdraw_permissions_by_recipient( - "bob",withdraw_permission, 251), fc::exception); - vector result =db_api.get_withdraw_permissions_by_recipient( - "bob",withdraw_permission,250); - BOOST_REQUIRE_EQUAL( result.size(), 0u); - }catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_CASE(api_limit_get_withdraw_permissions_by_giver){ - try{ - graphene::app::database_api db_api( db, &app.get_options()); - ACTORS((bob)) ; - withdraw_permission_id_type withdraw_permission; - GRAPHENE_CHECK_THROW(db_api.get_withdraw_permissions_by_giver( - "bob",withdraw_permission, 251), fc::exception); - vector result =db_api.get_withdraw_permissions_by_giver( - "bob",withdraw_permission,250); - BOOST_REQUIRE_EQUAL( result.size(), 0u); - }catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_CASE(api_limit_get_trade_history_by_sequence){ - try{ - app.enable_plugin("market_history"); - graphene::app::application_options opt=app.get_options(); - opt.has_market_history_plugin = true; - graphene::app::database_api db_api( db, &opt); - const auto& bitusd = create_bitasset("USDBIT"); - asset_id_type asset_1, asset_2; - asset_1 = bitusd.id; - asset_2 = asset_id_type(); - GRAPHENE_CHECK_THROW(db_api.get_trade_history_by_sequence( - std::string( static_cast(asset_1)), - std::string( static_cast(asset_2)), - 0,fc::time_point_sec(), 251), fc::exception); - vector result =db_api.get_trade_history_by_sequence( - std::string( static_cast(asset_1)), - std::string( static_cast(asset_2)), - 0,fc::time_point_sec(),250); - BOOST_REQUIRE_EQUAL( result.size(), 0u); - }catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE(api_limit_get_trade_history){ - try{ - app.enable_plugin("market_history"); - graphene::app::application_options opt=app.get_options(); - opt.has_market_history_plugin = true; - graphene::app::database_api db_api( db, &opt); - const auto& bitusd = create_bitasset("USDBIT"); - asset_id_type asset_1, asset_2; - asset_1 = bitusd.id; - asset_2 = asset_id_type(); - GRAPHENE_CHECK_THROW(db_api.get_trade_history( - std::string( static_cast(asset_1)), - std::string( static_cast(asset_2)), - fc::time_point_sec(),fc::time_point_sec(), - 251), fc::exception); - vector result =db_api.get_trade_history( - std::string( static_cast(asset_1)), - std::string( static_cast(asset_2)), - fc::time_point_sec(),fc::time_point_sec(),250); - BOOST_REQUIRE_EQUAL( result.size(), 0u); - }catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_CASE(api_limit_get_top_markets){ - try{ - app.enable_plugin("market_history"); - graphene::app::application_options opt=app.get_options(); - opt.has_market_history_plugin = true; - graphene::app::database_api db_api( db, &opt); - const auto& bitusd = create_bitasset("USDBIT"); - asset_id_type asset_1, asset_2; - asset_1 = bitusd.id; - asset_2 = asset_id_type(); - GRAPHENE_CHECK_THROW(db_api.get_top_markets(251), fc::exception); - vector result =db_api.get_top_markets(250); - BOOST_REQUIRE_EQUAL( result.size(), 0u); - }catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_CASE(api_limit_get_collateral_bids) { - try { - graphene::app::database_api db_api( db, &( app.get_options() )); - - int64_t init_balance = 10000; - ///account_id_type borrower, borrower2, feedproducer; - asset_id_type swan, back; - ACTORS((borrower) (borrower2) (feedproducer)) ; - const auto& bitusd = create_bitasset("USDBIT", feedproducer_id); - swan = bitusd.id; - back = asset_id_type(); - update_feed_producers(swan(db), {feedproducer_id}); - transfer(committee_account, borrower_id, asset(init_balance)); - transfer(committee_account, borrower2_id, asset(init_balance)); - - generate_blocks( HARDFORK_CORE_216_TIME ); - generate_block(); - - price_feed feed; - feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default - feed.settlement_price = swan(db).amount(1) / back(db).amount(1); - publish_feed(swan(db), feedproducer_id(db), feed); - // start out with 2:1 collateral - borrow(borrower_id(db), swan(db).amount(10), back(db).amount(2*10)); - borrow(borrower2_id(db), swan(db).amount(10), back(db).amount(4*10)); - //feed 1: 2 - feed.settlement_price = swan(db).amount(1) / back(db).amount(2); - publish_feed(swan(db), feedproducer_id(db), feed); - - // this sell order is designed to trigger a black swan - - create_sell_order( borrower2_id(db), swan(db).amount(1), back(db).amount(3) ); - BOOST_CHECK( swan(db).bitasset_data(db).has_settlement() ); - //making 3 collateral bids - for (int i=0; i<3; i++) { - - std::string acct_name = "mytempacct" + std::to_string(i); - account_id_type account_id=create_account(acct_name).id; - transfer(committee_account, account_id, asset(init_balance)); - bid_collateral(account_id(db), back(db).amount(10), swan(db).amount(1)); - } - auto swan_symbol = swan(db).symbol; - - - //validating normal case; total_bids =3 ; result_bids=3 - vector result_bids = db_api.get_collateral_bids(swan_symbol, 250, 0); - BOOST_CHECK_EQUAL( 3u, result_bids.size() ); - - //verify skip /// inefficient code test - //skip < total_bids; skip=1; total_bids =3 ; result_bids=2 - result_bids = db_api.get_collateral_bids(swan_symbol, 250, 1); - BOOST_CHECK_EQUAL( 2u, result_bids.size() ); - //skip= total_bids; skip=3; total_bids =3 ; result_bids=0 - result_bids = db_api.get_collateral_bids(swan_symbol, 250, 3); - BOOST_CHECK_EQUAL( 0u, result_bids.size() ); - //skip> total_bids; skip=4; total_bids =3 ; result_bids=0 - result_bids = db_api.get_collateral_bids(swan_symbol, 250, 4); - BOOST_CHECK_EQUAL( 0u, result_bids.size() ); - - //verify limit // inefficient code test - //limit= api_limit - for (int i=3; i<255; i++) { - std::string acct_name = "mytempacct" + std::to_string(i); - account_id_type account_id=create_account(acct_name).id; - transfer(committee_account, account_id, asset(init_balance)); - bid_collateral(account_id(db), back(db).amount(10), swan(db).amount(1)); - } - result_bids=db_api.get_collateral_bids(swan_symbol, 250, 0); - BOOST_CHECK_EQUAL( 250u, result_bids.size() ); - //limit> api_limit throw error - GRAPHENE_CHECK_THROW(db_api.get_collateral_bids(swan_symbol, 253, 3), fc::exception); - - } - catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_CASE(api_limit_get_account_limit_orders) { - try { - graphene::app::database_api db_api( db, &( app.get_options() )); - ACTORS((seller)); - const auto &bitcny = create_bitasset("CNY"); - const auto &core = asset_id_type()(db); - - int64_t init_balance(10000000); - transfer(committee_account, seller_id, asset(init_balance)); - - /// Create versatile orders - for (size_t i = 0; i < 250; ++i) { - BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250+i))); - } - - - std::vector results=db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, "CNY",250); - BOOST_REQUIRE_EQUAL( results.size(), 250u); - GRAPHENE_CHECK_THROW( db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, "CNY",251), fc::exception); - - } - catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} -BOOST_AUTO_TEST_CASE( api_limit_lookup_vote_ids ) { - try{ - graphene::app::database_api db_api( db, &( app.get_options() )); - ACTORS( (connie)(whitney)(wolverine) ); - fund(connie); - upgrade_to_lifetime_member(connie); - fund(whitney); - upgrade_to_lifetime_member(whitney); - fund(wolverine); - upgrade_to_lifetime_member(wolverine); - const auto& committee = create_committee_member( connie ); - const auto& witness = create_witness( whitney ); - const auto& worker = create_worker( wolverine_id ); - std::vector votes; - votes.push_back( committee.vote_id ); - votes.push_back( witness.vote_id ); - const auto results = db_api.lookup_vote_ids( votes ); - BOOST_REQUIRE_EQUAL( results.size(), 2u); - votes.push_back( worker.vote_for ); - GRAPHENE_CHECK_THROW(db_api.lookup_vote_ids(votes), fc::exception); - - } catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} - -BOOST_AUTO_TEST_CASE( api_limit_lookup_committee_member_accounts ) { - try{ - graphene::app::database_api db_api( db, &( app.get_options() )); - ACTORS((bob)); - GRAPHENE_CHECK_THROW(db_api.lookup_committee_member_accounts("bob",220), fc::exception); - std::map result =db_api.lookup_committee_member_accounts("bob",190); - BOOST_REQUIRE_EQUAL( result.size(), 10u); - - } catch (fc::exception& e) { - edump((e.to_detail_string())); - throw; - } -} BOOST_AUTO_TEST_SUITE_END() From c851b2a72530307b3347065dc207b8775473d028 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 18 Apr 2020 16:19:43 +0000 Subject: [PATCH 27/47] Fix API docs --- libraries/app/include/graphene/app/database_api.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index a2bea87646..3e6dd77989 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -441,7 +441,7 @@ class database_api * @return List of limit orders of the specified account * * @note - * 1. if @p account_name_or_id cannot be tied to an account, empty result will be returned + * 1. if @p account_name_or_id cannot be tied to an account, an error will be returned * 2. @p limit can be omitted or be null, if so the default value 101 will be used * 3. @p start_id can be omitted or be null, if so the api will return the "first page" of orders * 4. can only omit one or more arguments in the end of the list, but not one or more in the middle @@ -465,7 +465,7 @@ class database_api * @return List of orders from @p account_name_or_id to the corresponding account * * @note - * 1. if @p account_name_or_id cannot be tied to an account, empty result will be returned + * 1. if @p account_name_or_id cannot be tied to an account, an error will be returned * 2. @p ostart_id and @p ostart_price can be empty, if so the api will return the "first page" of orders; * if @p ostart_id is specified, its price will be used to do page query preferentially, * otherwise the @p ostart_price will be used; From afcd7d54126c976248aec36daba26989c626e066 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 18 Apr 2020 16:20:19 +0000 Subject: [PATCH 28/47] Add test cases for get_limit_orders_by_account API --- tests/common/database_fixture.cpp | 7 +- tests/tests/api_limit_tests.cpp | 18 +++++ tests/tests/database_api_tests.cpp | 109 +++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 0853135a89..5630b659d1 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -175,6 +175,11 @@ database_fixture::database_fixture(const fc::time_point_sec &initial_timestamp) options.insert(std::make_pair("api-limit-get-limit-orders", boost::program_options::variable_value( (uint64_t)350, false))); } + if(current_test_name =="api_limit_get_limit_orders_by_account") + { + options.insert(std::make_pair("api-limit-get-limit-orders-by-account", boost::program_options::variable_value( + (uint64_t)150, false))); + } if(current_test_name =="api_limit_get_call_orders") { options.insert(std::make_pair("api-limit-get-call-orders", boost::program_options::variable_value( @@ -455,7 +460,7 @@ bool database_fixture::validation_current_test_name_for_setting_api_limit( const vector valid_testcase {"api_limit_get_account_history_operations","api_limit_get_account_history" ,"api_limit_get_grouped_limit_orders","api_limit_get_relative_account_history" ,"api_limit_get_account_history_by_operations","api_limit_get_asset_holders" - ,"api_limit_get_key_references","api_limit_get_limit_orders" + ,"api_limit_get_key_references","api_limit_get_limit_orders","api_limit_get_limit_orders_by_account" ,"api_limit_get_call_orders","api_limit_get_settle_orders" ,"api_limit_get_order_book","api_limit_lookup_accounts" ,"api_limit_lookup_witness_accounts","api_limit_lookup_committee_member_accounts" diff --git a/tests/tests/api_limit_tests.cpp b/tests/tests/api_limit_tests.cpp index f9878b556e..5c332322a8 100644 --- a/tests/tests/api_limit_tests.cpp +++ b/tests/tests/api_limit_tests.cpp @@ -145,6 +145,24 @@ BOOST_AUTO_TEST_CASE( api_limit_get_limit_orders ){ throw; } } + +BOOST_AUTO_TEST_CASE( api_limit_get_limit_orders_by_account ){ + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + const auto& test = create_user_issued_asset("TESTASSET"); + create_sell_order( account_id_type(), asset(1,asset_id_type()), test.amount(1) ); + GRAPHENE_CHECK_THROW(db_api.get_limit_orders_by_account( + std::string(static_cast(account_id_type())), 160), fc::exception); + vector limit_orders =db_api.get_limit_orders_by_account( + std::string(static_cast(account_id_type())), 145); + BOOST_REQUIRE_EQUAL( limit_orders.size(), 1u); + + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_CASE( api_limit_get_call_orders ){ try{ graphene::app::database_api db_api( db, &( app.get_options() )); diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index 730886aca0..e74b622284 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -1115,6 +1115,115 @@ BOOST_AUTO_TEST_CASE( lookup_vote_ids ) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(get_limit_orders_by_account) +{ try { + graphene::app::database_api db_api( db, &( app.get_options() )); + ACTORS((seller)(buyer)(watcher)); + + const auto& bitcny = create_user_issued_asset("CNY"); + const auto& core = asset_id_type()(db); + + int64_t init_balance(10000000); + transfer( committee_account, seller_id, asset(init_balance) ); + issue_uia( buyer_id, bitcny.amount(init_balance) ); + BOOST_CHECK_EQUAL( 10000000, get_balance(seller, core) ); + BOOST_CHECK_EQUAL( 10000000, get_balance(buyer, bitcny) ); + + std::vector results, results2; + limit_order_object o; + + // limit too large + BOOST_CHECK_THROW( db_api.get_limit_orders_by_account( seller.name, 102 ), fc::exception ); + + // The order book is empty + results = db_api.get_limit_orders_by_account( seller.name ); + BOOST_CHECK_EQUAL( results.size(), 0 ); + + // Seller create 50 orders + for (size_t i = 0 ; i < 50 ; ++i) + { + BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250))); + } + + // Get all orders + results = db_api.get_limit_orders_by_account( seller.name ); + BOOST_CHECK_EQUAL( results.size(), 50 ); + + // Seller create 200 orders + for (size_t i = 1 ; i < 101 ; ++i) + { + BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250 + i))); + BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250 - i))); + } + + // Buyer create 20 orders + for (size_t i = 0 ; i < 70 ; ++i) + { + BOOST_CHECK(create_sell_order(buyer, bitcny.amount(100), core.amount(5000 + i))); + } + + // Get the first 101 orders + results = db_api.get_limit_orders_by_account( seller.name ); + BOOST_CHECK_EQUAL( results.size(), 101 ); + for (size_t i = 0 ; i < results.size() - 2 ; ++i) + { + BOOST_CHECK(results[i].id < results[i+1].id); + } + BOOST_CHECK(results.front().sell_price == price(core.amount(100), bitcny.amount(250))); + BOOST_CHECK(results.back().sell_price == price(core.amount(100), bitcny.amount(276))); + o = results.back(); + + // Get the No. 101-201 orders + results = db_api.get_limit_orders_by_account( seller.name, {}, o.id ); + BOOST_CHECK_EQUAL( results.size(), 101 ); + for (size_t i = 0 ; i < results.size() - 2 ; ++i) + { + BOOST_CHECK(results[i].id < results[i+1].id); + } + BOOST_CHECK(results.front().sell_price == price(core.amount(100), bitcny.amount(276))); + BOOST_CHECK(results.back().sell_price == price(core.amount(100), bitcny.amount(326))); + o = results.back(); + + // Get the No. 201- orders + results = db_api.get_limit_orders_by_account( seller.name, {}, o.id ); + BOOST_CHECK_EQUAL( results.size(), 50 ); + for (size_t i = 0 ; i < results.size() - 2 ; ++i) + { + BOOST_CHECK(results[i].id < results[i+1].id); + } + BOOST_CHECK(results.front().sell_price == price(core.amount(100), bitcny.amount(326))); + BOOST_CHECK(results.back().sell_price == price(core.amount(100), bitcny.amount(150))); + + // Get the No. 201-210 orders + results2 = db_api.get_limit_orders_by_account( seller.name, 10, o.id ); + BOOST_CHECK_EQUAL( results2.size(), 10 ); + for (size_t i = 0 ; i < results2.size() - 2 ; ++i) + { + BOOST_CHECK(results2[i].id < results2[i+1].id); + BOOST_CHECK(results[i].id == results2[i].id); + } + BOOST_CHECK(results2.front().sell_price == price(core.amount(100), bitcny.amount(326))); + BOOST_CHECK(results2.back().sell_price == price(core.amount(100), bitcny.amount(170))); + + // Buyer has 70 orders, all IDs are greater than sellers + results = db_api.get_limit_orders_by_account( buyer.name, 90, o.id ); + BOOST_CHECK_EQUAL( results.size(), 70 ); + o = results.back(); + + // All seller's order IDs are smaller, so querying with a buyer's ID will get nothing + results = db_api.get_limit_orders_by_account( seller.name, 90, o.id ); + BOOST_CHECK_EQUAL( results.size(), 0 ); + + // Watcher has no order + results = db_api.get_limit_orders_by_account( watcher.name ); + BOOST_CHECK_EQUAL( results.size(), 0 ); + + // unregistered account, throws exception + BOOST_CHECK_THROW( db_api.get_limit_orders_by_account( "not-a-user", 10, limit_order_id_type() ), + fc::exception ); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE(get_account_limit_orders) { try { graphene::app::database_api db_api( db, &( app.get_options() )); From 911ff452d04bb720ad55ae37662c6fddaf3d1138 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 18 Apr 2020 16:57:04 +0000 Subject: [PATCH 29/47] Fix test cases --- tests/tests/database_api_tests.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index e74b622284..3d778d4e98 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -1165,7 +1165,7 @@ BOOST_AUTO_TEST_CASE(get_limit_orders_by_account) // Get the first 101 orders results = db_api.get_limit_orders_by_account( seller.name ); BOOST_CHECK_EQUAL( results.size(), 101 ); - for (size_t i = 0 ; i < results.size() - 2 ; ++i) + for (size_t i = 0 ; i < results.size() - 1 ; ++i) { BOOST_CHECK(results[i].id < results[i+1].id); } @@ -1176,7 +1176,7 @@ BOOST_AUTO_TEST_CASE(get_limit_orders_by_account) // Get the No. 101-201 orders results = db_api.get_limit_orders_by_account( seller.name, {}, o.id ); BOOST_CHECK_EQUAL( results.size(), 101 ); - for (size_t i = 0 ; i < results.size() - 2 ; ++i) + for (size_t i = 0 ; i < results.size() - 1 ; ++i) { BOOST_CHECK(results[i].id < results[i+1].id); } @@ -1187,7 +1187,7 @@ BOOST_AUTO_TEST_CASE(get_limit_orders_by_account) // Get the No. 201- orders results = db_api.get_limit_orders_by_account( seller.name, {}, o.id ); BOOST_CHECK_EQUAL( results.size(), 50 ); - for (size_t i = 0 ; i < results.size() - 2 ; ++i) + for (size_t i = 0 ; i < results.size() - 1 ; ++i) { BOOST_CHECK(results[i].id < results[i+1].id); } @@ -1197,7 +1197,7 @@ BOOST_AUTO_TEST_CASE(get_limit_orders_by_account) // Get the No. 201-210 orders results2 = db_api.get_limit_orders_by_account( seller.name, 10, o.id ); BOOST_CHECK_EQUAL( results2.size(), 10 ); - for (size_t i = 0 ; i < results2.size() - 2 ; ++i) + for (size_t i = 0 ; i < results2.size() - 1 ; ++i) { BOOST_CHECK(results2[i].id < results2[i+1].id); BOOST_CHECK(results[i].id == results2[i].id); From 5a1dc7268fa8f9fd0395a66f0d2c02912e3e3a08 Mon Sep 17 00:00:00 2001 From: abitmore Date: Mon, 20 Apr 2020 00:16:34 +0000 Subject: [PATCH 30/47] Update database APIs related to workers - get_all_workers: add an optional `is_expired` parameter - get_workers_by_account: remove `optional` from the return type, improve performance --- libraries/app/database_api.cpp | 57 ++++++++++++------- libraries/app/database_api_impl.hxx | 4 +- .../app/include/graphene/app/database_api.hpp | 9 +-- .../include/graphene/chain/worker_object.hpp | 4 +- 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 5cc9dcede7..8c612b307c 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1765,39 +1765,56 @@ uint64_t database_api_impl::get_committee_count()const // // ////////////////////////////////////////////////////////////////////// -vector database_api::get_all_workers()const +vector database_api::get_all_workers( const optional is_expired )const { - return my->get_all_workers(); + return my->get_all_workers( is_expired ); } -vector database_api_impl::get_all_workers()const +vector database_api_impl::get_all_workers( const optional is_expired )const { - vector result; - const auto& workers_idx = _db.get_index_type().indices().get(); - for( const auto& w : workers_idx ) - { - result.push_back( w ); - } - return result; + vector result; + + if( !is_expired.valid() ) // query for all workers + { + const auto& workers_idx = _db.get_index_type().indices().get(); + result.reserve( workers_idx.size() ); + for( const auto& w : workers_idx ) + { + result.push_back( w ); + } + } + else // query for workers that are expired only or not expired only + { + const time_point_sec now = _db.head_block_time(); + const auto& workers_idx = _db.get_index_type().indices().get(); + auto itr = *is_expired ? workers_idx.begin() : workers_idx.lower_bound( now ); + auto end = *is_expired ? workers_idx.upper_bound( now ) : workers_idx.end(); + for( ; itr != end; ++itr ) + { + result.push_back( *itr ); + } + } + + return result; } -vector> database_api::get_workers_by_account(const std::string account_id_or_name)const +vector database_api::get_workers_by_account(const std::string account_id_or_name)const { - return my->get_workers_by_account( account_id_or_name ); + return my->get_workers_by_account( account_id_or_name ); } -vector> database_api_impl::get_workers_by_account(const std::string account_id_or_name)const +vector database_api_impl::get_workers_by_account(const std::string account_id_or_name)const { - vector> result; + vector result; const auto& workers_idx = _db.get_index_type().indices().get(); const account_id_type account = get_account_from_string(account_id_or_name)->id; - for( const auto& w : workers_idx ) - { - if( w.worker_account == account ) - result.push_back( w ); - } - return result; + auto range = workers_idx.equal_range(account); + for(auto itr = range.first; itr != range.second; ++itr) + { + result.push_back( *itr ); + } + return result; } uint64_t database_api::get_worker_count()const diff --git a/libraries/app/database_api_impl.hxx b/libraries/app/database_api_impl.hxx index e2e7c376a4..561dc56ba9 100644 --- a/libraries/app/database_api_impl.hxx +++ b/libraries/app/database_api_impl.hxx @@ -150,8 +150,8 @@ class database_api_impl : public std::enable_shared_from_this uint64_t get_committee_count()const; // Workers - vector get_all_workers()const; - vector> get_workers_by_account(const std::string account_id_or_name)const; + vector get_all_workers( const optional is_expired = optional() )const; + vector get_workers_by_account(const std::string account_id_or_name)const; uint64_t get_worker_count()const; // Votes diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 1960e64824..1bdd2e7136 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -674,18 +674,19 @@ class database_api /////////////////////// /** - * @brief Get all workers - * @return All the workers + * @brief Get workers + * @param is_expired null for all workers, true for expired workers only, false for non-expired workers only + * @return A list of worker objects * */ - vector get_all_workers()const; + vector get_all_workers( const optional is_expired = optional() )const; /** * @brief Get the workers owned by a given account * @param account_name_or_id The name or ID of the account whose worker should be retrieved * @return A list of worker objects owned by the account */ - vector> get_workers_by_account(const std::string account_name_or_id)const; + vector get_workers_by_account(const std::string account_name_or_id)const; /** * @brief Get the total number of workers registered with the blockchain diff --git a/libraries/chain/include/graphene/chain/worker_object.hpp b/libraries/chain/include/graphene/chain/worker_object.hpp index da5c170ca1..34528a79ef 100644 --- a/libraries/chain/include/graphene/chain/worker_object.hpp +++ b/libraries/chain/include/graphene/chain/worker_object.hpp @@ -145,13 +145,15 @@ class worker_object : public abstract_object struct by_account; struct by_vote_for; struct by_vote_against; +struct by_end_date; typedef multi_index_container< worker_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, ordered_non_unique< tag, member< worker_object, account_id_type, &worker_object::worker_account > >, ordered_unique< tag, member< worker_object, vote_id_type, &worker_object::vote_for > >, - ordered_unique< tag, member< worker_object, vote_id_type, &worker_object::vote_against > > + ordered_unique< tag, member< worker_object, vote_id_type, &worker_object::vote_against > >, + ordered_non_unique< tag, member< worker_object, time_point_sec, &worker_object::work_end_date> > > > worker_object_multi_index_type; From aaf96937a4d07f403e07747de6a252e58b430725 Mon Sep 17 00:00:00 2001 From: abitmore Date: Mon, 20 Apr 2020 01:10:37 +0000 Subject: [PATCH 31/47] Add test cases for get_all_workers API --- tests/tests/database_api_tests.cpp | 80 ++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index 98febcf2af..52fad42ce5 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -1090,6 +1090,86 @@ BOOST_AUTO_TEST_CASE( subscription_notification_test ) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( get_all_workers ) +{ try { + graphene::app::database_api db_api( db, &( app.get_options() )); + ACTORS( (connie)(whitney)(wolverine) ); + + fund(connie); + upgrade_to_lifetime_member(connie); + fund(whitney); + upgrade_to_lifetime_member(whitney); + fund(wolverine); + upgrade_to_lifetime_member(wolverine); + + vector results; + + const auto& worker1 = create_worker( connie_id, 1000, fc::days(10) ); + worker_id_type worker1_id = worker1.id; + + BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 1 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 0 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 1 ); + BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id ); + BOOST_CHECK( db_api.get_all_workers(false).front().id == worker1_id ); + + generate_blocks( db.head_block_time() + fc::days(11) ); + set_expiration( db, trx ); + + BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 1 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 1 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 0 ); + BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id ); + BOOST_CHECK( db_api.get_all_workers(true).front().id == worker1_id ); + + const auto& worker2 = create_worker( whitney_id, 1000, fc::days(50) ); + worker_id_type worker2_id = worker2.id; + + BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 2 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 1 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 1 ); + BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id ); + BOOST_CHECK( db_api.get_all_workers().back().id == worker2_id ); + BOOST_CHECK( db_api.get_all_workers(true).front().id == worker1_id ); + BOOST_CHECK( db_api.get_all_workers(false).front().id == worker2_id ); + + const auto& worker3 = create_worker( wolverine_id, 1000, fc::days(100) ); + worker_id_type worker3_id = worker3.id; + + BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 3 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 1 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 2 ); + BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id ); + BOOST_CHECK( db_api.get_all_workers().back().id == worker3_id ); + BOOST_CHECK( db_api.get_all_workers(true).front().id == worker1_id ); + BOOST_CHECK( db_api.get_all_workers(false).front().id == worker2_id ); + BOOST_CHECK( db_api.get_all_workers(false).back().id == worker3_id ); + + generate_blocks( db.head_block_time() + fc::days(55) ); + set_expiration( db, trx ); + + BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 3 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 2 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 1 ); + BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id ); + BOOST_CHECK( db_api.get_all_workers().back().id == worker3_id ); + BOOST_CHECK( db_api.get_all_workers(true).front().id == worker1_id ); + BOOST_CHECK( db_api.get_all_workers(true).back().id == worker2_id ); + BOOST_CHECK( db_api.get_all_workers(false).front().id == worker3_id ); + + generate_blocks( db.head_block_time() + fc::days(55) ); + set_expiration( db, trx ); + + BOOST_REQUIRE_EQUAL( db_api.get_all_workers().size(), 3 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(true).size(), 3 ); + BOOST_REQUIRE_EQUAL( db_api.get_all_workers(false).size(), 0 ); + BOOST_CHECK( db_api.get_all_workers().front().id == worker1_id ); + BOOST_CHECK( db_api.get_all_workers().back().id == worker3_id ); + BOOST_CHECK( db_api.get_all_workers(true).front().id == worker1_id ); + BOOST_CHECK( db_api.get_all_workers(true).back().id == worker3_id ); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE( lookup_vote_ids ) { try { graphene::app::database_api db_api( db, &( app.get_options() )); From 4e04c1d9d8363d809d8165da1f5b460742a31513 Mon Sep 17 00:00:00 2001 From: abitmore Date: Mon, 20 Apr 2020 01:21:04 +0000 Subject: [PATCH 32/47] Add test cases for get_workers_by_account API --- tests/tests/database_api_tests.cpp | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index 52fad42ce5..c37d46a3e4 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -1170,6 +1170,42 @@ BOOST_AUTO_TEST_CASE( get_all_workers ) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( get_workers_by_account ) +{ try { + graphene::app::database_api db_api( db, &( app.get_options() )); + ACTORS( (connie)(whitney)(wolverine) ); + + fund(connie); + upgrade_to_lifetime_member(connie); + fund(whitney); + upgrade_to_lifetime_member(whitney); + fund(wolverine); + upgrade_to_lifetime_member(wolverine); + + vector results; + + const auto& worker1 = create_worker( connie_id ); + worker_id_type worker1_id = worker1.id; + + const auto& worker2 = create_worker( whitney_id, 1000, fc::days(50) ); + worker_id_type worker2_id = worker2.id; + + const auto& worker3 = create_worker( whitney_id, 1000, fc::days(100) ); + worker_id_type worker3_id = worker3.id; + + BOOST_REQUIRE_EQUAL( db_api.get_workers_by_account("connie").size(), 1 ); + BOOST_CHECK( db_api.get_workers_by_account("connie").front().id == worker1_id ); + + BOOST_REQUIRE_EQUAL( db_api.get_workers_by_account(string(whitney.id)).size(), 2 ); + BOOST_CHECK( db_api.get_workers_by_account(string(whitney.id)).front().id == worker2_id ); + BOOST_CHECK( db_api.get_workers_by_account(string(whitney.id)).back().id == worker3_id ); + + BOOST_REQUIRE_EQUAL( db_api.get_workers_by_account("wolverine").size(), 0 ); + + BOOST_REQUIRE_THROW( db_api.get_workers_by_account("not-a-user"), fc::exception ); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE( lookup_vote_ids ) { try { graphene::app::database_api db_api( db, &( app.get_options() )); From 4d6c20ae2aab6cb73c32582a72306f6048cc8e65 Mon Sep 17 00:00:00 2001 From: abitmore Date: Tue, 21 Apr 2020 12:50:28 +0000 Subject: [PATCH 33/47] Update default config.ini for docker --- docker/default_config.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker/default_config.ini b/docker/default_config.ini index 0bbd64733c..53aa03bcfc 100644 --- a/docker/default_config.ini +++ b/docker/default_config.ini @@ -82,6 +82,9 @@ rpc-endpoint = 0.0.0.0:8090 # For database_api_impl::get_limit_orders to set max limit value # api-limit-get-limit-orders = 300 +# For database_api_impl::get_limit_orders_by_account to set max limit value +# api-limit-get-limit-orders-by-account = 101 + # For database_api_impl::get_order_book to set max limit value # api-limit-get-order-book = 50 From f99d81f2d82dd848b43737ed629aa493a8cef4a5 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Tue, 21 Apr 2020 11:20:18 -0400 Subject: [PATCH 34/47] BSIP81: Test improvements --- tests/tests/simple_maker_taker_fee_tests.cpp | 42 +++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index 3e806bb2fe..66f15bd893 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -1,3 +1,27 @@ +/* + * Copyright (c) 2020 Michel Santos, 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 @@ -641,7 +665,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa /** * Test of different maker and taker fees charged when filling limit orders after HF for a UIA */ - BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_uia) { + BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_uia_1) { try { // Initialize for the current time trx.clear(); @@ -1310,6 +1334,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa /** * Test of different maker and taker fees charged when filling limit orders after HF for a smart asset + * and a user-issued asset * * 1. (Order 1) An order will be placed to offer JCOIN * @@ -1321,9 +1346,10 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa * 3. (Order 3) A matching order will be placed to offer JCOIN. * Order 3 should be charged a taker fee, and Order 2 should be charged a maker fee. * - * Summary: Order 2 should be charged a taker fee when matching Order 1, and Order 2 should be charged a maker fee when matching Order 3. + * Summary: Order 2 should be charged a taker fee when matching Order 1, + * and Order 2 should be charged a maker fee when matching Order 3. */ - BOOST_AUTO_TEST_CASE(partial_maker_partial_taker_fills) { + BOOST_AUTO_TEST_CASE(partial_maker_partial_taker_fills_1) { try { // Initialize for the current time trx.clear(); @@ -1398,6 +1424,10 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa BOOST_CHECK(updated_asset.options.extensions.value.taker_fee_percent.valid()); BOOST_CHECK_EQUAL(expected_taker_fee_percent, *updated_asset.options.extensions.value.taker_fee_percent); + // Check the maker fee for JILLCOIN + uint16_t expected_maker_fee_percent = jill_maker_fee_percent; + BOOST_CHECK_EQUAL(expected_maker_fee_percent, updated_asset.options.market_fee_percent); + // Set the new taker fee for SMARTBIT uop = asset_update_operation(); @@ -1426,7 +1456,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa ////// - // Create Orders 1 and 2 that will match. + // Create Orders 1 and 2 to match. // Order 1 will be completely filled, and Order 2 will be partially filled. ////// // Initialize token balance of actors @@ -1503,7 +1533,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa ////// - // Create Order 3 that will the remainder of match Order 2 + // Create Order 3 to match the remainder of match Order 2 ////// // Initialize token balance of actors BOOST_TEST_MESSAGE("Issuing 5 JCOIN to charlie"); @@ -1553,7 +1583,7 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa // Check the asset issuer's accumulated fees share_type expected_smartbit_fee_after_order_3 = expected_smartbit_fee_after_order_2 + expected_smartbit_fee.amount; - share_type expected_jill_fee_after_order_3 = expected_jill_fee_after_order_2 + expected_jill_fee.amount; + share_type expected_jill_fee_after_order_3 = expected_jill_fee_after_order_2 + expected_jill_order_3_fee.amount; BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == expected_smartbit_fee_after_order_3); BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee_after_order_3); From 0f712cc297f1d82fb858b20270c736531d5bebeb Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Tue, 21 Apr 2020 11:17:54 -0400 Subject: [PATCH 35/47] BSIP81: Test default taker fee usage after hardfork if the asset owner does not set the taker fee --- tests/tests/simple_maker_taker_fee_tests.cpp | 359 +++++++++++++++++++ 1 file changed, 359 insertions(+) diff --git a/tests/tests/simple_maker_taker_fee_tests.cpp b/tests/tests/simple_maker_taker_fee_tests.cpp index 66f15bd893..97a609b634 100644 --- a/tests/tests/simple_maker_taker_fee_tests.cpp +++ b/tests/tests/simple_maker_taker_fee_tests.cpp @@ -1152,6 +1152,141 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa } + /** + * Test of **default** taker fees charged when filling limit orders after HF for a UIA. + * + * This test is similar to simple_match_and_fill_with_different_fees_uia_1 + * except that the taker fee is not explicitly set and instead defaults to the maker fee. + */ + BOOST_AUTO_TEST_CASE(simple_match_and_fill_with_different_fees_uia_4) { + try { + // Initialize for the current time + trx.clear(); + set_expiration(db, trx); + + // Initialize actors + ACTORS((jill)(izzy)(alice)(bob)); + + // Initialize tokens + price price(asset(1, asset_id_type(1)), asset(1)); + + const uint16_t JILL_PRECISION = 100; + const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT; + create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, + jill_market_fee_percent); + + const uint16_t IZZY_PRECISION = 1000; + const uint16_t izzy_market_fee_percent = 5 * GRAPHENE_1_PERCENT; + create_user_issued_asset("ICOIN", izzy, charge_market_fee, price, 3, + izzy_market_fee_percent); + + // Obtain asset object after a block is generated to obtain the final object that is commited to the database + generate_block(); + const asset_object& jillcoin = get_asset("JCOIN"); + const asset_object& izzycoin = get_asset("ICOIN"); + + + ////// + // Advance to activate hardfork + ////// + generate_blocks(HARDFORK_BSIP_81_TIME); + generate_block(); + trx.clear(); + set_expiration(db, trx); + + + ////// + // After HF, test that default taker values has not been set + ////// + // The taker fees should automatically default to maker fees if the taker fee is not explicitly set + // UNUSED: uint16_t jill_maker_fee_percent = jill_market_fee_percent; + uint16_t jill_taker_fee_percent = jill_market_fee_percent; + + uint16_t izzy_maker_fee_percent = izzy_market_fee_percent; + // UNUSED: uint16_t izzy_taker_fee_percent = izzy_market_fee_percent; + + // Check the taker fee for JCOIN: it should still not be set + asset_object updated_asset = jillcoin.get_id()(db); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); + + // Check the taker fee for ICOIN: it should still not be set + updated_asset = izzycoin.get_id()(db); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); + + + ////// + // After HF, create limit orders that will perfectly match + ////// + BOOST_TEST_MESSAGE("Issuing 10 jillcoin to alice"); + issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION)); + BOOST_TEST_MESSAGE("Checking alice's balance"); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION); + + BOOST_TEST_MESSAGE("Issuing 300 izzycoin to bob"); + issue_uia(bob, izzycoin.amount(300 * IZZY_PRECISION)); + BOOST_TEST_MESSAGE("Checking bob's balance"); + BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 300 * IZZY_PRECISION); + + // Alice and Bob place orders which match, and are completely filled by each other + // Alice is willing to sell 10 JILLCOIN for at least 300 IZZYCOIN + limit_order_create_operation alice_sell_op = create_sell_operation(alice.id, + jillcoin.amount(10 * JILL_PRECISION), + izzycoin.amount(300 * + IZZY_PRECISION)); + trx.clear(); + trx.operations.push_back(alice_sell_op); + asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type alice_order_id = ptx.operation_results[0].get(); + + const limit_order_object* alice_order_before = db.find(alice_order_id); + BOOST_CHECK(alice_order_before != nullptr); + + // Bob is willing to sell 300 IZZYCOIN for at least 10 JILLCOIN + limit_order_create_operation bob_sell_op = create_sell_operation(bob.id, izzycoin.amount(300 * IZZY_PRECISION), + jillcoin.amount( + 10 * + JILL_PRECISION)); + trx.clear(); + trx.operations.push_back(bob_sell_op); + asset bob_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, bob_private_key); + ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type bob_order_id = ptx.operation_results[0].get(); + + // Check that the orders were filled by ensuring that they are no longer on the order books + const limit_order_object* alice_order = db.find(alice_order_id); + BOOST_CHECK(alice_order == nullptr); + const limit_order_object* bob_order = db.find(bob_order_id); + BOOST_CHECK(bob_order == nullptr); + + + // Check the new balances of the maker + // Alice was the maker; she is receiving IZZYCOIN + asset expected_izzy_fee = izzycoin.amount( + 300 * IZZY_PRECISION * izzy_maker_fee_percent / GRAPHENE_100_PERCENT); + BOOST_REQUIRE_EQUAL(get_balance(alice, izzycoin), + (300 * IZZY_PRECISION) - alice_sell_fee.amount.value - expected_izzy_fee.amount.value); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0); + + // Check the new balance of the taker + // Bob was the taker; he is receiving JILLCOIN + asset expected_jill_fee = jillcoin.amount( + 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT); + BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), + (10 * JILL_PRECISION) - bob_sell_fee.amount.value - expected_jill_fee.amount.value); + BOOST_REQUIRE_EQUAL(get_balance(bob, izzycoin), 0); + + // Check the asset issuer's accumulated fees + BOOST_CHECK(izzycoin.dynamic_asset_data_id(db).accumulated_fees == expected_izzy_fee.amount); + BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee.amount); + + } FC_LOG_AND_RETHROW() + } + + + /** * Test of different maker and taker fees charged when filling limit orders after HF for a smart asset */ @@ -1590,4 +1725,228 @@ BOOST_FIXTURE_TEST_SUITE(simple_maker_taker_fee_tests, simple_maker_taker_databa } FC_LOG_AND_RETHROW() } + + /** + * Test of **default** taker fees charged when filling limit orders after HF for a smart asset + * and a user-issued asset + * + * This test is similar to partial_maker_partial_taker_fills_1 except that + * (a) the taker fee is not explicitly set and instead defaults to the maker fee, and + * (b) Orders 1 and 2 are placed before the HF and Order 3 is placed after the HF. + * + * 1. (Order 1) An order will be placed to offer JCOIN + * + * 2. (Order 2) A matching-order will be placed to offer SMARTBIT. + * Order 2 is large enough that it should be partially filled, and Order 1 will be completely filled. + * Order 1 should be charged a maker fee, and Order 2 should be charged a taker fee. + * Order 2 should remain on the book. + * + * 3. (Order 3) A matching order will be placed to offer JCOIN. + * Order 3 should be charged a taker fee, and Order 2 should be charged a maker fee. + * + * Summary: Order 2 should be charged a taker fee when matching Order 1, + * and Order 2 should be charged a maker fee when matching Order 3. + */ + BOOST_AUTO_TEST_CASE(partial_maker_partial_taker_fills_2) { + try { + // Initialize for the current time + trx.clear(); + set_expiration(db, trx); + + // Initialize actors + ACTORS((jill)(izzy)(alice)(bob)(charlie)); + ACTORS((smartissuer)(feedproducer)); + + // Initialize tokens + price price(asset(1, asset_id_type(1)), asset(1)); + const uint16_t JILL_PRECISION = 100; + const uint16_t jill_market_fee_percent = 2 * GRAPHENE_1_PERCENT; + const asset_object jillcoin = create_user_issued_asset("JCOIN", jill, charge_market_fee, price, 2, + jill_market_fee_percent); + + const uint16_t SMARTBIT_PRECISION = 10000; + const uint16_t smartbit_market_fee_percent = 2 * GRAPHENE_1_PERCENT; + create_bitasset("SMARTBIT", smartissuer.id, smartbit_market_fee_percent, + charge_market_fee, 4); + + uint16_t jill_maker_fee_percent = jill_market_fee_percent; + uint16_t jill_taker_fee_percent = jill_market_fee_percent; + + uint16_t smartbit_maker_fee_percent = smartbit_market_fee_percent; + uint16_t smartbit_taker_fee_percent = smartbit_market_fee_percent; + + + // Obtain asset object after a block is generated to obtain the final object that is commited to the database + generate_block(); + const asset_object &smartbit = get_asset("SMARTBIT"); + + const auto &core = asset_id_type()(db); + + update_feed_producers(smartbit, {feedproducer.id}); + + price_feed current_feed; + current_feed.settlement_price = smartbit.amount(100) / core.amount(100); + current_feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default + publish_feed(smartbit, feedproducer, current_feed); + + FC_ASSERT(smartbit.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price); + + + ////// + // Create Orders 1 and 2 to match. + // Order 1 will be completely filled, and Order 2 will be partially filled. + ////// + // Initialize token balance of actors + BOOST_TEST_MESSAGE("Issuing 10 JCOIN to alice"); + issue_uia(alice, jillcoin.amount(10 * JILL_PRECISION)); + BOOST_TEST_MESSAGE("Checking alice's balance"); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 10 * JILL_PRECISION); + + BOOST_TEST_MESSAGE("Issuing 600 SMARTBIT to bob"); + transfer(committee_account, bob.id, asset(2 * 1000 * SMARTBIT_PRECISION)); + publish_feed(smartbit, feedproducer, current_feed); // Publish a recent feed + borrow(bob, smartbit.amount(600 * SMARTBIT_PRECISION), asset(2 * 600 * SMARTBIT_PRECISION)); + BOOST_TEST_MESSAGE("Checking bob's balance"); + BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 600 * SMARTBIT_PRECISION); + + // Alice and Bob place orders which match, and are completely filled by each other + // Alice is willing to sell 10 JILLCOIN for at least 300 SMARTBIT + limit_order_create_operation order_1_op = create_sell_operation(alice.id, + jillcoin.amount(10 * JILL_PRECISION), + smartbit.amount(300 * SMARTBIT_PRECISION)); + trx.clear(); + trx.operations.push_back(order_1_op); + asset alice_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type order_1_id = ptx.operation_results[0].get(); + + const limit_order_object *order_1_before = db.find(order_1_id); + BOOST_CHECK(order_1_before != nullptr); + + + // Bob is willing to sell 600 SMARTBIT for at least 20 JILLCOIN + limit_order_create_operation order_2_op + = create_sell_operation(bob.id, smartbit.amount(600 * SMARTBIT_PRECISION), + jillcoin.amount(20 * JILL_PRECISION)); + trx.clear(); + trx.operations.push_back(order_2_op); + asset order_2_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, bob_private_key); + ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type order_2_id = ptx.operation_results[0].get(); + + // Check that order 1 was completely filled by ensuring that they it is no longer on the order book + const limit_order_object *order_1 = db.find(order_1_id); + BOOST_CHECK(order_1 == nullptr); + // Check that order 2 was partially filled by ensuring that they it is still on the order book + const limit_order_object *order_2 = db.find(order_2_id); + BOOST_CHECK(order_2 != nullptr); + + + // Check the new balances of the maker + // Alice was the maker; she is receiving SMARTBIT + asset expected_smartbit_fee = smartbit.amount( + 300 * SMARTBIT_PRECISION * smartbit_maker_fee_percent / GRAPHENE_100_PERCENT); + int64_t expected_alice_balance_after_order_2 = + (300 * SMARTBIT_PRECISION) - alice_sell_fee.amount.value - expected_smartbit_fee.amount.value; + BOOST_REQUIRE_EQUAL(get_balance(alice, smartbit), expected_alice_balance_after_order_2); + BOOST_REQUIRE_EQUAL(get_balance(alice, jillcoin), 0); + + // Check the new balance of the taker + // Bob was the taker; he is receiving JILLCOIN + asset expected_jill_fee = jillcoin.amount( + 10 * JILL_PRECISION * jill_taker_fee_percent / GRAPHENE_100_PERCENT); + int64_t expected_bob_balance_after_order_2 = + (10 * JILL_PRECISION) - order_2_sell_fee.amount.value - expected_jill_fee.amount.value; + BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), expected_bob_balance_after_order_2); + BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 0); + + // Check the asset issuer's accumulated fees + share_type expected_smartbit_fee_after_order_2 = expected_smartbit_fee.amount; + share_type expected_jill_fee_after_order_2 = expected_jill_fee.amount; + BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == expected_smartbit_fee_after_order_2); + BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee_after_order_2); + + + ////// + // After HF, The taker fees should automatically default to maker fees when the taker fee is not explicitly set + ////// + // Check the taker fee for JILLCOIN + asset_object updated_asset = jillcoin.get_id()(db); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); + + // Check the maker fee for JILLCOIN + uint16_t expected_maker_fee_percent = jill_maker_fee_percent; + BOOST_CHECK_EQUAL(expected_maker_fee_percent, updated_asset.options.market_fee_percent); + + // Check the taker fee for SMARTBIT + updated_asset = smartbit.get_id()(db); + BOOST_CHECK(!updated_asset.options.extensions.value.taker_fee_percent.valid()); + + // Check the maker fee for SMARTBIT + expected_maker_fee_percent = smartbit_maker_fee_percent; + BOOST_CHECK_EQUAL(expected_maker_fee_percent, updated_asset.options.market_fee_percent); + + + ////// + // Create Order 3 to match the remainder of match Order 2 + ////// + // Initialize token balance of actors + BOOST_TEST_MESSAGE("Issuing 5 JCOIN to charlie"); + trx.clear(); + issue_uia(charlie, jillcoin.amount(5 * JILL_PRECISION)); + BOOST_TEST_MESSAGE("Checking charlie's balance"); + BOOST_REQUIRE_EQUAL(get_balance(charlie, jillcoin), 5 * JILL_PRECISION); + + // Charlie is is willing to sell 5 JILLCOIN for at least 150 SMARTBIT + limit_order_create_operation order_3_op = create_sell_operation(charlie.id, + jillcoin.amount(5 * JILL_PRECISION), + smartbit.amount(150 * SMARTBIT_PRECISION)); + trx.clear(); + trx.operations.push_back(order_3_op); + asset charlie_sell_fee = db.current_fee_schedule().set_fee(trx.operations.back()); + sign(trx, charlie_private_key); + ptx = PUSH_TX(db, trx); // No exception should be thrown + limit_order_id_type order_3_id = ptx.operation_results[0].get(); + + // Order 3 should be completely filled + const limit_order_object *order_3 = db.find(order_3_id); + BOOST_CHECK(order_3 == nullptr); + + // Order 2 should be partially filled and still present on the order books + const limit_order_object *order_2_after = db.find(order_2_id); + BOOST_CHECK(order_2_after != nullptr); + + // Check the new balance of the taker + // Charlie was the taker; he is receiving SMARTBIT + expected_smartbit_fee = smartbit.amount( + 150 * SMARTBIT_PRECISION * smartbit_taker_fee_percent / GRAPHENE_100_PERCENT); + int64_t expected_charlie_balance_after_order_3 = + (150 * SMARTBIT_PRECISION) - charlie_sell_fee.amount.value - expected_smartbit_fee.amount.value; + BOOST_REQUIRE_EQUAL(get_balance(charlie, smartbit), expected_charlie_balance_after_order_3); + BOOST_REQUIRE_EQUAL(get_balance(charlie, jillcoin), 0); + + // Check the new balance of the maker + // Bob was the maker; he is receiving JILLCOIN + asset expected_jill_order_3_fee = jillcoin.amount( + 5 * JILL_PRECISION * jill_maker_fee_percent / GRAPHENE_100_PERCENT); + int64_t expected_bob_balance_after_order_3 = + expected_bob_balance_after_order_2 + + (5 * JILL_PRECISION) - expected_jill_order_3_fee.amount.value; + BOOST_REQUIRE_EQUAL(get_balance(bob, jillcoin), expected_bob_balance_after_order_3); + BOOST_REQUIRE_EQUAL(get_balance(bob, smartbit), 0); + + // Check the asset issuer's accumulated fees + share_type expected_smartbit_fee_after_order_3 = + expected_smartbit_fee_after_order_2 + expected_smartbit_fee.amount; + share_type expected_jill_fee_after_order_3 = expected_jill_fee_after_order_2 + expected_jill_order_3_fee.amount; + BOOST_CHECK(smartbit.dynamic_asset_data_id(db).accumulated_fees == expected_smartbit_fee_after_order_3); + BOOST_CHECK(jillcoin.dynamic_asset_data_id(db).accumulated_fees == expected_jill_fee_after_order_3); + wdump((jillcoin.dynamic_asset_data_id(db).accumulated_fees)(expected_jill_fee_after_order_3)(expected_jill_fee_after_order_2)(expected_jill_fee.amount)); + wdump((get_asset("JCOIN").dynamic_asset_data_id(db).accumulated_fees)); + + } FC_LOG_AND_RETHROW() + } + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From f1e0bae95f96b74eea4fb63e8a92caf5eb1ce6ac Mon Sep 17 00:00:00 2001 From: abitmore Date: Wed, 22 Apr 2020 16:49:21 +0000 Subject: [PATCH 36/47] Report the actual API limits in error messages For issue https://github.com/bitshares/bitshares-core/issues/2141 --- libraries/app/api.cpp | 83 +++++++--- libraries/app/database_api.cpp | 177 ++++++++++++++++----- libraries/app/include/graphene/app/api.hpp | 4 +- 3 files changed, 198 insertions(+), 66 deletions(-) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index ef03a4181c..1f22020adb 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -342,8 +342,12 @@ namespace graphene { namespace app { { FC_ASSERT( _app.chain_database() ); const auto& db = *_app.chain_database(); - uint64_t api_limit_get_account_history=_app.get_options().api_limit_get_account_history; - FC_ASSERT( limit <= api_limit_get_account_history ); + + const auto configured_limit = _app.get_options().api_limit_get_account_history; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + vector result; account_id_type account; try { @@ -387,12 +391,16 @@ namespace graphene { namespace app { int operation_type, operation_history_id_type start, operation_history_id_type stop, - unsigned limit) const + unsigned limit ) const { FC_ASSERT( _app.chain_database() ); const auto& db = *_app.chain_database(); - uint64_t api_limit_get_account_history_operations=_app.get_options().api_limit_get_account_history_operations; - FC_ASSERT(limit <= api_limit_get_account_history_operations); + + const auto configured_limit = _app.get_options().api_limit_get_account_history_operations; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + vector result; account_id_type account; try { @@ -427,12 +435,16 @@ namespace graphene { namespace app { vector history_api::get_relative_account_history( const std::string account_id_or_name, uint64_t stop, unsigned limit, - uint64_t start) const + uint64_t start ) const { FC_ASSERT( _app.chain_database() ); const auto& db = *_app.chain_database(); - uint64_t api_limit_get_relative_account_history=_app.get_options().api_limit_get_relative_account_history; - FC_ASSERT(limit <= api_limit_get_relative_account_history); + + const auto configured_limit = _app.get_options().api_limit_get_relative_account_history; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + vector result; account_id_type account; try { @@ -469,24 +481,38 @@ namespace graphene { namespace app { return hist->tracked_buckets(); } - history_operation_detail history_api::get_account_history_by_operations(const std::string account_id_or_name, vector operation_types, uint32_t start, unsigned limit) + history_operation_detail history_api::get_account_history_by_operations( const std::string account_id_or_name, + flat_set operation_types, + uint32_t start, unsigned limit )const { - uint64_t api_limit_get_account_history_by_operations=_app.get_options().api_limit_get_account_history_by_operations; - FC_ASSERT(limit <= api_limit_get_account_history_by_operations); + const auto configured_limit = _app.get_options().api_limit_get_account_history_by_operations; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + history_operation_detail result; - vector objs = get_relative_account_history(account_id_or_name, start, limit, limit + start - 1); - std::for_each(objs.begin(), objs.end(), [&](const operation_history_object &o) { - if (operation_types.empty() || find(operation_types.begin(), operation_types.end(), o.op.which()) != operation_types.end()) { - result.operation_history_objs.push_back(o); - } - }); + vector objs = get_relative_account_history( account_id_or_name, start, limit, + limit + start - 1 ); + result.total_count = objs.size(); + + if( operation_types.empty() ) + result.operation_history_objs = std::move(objs); + else + { + for( const operation_history_object &o : objs ) + { + if( operation_types.find(o.op.which()) != operation_types.end() ) { + result.operation_history_objs.push_back(o); + } + } + } - result.total_count = objs.size(); - return result; + return result; } vector history_api::get_market_history( std::string asset_a, std::string asset_b, - uint32_t bucket_seconds, fc::time_point_sec start, fc::time_point_sec end )const + uint32_t bucket_seconds, + fc::time_point_sec start, fc::time_point_sec end )const { try { FC_ASSERT(_app.chain_database()); const auto& db = *_app.chain_database(); @@ -577,9 +603,13 @@ namespace graphene { namespace app { ) { } asset_api::~asset_api() { } - vector asset_api::get_asset_holders( std::string asset, uint32_t start, uint32_t limit ) const { - uint64_t api_limit_get_asset_holders=_app.get_options().api_limit_get_asset_holders; - FC_ASSERT(limit <= api_limit_get_asset_holders); + vector asset_api::get_asset_holders( std::string asset, uint32_t start, uint32_t limit ) const + { + const auto configured_limit = _app.get_options().api_limit_get_asset_holders; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + asset_id_type asset_id = database_api.get_asset_id_from_string( asset ); const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >(); auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) ); @@ -660,8 +690,11 @@ namespace graphene { namespace app { optional start, uint32_t limit )const { - uint64_t api_limit_get_grouped_limit_orders=_app.get_options().api_limit_get_grouped_limit_orders; - FC_ASSERT( limit <= api_limit_get_grouped_limit_orders ); + const auto configured_limit = _app.get_options().api_limit_get_grouped_limit_orders; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + auto plugin = _app.get_plugin( "grouped_orders" ); FC_ASSERT( plugin ); const auto& limit_groups = plugin->limit_order_groups(); diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 1ff78df10a..8ebaeaa309 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -339,8 +339,11 @@ vector> database_api_impl::get_key_references( vector< FC_ASSERT( _app_options && _app_options->has_api_helper_indexes_plugin, "api_helper_indexes plugin is not enabled on this server." ); - uint64_t api_limit_get_key_references=_app_options->api_limit_get_key_references; - FC_ASSERT(keys.size() <= api_limit_get_key_references); + const auto configured_limit = _app_options->api_limit_get_key_references; + FC_ASSERT( keys.size() <= configured_limit, + "Number of querying keys can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + const auto& idx = _db.get_index_type(); const auto& aidx = dynamic_cast(idx); const auto& refs = aidx.get_secondary_index(); @@ -461,7 +464,11 @@ std::map database_api::get_full_accounts( const vector database_api_impl::get_full_accounts( const vector& names_or_ids, optional subscribe ) { - FC_ASSERT( names_or_ids.size() <= _app_options->api_limit_get_full_accounts ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_full_accounts; + FC_ASSERT( names_or_ids.size() <= configured_limit, + "Number of querying accounts can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); bool to_subscribe = get_whether_to_subscribe( subscribe ); @@ -702,7 +709,12 @@ map database_api_impl::lookup_accounts( const string& lo uint32_t limit, optional subscribe )const { - FC_ASSERT( limit <= _app_options->api_limit_lookup_accounts ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_lookup_accounts; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + const auto& accounts_by_name = _db.get_index_type().indices().get(); map result; @@ -888,8 +900,11 @@ vector database_api::list_assets(const string& lower_boun vector database_api_impl::list_assets(const string& lower_bound_symbol, uint32_t limit)const { - uint64_t api_limit_get_assets = _app_options->api_limit_get_assets; - FC_ASSERT( limit <= api_limit_get_assets ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_assets; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); const auto& assets_by_symbol = _db.get_index_type().indices().get(); vector result; @@ -925,8 +940,11 @@ vector database_api::get_assets_by_issuer(const std::stri vector database_api_impl::get_assets_by_issuer(const std::string& issuer_name_or_id, asset_id_type start, uint32_t limit)const { - uint64_t api_limit_get_assets = _app_options->api_limit_get_assets; - FC_ASSERT( limit <= api_limit_get_assets ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_assets; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); vector result; const account_id_type account = get_account_from_string(issuer_name_or_id)->id; @@ -980,8 +998,11 @@ vector database_api::get_limit_orders(std::string a, std::st vector database_api_impl::get_limit_orders( const std::string& a, const std::string& b, uint32_t limit )const { - uint64_t api_limit_get_limit_orders=_app_options->api_limit_get_limit_orders; - FC_ASSERT( limit <= api_limit_get_limit_orders ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_limit_orders; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); const asset_id_type asset_a_id = get_asset_from_string(a)->id; const asset_id_type asset_b_id = get_asset_from_string(b)->id; @@ -999,9 +1020,12 @@ vector database_api_impl::get_limit_orders_by_account( const optional olimit, optional ostart_id ) { uint32_t limit = olimit.valid() ? *olimit : 101; - FC_ASSERT( limit <= _app_options->api_limit_get_limit_orders_by_account, - "limit can not be greater than ${limit}", - ("limit", _app_options->api_limit_get_limit_orders_by_account) ); + + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_limit_orders_by_account; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); vector results; @@ -1037,7 +1061,11 @@ vector database_api_impl::get_account_limit_orders( const string& account_name_or_id, const string &base, const string "e, uint32_t limit, optional ostart_id, optional ostart_price ) { - FC_ASSERT( limit <= _app_options->api_limit_get_account_limit_orders ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_account_limit_orders; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); vector results; uint32_t count = 0; @@ -1121,8 +1149,11 @@ vector database_api::get_call_orders(const std::string& a, ui vector database_api_impl::get_call_orders(const std::string& a, uint32_t limit)const { - uint64_t api_limit_get_call_orders = _app_options->api_limit_get_call_orders; - FC_ASSERT( limit <= api_limit_get_call_orders ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_call_orders; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); const asset_object* mia = get_asset_from_string(a); const auto& call_index = _db.get_index_type().indices().get(); @@ -1148,8 +1179,11 @@ vector database_api::get_call_orders_by_account(const std::st vector database_api_impl::get_call_orders_by_account(const std::string& account_name_or_id, asset_id_type start, uint32_t limit)const { - uint64_t api_limit_get_call_orders = _app_options->api_limit_get_call_orders; - FC_ASSERT( limit <= api_limit_get_call_orders ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_call_orders; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); vector result; const account_id_type account = get_account_from_string(account_name_or_id)->id; @@ -1171,8 +1205,11 @@ vector database_api::get_settle_orders(const std::strin vector database_api_impl::get_settle_orders(const std::string& a, uint32_t limit)const { - uint64_t api_limit_get_settle_orders = _app_options->api_limit_get_settle_orders; - FC_ASSERT( limit <= api_limit_get_settle_orders ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_settle_orders; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); const asset_id_type asset_a_id = get_asset_from_string(a)->id; const auto& settle_index = _db.get_index_type().indices().get(); @@ -1202,8 +1239,11 @@ vector database_api_impl::get_settle_orders_by_account( force_settlement_id_type start, uint32_t limit )const { - uint64_t api_limit_get_settle_orders = _app_options->api_limit_get_settle_orders; - FC_ASSERT( limit <= api_limit_get_settle_orders ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_settle_orders; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); vector result; const account_id_type account = get_account_from_string(account_name_or_id)->id; @@ -1252,7 +1292,12 @@ vector database_api::get_collateral_bids( const std::stri vector database_api_impl::get_collateral_bids( const std::string& asset, uint32_t limit, uint32_t skip )const { try { - FC_ASSERT( limit <= _app_options->api_limit_get_collateral_bids ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_collateral_bids; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + const asset_id_type asset_id = get_asset_from_string(asset)->id; const asset_object& swan = asset_id(_db); FC_ASSERT( swan.is_market_issued() ); @@ -1368,8 +1413,12 @@ order_book database_api::get_order_book( const string& base, const string& quote order_book database_api_impl::get_order_book( const string& base, const string& quote, unsigned limit )const { - FC_ASSERT( limit <= _app_options->api_limit_get_order_book ); - + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_order_book; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + order_book result; result.base = base; result.quote = quote; @@ -1418,7 +1467,10 @@ vector database_api_impl::get_top_markets(uint32_t limit)const { FC_ASSERT( _app_options && _app_options->has_market_history_plugin, "Market history plugin is not enabled." ); - FC_ASSERT( limit <= _app_options->api_limit_get_top_markets ); + const auto configured_limit = _app_options->api_limit_get_top_markets; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); const auto& volume_idx = _db.get_index_type().indices().get(); auto itr = volume_idx.rbegin(); @@ -1456,7 +1508,10 @@ vector database_api_impl::get_trade_history( const string& base, { FC_ASSERT( _app_options && _app_options->has_market_history_plugin, "Market history plugin is not enabled." ); - FC_ASSERT( limit <= _app_options->api_limit_get_trade_history ); + const auto configured_limit = _app_options->api_limit_get_trade_history; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); auto assets = lookup_asset_symbols( {base, quote} ); FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) ); @@ -1548,7 +1603,11 @@ vector database_api_impl::get_trade_history_by_sequence( { FC_ASSERT( _app_options && _app_options->has_market_history_plugin, "Market history plugin is not enabled." ); - FC_ASSERT( limit <= _app_options->api_limit_get_trade_history_by_sequence ); + const auto configured_limit = _app_options->api_limit_get_trade_history_by_sequence; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + FC_ASSERT( start >= 0 ); int64_t start_seq = -start; @@ -1682,7 +1741,12 @@ map database_api::lookup_witness_accounts( const string map database_api_impl::lookup_witness_accounts( const string& lower_bound_name, uint32_t limit )const { - FC_ASSERT( limit <= _app_options->api_limit_lookup_witness_accounts ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_lookup_witness_accounts; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + const auto& witnesses_by_id = _db.get_index_type().indices().get(); // we want to order witnesses by account name, but that name is in the account object @@ -1764,7 +1828,12 @@ map database_api::lookup_committee_member_acco map database_api_impl::lookup_committee_member_accounts( const string& lower_bound_name, uint32_t limit )const { - FC_ASSERT( limit <= _app_options->api_limit_lookup_committee_member_accounts ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_lookup_committee_member_accounts; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + const auto& committee_members_by_id = _db.get_index_type().indices().get(); // we want to order committee_members by account name, but that name is in the account object @@ -1879,7 +1948,11 @@ vector database_api::lookup_vote_ids( const vector& votes vector database_api_impl::lookup_vote_ids( const vector& votes )const { - FC_ASSERT( votes.size() < _app_options->api_limit_lookup_vote_ids ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_lookup_vote_ids; + FC_ASSERT( votes.size() <= configured_limit, + "Number of querying votes can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); const auto& witness_idx = _db.get_index_type().indices().get(); const auto& committee_idx = _db.get_index_type().indices().get(); @@ -2107,7 +2180,7 @@ bool database_api_impl::verify_account_authority( const string& account_name_or_ // Use a no-op lookup for custom authorities; we don't want it even if one does apply for our dummy op [](auto, auto, auto*) { return vector(); }, true, MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(_db.head_block_time()) ); - } + } catch (fc::exception& ex) { return false; @@ -2292,7 +2365,12 @@ vector database_api_impl::get_withdraw_permissions_b withdraw_permission_id_type start, uint32_t limit)const { - FC_ASSERT( limit <= _app_options->api_limit_get_withdraw_permissions_by_giver ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_withdraw_permissions_by_giver; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + vector result; const auto& withdraw_idx = _db.get_index_type().indices().get(); @@ -2321,7 +2399,12 @@ vector database_api_impl::get_withdraw_permissions_b withdraw_permission_id_type start, uint32_t limit)const { - FC_ASSERT( limit <= _app_options->api_limit_get_withdraw_permissions_by_recipient ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_withdraw_permissions_by_recipient; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + vector result; const auto& withdraw_idx = _db.get_index_type().indices().get(); @@ -2366,7 +2449,12 @@ vector database_api::get_htlc_by_from( const std::string account_id vector database_api_impl::get_htlc_by_from( const std::string account_id_or_name, htlc_id_type start, uint32_t limit ) const { - FC_ASSERT( limit <= _app_options->api_limit_get_htlc_by ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_htlc_by; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); + vector result; const auto& htlc_idx = _db.get_index_type< htlc_index >().indices().get< by_from_id >(); @@ -2391,8 +2479,12 @@ vector database_api::get_htlc_by_to( const std::string account_id_o vector database_api_impl::get_htlc_by_to( const std::string account_id_or_name, htlc_id_type start, uint32_t limit ) const { + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_htlc_by; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); - FC_ASSERT( limit <= _app_options->api_limit_get_htlc_by ); vector result; const auto& htlc_idx = _db.get_index_type< htlc_index >().indices().get< by_to_id >(); @@ -2415,7 +2507,11 @@ vector database_api::list_htlcs(const htlc_id_type start, uint32_t vector database_api_impl::list_htlcs(const htlc_id_type start, uint32_t limit) const { - FC_ASSERT( limit <= _app_options->api_limit_list_htlcs ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_list_htlcs; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); vector result; const auto& htlc_idx = _db.get_index_type().indices().get(); @@ -2497,8 +2593,11 @@ vector> database_api_impl::get_assets( const vec vector database_api_impl::get_limit_orders( const asset_id_type a, const asset_id_type b, const uint32_t limit )const { - uint64_t api_limit_get_limit_orders=_app_options->api_limit_get_limit_orders; - FC_ASSERT( limit <= api_limit_get_limit_orders ); + FC_ASSERT( _app_options, "Internal error" ); + const auto configured_limit = _app_options->api_limit_get_limit_orders; + FC_ASSERT( limit <= configured_limit, + "limit can not be greater than ${configured_limit}", + ("configured_limit", configured_limit) ); const auto& limit_order_idx = _db.get_index_type(); const auto& limit_price_idx = limit_order_idx.indices().get(); diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index e861ce2a98..92c08c266a 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -151,10 +151,10 @@ namespace graphene { namespace app { */ history_operation_detail get_account_history_by_operations( const std::string account_id_or_name, - vector operation_types, + flat_set operation_types, uint32_t start, unsigned limit - ); + )const; /** * @brief Get only asked operations relevant to the specified account From 13c0ff6173111a5b1b6115eb659abf67abffcc50 Mon Sep 17 00:00:00 2001 From: abitmore Date: Wed, 22 Apr 2020 20:54:37 +0000 Subject: [PATCH 37/47] Fix wallet and tests --- libraries/wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 2 +- tests/tests/history_api_tests.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 3f4d6bb10f..27ca70e6c7 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -241,7 +241,7 @@ class wallet_api * @returns account_history_operation_detail */ account_history_operation_detail get_account_history_by_operations( string name, - vector operation_types, + flat_set operation_types, uint32_t start, int limit); /** Returns the block chain's rapidly-changing properties. diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 3c73d7530f..ea2b6556e5 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -369,7 +369,7 @@ vector wallet_api::get_relative_account_history( account_history_operation_detail wallet_api::get_account_history_by_operations( string name, - vector operation_types, + flat_set operation_types, uint32_t start, int limit) { diff --git a/tests/tests/history_api_tests.cpp b/tests/tests/history_api_tests.cpp index e8856fda28..877762bc3b 100644 --- a/tests/tests/history_api_tests.cpp +++ b/tests/tests/history_api_tests.cpp @@ -744,7 +744,7 @@ BOOST_AUTO_TEST_CASE(api_limit_get_relative_account_history) { BOOST_AUTO_TEST_CASE(api_limit_get_account_history_by_operations) { try { graphene::app::history_api hist_api(app); - vector operation_types; + flat_set operation_types; //account_id_type() do 3 ops create_bitasset("USD", account_id_type()); create_account("dan"); From 850079ef46cb427d9f0bd09186a0f792a4d97728 Mon Sep 17 00:00:00 2001 From: abitmore Date: Wed, 22 Apr 2020 21:00:30 +0000 Subject: [PATCH 38/47] Fix cli_test --- tests/cli/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 58959f7c88..c51c69c56e 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -997,7 +997,7 @@ BOOST_AUTO_TEST_CASE( cli_multisig_transaction ) auto balances = con.wallet_api_ptr->list_account_balances( "cifer.test" ); for (auto b : balances) { if (b.asset_id == asset_id_type()) { - BOOST_ASSERT(b == asset(900000000 - 3000000)); + BOOST_CHECK(b == asset(900000000 - 3000000)); } } @@ -1572,7 +1572,7 @@ BOOST_FIXTURE_TEST_CASE(cli_use_authorized_transfer, cli_fixture) { BOOST_CHECK_EQUAL(charlie_balances.size(), 1); asset charlie_core_balance = charlie_balances.front(); asset expected_charlie_core_balance = transfer_amount; - BOOST_ASSERT(charlie_core_balance == expected_charlie_core_balance); + BOOST_CHECK(charlie_core_balance == expected_charlie_core_balance); // Check Bob's balances vector bob_balances = con.wallet_api_ptr->list_account_balances("bob"); From 25b96e718287fcc1dcda8499730731de51c39416 Mon Sep 17 00:00:00 2001 From: abitmore Date: Wed, 22 Apr 2020 21:07:48 +0000 Subject: [PATCH 39/47] Fix test case for api-limit-lookup-vote-ids --- tests/common/database_fixture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 5630b659d1..0ad8c1c0fb 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -223,7 +223,7 @@ database_fixture::database_fixture(const fc::time_point_sec &initial_timestamp) if(current_test_name =="api_limit_lookup_vote_ids") { options.insert(std::make_pair("api-limit-lookup-vote-ids", boost::program_options::variable_value - ((uint64_t)3, false))); + ((uint64_t)2, false))); } if(current_test_name =="api_limit_get_account_limit_orders") { From 0412a9c7bcf06fe1b0fd394b6e6eebe05093de55 Mon Sep 17 00:00:00 2001 From: Abit Date: Thu, 23 Apr 2020 17:22:41 +0200 Subject: [PATCH 40/47] Update comment --- libraries/chain/asset_evaluator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index dcff3f69fa..b7b4beb533 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -50,7 +50,7 @@ namespace detail { void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options) { if (block_time < HARDFORK_BSIP_81_TIME) { - // Taker fees should be zero until activation of BSIP81 + // Taker fees should not be set until activation of BSIP81 FC_ASSERT(!options.extensions.value.taker_fee_percent.valid(), "Taker fee percent should not be defined before HARDFORK_BSIP_81_TIME"); } From d04d9138b42a91d7b97a1451ac59ae14dce4e72d Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Fri, 24 Apr 2020 22:37:56 -0400 Subject: [PATCH 41/47] Add collateral-denominated fees container --- .../include/graphene/chain/asset_object.hpp | 22 +++++++++++++++++++ tests/common/database_fixture.cpp | 1 + 2 files changed, 23 insertions(+) diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 3541f87f5b..a3d5733390 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -65,6 +65,7 @@ namespace graphene { namespace chain { share_type current_supply; share_type confidential_supply; ///< total asset held in confidential balances share_type accumulated_fees; ///< fees accumulate to be paid out over time + share_type accumulated_collateral_fees; ///< accumulated collateral-denominated fees (for bitassets) share_type fee_pool; ///< in core asset }; @@ -184,6 +185,27 @@ namespace graphene { namespace chain { /// The tunable options for BitAssets are stored in this field. bitasset_options options; + /// Get reference to collateral asset object + template + const asset_object& collateral_asset(const DB& db) const + { return options.short_backing_asset(db); } + + /// Check collateral-denominated fees: + template + bool collateral_fees_are_zero(const DB& db) const + { return asset_id(db).dynamic_asset_data_id(db).accumulated_collateral_fees > 0; } + + /// Add to asset's collateral-denominated fees, validating that fee is compatible asset + template + void receive_collateral_fee(DB& db, const asset& fee) const + { + FC_ASSERT( fee.asset_id == options.short_backing_asset ); + const auto& asset_dyn_data = asset_id(db).dynamic_asset_data_id(db); + db.modify( asset_dyn_data, [&fee]( asset_dynamic_data_object& obj ){ + obj.accumulated_collateral_fees += fee.amount; + }); + } + /// Feeds published for this asset. If issuer is not committee, the keys in this map are the feed publishing /// accounts; otherwise, the feed publishers are the currently active committee_members and witnesses and this map /// should be treated as an implementation detail. The timestamp on each feed is the time it was published. diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 0ad8c1c0fb..abdc8e306a 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -529,6 +529,7 @@ void database_fixture::verify_asset_supplies( const database& db ) { const auto& bad = asset_obj.bitasset_data(db); total_balances[bad.options.short_backing_asset] += bad.settlement_fund; + total_balances[bad.options.short_backing_asset] += dasset_obj.accumulated_collateral_fees; } total_balances[asset_obj.id] += dasset_obj.confidential_supply.value; } From 43026a8dd3cd6c4f8aaca09b889e0ecc31751625 Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Sat, 25 Apr 2020 12:40:20 -0400 Subject: [PATCH 42/47] Add accumulate_fee() method to asset_object --- .../include/graphene/chain/asset_object.hpp | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index a3d5733390..28cbc70b2b 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -165,6 +165,37 @@ namespace graphene { namespace chain { template share_type reserved( const DB& db )const { return options.max_supply - dynamic_data(db).current_supply; } + + /*** + * @brief receive a fee asset to accrue in dynamic_data object + * + * @details Asset owners define various fees (market fees, force-settle fees, etc.) to + * be collected for the asset owners. These fees are typically denominated in the asset + * itself, but for bitassets some of the fees are denominated in the collateral + * asset. This will place the fee in the right container. + */ + template + void accumulate_fee(DB& db, const asset& fee) const + { + const auto& dyn_data = dynamic_asset_data_id(db); + if (fee.asset_id == get_id()) { // fee same as asset + db.modify( dyn_data, [&fee]( asset_dynamic_data_object& obj ){ + obj.accumulated_fees += fee.amount; + }); + } else { // fee different asset; perhaps collateral-denominated fee + FC_ASSERT( is_market_issued(), + "Asset ${a} (${id}) cannot accept fee of asset (${fid}).", + ("a",this->symbol)("id",this->id)("fid",fee.asset_id) ); + const auto & bad = bitasset_data(db); + FC_ASSERT( fee.asset_id == bad.options.short_backing_asset, + "Asset ${a} (${id}) cannot accept fee of asset (${fid}).", + ("a",this->symbol)("id",this->id)("fid",fee.asset_id) ); + db.modify( dyn_data, [&fee]( asset_dynamic_data_object& obj ){ + obj.accumulated_collateral_fees += fee.amount; + }); + } + } + }; /** @@ -185,27 +216,11 @@ namespace graphene { namespace chain { /// The tunable options for BitAssets are stored in this field. bitasset_options options; - /// Get reference to collateral asset object - template - const asset_object& collateral_asset(const DB& db) const - { return options.short_backing_asset(db); } - /// Check collateral-denominated fees: template bool collateral_fees_are_zero(const DB& db) const { return asset_id(db).dynamic_asset_data_id(db).accumulated_collateral_fees > 0; } - /// Add to asset's collateral-denominated fees, validating that fee is compatible asset - template - void receive_collateral_fee(DB& db, const asset& fee) const - { - FC_ASSERT( fee.asset_id == options.short_backing_asset ); - const auto& asset_dyn_data = asset_id(db).dynamic_asset_data_id(db); - db.modify( asset_dyn_data, [&fee]( asset_dynamic_data_object& obj ){ - obj.accumulated_collateral_fees += fee.amount; - }); - } - /// Feeds published for this asset. If issuer is not committee, the keys in this map are the feed publishing /// accounts; otherwise, the feed publishers are the currently active committee_members and witnesses and this map /// should be treated as an implementation detail. The timestamp on each feed is the time it was published. From 985733ea2bc8f6bf3056a338ed6af7225b6159e4 Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Sun, 26 Apr 2020 01:11:12 -0400 Subject: [PATCH 43/47] Add ability to claim collateral-denominated fees. --- libraries/chain/asset_evaluator.cpp | 42 +++++++++++++++---- .../include/graphene/chain/asset_object.hpp | 11 ++++- libraries/protocol/asset_ops.cpp | 1 + .../include/graphene/protocol/asset_ops.hpp | 19 +++++++-- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index b7b4beb533..2fe7e9f112 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -920,25 +920,51 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope } FC_CAPTURE_AND_RETHROW((o)) } - +/*** + * @brief evaluator for asset_claim_fees operation + * + * Checks that we are able to claim fees denominated in asset Y (the amount_to_claim asset), + * from some container asset X which is presumed to have accumulated the fees we wish to claim. + * The container asset is either explicitly named in the extensions, or else assumed as the same + * asset as the amount_to_claim asset. Evaluation fails if either (a) operation issuer is not + * the same as the container_asset issuer, or (b) container_asset has no fee bucket for + * amount_to_claim asset. + */ void_result asset_claim_fees_evaluator::do_evaluate( const asset_claim_fees_operation& o ) { try { - FC_ASSERT( o.amount_to_claim.asset_id(db()).issuer == o.issuer, "Asset fees may only be claimed by the issuer" ); + const asset_object & container_asset = o.extensions.claim_from_asset_id.valid() ? + (*o.extensions.claim_from_asset_id)(db()) : o.amount_to_claim.asset_id(db()); + FC_ASSERT( container_asset.issuer == o.issuer, "Asset fees may only be claimed by the issuer" ); + FC_ASSERT( container_asset.can_accumulate_fee(db(),o.amount_to_claim), + "Asset ${a} (${id}) is not backed by asset (${fid}) and does not hold it as fees.", + ("a",container_asset.symbol)("id",container_asset.id)("fid",o.amount_to_claim.asset_id) ); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } +/*** + * @brief apply asset_claim_fees operation + */ void_result asset_claim_fees_evaluator::do_apply( const asset_claim_fees_operation& o ) { try { database& d = db(); - const asset_object& a = o.amount_to_claim.asset_id(d); - const asset_dynamic_data_object& addo = a.dynamic_asset_data_id(d); - FC_ASSERT( o.amount_to_claim.amount <= addo.accumulated_fees, "Attempt to claim more fees than have accumulated", ("addo",addo) ); + const asset_object & c = o.extensions.claim_from_asset_id.valid() ? + (*o.extensions.claim_from_asset_id)(d) : o.amount_to_claim.asset_id(d); + const asset_dynamic_data_object& ddo = c.dynamic_asset_data_id(d); + const asset_object & a = o.amount_to_claim.asset_id(d); - d.modify( addo, [&]( asset_dynamic_data_object& _addo ) { - _addo.accumulated_fees -= o.amount_to_claim.amount; - }); + if ( c.get_id() == a.get_id() ) { + FC_ASSERT( o.amount_to_claim.amount <= ddo.accumulated_fees, "Attempt to claim more fees than have accumulated", ("ddo",ddo) ); + d.modify( ddo, [&]( asset_dynamic_data_object& _addo ) { + _addo.accumulated_fees -= o.amount_to_claim.amount; + }); + } else { + FC_ASSERT( o.amount_to_claim.amount <= ddo.accumulated_collateral_fees, "Attempt to claim more fees than have accumulated", ("ddo",ddo) ); + d.modify( ddo, [&]( asset_dynamic_data_object& _addo ) { + _addo.accumulated_collateral_fees -= o.amount_to_claim.amount; + }); + } d.adjust_balance( o.issuer, o.amount_to_claim ); diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 28cbc70b2b..55dc07d5d3 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -166,11 +166,18 @@ namespace graphene { namespace chain { share_type reserved( const DB& db )const { return options.max_supply - dynamic_data(db).current_supply; } + /// @return true if asset can accumulate fees in the given denomination + template + bool can_accumulate_fee(const DB& db, const asset& fee) const { + return (( fee.asset_id == get_id() ) || + ( is_market_issued() && fee.asset_id == bitasset_data(db).options.short_backing_asset )); + } + /*** * @brief receive a fee asset to accrue in dynamic_data object * - * @details Asset owners define various fees (market fees, force-settle fees, etc.) to - * be collected for the asset owners. These fees are typically denominated in the asset + * Asset owners define various fees (market fees, force-settle fees, etc.) to be + * collected for the asset owners. These fees are typically denominated in the asset * itself, but for bitassets some of the fees are denominated in the collateral * asset. This will place the fee in the right container. */ diff --git a/libraries/protocol/asset_ops.cpp b/libraries/protocol/asset_ops.cpp index 90c1e2917a..43b6a8d824 100644 --- a/libraries/protocol/asset_ops.cpp +++ b/libraries/protocol/asset_ops.cpp @@ -271,6 +271,7 @@ GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_settle_oper GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_fund_fee_pool_operation::fee_parameters_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_pool_operation::fee_parameters_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::fee_parameters_type ) +GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::additional_options_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_operation::fee_parameters_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_issuer_operation::fee_parameters_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_bitasset_operation::fee_parameters_type ) diff --git a/libraries/protocol/include/graphene/protocol/asset_ops.hpp b/libraries/protocol/include/graphene/protocol/asset_ops.hpp index 858c25b512..86eedbcce6 100644 --- a/libraries/protocol/include/graphene/protocol/asset_ops.hpp +++ b/libraries/protocol/include/graphene/protocol/asset_ops.hpp @@ -446,10 +446,20 @@ namespace graphene { namespace protocol { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; }; + struct additional_options_type + { + /// Which asset to claim fees from. This is needed, e.g., to claim collateral- + /// denominated fees from a collateral-backed smart asset. If unset, assumed to be same + /// asset as amount_to_claim is denominated in, such as would be the case when claiming + /// market fees. + fc::optional claim_from_asset_id; + }; + asset fee; - account_id_type issuer; - asset amount_to_claim; /// amount_to_claim.asset_id->issuer must == issuer - extensions_type extensions; + account_id_type issuer; /// must match issuer of asset from which we claim fees + asset amount_to_claim; + + additional_options_type extensions; account_id_type fee_payer()const { return issuer; } void validate()const; @@ -521,6 +531,8 @@ namespace graphene { namespace protocol { FC_REFLECT( graphene::protocol::asset_claim_fees_operation, (fee)(issuer)(amount_to_claim)(extensions) ) FC_REFLECT( graphene::protocol::asset_claim_fees_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::protocol::asset_claim_fees_operation::additional_options_type, (claim_from_asset_id) ) + FC_REFLECT( graphene::protocol::asset_claim_pool_operation, (fee)(issuer)(asset_id)(amount_to_claim)(extensions) ) FC_REFLECT( graphene::protocol::asset_claim_pool_operation::fee_parameters_type, (fee) ) @@ -619,6 +631,7 @@ GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_settle_operat GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_fund_fee_pool_operation::fee_parameters_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_pool_operation::fee_parameters_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::fee_parameters_type ) +GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_claim_fees_operation::additional_options_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_operation::fee_parameters_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_issuer_operation::fee_parameters_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::asset_update_bitasset_operation::fee_parameters_type ) From d116fc8011a170692c5330a35e67d1f38e1f349c Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Mon, 27 Apr 2020 00:56:08 -0400 Subject: [PATCH 44/47] Check collateral fees empty before changing backing asset. --- libraries/chain/asset_evaluator.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 2fe7e9f112..0e4bcee14e 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -455,6 +455,9 @@ void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bita FC_ASSERT( asset_obj.dynamic_asset_data_id(d).current_supply == 0, "Cannot update a bitasset if there is already a current supply." ); + FC_ASSERT( asset_obj.dynamic_asset_data_id(d).accumulated_collateral_fees == 0, + "Must claim collateral-denominated fees before changing backing asset." ); + const asset_object& new_backing_asset = op.new_options.short_backing_asset(d); // check if the asset exists if( after_hf_core_922_931 ) From f608039a7e1ca5ee92b64b4dc94e690bfb3983ee Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Mon, 27 Apr 2020 11:58:21 -0400 Subject: [PATCH 45/47] Hardfork protection on asset_claim_fees extension. --- libraries/chain/asset_evaluator.cpp | 11 +++++++++-- .../chain/hardfork.d/CORE_BSIP_87_74_COLLATFEE.hf | 7 +++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 libraries/chain/hardfork.d/CORE_BSIP_87_74_COLLATFEE.hf diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 0e4bcee14e..cf6e6dc997 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -935,10 +935,17 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope */ void_result asset_claim_fees_evaluator::do_evaluate( const asset_claim_fees_operation& o ) { try { + database& d = db(); + + // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes: + FC_ASSERT( !o.extensions.claim_from_asset_id.valid() || + d.head_block_time() >= HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME, + "Collateral-denominated fees are not yet active and therefore cannot be claimed." ); + const asset_object & container_asset = o.extensions.claim_from_asset_id.valid() ? - (*o.extensions.claim_from_asset_id)(db()) : o.amount_to_claim.asset_id(db()); + (*o.extensions.claim_from_asset_id)(d) : o.amount_to_claim.asset_id(d); FC_ASSERT( container_asset.issuer == o.issuer, "Asset fees may only be claimed by the issuer" ); - FC_ASSERT( container_asset.can_accumulate_fee(db(),o.amount_to_claim), + FC_ASSERT( container_asset.can_accumulate_fee(d,o.amount_to_claim), "Asset ${a} (${id}) is not backed by asset (${fid}) and does not hold it as fees.", ("a",container_asset.symbol)("id",container_asset.id)("fid",o.amount_to_claim.asset_id) ); return void_result(); diff --git a/libraries/chain/hardfork.d/CORE_BSIP_87_74_COLLATFEE.hf b/libraries/chain/hardfork.d/CORE_BSIP_87_74_COLLATFEE.hf new file mode 100644 index 0000000000..3f1add317a --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_BSIP_87_74_COLLATFEE.hf @@ -0,0 +1,7 @@ +// This hardfork enables the extension to asset_claim_fees_operation to claim collateral-denominated fees. +// These fees are collected by BSIPs 87 and 74. This should be set to match the earlier of either +// HARDFORK_CORE_BSIP87_TIME or HARDFORK_CORE_BSIP74_TIME. +// This hardfork check should be removable after the hardfork date passes. +#ifndef HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME +#define HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME (fc::time_point_sec( 1679955066 ) ) // Temporary date until actual hardfork date is set +#endif From 0caf73eac3e9e35b9557e6582d6ca91cf83de08d Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Mon, 27 Apr 2020 15:48:20 -0400 Subject: [PATCH 46/47] Proposal hardfork guards on asset_claim_fees_operation --- libraries/chain/asset_evaluator.cpp | 16 +++++++++++----- libraries/chain/proposal_evaluator.cpp | 5 +++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index cf6e6dc997..9943b76505 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -55,7 +55,16 @@ namespace detail { "Taker fee percent should not be defined before HARDFORK_BSIP_81_TIME"); } } -} + + void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time, const asset_claim_fees_operation& op) + { + // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes: + FC_ASSERT( !op.extensions.claim_from_asset_id.valid() || + block_time >= HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME, + "Collateral-denominated fees are not yet active and therefore cannot be claimed." ); + } + +} // graphene::chain::detail void_result asset_create_evaluator::do_evaluate( const asset_create_operation& op ) { try { @@ -937,10 +946,7 @@ void_result asset_claim_fees_evaluator::do_evaluate( const asset_claim_fees_oper { try { database& d = db(); - // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes: - FC_ASSERT( !o.extensions.claim_from_asset_id.valid() || - d.head_block_time() >= HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME, - "Collateral-denominated fees are not yet active and therefore cannot be claimed." ); + detail::check_asset_claim_fees_hardfork_87_74_collatfee(d.head_block_time(), o); // HF_REMOVABLE const asset_object & container_asset = o.extensions.claim_from_asset_id.valid() ? (*o.extensions.claim_from_asset_id)(d) : o.amount_to_claim.asset_id(d); diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index a6a25d32b6..31d21bbd60 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -31,6 +31,7 @@ namespace graphene { namespace chain { namespace detail { void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options); void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options); + void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time, const asset_claim_fees_operation& op); } struct proposal_operation_hardfork_visitor @@ -61,6 +62,10 @@ struct proposal_operation_hardfork_visitor detail::check_asset_options_hf_bsip81(block_time, v.new_options); } + void operator()(const graphene::chain::asset_claim_fees_operation &v) const { + detail::check_asset_claim_fees_hardfork_87_74_collatfee(block_time, v); // HF_REMOVABLE + } + void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const { if (block_time < HARDFORK_CORE_1468_TIME) { FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(), "Unable to set HTLC options before hardfork 1468"); From 02f9da880c9af6f017ddba6382b924d5cb83e386 Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Sat, 2 May 2020 17:02:42 -0400 Subject: [PATCH 47/47] Changes requested in code review --- libraries/chain/asset_evaluator.cpp | 39 ++++++++++--------- libraries/chain/asset_object.cpp | 2 +- .../graphene/chain/asset_evaluator.hpp | 3 ++ .../include/graphene/chain/asset_object.hpp | 7 +--- libraries/protocol/asset_ops.cpp | 2 + .../include/graphene/protocol/asset_ops.hpp | 7 ++-- 6 files changed, 33 insertions(+), 27 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 9943b76505..0f32a9a32b 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -59,7 +59,7 @@ namespace detail { void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time, const asset_claim_fees_operation& op) { // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes: - FC_ASSERT( !op.extensions.claim_from_asset_id.valid() || + FC_ASSERT( !op.extensions.value.claim_from_asset_id.valid() || block_time >= HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME, "Collateral-denominated fees are not yet active and therefore cannot be claimed." ); } @@ -940,20 +940,30 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope * The container asset is either explicitly named in the extensions, or else assumed as the same * asset as the amount_to_claim asset. Evaluation fails if either (a) operation issuer is not * the same as the container_asset issuer, or (b) container_asset has no fee bucket for - * amount_to_claim asset. + * amount_to_claim asset, or (c) accumulated fees are insufficient to cover amount claimed. */ void_result asset_claim_fees_evaluator::do_evaluate( const asset_claim_fees_operation& o ) { try { - database& d = db(); + const database& d = db(); detail::check_asset_claim_fees_hardfork_87_74_collatfee(d.head_block_time(), o); // HF_REMOVABLE - const asset_object & container_asset = o.extensions.claim_from_asset_id.valid() ? - (*o.extensions.claim_from_asset_id)(d) : o.amount_to_claim.asset_id(d); - FC_ASSERT( container_asset.issuer == o.issuer, "Asset fees may only be claimed by the issuer" ); - FC_ASSERT( container_asset.can_accumulate_fee(d,o.amount_to_claim), + container_asset = o.extensions.value.claim_from_asset_id.valid() ? + &(*o.extensions.value.claim_from_asset_id)(d) : &o.amount_to_claim.asset_id(d); + + FC_ASSERT( container_asset->issuer == o.issuer, "Asset fees may only be claimed by the issuer" ); + FC_ASSERT( container_asset->can_accumulate_fee(d,o.amount_to_claim), "Asset ${a} (${id}) is not backed by asset (${fid}) and does not hold it as fees.", - ("a",container_asset.symbol)("id",container_asset.id)("fid",o.amount_to_claim.asset_id) ); + ("a",container_asset->symbol)("id",container_asset->id)("fid",o.amount_to_claim.asset_id) ); + + container_ddo = &container_asset->dynamic_asset_data_id(d); + + FC_ASSERT( o.amount_to_claim.amount <= ((container_asset->get_id() == o.amount_to_claim.asset_id) ? + container_ddo->accumulated_fees : + container_ddo->accumulated_collateral_fees), + "Attempt to claim more fees than have accumulated within asset ${a} (${id})", + ("a",container_asset->symbol)("id",container_asset->id)("ddo",*container_ddo) ); + return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } @@ -965,19 +975,12 @@ void_result asset_claim_fees_evaluator::do_apply( const asset_claim_fees_operati { try { database& d = db(); - const asset_object & c = o.extensions.claim_from_asset_id.valid() ? - (*o.extensions.claim_from_asset_id)(d) : o.amount_to_claim.asset_id(d); - const asset_dynamic_data_object& ddo = c.dynamic_asset_data_id(d); - const asset_object & a = o.amount_to_claim.asset_id(d); - - if ( c.get_id() == a.get_id() ) { - FC_ASSERT( o.amount_to_claim.amount <= ddo.accumulated_fees, "Attempt to claim more fees than have accumulated", ("ddo",ddo) ); - d.modify( ddo, [&]( asset_dynamic_data_object& _addo ) { + if ( container_asset->get_id() == o.amount_to_claim.asset_id ) { + d.modify( *container_ddo, [&o]( asset_dynamic_data_object& _addo ) { _addo.accumulated_fees -= o.amount_to_claim.amount; }); } else { - FC_ASSERT( o.amount_to_claim.amount <= ddo.accumulated_collateral_fees, "Attempt to claim more fees than have accumulated", ("ddo",ddo) ); - d.modify( ddo, [&]( asset_dynamic_data_object& _addo ) { + d.modify( *container_ddo, [&o]( asset_dynamic_data_object& _addo ) { _addo.accumulated_collateral_fees -= o.amount_to_claim.amount; }); } diff --git a/libraries/chain/asset_object.cpp b/libraries/chain/asset_object.cpp index 50e874f858..ce74e934b4 100644 --- a/libraries/chain/asset_object.cpp +++ b/libraries/chain/asset_object.cpp @@ -178,7 +178,7 @@ string asset_object::amount_to_string(share_type amount) const } FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::asset_dynamic_data_object, (graphene::db::object), - (current_supply)(confidential_supply)(accumulated_fees)(fee_pool) ) + (current_supply)(confidential_supply)(accumulated_fees)(accumulated_collateral_fees)(fee_pool) ) FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::asset_bitasset_data_object, (graphene::db::object), (asset_id) diff --git a/libraries/chain/include/graphene/chain/asset_evaluator.hpp b/libraries/chain/include/graphene/chain/asset_evaluator.hpp index 544b4b8b21..068f2cf93e 100644 --- a/libraries/chain/include/graphene/chain/asset_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/asset_evaluator.hpp @@ -166,6 +166,9 @@ namespace graphene { namespace chain { void_result do_evaluate( const asset_claim_fees_operation& o ); void_result do_apply( const asset_claim_fees_operation& o ); + + const asset_object* container_asset = nullptr; + const asset_dynamic_data_object* container_ddo = nullptr; }; class asset_claim_pool_evaluator : public evaluator diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 55dc07d5d3..4e6fccee83 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -184,6 +184,8 @@ namespace graphene { namespace chain { template void accumulate_fee(DB& db, const asset& fee) const { + if (fee.amount == 0) return; + FC_ASSERT( fee.amount >= 0, "Fee amount must be non-negative." ); const auto& dyn_data = dynamic_asset_data_id(db); if (fee.asset_id == get_id()) { // fee same as asset db.modify( dyn_data, [&fee]( asset_dynamic_data_object& obj ){ @@ -223,11 +225,6 @@ namespace graphene { namespace chain { /// The tunable options for BitAssets are stored in this field. bitasset_options options; - /// Check collateral-denominated fees: - template - bool collateral_fees_are_zero(const DB& db) const - { return asset_id(db).dynamic_asset_data_id(db).accumulated_collateral_fees > 0; } - /// Feeds published for this asset. If issuer is not committee, the keys in this map are the feed publishing /// accounts; otherwise, the feed publishers are the currently active committee_members and witnesses and this map /// should be treated as an implementation detail. The timestamp on each feed is the time it was published. diff --git a/libraries/protocol/asset_ops.cpp b/libraries/protocol/asset_ops.cpp index 43b6a8d824..8ba6aa2b26 100644 --- a/libraries/protocol/asset_ops.cpp +++ b/libraries/protocol/asset_ops.cpp @@ -251,6 +251,8 @@ void asset_options::validate()const void asset_claim_fees_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount_to_claim.amount > 0 ); + if( extensions.value.claim_from_asset_id.valid() ) + FC_ASSERT( *extensions.value.claim_from_asset_id != amount_to_claim.asset_id ); } void asset_claim_pool_operation::validate()const { diff --git a/libraries/protocol/include/graphene/protocol/asset_ops.hpp b/libraries/protocol/include/graphene/protocol/asset_ops.hpp index 86eedbcce6..9aa410d4ca 100644 --- a/libraries/protocol/include/graphene/protocol/asset_ops.hpp +++ b/libraries/protocol/include/graphene/protocol/asset_ops.hpp @@ -451,15 +451,16 @@ namespace graphene { namespace protocol { /// Which asset to claim fees from. This is needed, e.g., to claim collateral- /// denominated fees from a collateral-backed smart asset. If unset, assumed to be same /// asset as amount_to_claim is denominated in, such as would be the case when claiming - /// market fees. + /// market fees. If set, validation requires it to be a different asset_id than + /// amount_to_claim (else there would exist two ways to form the same request). fc::optional claim_from_asset_id; }; asset fee; - account_id_type issuer; /// must match issuer of asset from which we claim fees + account_id_type issuer; ///< must match issuer of asset from which we claim fees asset amount_to_claim; - additional_options_type extensions; + extension extensions; account_id_type fee_payer()const { return issuer; } void validate()const;