From 5d6091e58190cfa8ab1d8cf224d603c5242fc670 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 27 Oct 2015 11:05:59 -0400 Subject: [PATCH 1/3] HARDFORK - adding operation to claim asset fees #413 --- libraries/app/impacted.cpp | 1 + libraries/chain/asset_evaluator.cpp | 27 +++++++++++++++++++ libraries/chain/db_init.cpp | 1 + .../graphene/chain/asset_evaluator.hpp | 9 +++++++ .../chain/include/graphene/chain/hardfork.hpp | 2 -- .../graphene/chain/protocol/asset_ops.hpp | 22 +++++++++++++++ .../graphene/chain/protocol/operations.hpp | 4 +-- libraries/chain/market_evaluator.cpp | 1 + libraries/chain/protocol/asset_ops.cpp | 5 ++++ 9 files changed, 68 insertions(+), 4 deletions(-) diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index f5ed1051..e928a369 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -39,6 +39,7 @@ struct get_impacted_account_visitor _impacted.insert( op.to ); } + void operator()( const asset_claim_fees_operation& op ){} void operator()( const limit_order_create_operation& op ) {} void operator()( const limit_order_cancel_operation& op ) { diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 2fe586da..bc84cefd 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -514,4 +514,31 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope return void_result(); } FC_CAPTURE_AND_RETHROW((o)) } + + +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" ); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (o) ) } + + +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) ); + + d.modify( addo, [&]( asset_dynamic_data_object& _addo ) { + _addo.accumulated_fees -= o.amount_to_claim.amount; + }); + + d.adjust_balance( o.issuer, o.amount_to_claim ); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( (o) ) } + + } } // graphene::chain diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 2d06aa94..ba2d07a6 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -158,6 +158,7 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); } void database::initialize_indexes() diff --git a/libraries/chain/include/graphene/chain/asset_evaluator.hpp b/libraries/chain/include/graphene/chain/asset_evaluator.hpp index 7e5b9268..d302450e 100644 --- a/libraries/chain/include/graphene/chain/asset_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/asset_evaluator.hpp @@ -133,4 +133,13 @@ namespace graphene { namespace chain { std::map,price_feed> median_feed_values; }; + class asset_claim_fees_evaluator : public evaluator + { + public: + typedef asset_claim_fees_operation operation_type; + + void_result do_evaluate( const asset_claim_fees_operation& o ); + void_result do_apply( const asset_claim_fees_operation& o ); + }; + } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/hardfork.hpp b/libraries/chain/include/graphene/chain/hardfork.hpp index 01907932..e5da0a01 100644 --- a/libraries/chain/include/graphene/chain/hardfork.hpp +++ b/libraries/chain/include/graphene/chain/hardfork.hpp @@ -20,5 +20,3 @@ */ #pragma once -#define HARDFORK_357_TIME (fc::time_point_sec( 1444416300 )) -#define HARDFORK_359_TIME (fc::time_point_sec( 1444416300 )) diff --git a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp index e0cd26eb..f81f39f7 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp @@ -420,8 +420,30 @@ namespace graphene { namespace chain { void validate()const; }; + /** + * @brief used to transfer accumulated fees back to the issuer's balance. + */ + struct asset_claim_fees_operation : public base_operation + { + struct fee_parameters_type { + uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + + asset fee; + account_id_type issuer; + asset amount_to_claim; /// amount_to_claim.asset_id->issuer must == issuer + extensions_type extensions; + + account_id_type fee_payer()const { return issuer; } + void validate()const; + }; + + } } // graphene::chain +FC_REFLECT( graphene::chain::asset_claim_fees_operation, (fee)(issuer)(amount_to_claim)(extensions) ) +FC_REFLECT( graphene::chain::asset_claim_fees_operation::fee_parameters_type, (fee) ) + FC_REFLECT( graphene::chain::asset_options, (max_supply) (market_fee_percent) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 9ef0c549..55cfdf93 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -85,8 +85,8 @@ namespace graphene { namespace chain { transfer_to_blind_operation, blind_transfer_operation, transfer_from_blind_operation, - - asset_settle_cancel_operation // VIRTUAL + asset_settle_cancel_operation, // VIRTUAL + asset_claim_fees_operation > operation; /// @} // operations group diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index a0eeac97..723735fa 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -95,6 +95,7 @@ asset limit_order_cancel_evaluator::do_apply(const limit_order_cancel_operation& auto quote_asset = _order->sell_price.quote.asset_id; auto refunded = _order->amount_for_sale(); + /// TODO... implement this refund in a better way if( true ) // HARD FORK d.head_block_time() > { const auto& fees = d.current_fee_schedule(); diff --git a/libraries/chain/protocol/asset_ops.cpp b/libraries/chain/protocol/asset_ops.cpp index b73a0b82..bf0c126a 100644 --- a/libraries/chain/protocol/asset_ops.cpp +++ b/libraries/chain/protocol/asset_ops.cpp @@ -224,4 +224,9 @@ void asset_options::validate()const } } +void asset_claim_fees_operation::validate()const { + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( amount_to_claim.amount > 0 ); +} + } } // namespace graphene::chain From 966b3edf0c777906e8a75772d8176d87ebe10797 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Fri, 30 Oct 2015 13:02:44 -0400 Subject: [PATCH 2/3] Add hardfork logic #413 --- libraries/chain/asset_evaluator.cpp | 1 + libraries/chain/include/graphene/chain/hardfork.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index bc84cefd..889d9f25 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -518,6 +518,7 @@ 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 { + FC_ASSERT( db().head_block_time() > HARDFORK_413_TIME ); FC_ASSERT( o.amount_to_claim.asset_id(db()).issuer == o.issuer, "Asset fees may only be claimed by the issuer" ); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } diff --git a/libraries/chain/include/graphene/chain/hardfork.hpp b/libraries/chain/include/graphene/chain/hardfork.hpp index e5da0a01..2c557108 100644 --- a/libraries/chain/include/graphene/chain/hardfork.hpp +++ b/libraries/chain/include/graphene/chain/hardfork.hpp @@ -20,3 +20,4 @@ */ #pragma once +#define HARDFORK_413_TIME (fc::time_point_sec( 1446652800 )) From ef1c721690327b80c435964ce876e17a9fbfe0b0 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Fri, 30 Oct 2015 16:58:56 -0400 Subject: [PATCH 3/3] fee_tests.cpp: Implement asset_claim_fees_test #413 --- tests/tests/fee_tests.cpp | 133 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index ce69abad..e65cb92b 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -67,6 +68,138 @@ BOOST_AUTO_TEST_CASE( nonzero_fee_test ) } } +BOOST_AUTO_TEST_CASE(asset_claim_fees_test) +{ + try + { + ACTORS((alice)(bob)(izzy)(jill)); + // Izzy issues asset to Alice + // Jill issues asset to Bob + // Alice and Bob trade in the market and pay fees + // Verify that Izzy and Jill can claim the fees + + const share_type core_prec = asset::scaled_precision( asset_id_type()(db).precision ); + + // Return number of core shares (times precision) + auto _core = [&]( int64_t x ) -> asset + { return asset( x*core_prec ); }; + + transfer( committee_account, alice_id, _core(1000000) ); + transfer( committee_account, bob_id, _core(1000000) ); + transfer( committee_account, izzy_id, _core(1000000) ); + transfer( committee_account, jill_id, _core(1000000) ); + + asset_id_type izzycoin_id = create_bitasset( "IZZYCOIN", izzy_id, GRAPHENE_1_PERCENT, charge_market_fee ).id; + asset_id_type jillcoin_id = create_bitasset( "JILLCOIN", jill_id, 2*GRAPHENE_1_PERCENT, charge_market_fee ).id; + + const share_type izzy_prec = asset::scaled_precision( asset_id_type(izzycoin_id)(db).precision ); + const share_type jill_prec = asset::scaled_precision( asset_id_type(jillcoin_id)(db).precision ); + + auto _izzy = [&]( int64_t x ) -> asset + { return asset( x*izzy_prec, izzycoin_id ); }; + auto _jill = [&]( int64_t x ) -> asset + { return asset( x*jill_prec, jillcoin_id ); }; + + update_feed_producers( izzycoin_id(db), { izzy_id } ); + update_feed_producers( jillcoin_id(db), { jill_id } ); + + const asset izzy_satoshi = asset(1, izzycoin_id); + const asset jill_satoshi = asset(1, jillcoin_id); + + // Izzycoin is worth 100 BTS + price_feed feed; + feed.settlement_price = price( _izzy(1), _core(100) ); + feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; + feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; + publish_feed( izzycoin_id(db), izzy, feed ); + + // Jillcoin is worth 30 BTS + feed.settlement_price = price( _jill(1), _core(30) ); + feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; + feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; + publish_feed( jillcoin_id(db), jill, feed ); + + enable_fees(); + + // Alice and Bob create some coins + borrow( alice_id, _izzy( 200), _core( 60000) ); + borrow( bob_id, _jill(2000), _core(180000) ); + + // Alice and Bob place orders which match + create_sell_order( alice_id, _izzy(100), _jill(300) ); // Alice is willing to sell her Izzy's for 3 Jill + create_sell_order( bob_id, _jill(700), _izzy(200) ); // Bob is buying up to 200 Izzy's for up to 3.5 Jill + + // 100 Izzys and 300 Jills are matched, so the fees should be + // 1 Izzy (1%) and 6 Jill (2%). + + auto claim_fees = [&]( account_id_type issuer, asset amount_to_claim ) + { + asset_claim_fees_operation claim_op; + claim_op.issuer = issuer; + claim_op.amount_to_claim = amount_to_claim; + signed_transaction tx; + tx.operations.push_back( claim_op ); + db.current_fee_schedule().set_fee( tx.operations.back() ); + set_expiration( db, tx ); + fc::ecc::private_key my_pk = (issuer == izzy_id) ? izzy_private_key : jill_private_key; + fc::ecc::private_key your_pk = (issuer == izzy_id) ? jill_private_key : izzy_private_key; + sign( tx, your_pk ); + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), fc::exception ); + tx.signatures.clear(); + sign( tx, my_pk ); + PUSH_TX( db, tx ); + }; + + { + const asset_object& izzycoin = izzycoin_id(db); + const asset_object& jillcoin = jillcoin_id(db); + + //wdump( (izzycoin)(izzycoin.dynamic_asset_data_id(db))((*izzycoin.bitasset_data_id)(db)) ); + //wdump( (jillcoin)(jillcoin.dynamic_asset_data_id(db))((*jillcoin.bitasset_data_id)(db)) ); + + // check the correct amount of fees has been awarded + BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == _izzy(1).amount ); + BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(6).amount ); + + } + + { + // can't claim before hardfork + GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, _izzy(1) ), fc::exception ); + generate_blocks( HARDFORK_413_TIME ); + while( db.head_block_time() <= HARDFORK_413_TIME ) + { + generate_block(); + } + } + + { + const asset_object& izzycoin = izzycoin_id(db); + const asset_object& jillcoin = jillcoin_id(db); + + // can't claim more than balance + GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, _izzy(1) + izzy_satoshi ), fc::exception ); + GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, _jill(6) + jill_satoshi ), fc::exception ); + + // can't claim asset that doesn't belong to you + GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, izzy_satoshi ), fc::exception ); + GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, jill_satoshi ), fc::exception ); + + // can claim asset in one go + claim_fees( izzy_id, _izzy(1) ); + GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, izzy_satoshi ), fc::exception ); + BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == _izzy(0).amount ); + + // can claim in multiple goes + claim_fees( jill_id, _jill(4) ); + BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(2).amount ); + GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, _jill(2) + jill_satoshi ), fc::exception ); + claim_fees( jill_id, _jill(2) ); + BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(0).amount ); + } + } + FC_LOG_AND_RETHROW() +} /////////////////////////////////////////////////////////////// // cashback_test infrastructure //