From 895509a75c75f6a39285a8c23eb5dacc05dfe0a8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 20 Sep 2019 12:55:12 -0500 Subject: [PATCH 01/81] Add memo to htlc --- libraries/chain/htlc_evaluator.cpp | 2 ++ .../chain/include/graphene/chain/htlc_object.hpp | 2 ++ .../protocol/include/graphene/protocol/htlc.hpp | 13 +++++++++++-- tests/tests/htlc_tests.cpp | 5 +++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 1009a75c53..7196b88aaf 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -74,6 +74,8 @@ namespace graphene { esc.conditions.hash_lock.preimage_hash = o.preimage_hash; esc.conditions.hash_lock.preimage_size = o.preimage_size; esc.conditions.time_lock.expiration = dbase.head_block_time() + o.claim_period_seconds; + if ( o.extensions.value.memo.valid() ) + esc.memo = o.extensions.value.memo; }); return esc.id; diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 6679c6b636..a9f8cf5f43 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -59,6 +59,8 @@ namespace graphene { namespace chain { } time_lock; } conditions; + fc::optional memo; + /**** * Index helper for timelock */ diff --git a/libraries/protocol/include/graphene/protocol/htlc.hpp b/libraries/protocol/include/graphene/protocol/htlc.hpp index ac1732cd9d..bfd9c286e7 100644 --- a/libraries/protocol/include/graphene/protocol/htlc.hpp +++ b/libraries/protocol/include/graphene/protocol/htlc.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include // std::max namespace graphene { namespace protocol { @@ -45,6 +46,7 @@ namespace graphene { namespace protocol { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; + // paid to network asset fee; // where the held monies are to come from @@ -59,8 +61,13 @@ namespace graphene { namespace protocol { uint16_t preimage_size; // The time the funds will be returned to the source if not claimed uint32_t claim_period_seconds; - // for future expansion - extensions_type extensions; + + // additional extensions + struct additional_options_type + { + fc::optional memo; + }; + extension extensions; /*** * @brief Does simple validation of this object @@ -192,6 +199,7 @@ namespace graphene { namespace protocol { FC_REFLECT_TYPENAME( graphene::protocol::htlc_hash ) FC_REFLECT( graphene::protocol::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) +FC_REFLECT( graphene::protocol::htlc_create_operation::additional_options_type, (memo)) FC_REFLECT( graphene::protocol::htlc_redeem_operation::fee_parameters_type, (fee) (fee_per_kb) ) FC_REFLECT( graphene::protocol::htlc_redeemed_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::protocol::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) @@ -205,6 +213,7 @@ FC_REFLECT( graphene::protocol::htlc_extend_operation, (fee)(htlc_id)(update_iss FC_REFLECT( graphene::protocol::htlc_refund_operation, (fee)(htlc_id)(to)) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation::fee_parameters_type ) +GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation::additional_options_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_redeem_operation::fee_parameters_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_extend_operation::fee_parameters_type ) GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation ) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index fda3ab2fb9..ce581896cd 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -101,6 +101,11 @@ try { create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + create_operation.extensions.value.memo = memo_data(); + create_operation.extensions.value.memo->from = alice_public_key; + create_operation.extensions.value.memo->to = bob_public_key; + create_operation.extensions.value.memo->set_message( + alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); From 04cccbcaf2fb8976d8bad3790044a90021a79cd3 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 20 Sep 2019 13:11:59 -0500 Subject: [PATCH 02/81] Include HASH160 as available option --- libraries/protocol/include/graphene/protocol/htlc.hpp | 4 +++- libraries/protocol/include/graphene/protocol/types.hpp | 1 + libraries/wallet/wallet.cpp | 7 +++++++ tests/tests/htlc_tests.cpp | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/protocol/include/graphene/protocol/htlc.hpp b/libraries/protocol/include/graphene/protocol/htlc.hpp index bfd9c286e7..700fe1ed09 100644 --- a/libraries/protocol/include/graphene/protocol/htlc.hpp +++ b/libraries/protocol/include/graphene/protocol/htlc.hpp @@ -33,11 +33,13 @@ namespace graphene { namespace protocol { typedef fc::ripemd160 htlc_algo_ripemd160; typedef fc::sha1 htlc_algo_sha1; typedef fc::sha256 htlc_algo_sha256; + typedef fc::hash160 htlc_algo_hash160; typedef fc::static_variant< htlc_algo_ripemd160, htlc_algo_sha1, - htlc_algo_sha256 + htlc_algo_sha256, + htlc_algo_hash160 > htlc_hash; struct htlc_create_operation : public base_operation diff --git a/libraries/protocol/include/graphene/protocol/types.hpp b/libraries/protocol/include/graphene/protocol/types.hpp index b861efc78a..43426a9549 100644 --- a/libraries/protocol/include/graphene/protocol/types.hpp +++ b/libraries/protocol/include/graphene/protocol/types.hpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index e35082f89c..01033b8710 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -301,6 +301,11 @@ class htlc_hash_to_string_visitor { return "SHA256 " + hash.str(); } + + result_type operator()( const fc::hash160& hash )const + { + return "HASH160 " + hash.str(); + } }; /* meta contains lines of the form "key=value". @@ -3405,6 +3410,8 @@ fc::optional wallet_api::get_htlc(std::string htlc_id) const { return convert("SHA1", obj.str()); } result_type operator()(const fc::sha256& obj)const { return convert("SHA256", obj.str()); } + result_type operator()(const fc::hash160& obj)const + { return convert("HASH160", obj.str()); } private: result_type convert(const std::string& type, const std::string& hash)const { diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index ce581896cd..f85c58bea6 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -97,7 +97,7 @@ try { create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); From dfbde9c5690d22beae9f8a121d3bd2cad02dac8c Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 20 Sep 2019 15:10:05 -0500 Subject: [PATCH 03/81] Add hardfork checks --- libraries/chain/hardfork.d/CORE_BSIP64.hf | 4 + libraries/chain/htlc_evaluator.cpp | 14 +- tests/tests/htlc_tests.cpp | 158 ++++++++++++++++++++-- 3 files changed, 162 insertions(+), 14 deletions(-) create mode 100644 libraries/chain/hardfork.d/CORE_BSIP64.hf diff --git a/libraries/chain/hardfork.d/CORE_BSIP64.hf b/libraries/chain/hardfork.d/CORE_BSIP64.hf new file mode 100644 index 0000000000..a035996482 --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_BSIP64.hf @@ -0,0 +1,4 @@ +// bitshares BSIP 64 HTLC modifications +#ifndef HARDFORK_CORE_BSIP64_TIME +#define HARDFORK_CORE_BSIP64_TIME (fc::time_point_sec( 1600000000 ) ) // Sep 2020 +#endif diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 7196b88aaf..559c7259ff 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -48,6 +48,13 @@ namespace graphene { FC_ASSERT( o.preimage_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure the sender has the funds for the HTLC FC_ASSERT( d.get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; + // memo field added at harfork BSIP64 + // NOTE: this check can be removed after hardfork time + FC_ASSERT( d.head_block_time() > HARDFORK_CORE_BSIP64_TIME || !o.extensions.value.memo.valid(), + "Memo unavailable until after HARDFORK BSIP64"); + // HASH160 added at hardfork BSIP64 + FC_ASSERT( d.head_block_time() > HARDFORK_CORE_BSIP64_TIME || o.preimage_hash.which() != + htlc_hash(fc::hash160()).which(), "HASH160 unavailable until after HARDFORK BSIP64" ); const auto& asset_to_transfer = o.amount.asset_id( d ); const auto& from_account = o.from( d ); const auto& to_account = o.to( d ); @@ -102,8 +109,11 @@ namespace graphene { void_result htlc_redeem_evaluator::do_evaluate(const htlc_redeem_operation& o) { htlc_obj = &db().get(o.htlc_id); - - FC_ASSERT(o.preimage.size() == htlc_obj->conditions.hash_lock.preimage_size, "Preimage size mismatch."); + // TODO: The hardfork portion of this check can be removed if no HTLC redemptions are + // attempted on an HTLC with a 0 preimage size before the hardfork date. + if ( htlc_obj->conditions.hash_lock.preimage_size > 0U || + db().head_block_time() <= HARDFORK_CORE_BSIP64_TIME ) + FC_ASSERT(o.preimage.size() == htlc_obj->conditions.hash_lock.preimage_size, "Preimage size mismatch."); const htlc_redeem_visitor vtor( o.preimage ); FC_ASSERT( htlc_obj->conditions.hash_lock.preimage_hash.visit( vtor ), "Provided preimage does not generate correct hash."); diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index f85c58bea6..cf2d8e7e37 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -63,7 +63,7 @@ void generate_random_preimage(uint16_t key_size, std::vector& vec) return; } -void advance_past_hardfork(database_fixture* db_fixture) +void advance_past_htlc_first_hardfork(database_fixture* db_fixture) { db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME); set_expiration(db_fixture->db, db_fixture->trx); @@ -80,7 +80,7 @@ try { transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - advance_past_hardfork(this); + advance_past_htlc_first_hardfork(this); uint16_t preimage_size = 256; std::vector pre_image(256); @@ -97,15 +97,10 @@ try { create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); - create_operation.extensions.value.memo = memo_data(); - create_operation.extensions.value.memo->from = alice_public_key; - create_operation.extensions.value.memo->to = bob_public_key; - create_operation.extensions.value.memo->set_message( - alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); @@ -182,6 +177,145 @@ try { } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( htlc_hf_bsip64 ) +{ +try { + ACTORS((alice)(bob)(joker)); + + int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + transfer( committee_account, joker_id, graphene::chain::asset(init_balance) ); + + advance_past_htlc_first_hardfork(this); + + uint16_t preimage_size = 256; + std::vector pre_image(256); + generate_random_preimage(preimage_size, pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + // Alice attempts to put a contract on the blockchain with HASH160 + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = preimage_size; + create_operation.from = alice_id; + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + // this try/catch is just to get some console output to verify the exception + try { + PUSH_TX(db, trx, ~0); + BOOST_TEST_FAIL("crate_operation should have failed due to the HASH160 hash"); + } catch(fc::exception& ex) { + if ( ex.to_string().find("HASH160 unavailable") == std::string::npos ) + BOOST_TEST_FAIL("create_operation failed but not due to the hash160 function"); + } + trx.clear(); + } + + // Alice attempts to put a contract on the blockchain with a memo + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = preimage_size; + create_operation.from = alice_id; + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + create_operation.extensions.value.memo = memo_data(); + create_operation.extensions.value.memo->from = alice_public_key; + create_operation.extensions.value.memo->to = bob_public_key; + create_operation.extensions.value.memo->set_message( + alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + try { + PUSH_TX(db, trx, ~0); + BOOST_TEST_FAIL("crate_operation should have failed due to the memo field"); + } catch(fc::exception& ex) { + if (ex.to_string().find("Memo unavailable") == std::string::npos ) + BOOST_TEST_FAIL("create_operation failed but not due to the memo field."); + } + trx.clear(); + } + + // fast forward to after the hardfork + generate_blocks( HARDFORK_CORE_BSIP64_TIME + 60 ); + set_expiration( db, trx ); + + // Alice attempts to put a contract on the blockchain that is correct (and preimage size of 0) + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = 0; + create_operation.from = alice_id; + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + create_operation.extensions.value.memo = memo_data(); + create_operation.extensions.value.memo->from = alice_public_key; + create_operation.extensions.value.memo->to = bob_public_key; + create_operation.extensions.value.memo->set_message( + alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + graphene::chain::signed_block blk = generate_block(); + processed_transaction alice_trx = blk.transactions[0]; + alice_htlc_id = alice_trx.operation_results[0].get(); + generate_block(); + } + + // verify funds on hold... 100 - 3 = 97, minus the 4 coin fee = 93 + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 93 * GRAPHENE_BLOCKCHAIN_PRECISION ); + + // make sure Bob (or anyone) can see the details of the transaction + graphene::app::database_api db_api(db); + auto obj = db_api.get_objects( {alice_htlc_id }).front(); + graphene::chain::htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); + + // grab number of history objects to make sure everyone gets notified + size_t alice_num_history = get_operation_history(alice_id).size(); + size_t bob_num_history = get_operation_history(bob_id).size(); + size_t joker_num_history = get_operation_history(joker_id).size(); + + // joker sends a redeem operation to claim the funds for bob + { + graphene::chain::htlc_redeem_operation update_operation; + update_operation.redeemer = joker_id; + update_operation.htlc_id = alice_htlc_id; + update_operation.preimage = pre_image; + update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation ); + trx.operations.push_back( update_operation ); + sign(trx, joker_private_key); + PUSH_TX( db, trx, ~0 ); + generate_block(); + trx.clear(); + } + // verify funds end up in Bob's account (3) + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify funds remain out of Alice's acount ( 100 - 3 - 4 ) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 93 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify all three get notified + BOOST_CHECK_EQUAL( get_operation_history(alice_id).size(), alice_num_history + 1); + BOOST_CHECK_EQUAL( get_operation_history(bob_id).size(), bob_num_history + 1); + BOOST_CHECK_EQUAL( get_operation_history(joker_id).size(), joker_num_history + 1); +} FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_CASE( htlc_fulfilled ) { try { @@ -193,7 +327,7 @@ try { transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); transfer( committee_account, joker_id, graphene::chain::asset(init_balance) ); - advance_past_hardfork(this); + advance_past_htlc_first_hardfork(this); uint16_t preimage_size = 256; std::vector pre_image(preimage_size); @@ -276,7 +410,7 @@ try { BOOST_AUTO_TEST_CASE( other_peoples_money ) { try { - advance_past_hardfork(this); + advance_past_htlc_first_hardfork(this); ACTORS((alice)(bob)); @@ -393,7 +527,7 @@ BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) // now things should start working... BOOST_TEST_MESSAGE("Advancing to HTLC hardfork time."); - advance_past_hardfork(this); + advance_past_htlc_first_hardfork(this); proposal_id_type good_proposal_id; BOOST_TEST_MESSAGE( "Creating a proposal to change the max_preimage_size to 2048 and set higher fees" ); @@ -638,7 +772,7 @@ try { fund( alice, graphene::chain::asset(init_balance) ); fund( bob, graphene::chain::asset(init_balance) ); - advance_past_hardfork(this); + advance_past_htlc_first_hardfork(this); // blacklist bob { From 343c667d5858bd1c00ddbc3b599b17a4e5d26348 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 20 Sep 2019 15:14:26 -0500 Subject: [PATCH 04/81] Bump DB_VERSION (BSIP64) --- libraries/chain/include/graphene/chain/config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index e508eb16b2..8f078da7c3 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -30,7 +30,7 @@ #define GRAPHENE_MAX_NESTED_OBJECTS (200) -#define GRAPHENE_CURRENT_DB_VERSION "20190818" +#define GRAPHENE_CURRENT_DB_VERSION "20190920" #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 From 2a5b29b8a6084e8942f6efa6370bcc2f0e76d298 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 7 Apr 2020 10:41:08 -0500 Subject: [PATCH 05/81] include preimage in operation history --- libraries/chain/htlc_evaluator.cpp | 2 +- .../include/graphene/protocol/htlc.hpp | 6 +- tests/tests/htlc_tests.cpp | 76 +++++++++++++++++-- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 559c7259ff..e1833135d0 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -126,7 +126,7 @@ namespace graphene { db().adjust_balance(htlc_obj->transfer.to, asset(htlc_obj->transfer.amount, htlc_obj->transfer.asset_id) ); // notify related parties htlc_redeemed_operation virt_op( htlc_obj->id, htlc_obj->transfer.from, htlc_obj->transfer.to, - o.redeemer, asset(htlc_obj->transfer.amount, htlc_obj->transfer.asset_id ) ); + o.redeemer, asset(htlc_obj->transfer.amount, htlc_obj->transfer.asset_id ), o.preimage ); db().push_applied_operation( virt_op ); db().remove(*htlc_obj); return void_result(); diff --git a/libraries/protocol/include/graphene/protocol/htlc.hpp b/libraries/protocol/include/graphene/protocol/htlc.hpp index 700fe1ed09..7dd01fb82f 100644 --- a/libraries/protocol/include/graphene/protocol/htlc.hpp +++ b/libraries/protocol/include/graphene/protocol/htlc.hpp @@ -129,8 +129,9 @@ namespace graphene { namespace protocol { struct fee_parameters_type {}; htlc_redeemed_operation() {} - htlc_redeemed_operation( htlc_id_type htlc_id, account_id_type from, account_id_type to, account_id_type redeemer, asset amount ) : - htlc_id(htlc_id), from(from), to(to), redeemer(redeemer), amount(amount) {} + htlc_redeemed_operation( htlc_id_type htlc_id, account_id_type from, account_id_type to, + account_id_type redeemer, asset amount, std::vector preimage ) : + htlc_id(htlc_id), from(from), to(to), redeemer(redeemer), amount(amount), preimage(preimage) {} account_id_type fee_payer()const { return to; } void validate()const { FC_ASSERT( !"virtual operation" ); } @@ -141,6 +142,7 @@ namespace graphene { namespace protocol { account_id_type from, to, redeemer; asset amount; asset fee; + std::vector preimage; }; struct htlc_extend_operation : public base_operation diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index cf2d8e7e37..89507fe248 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -177,6 +177,68 @@ try { } FC_LOG_AND_RETHROW() } +class display_operation_history +{ + public: + display_operation_history( std::vector< operation_history_object > history, const account_object& acct ); + const account_object& acct; +}; + +class op_printer +{ + public: + op_printer( std::ostream& out, display_operation_history* displayer, const operation_history_object& obj ) + : out(out), caller(displayer), obj(obj) {} + typedef std::string result_type; + + template + std::string operator()(const T& op)const + { + return ""; + } + + std::string operator()(const htlc_redeemed_operation& op)const + { + out << "Called by " << caller->acct.name + << " Redeemer: " << id_to_string(op.redeemer) + << " Preimage: " << preimage_to_string(op.preimage); + return ""; + } + + std::string operator()(const htlc_redeem_operation& op)const + { + out << "Called by " << caller->acct.name + << " Redeemer: " << id_to_string(op.redeemer) + << " Preimage: " << preimage_to_string(op.preimage); + return ""; + } + + private: + std::ostream& out; + display_operation_history* caller; + const operation_history_object& obj; + static std::string id_to_string( object_id_type id) + { return "" + std::to_string(id.space()) + "." + std::to_string(id.type()) + "." + std::to_string(id.instance()); } + static std::string preimage_to_string( std::vector in) { + std::string out; + std::for_each(in.begin(), in.end(), [&out](const char& in) + { out += std::to_string((int)in); }); + return out; + } +}; + +display_operation_history::display_operation_history( std::vector history, const account_object& acct) + : acct(acct) +{ + for( auto itr = history.begin(); itr != history.end(); itr++) + { + std::stringstream ss; + (*itr).op.visit( op_printer(ss, this, *itr) ); + if ( ss.rdbuf()->in_avail() > 0 ) + BOOST_TEST_MESSAGE( ss.str() ); + }; +} + BOOST_AUTO_TEST_CASE( htlc_hf_bsip64 ) { try { @@ -310,9 +372,15 @@ try { // verify funds remain out of Alice's acount ( 100 - 3 - 4 ) BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 93 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify all three get notified - BOOST_CHECK_EQUAL( get_operation_history(alice_id).size(), alice_num_history + 1); - BOOST_CHECK_EQUAL( get_operation_history(bob_id).size(), bob_num_history + 1); - BOOST_CHECK_EQUAL( get_operation_history(joker_id).size(), joker_num_history + 1); + std::vector history = get_operation_history(alice_id); + BOOST_CHECK_EQUAL( history.size(), alice_num_history + 1); + display_operation_history{ history, alice }; + history = get_operation_history(bob_id); + BOOST_CHECK_EQUAL( history.size(), bob_num_history + 1); + display_operation_history{ history, bob }; + history = get_operation_history(joker_id); + BOOST_CHECK_EQUAL( history.size(), joker_num_history + 1); + display_operation_history{ history, joker }; } FC_LOG_AND_RETHROW() } @@ -1063,6 +1131,4 @@ try { } } - - BOOST_AUTO_TEST_SUITE_END() From 42643486c0640734ec95abcf86350f39e23f3bc8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 7 Apr 2020 10:47:40 -0500 Subject: [PATCH 06/81] ref wallet - pretty-print htlc redeemed op --- libraries/wallet/wallet.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 01033b8710..81018c1921 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -157,6 +157,7 @@ struct operation_printer std::string operator()(const asset_create_operation& op)const; std::string operator()(const htlc_create_operation& op)const; std::string operator()(const htlc_redeem_operation& op)const; + std::string operator()(const htlc_redeemed_operation& op)const; }; template @@ -3221,6 +3222,19 @@ std::string operation_printer::operator()(const htlc_redeem_operation& op) const return fee(op.fee); } +std::string operation_printer::operator()(const htlc_redeemed_operation& op) const +{ + out << "Redeem HTLC with database id " + << std::to_string(op.htlc_id.space_id) + << "." << std::to_string(op.htlc_id.type_id) + << "." << std::to_string((uint64_t)op.htlc_id.instance) + << " with preimage \""; + for (unsigned char c : op.preimage) + out << c; + out << "\""; + return fee(op.fee); +} + std::string operation_printer::operator()(const htlc_create_operation& op) const { static htlc_hash_to_string_visitor vtor; From 09970f675ec56a9ed06e796bb1368ee334f26c41 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 10 Apr 2020 10:04:49 -0500 Subject: [PATCH 07/81] 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 a59540f9e6c6d0bba735c08b2670bd9770c8b32e Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 11 Apr 2020 09:50:28 -0500 Subject: [PATCH 08/81] fixes for wallet operation_printer --- libraries/wallet/operation_printer.cpp | 17 +++++++++++++++++ libraries/wallet/operation_printer.hpp | 1 + 2 files changed, 18 insertions(+) diff --git a/libraries/wallet/operation_printer.cpp b/libraries/wallet/operation_printer.cpp index 0d948e023c..bba4a17e48 100644 --- a/libraries/wallet/operation_printer.cpp +++ b/libraries/wallet/operation_printer.cpp @@ -47,6 +47,10 @@ class htlc_hash_to_string_visitor { return "SHA256 " + hash.str(); } + result_type operator()( const fc::hash160& hash )const + { + return "HASH160 " + hash.str(); + } }; std::string operation_printer::fee(const graphene::protocol::asset& a)const { @@ -146,6 +150,19 @@ std::string operation_printer::operator()(const htlc_redeem_operation& op) const return fee(op.fee); } +std::string operation_printer::operator()(const htlc_redeemed_operation& op) const +{ + out << "Redeem HTLC with database id " + << std::to_string(op.htlc_id.space_id) + << "." << std::to_string(op.htlc_id.type_id) + << "." << std::to_string((uint64_t)op.htlc_id.instance) + << " with preimage \""; + for (unsigned char c : op.preimage) + out << c; + out << "\""; + return fee(op.fee); +} + std::string operation_printer::operator()(const htlc_create_operation& op) const { static htlc_hash_to_string_visitor vtor; diff --git a/libraries/wallet/operation_printer.hpp b/libraries/wallet/operation_printer.hpp index 9ff09586cf..c124096a8d 100644 --- a/libraries/wallet/operation_printer.hpp +++ b/libraries/wallet/operation_printer.hpp @@ -98,6 +98,7 @@ struct operation_printer std::string operator()(const graphene::protocol::asset_create_operation& op)const; std::string operator()(const graphene::protocol::htlc_create_operation& op)const; std::string operator()(const graphene::protocol::htlc_redeem_operation& op)const; + std::string operator()(const graphene::protocol::htlc_redeemed_operation& op)const; }; }}} // namespace graphene::wallet::detail From 99759484a10d1cf732c0ddb12e5465b465784e4e Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 15 Apr 2020 08:34:44 -0500 Subject: [PATCH 09/81] bsip64 memo fixes and addl testing --- libraries/chain/htlc_evaluator.cpp | 6 +- libraries/chain/small_objects.cpp | 2 +- .../wallet/include/graphene/wallet/wallet.hpp | 3 +- libraries/wallet/operation_printer.cpp | 6 + libraries/wallet/wallet.cpp | 6 +- libraries/wallet/wallet_api_impl.hpp | 2 +- libraries/wallet/wallet_transfer.cpp | 15 +- tests/cli/main.cpp | 196 +++++++++++++++++- tests/tests/htlc_tests.cpp | 21 +- 9 files changed, 238 insertions(+), 19 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index d1400b1c8a..5cf2d2f1a5 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -54,7 +54,7 @@ namespace graphene { "Memo unavailable until after HARDFORK BSIP64"); // HASH160 added at hardfork BSIP64 FC_ASSERT( d.head_block_time() > HARDFORK_CORE_BSIP64_TIME || o.preimage_hash.which() != - htlc_hash(fc::hash160()).which(), "HASH160 unavailable until after HARDFORK BSIP64" ); + htlc_hash(fc::hash160()).which(), "HASH160 unavailable until after HARDFORK BSIP64" ); const auto& asset_to_transfer = o.amount.asset_id( d ); const auto& from_account = o.from( d ); const auto& to_account = o.to( d ); @@ -64,6 +64,8 @@ namespace graphene { FC_ASSERT( is_authorized_asset( d, to_account, asset_to_transfer ), "Asset ${asset} is not authorized for account ${acct}.", ( "asset", asset_to_transfer.id )( "acct", to_account.id ) ); + FC_ASSERT( d.head_block_time() < HARDFORK_CORE_BSIP64_TIME || !asset_to_transfer.is_transfer_restricted(), + "Asset ${asset} cannot be transfered.", ("asset", asset_to_transfer.id) ); return void_result(); } @@ -80,9 +82,9 @@ namespace graphene { esc.transfer.asset_id = o.amount.asset_id; esc.conditions.hash_lock.preimage_hash = o.preimage_hash; esc.conditions.hash_lock.preimage_size = o.preimage_size; - esc.conditions.time_lock.expiration = dbase.head_block_time() + o.claim_period_seconds; if ( o.extensions.value.memo.valid() ) esc.memo = o.extensions.value.memo; + esc.conditions.time_lock.expiration = dbase.head_block_time() + o.claim_period_seconds; }); return esc.id; diff --git a/libraries/chain/small_objects.cpp b/libraries/chain/small_objects.cpp index 0233edc54a..c308519e4e 100644 --- a/libraries/chain/small_objects.cpp +++ b/libraries/chain/small_objects.cpp @@ -128,7 +128,7 @@ FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::htlc_object::condition_info::ti FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::htlc_object::condition_info, BOOST_PP_SEQ_NIL, (hash_lock)(time_lock) ) FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::htlc_object, (graphene::db::object), - (transfer) (conditions) ) + (transfer) (conditions) (memo) ) FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::operation_history_object, (graphene::chain::object), (op)(result)(block_num)(trx_in_block)(op_in_trx)(virtual_op) ) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 3f4d6bb10f..45ed8e6914 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1446,12 +1446,13 @@ class wallet_api * @param preimage_hash the hash of the preimage * @param preimage_size the size of the preimage in bytes * @param claim_period_seconds how long after creation until the lock expires + * @param memo the memo * @param broadcast true if you wish to broadcast the transaction * @return the signed transaction */ signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, - const uint32_t claim_period_seconds, bool broadcast = false ); + const uint32_t claim_period_seconds, const std::string& memo, bool broadcast = false ); /**** * Update a hashed time lock contract diff --git a/libraries/wallet/operation_printer.cpp b/libraries/wallet/operation_printer.cpp index bba4a17e48..393cb1d63a 100644 --- a/libraries/wallet/operation_printer.cpp +++ b/libraries/wallet/operation_printer.cpp @@ -139,26 +139,32 @@ std::string operation_printer::operator()(const asset_create_operation& op) cons std::string operation_printer::operator()(const htlc_redeem_operation& op) const { + auto flags = out.flags(); out << "Redeem HTLC with database id " << std::to_string(op.htlc_id.space_id) << "." << std::to_string(op.htlc_id.type_id) << "." << std::to_string((uint64_t)op.htlc_id.instance) << " with preimage \""; + out << std::hex; for (unsigned char c : op.preimage) out << c; + out.flags(flags); out << "\""; return fee(op.fee); } std::string operation_printer::operator()(const htlc_redeemed_operation& op) const { + auto flags = out.flags(); out << "Redeem HTLC with database id " << std::to_string(op.htlc_id.space_id) << "." << std::to_string(op.htlc_id.type_id) << "." << std::to_string((uint64_t)op.htlc_id.instance) << " with preimage \""; + out << std::hex; for (unsigned char c : op.preimage) out << c; + out.flags(flags); out << "\""; return fee(op.fee); } diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 6295a98810..bb7f1324b1 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -202,10 +202,10 @@ uint64_t wallet_api::get_asset_count()const signed_transaction wallet_api::htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, - const uint32_t claim_period_seconds, bool broadcast) + const uint32_t claim_period_seconds, const std::string& memo, bool broadcast) { return my->htlc_create(source, destination, amount, asset_symbol, hash_algorithm, preimage_hash, preimage_size, - claim_period_seconds, broadcast); + claim_period_seconds, memo, broadcast); } fc::optional wallet_api::get_htlc(std::string htlc_id) const @@ -223,6 +223,8 @@ fc::optional wallet_api::get_htlc(std::string htlc_id) const const auto& asset = my->get_asset( obj.transfer.asset_id ); transfer["asset"] = asset.symbol; transfer["amount"] = graphene::app::uint128_amount_to_string( obj.transfer.amount.value, asset.precision ); + if (obj.memo.valid()) + transfer["memo"] = my->read_memo( *obj.memo ); class htlc_hash_to_variant_visitor { public: diff --git a/libraries/wallet/wallet_api_impl.hpp b/libraries/wallet/wallet_api_impl.hpp index 465c071f0b..6727723226 100644 --- a/libraries/wallet/wallet_api_impl.hpp +++ b/libraries/wallet/wallet_api_impl.hpp @@ -300,7 +300,7 @@ class wallet_api_impl signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, - const uint32_t claim_period_seconds, bool broadcast = false ); + const uint32_t claim_period_seconds, const std::string& memo, bool broadcast = false ); signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ); diff --git a/libraries/wallet/wallet_transfer.cpp b/libraries/wallet/wallet_transfer.cpp index 7228cec5dc..0b41cefc41 100644 --- a/libraries/wallet/wallet_transfer.cpp +++ b/libraries/wallet/wallet_transfer.cpp @@ -43,6 +43,8 @@ namespace graphene { namespace wallet { namespace detail { return fc::sha256( hash ); if( name_upper == "SHA1" ) return fc::sha1( hash ); + if( name_upper == "HASH160" ) + return fc::hash160( hash ); FC_THROW_EXCEPTION( fc::invalid_arg_exception, "Unknown algorithm '${a}'", ("a",algorithm) ); } @@ -83,7 +85,7 @@ namespace graphene { namespace wallet { namespace detail { signed_transaction wallet_api_impl::htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, - const uint32_t claim_period_seconds, bool broadcast ) + const uint32_t claim_period_seconds, const std::string& memo, bool broadcast ) { try { @@ -91,6 +93,8 @@ namespace graphene { namespace wallet { namespace detail { fc::optional asset_obj = get_asset(asset_symbol); FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); + const account_object& from_acct = get_account(source); + const account_object& to_acct = get_account(destination); htlc_create_operation create_op; create_op.from = get_account(source).id; create_op.to = get_account(destination).id; @@ -98,6 +102,15 @@ namespace graphene { namespace wallet { namespace detail { create_op.claim_period_seconds = claim_period_seconds; create_op.preimage_hash = do_hash( hash_algorithm, preimage_hash ); create_op.preimage_size = preimage_size; + if (!memo.empty()) + { + memo_data data; + data.from = from_acct.options.memo_key; + data.to = to_acct.options.memo_key; + data.set_message( + get_private_key(from_acct.options.memo_key), to_acct.options.memo_key, memo); + create_op.extensions.value.memo = data; + } signed_transaction tx; tx.operations.push_back(create_op); diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 58959f7c88..c4787c722a 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -1126,8 +1126,6 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) BOOST_CHECK(!bki.brain_priv_key.empty()); signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "alice", "nathan", "nathan", true); - // save the private key for this new account in the wallet file - BOOST_CHECK(con.wallet_api_ptr->import_key("alice", bki.wif_priv_key)); con.wallet_api_ptr->save_wallet_file(con.wallet_filename); // attempt to give alice some bitsahres BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to alice"); @@ -1141,9 +1139,8 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) BOOST_CHECK(!bki.brain_priv_key.empty()); signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "bob", "nathan", "nathan", true); - // save the private key for this new account in the wallet file - BOOST_CHECK(con.wallet_api_ptr->import_key("bob", bki.wif_priv_key)); - con.wallet_api_ptr->save_wallet_file(con.wallet_filename); + // this should cause resync which will import the keys of alice and bob + generate_block(app1); // attempt to give bob some bitsahres BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to Bob"); signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "bob", "10000", "1.3.0", @@ -1151,7 +1148,6 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) con.wallet_api_ptr->issue_asset("bob", "5", "BOBCOIN", "Here are your BOBCOINs", true); } - BOOST_TEST_MESSAGE("Alice has agreed to buy 3 BOBCOIN from Bob for 3 BTS. Alice creates an HTLC"); // create an HTLC std::string preimage_string = "My Secret"; @@ -1168,7 +1164,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) uint32_t timelock = fc::days(1).to_seconds(); graphene::chain::signed_transaction result_tx = con.wallet_api_ptr->htlc_create("alice", "bob", - "3", "1.3.0", "SHA256", hash_str, preimage_string.size(), timelock, true); + "3", "1.3.0", "SHA256", hash_str, preimage_string.size(), timelock, "", true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: std::string alice_htlc_id_as_string; @@ -1190,7 +1186,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC con.wallet_api_ptr->htlc_create("bob", "alice", - "3", "BOBCOIN", "SHA256", hash_str, preimage_string.size(), timelock, true); + "3", "BOBCOIN", "SHA256", hash_str, preimage_string.size(), timelock, "", true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: std::string bob_htlc_id_as_string; @@ -1593,3 +1589,187 @@ BOOST_FIXTURE_TEST_CASE(cli_use_authorized_transfer, cli_fixture) { throw; } } + +BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) +{ + using namespace graphene::chain; + using namespace graphene::app; + std::shared_ptr app1; + try { + fc::temp_directory app_dir( graphene::utilities::temp_directory_path() ); + + int server_port_number = 0; + app1 = start_application(app_dir, server_port_number); + // set committee parameters + app1->chain_database()->modify(app1->chain_database()->get_global_properties(), [](global_property_object& p) { + graphene::chain::htlc_options params; + params.max_preimage_size = 1024; + params.max_timeout_secs = 60 * 60 * 24 * 28; + p.parameters.extensions.value.updatable_htlc_options = params; + }); + + // connect to the server + client_connection con(app1, app_dir, server_port_number); + + // get past hardforks + generate_blocks( app1, HARDFORK_CORE_BSIP64_TIME + 10); + + BOOST_TEST_MESSAGE("Setting wallet password"); + con.wallet_api_ptr->set_password("supersecret"); + con.wallet_api_ptr->unlock("supersecret"); + + // import Nathan account + BOOST_TEST_MESSAGE("Importing nathan key"); + std::vector nathan_keys{"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"}; + BOOST_CHECK_EQUAL(nathan_keys[0], "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"); + BOOST_CHECK(con.wallet_api_ptr->import_key("nathan", nathan_keys[0])); + + BOOST_TEST_MESSAGE("Importing nathan's balance"); + std::vector import_txs = con.wallet_api_ptr->import_balance("nathan", nathan_keys, true); + account_object nathan_acct_before_upgrade = con.wallet_api_ptr->get_account("nathan"); + + // upgrade nathan + BOOST_TEST_MESSAGE("Upgrading Nathan to LTM"); + signed_transaction upgrade_tx = con.wallet_api_ptr->upgrade_account("nathan", true); + account_object nathan_acct_after_upgrade = con.wallet_api_ptr->get_account("nathan"); + + // verify that the upgrade was successful + BOOST_CHECK_PREDICATE( std::not_equal_to(), (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch()) + (nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) ); + BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member()); + + // Create new asset called BOBCOIN + try + { + graphene::chain::asset_options asset_ops; + asset_ops.max_supply = 1000000; + asset_ops.core_exchange_rate = price(asset(2),asset(1,asset_id_type(1))); + fc::optional bit_opts; + con.wallet_api_ptr->create_asset("nathan", "BOBCOIN", 5, asset_ops, bit_opts, true); + } + catch(exception& e) + { + BOOST_FAIL(e.what()); + } + catch(...) + { + BOOST_FAIL("Unknown exception creating BOBCOIN"); + } + + // create a new account for Alice + { + graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key(); + BOOST_CHECK(!bki.brain_priv_key.empty()); + signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "alice", + "nathan", "nathan", true); + con.wallet_api_ptr->save_wallet_file(con.wallet_filename); + // attempt to give alice some bitsahres + BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to alice"); + signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "alice", "10000", "1.3.0", + "Here are some CORE token for your new account", true); + } + + // create a new account for Bob + { + graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key(); + BOOST_CHECK(!bki.brain_priv_key.empty()); + signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "bob", + "nathan", "nathan", true); + // this should cause resync which will import the keys of alice and bob + generate_block(app1); + // attempt to give bob some bitsahres + BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to Bob"); + signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "bob", "10000", "1.3.0", + "Here are some CORE token for your new account", true); + con.wallet_api_ptr->issue_asset("bob", "5", "BOBCOIN", "Here are your BOBCOINs", true); + } + + BOOST_TEST_MESSAGE("Alice has agreed to buy 3 BOBCOIN from Bob for 3 BTS. Alice creates an HTLC"); + // create an HTLC + std::string preimage_string = "My Secret"; + fc::hash160 preimage_md = fc::hash160::hash(preimage_string); + std::stringstream ss; + for(size_t i = 0; i < preimage_md.data_size(); i++) + { + char d = preimage_md.data()[i]; + unsigned char uc = static_cast(d); + ss << std::setfill('0') << std::setw(2) << std::hex << (int)uc; + } + std::string hash_str = ss.str(); + BOOST_TEST_MESSAGE("Secret is " + preimage_string + " and hash is " + hash_str); + uint32_t timelock = fc::days(1).to_seconds(); + graphene::chain::signed_transaction result_tx + = con.wallet_api_ptr->htlc_create("alice", "bob", + "3", "1.3.0", "HASH160", hash_str, preimage_string.size(), timelock, "Alice to Bob", true); + + // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: + std::string alice_htlc_id_as_string; + { + BOOST_TEST_MESSAGE("The system is generating a block"); + graphene::chain::signed_block result_block; + BOOST_CHECK(generate_block(app1, result_block)); + + // get the ID: + htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get(); + alice_htlc_id_as_string = (std::string)(object_id_type)htlc_id; + BOOST_TEST_MESSAGE("Alice shares the HTLC ID with Bob. The HTLC ID is: " + alice_htlc_id_as_string); + } + + // Bob can now look over Alice's HTLC, to see if it is what was agreed to. + BOOST_TEST_MESSAGE("Bob retrieves the HTLC Object by ID to examine it."); + auto alice_htlc = con.wallet_api_ptr->get_htlc(alice_htlc_id_as_string); + BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(alice_htlc)); + + // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC + con.wallet_api_ptr->htlc_create("bob", "alice", + "3", "BOBCOIN", "HASH160", hash_str, preimage_string.size(), timelock, "Bob to Alice", true); + + // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: + std::string bob_htlc_id_as_string; + { + BOOST_TEST_MESSAGE("The system is generating a block"); + graphene::chain::signed_block result_block; + BOOST_CHECK(generate_block(app1, result_block)); + + // get the ID: + htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get(); + bob_htlc_id_as_string = (std::string)(object_id_type)htlc_id; + BOOST_TEST_MESSAGE("Bob shares the HTLC ID with Alice. The HTLC ID is: " + bob_htlc_id_as_string); + } + + // Alice can now look over Bob's HTLC, to see if it is what was agreed to: + BOOST_TEST_MESSAGE("Alice retrieves the HTLC Object by ID to examine it."); + auto bob_htlc = con.wallet_api_ptr->get_htlc(bob_htlc_id_as_string); + BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(bob_htlc)); + + // Alice likes what she sees, so uses her preimage to get her BOBCOIN + { + BOOST_TEST_MESSAGE("Alice uses her preimage to retrieve the BOBCOIN"); + std::string secret = "My Secret"; + con.wallet_api_ptr->htlc_redeem(bob_htlc_id_as_string, "alice", secret, true); + BOOST_TEST_MESSAGE("The system is generating a block"); + BOOST_CHECK(generate_block(app1)); + } + + // TODO: Bob can look at Alice's history to see her preimage + // Bob can use the preimage to retrieve his BTS + { + BOOST_TEST_MESSAGE("Bob uses Alice's preimage to retrieve the BOBCOIN"); + std::string secret = "My Secret"; + con.wallet_api_ptr->htlc_redeem(alice_htlc_id_as_string, "bob", secret, true); + BOOST_TEST_MESSAGE("The system is generating a block"); + BOOST_CHECK(generate_block(app1)); + } + + // wait for everything to finish up + fc::usleep(fc::seconds(1)); + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } + app1->shutdown(); + app1.reset(); + // Intentional delay after app1->shutdown + std::cout << "cli_create_htlc conclusion: Intentional delay" << std::endl; + fc::usleep(fc::seconds(1)); +} \ No newline at end of file diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 89507fe248..5e1db137ef 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -290,7 +290,7 @@ try { create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); @@ -322,7 +322,7 @@ try { create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = 0; create_operation.from = alice_id; create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); @@ -348,12 +348,27 @@ try { graphene::app::database_api db_api(db); auto obj = db_api.get_objects( {alice_htlc_id }).front(); graphene::chain::htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); - + BOOST_CHECK( htlc.memo.valid() ); + // grab number of history objects to make sure everyone gets notified size_t alice_num_history = get_operation_history(alice_id).size(); size_t bob_num_history = get_operation_history(bob_id).size(); size_t joker_num_history = get_operation_history(joker_id).size(); + // joker sends a redeem operation with a bad hash + { + std::vector bad_pre_image{ 0x00, 0x01, 0x02 }; + graphene::chain::htlc_redeem_operation update_operation; + update_operation.redeemer = joker_id; + update_operation.htlc_id = alice_htlc_id; + update_operation.preimage = bad_pre_image; + update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation ); + trx.operations.push_back( update_operation ); + sign(trx, joker_private_key); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::exception ); + trx.clear(); + } + // joker sends a redeem operation to claim the funds for bob { graphene::chain::htlc_redeem_operation update_operation; From 7eee72c17f28ba8dc32cd87769733f994fa12998 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 15 Apr 2020 08:35:38 -0500 Subject: [PATCH 10/81] fix constexpr warnings --- programs/build_helpers/member_enumerator.cpp | 2 +- programs/js_operation_serializer/main.cpp | 4 ++-- programs/size_checker/main.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/programs/build_helpers/member_enumerator.cpp b/programs/build_helpers/member_enumerator.cpp index 6a292298ee..830878e314 100644 --- a/programs/build_helpers/member_enumerator.cpp +++ b/programs/build_helpers/member_enumerator.cpp @@ -103,7 +103,7 @@ void class_processor::process_class( const static_variant< T... >* dummy ) static_variant dummy2; static_variant_visitor vtor( this ); - for( int w=0; w, false > { init = true; fc::static_variant var; - for( int i = 0; i < var.count(); ++i ) + for( size_t i = 0; i < var.count(); ++i ) { var.set_which(i); var.visit( register_type_visitor() ); @@ -372,7 +372,7 @@ int main( int argc, char** argv ) operation op; std::cout << "ChainTypes.operations=\n"; - for( int i = 0; i < op.count(); ++i ) + for( size_t i = 0; i < op.count(); ++i ) { op.set_which(i); op.visit( detail_ns::serialize_type_visitor(i) ); diff --git a/programs/size_checker/main.cpp b/programs/size_checker/main.cpp index a7c09308fc..e283e2d6e6 100644 --- a/programs/size_checker/main.cpp +++ b/programs/size_checker/main.cpp @@ -85,7 +85,7 @@ int main( int argc, char** argv ) idump( (witnesses) ); - for( int32_t i = 0; i < op.count(); ++i ) + for( size_t i = 0; i < op.count(); ++i ) { op.set_which(i); op.visit( size_check_type_visitor(i) ); From f161b3884baabf72978b0cfcaf017cd720146879 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 15 Apr 2020 09:31:49 -0500 Subject: [PATCH 11/81] verify hash appears in history --- tests/cli/main.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index c4787c722a..1af768dd1f 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -1735,6 +1735,20 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get(); bob_htlc_id_as_string = (std::string)(object_id_type)htlc_id; BOOST_TEST_MESSAGE("Bob shares the HTLC ID with Alice. The HTLC ID is: " + bob_htlc_id_as_string); + // test operation_printer + auto hist = con.wallet_api_ptr->get_account_history("alice", 10); + for(size_t i = 0; i < hist.size(); ++i) + { + if (i < 2) + { + auto obj = hist[i]; + std::stringstream ss; + ss << "Description: " << obj.description << " Memo: " << obj.memo << "\n"; + auto str = ss.str(); + BOOST_TEST_MESSAGE( str ); + BOOST_CHECK( str.find("HASH160 008e") != std::string::npos ); + } + } } // Alice can now look over Bob's HTLC, to see if it is what was agreed to: @@ -1772,4 +1786,4 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) // Intentional delay after app1->shutdown std::cout << "cli_create_htlc conclusion: Intentional delay" << std::endl; fc::usleep(fc::seconds(1)); -} \ No newline at end of file +} From f1a2ab0778211f3e47068ab1c043395fe8873d74 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 15 Apr 2020 11:14:20 -0500 Subject: [PATCH 12/81] add per kb fee for htlc memos --- libraries/chain/proposal_evaluator.cpp | 5 ++ libraries/protocol/htlc.cpp | 5 +- .../include/graphene/protocol/htlc.hpp | 3 +- tests/tests/htlc_tests.cpp | 89 ++++++++++++++++++- 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 5a4a2b69bc..3d7513d2ee 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -61,6 +61,11 @@ struct proposal_operation_hardfork_visitor FC_ASSERT(!op.new_parameters.current_fees->exists()); FC_ASSERT(!op.new_parameters.current_fees->exists()); } + if (block_time < HARDFORK_CORE_BSIP64_TIME) + { + FC_ASSERT( op.new_parameters.current_fees->get().fee_per_kb == 0, + "Unable to set htlc_create_operation per_kb_fee until after BSIP 64 hardfork"); + } if (!HARDFORK_BSIP_40_PASSED(block_time)) { FC_ASSERT(!op.new_parameters.extensions.value.custom_authority_options.valid(), "Unable to set Custom Authority Options before hardfork BSIP 40"); diff --git a/libraries/protocol/htlc.cpp b/libraries/protocol/htlc.cpp index b7fe9ef4a0..84ad4a8ac1 100644 --- a/libraries/protocol/htlc.cpp +++ b/libraries/protocol/htlc.cpp @@ -39,8 +39,11 @@ namespace graphene { namespace protocol { uint64_t days = ( claim_period_seconds + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; // multiply with overflow check uint64_t per_day_fee = fee_params.fee_per_day * days; + uint64_t per_kb_fee = 0; + if (extensions.value.memo.valid()) + per_kb_fee = calculate_data_fee( fc::raw::pack_size(extensions.value.memo), fee_params.fee_per_kb); FC_ASSERT( days == 0 || per_day_fee / days == fee_params.fee_per_day, "Fee calculation overflow" ); - return fee_params.fee + per_day_fee; + return fee_params.fee + per_day_fee + per_kb_fee; } void htlc_redeem_operation::validate()const { diff --git a/libraries/protocol/include/graphene/protocol/htlc.hpp b/libraries/protocol/include/graphene/protocol/htlc.hpp index 23a0559d76..d550518b4e 100644 --- a/libraries/protocol/include/graphene/protocol/htlc.hpp +++ b/libraries/protocol/include/graphene/protocol/htlc.hpp @@ -47,6 +47,7 @@ namespace graphene { namespace protocol { struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t fee_per_kb = 0; }; // paid to network @@ -215,7 +216,7 @@ namespace graphene { namespace protocol { FC_REFLECT_TYPENAME( graphene::protocol::htlc_hash ) -FC_REFLECT( graphene::protocol::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) +FC_REFLECT( graphene::protocol::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) (fee_per_kb) ) FC_REFLECT( graphene::protocol::htlc_create_operation::additional_options_type, (memo)) FC_REFLECT( graphene::protocol::htlc_redeem_operation::fee_parameters_type, (fee) (fee_per_kb) ) FC_REFLECT( graphene::protocol::htlc_redeemed_operation::fee_parameters_type, ) // VIRTUAL diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 5e1db137ef..19872347fb 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -545,6 +545,9 @@ try { BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) { try { + ACTORS( (alice) ); + int64_t init_balance(10000 * GRAPHENE_BLOCKCHAIN_PRECISION); + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); { // try to set committee parameters before hardfork proposal_create_operation cop = proposal_create_operation::committee_proposal( @@ -682,8 +685,92 @@ BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) const htlc_create_operation::fee_parameters_type& htlc_fee = current_fee_schedule.get(); BOOST_CHECK_EQUAL(htlc_fee.fee, 2 * GRAPHENE_BLOCKCHAIN_PRECISION); + + // verify that the per_kb fee can't be set until after BSIP 64 + { + BOOST_TEST_MESSAGE("Attempting to set HTLC per_kb fees before hard fork."); + + // get existing fee_schedule + const chain_parameters& existing_params = db.get_global_properties().parameters; + const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); + // create a new fee_shedule + std::shared_ptr new_fee_schedule = std::make_shared(); + new_fee_schedule->scale = existing_fee_schedule.scale; + // replace the old with the new + flat_map params_map = get_htlc_fee_parameters(); + htlc_create_operation::fee_parameters_type create_param; + create_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + create_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + create_param.fee_per_kb = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + params_map[((operation)htlc_create_operation()).which()] = create_param; + for(auto param : existing_fee_schedule.parameters) + { + auto itr = params_map.find(param.which()); + if (itr == params_map.end()) + new_fee_schedule->parameters.insert(param); + else + { + new_fee_schedule->parameters.insert( (*itr).second); + } + } + 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 uop; + uop.new_parameters.current_fees = new_fee_schedule; + cop.proposed_ops.emplace_back(uop); + cop.fee = asset( 100000 ); + trx.operations.push_back( cop ); + GRAPHENE_CHECK_THROW(db.push_transaction( trx ), fc::exception); + trx.clear(); + } + // verify that the per_kb fee can be set after BSIP 64 + generate_blocks( HARDFORK_CORE_BSIP64_TIME + 10); + set_expiration(db, trx); + { + BOOST_TEST_MESSAGE("Attempting to set HTLC per_kb fees after hard fork."); + + // get existing fee_schedule + const chain_parameters& existing_params = db.get_global_properties().parameters; + const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); + // create a new fee_shedule + std::shared_ptr new_fee_schedule = std::make_shared(); + new_fee_schedule->scale = existing_fee_schedule.scale; + // replace the old with the new + flat_map params_map = get_htlc_fee_parameters(); + htlc_create_operation::fee_parameters_type create_param; + create_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + create_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + create_param.fee_per_kb = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + params_map[((operation)htlc_create_operation()).which()] = create_param; + for(auto param : existing_fee_schedule.parameters) + { + auto itr = params_map.find(param.which()); + if (itr == params_map.end()) + new_fee_schedule->parameters.insert(param); + else + { + new_fee_schedule->parameters.insert( (*itr).second); + } + } + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db.get_global_properties().parameters, db.head_block_time()); + cop.fee_paying_account = alice_id; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation uop; + uop.new_parameters.current_fees = new_fee_schedule; + cop.proposed_ops.emplace_back(uop); + cop.fee = db.current_fee_schedule().calculate_fee(cop); + trx.operations.push_back( cop ); + sign(trx, alice_private_key); + db.push_transaction( trx ); + trx.clear(); + } -} FC_LOG_AND_RETHROW() } + } + FC_LOG_AND_RETHROW() +} BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) { try { From 0a55c6450334e4229ae7fd6de5403cfd322f96ed Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 15 Apr 2020 11:31:58 -0500 Subject: [PATCH 13/81] bump FC for hash160 changes --- libraries/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fc b/libraries/fc index 23f0c6ab8e..73a7f08f00 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 23f0c6ab8e9516c8d0bc62b64f3ebedfe2f698b6 +Subproject commit 73a7f08f00456b0984cd431dd8f55bd901282e15 From c5c763e40959f289d6b44bd294abee32701fd990 Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 17 Apr 2020 15:24:15 -0400 Subject: [PATCH 14/81] 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 15/81] 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 16/81] 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 17/81] 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 18/81] 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 19/81] 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 20/81] 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 21/81] 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 22/81] 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 23/81] 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 24/81] 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 25/81] 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 26/81] 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 27/81] 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 28/81] 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 29/81] 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 30/81] 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 31/81] 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 32/81] 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 33/81] 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 34/81] 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 35/81] 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 36/81] 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 37/81] 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 20e43f0646626a7fd97291fc9c69a886406aef48 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 18 Apr 2020 10:09:37 -0500 Subject: [PATCH 38/81] Adjust fee schedule --- libraries/chain/proposal_evaluator.cpp | 5 - libraries/protocol/fee_schedule_calc.cpp | 12 ++ libraries/protocol/htlc.cpp | 4 +- .../include/graphene/protocol/htlc.hpp | 5 +- tests/tests/htlc_tests.cpp | 106 +++--------------- 5 files changed, 30 insertions(+), 102 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 3d7513d2ee..5a4a2b69bc 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -61,11 +61,6 @@ struct proposal_operation_hardfork_visitor FC_ASSERT(!op.new_parameters.current_fees->exists()); FC_ASSERT(!op.new_parameters.current_fees->exists()); } - if (block_time < HARDFORK_CORE_BSIP64_TIME) - { - FC_ASSERT( op.new_parameters.current_fees->get().fee_per_kb == 0, - "Unable to set htlc_create_operation per_kb_fee until after BSIP 64 hardfork"); - } if (!HARDFORK_BSIP_40_PASSED(block_time)) { FC_ASSERT(!op.new_parameters.extensions.value.custom_authority_options.valid(), "Unable to set Custom Authority Options before hardfork BSIP 40"); diff --git a/libraries/protocol/fee_schedule_calc.cpp b/libraries/protocol/fee_schedule_calc.cpp index 8c0900d75f..9b62ef4f04 100644 --- a/libraries/protocol/fee_schedule_calc.cpp +++ b/libraries/protocol/fee_schedule_calc.cpp @@ -53,6 +53,18 @@ namespace graphene { namespace protocol { } }; + /** + * Specialization for htlc_create_operation + * + * For HTLCs, after HF_BSIP64, we need to add in the fee per kb + */ + template<> + uint64_t calc_fee_visitor::operator()(const htlc_create_operation& op)const + { + transfer_operation::fee_parameters_type t; + return op.calculate_fee( param.get(), t.price_per_kbyte).value; + } + asset fee_schedule::calculate_fee( const operation& op )const { uint64_t required_fee = op.visit( calc_fee_visitor( *this, op ) ); diff --git a/libraries/protocol/htlc.cpp b/libraries/protocol/htlc.cpp index 84ad4a8ac1..fc1004539a 100644 --- a/libraries/protocol/htlc.cpp +++ b/libraries/protocol/htlc.cpp @@ -34,14 +34,14 @@ namespace graphene { namespace protocol { FC_ASSERT( amount.amount > 0, "HTLC amount should be greater than zero" ); } - share_type htlc_create_operation::calculate_fee( const fee_parameters_type& fee_params )const + share_type htlc_create_operation::calculate_fee( const fee_parameters_type& fee_params, uint32_t fee_per_kb )const { uint64_t days = ( claim_period_seconds + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; // multiply with overflow check uint64_t per_day_fee = fee_params.fee_per_day * days; uint64_t per_kb_fee = 0; if (extensions.value.memo.valid()) - per_kb_fee = calculate_data_fee( fc::raw::pack_size(extensions.value.memo), fee_params.fee_per_kb); + per_kb_fee = calculate_data_fee( fc::raw::pack_size(extensions.value.memo), fee_per_kb); FC_ASSERT( days == 0 || per_day_fee / days == fee_params.fee_per_day, "Fee calculation overflow" ); return fee_params.fee + per_day_fee + per_kb_fee; } diff --git a/libraries/protocol/include/graphene/protocol/htlc.hpp b/libraries/protocol/include/graphene/protocol/htlc.hpp index d550518b4e..7d560cee7f 100644 --- a/libraries/protocol/include/graphene/protocol/htlc.hpp +++ b/libraries/protocol/include/graphene/protocol/htlc.hpp @@ -47,7 +47,6 @@ namespace graphene { namespace protocol { struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; - uint64_t fee_per_kb = 0; }; // paid to network @@ -85,7 +84,7 @@ namespace graphene { namespace protocol { /**** * @brief calculates the fee to be paid for this operation */ - share_type calculate_fee(const fee_parameters_type& fee_params)const; + share_type calculate_fee(const fee_parameters_type& fee_params, uint32_t fee_per_kb)const; }; struct htlc_redeem_operation : public base_operation @@ -216,7 +215,7 @@ namespace graphene { namespace protocol { FC_REFLECT_TYPENAME( graphene::protocol::htlc_hash ) -FC_REFLECT( graphene::protocol::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) (fee_per_kb) ) +FC_REFLECT( graphene::protocol::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) FC_REFLECT( graphene::protocol::htlc_create_operation::additional_options_type, (memo)) FC_REFLECT( graphene::protocol::htlc_redeem_operation::fee_parameters_type, (fee) (fee_per_kb) ) FC_REFLECT( graphene::protocol::htlc_redeemed_operation::fee_parameters_type, ) // VIRTUAL diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 19872347fb..09385b64f2 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -293,12 +293,12 @@ try { create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); create_operation.extensions.value.memo = memo_data(); create_operation.extensions.value.memo->from = alice_public_key; create_operation.extensions.value.memo->to = bob_public_key; create_operation.extensions.value.memo->set_message( alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); trx.operations.push_back(create_operation); sign(trx, alice_private_key); try { @@ -306,7 +306,11 @@ try { BOOST_TEST_FAIL("crate_operation should have failed due to the memo field"); } catch(fc::exception& ex) { if (ex.to_string().find("Memo unavailable") == std::string::npos ) - BOOST_TEST_FAIL("create_operation failed but not due to the memo field."); + { + std::stringstream ss; + ss << "create_operation failed but not due to the memo field. Message was: " << ex.to_detail_string(); + BOOST_FAIL(ss.str()); + } } trx.clear(); } @@ -325,12 +329,12 @@ try { create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = 0; create_operation.from = alice_id; - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); create_operation.extensions.value.memo = memo_data(); create_operation.extensions.value.memo->from = alice_public_key; create_operation.extensions.value.memo->to = bob_public_key; create_operation.extensions.value.memo->set_message( alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); @@ -341,8 +345,8 @@ try { generate_block(); } - // verify funds on hold... 100 - 3 = 97, minus the 4 coin fee = 93 - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 93 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify funds on hold... 100 - 3 = 97, minus the transaction fee = 91.78907 + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 91.78907 * GRAPHENE_BLOCKCHAIN_PRECISION ); // make sure Bob (or anyone) can see the details of the transaction graphene::app::database_api db_api(db); @@ -384,8 +388,8 @@ try { } // verify funds end up in Bob's account (3) BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); - // verify funds remain out of Alice's acount ( 100 - 3 - 4 ) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 93 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify funds remain out of Alice's acount ( 100 - 3 - transaction fee ) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 91.78907 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify all three get notified std::vector history = get_operation_history(alice_id); BOOST_CHECK_EQUAL( history.size(), alice_num_history + 1); @@ -686,88 +690,6 @@ BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) = current_fee_schedule.get(); BOOST_CHECK_EQUAL(htlc_fee.fee, 2 * GRAPHENE_BLOCKCHAIN_PRECISION); - // verify that the per_kb fee can't be set until after BSIP 64 - { - BOOST_TEST_MESSAGE("Attempting to set HTLC per_kb fees before hard fork."); - - // get existing fee_schedule - const chain_parameters& existing_params = db.get_global_properties().parameters; - const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); - // create a new fee_shedule - std::shared_ptr new_fee_schedule = std::make_shared(); - new_fee_schedule->scale = existing_fee_schedule.scale; - // replace the old with the new - flat_map params_map = get_htlc_fee_parameters(); - htlc_create_operation::fee_parameters_type create_param; - create_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; - create_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; - create_param.fee_per_kb = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; - params_map[((operation)htlc_create_operation()).which()] = create_param; - for(auto param : existing_fee_schedule.parameters) - { - auto itr = params_map.find(param.which()); - if (itr == params_map.end()) - new_fee_schedule->parameters.insert(param); - else - { - new_fee_schedule->parameters.insert( (*itr).second); - } - } - 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 uop; - uop.new_parameters.current_fees = new_fee_schedule; - cop.proposed_ops.emplace_back(uop); - cop.fee = asset( 100000 ); - trx.operations.push_back( cop ); - GRAPHENE_CHECK_THROW(db.push_transaction( trx ), fc::exception); - trx.clear(); - } - // verify that the per_kb fee can be set after BSIP 64 - generate_blocks( HARDFORK_CORE_BSIP64_TIME + 10); - set_expiration(db, trx); - { - BOOST_TEST_MESSAGE("Attempting to set HTLC per_kb fees after hard fork."); - - // get existing fee_schedule - const chain_parameters& existing_params = db.get_global_properties().parameters; - const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); - // create a new fee_shedule - std::shared_ptr new_fee_schedule = std::make_shared(); - new_fee_schedule->scale = existing_fee_schedule.scale; - // replace the old with the new - flat_map params_map = get_htlc_fee_parameters(); - htlc_create_operation::fee_parameters_type create_param; - create_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; - create_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; - create_param.fee_per_kb = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; - params_map[((operation)htlc_create_operation()).which()] = create_param; - for(auto param : existing_fee_schedule.parameters) - { - auto itr = params_map.find(param.which()); - if (itr == params_map.end()) - new_fee_schedule->parameters.insert(param); - else - { - new_fee_schedule->parameters.insert( (*itr).second); - } - } - proposal_create_operation cop = proposal_create_operation::committee_proposal( - db.get_global_properties().parameters, db.head_block_time()); - cop.fee_paying_account = alice_id; - cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; - committee_member_update_global_parameters_operation uop; - uop.new_parameters.current_fees = new_fee_schedule; - cop.proposed_ops.emplace_back(uop); - cop.fee = db.current_fee_schedule().calculate_fee(cop); - trx.operations.push_back( cop ); - sign(trx, alice_private_key); - db.push_transaction( trx ); - trx.clear(); - } - } FC_LOG_AND_RETHROW() } @@ -871,13 +793,13 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) htlc_create_operation create; // no days create.claim_period_seconds = 0; - BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 2 ); + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee, 2).value, 2 ); // exactly 1 day create.claim_period_seconds = 60 * 60 * 24; - BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee, 2).value, 4 ); // tad over a day create.claim_period_seconds++; - BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 6 ); + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee, 2).value, 6 ); } // redeem { From 322113d9f477757e8fbd69e1255871cea6803017 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 18 Apr 2020 14:41:33 +0000 Subject: [PATCH 39/81] 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 40/81] 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 41/81] 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 42/81] 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 5f68e640ebe4fb6c3fc5fecd99ded26e14496499 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 18 Apr 2020 13:06:41 -0500 Subject: [PATCH 43/81] do htlc hf checks in proposals --- libraries/chain/htlc_evaluator.cpp | 57 ++++++++++++++++++-------- libraries/chain/proposal_evaluator.cpp | 11 ++++- libraries/protocol/htlc.cpp | 4 +- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 5cf2d2f1a5..2bd592302f 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -29,6 +29,41 @@ namespace graphene { namespace chain { + namespace detail + { + void check_htlc_create_hf_bsip64(const fc::time_point_sec& block_time, + const htlc_create_operation& op, const asset_object& asset_to_transfer) + { + if (block_time < HARDFORK_CORE_BSIP64_TIME) + { + // memo field added at harfork BSIP64 + // NOTE: both of these checks can be removed after hardfork time + FC_ASSERT( !op.extensions.value.memo.valid(), + "Memo unavailable until after HARDFORK BSIP64"); + // HASH160 added at hardfork BSIP64 + FC_ASSERT( op.preimage_hash.which() != + htlc_hash(fc::hash160()).which(), "HASH160 unavailable until after HARDFORK BSIP64" ); + } + else + { + // this can be moved to the normal non-hf checks after HF_BSIP64 + // IF there were no restricted transfers before HF_BSIP64 + FC_ASSERT( !asset_to_transfer.is_transfer_restricted(), + "Asset ${asset} cannot be transfered.", ("asset", asset_to_transfer.id) ); + } + + } + + void check_htlc_redeem_hf_bsip64(const fc::time_point_sec& block_time, + const htlc_redeem_operation& op, const htlc_object* htlc_obj) + { + // TODO: The hardfork portion of this check can be removed if no HTLC redemptions are + // attempted on an HTLC with a 0 preimage size before the hardfork date. + if ( htlc_obj->conditions.hash_lock.preimage_size > 0U || + block_time <= HARDFORK_CORE_BSIP64_TIME ) + FC_ASSERT(op.preimage.size() == htlc_obj->conditions.hash_lock.preimage_size, "Preimage size mismatch."); + } + } // end of graphene::chain::details optional get_committee_htlc_options(graphene::chain::database& db) { @@ -48,24 +83,16 @@ namespace graphene { FC_ASSERT( o.preimage_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure the sender has the funds for the HTLC FC_ASSERT( d.get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; - // memo field added at harfork BSIP64 - // NOTE: this check can be removed after hardfork time - FC_ASSERT( d.head_block_time() > HARDFORK_CORE_BSIP64_TIME || !o.extensions.value.memo.valid(), - "Memo unavailable until after HARDFORK BSIP64"); - // HASH160 added at hardfork BSIP64 - FC_ASSERT( d.head_block_time() > HARDFORK_CORE_BSIP64_TIME || o.preimage_hash.which() != - htlc_hash(fc::hash160()).which(), "HASH160 unavailable until after HARDFORK BSIP64" ); const auto& asset_to_transfer = o.amount.asset_id( d ); const auto& from_account = o.from( d ); const auto& to_account = o.to( d ); + detail::check_htlc_create_hf_bsip64(d.head_block_time(), o, asset_to_transfer); FC_ASSERT( is_authorized_asset( d, from_account, asset_to_transfer ), "Asset ${asset} is not authorized for account ${acct}.", ( "asset", asset_to_transfer.id )( "acct", from_account.id ) ); FC_ASSERT( is_authorized_asset( d, to_account, asset_to_transfer ), "Asset ${asset} is not authorized for account ${acct}.", - ( "asset", asset_to_transfer.id )( "acct", to_account.id ) ); - FC_ASSERT( d.head_block_time() < HARDFORK_CORE_BSIP64_TIME || !asset_to_transfer.is_transfer_restricted(), - "Asset ${asset} cannot be transfered.", ("asset", asset_to_transfer.id) ); + ( "asset", asset_to_transfer.id )( "acct", to_account.id ) ); return void_result(); } @@ -110,12 +137,10 @@ namespace graphene { void_result htlc_redeem_evaluator::do_evaluate(const htlc_redeem_operation& o) { - htlc_obj = &db().get(o.htlc_id); - // TODO: The hardfork portion of this check can be removed if no HTLC redemptions are - // attempted on an HTLC with a 0 preimage size before the hardfork date. - if ( htlc_obj->conditions.hash_lock.preimage_size > 0U || - db().head_block_time() <= HARDFORK_CORE_BSIP64_TIME ) - FC_ASSERT(o.preimage.size() == htlc_obj->conditions.hash_lock.preimage_size, "Preimage size mismatch."); + auto& d = db(); + htlc_obj = &d.get(o.htlc_id); + detail::check_htlc_redeem_hf_bsip64(d.head_block_time(), o, htlc_obj); + const htlc_redeem_visitor vtor( o.preimage ); FC_ASSERT( htlc_obj->conditions.hash_lock.preimage_hash.visit( vtor ), "Provided preimage does not generate correct hash."); diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 5a4a2b69bc..bb304270e1 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -25,11 +25,17 @@ #include #include #include +#include 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_1774(const fc::time_point_sec& block_time, const asset_options& options); + void check_htlc_create_hf_bsip64(const fc::time_point_sec& block_time, const htlc_create_operation& op, + const asset_object& asset); + void check_htlc_redeem_hf_bsip64(const fc::time_point_sec& block_time, + const htlc_redeem_operation& op, const htlc_object* htlc_obj); + } struct proposal_operation_hardfork_visitor @@ -78,9 +84,12 @@ struct proposal_operation_hardfork_visitor } void operator()(const graphene::chain::htlc_create_operation &op) const { FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); + detail::check_htlc_create_hf_bsip64(block_time, op, op.amount.asset_id(db)); } void operator()(const graphene::chain::htlc_redeem_operation &op) const { FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); + const auto* htlc_obj = &op.htlc_id(db); + detail::check_htlc_redeem_hf_bsip64(block_time, op, htlc_obj); } void operator()(const graphene::chain::htlc_extend_operation &op) const { FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); diff --git a/libraries/protocol/htlc.cpp b/libraries/protocol/htlc.cpp index fc1004539a..fc77e390ef 100644 --- a/libraries/protocol/htlc.cpp +++ b/libraries/protocol/htlc.cpp @@ -38,8 +38,8 @@ namespace graphene { namespace protocol { { uint64_t days = ( claim_period_seconds + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; // multiply with overflow check - uint64_t per_day_fee = fee_params.fee_per_day * days; - uint64_t per_kb_fee = 0; + share_type per_day_fee = fee_params.fee_per_day * days; + share_type per_kb_fee = 0; if (extensions.value.memo.valid()) per_kb_fee = calculate_data_fee( fc::raw::pack_size(extensions.value.memo), fee_per_kb); FC_ASSERT( days == 0 || per_day_fee / days == fee_params.fee_per_day, "Fee calculation overflow" ); From bbab2f9d7586eee65284a5afbef7150942f0fd40 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 18 Apr 2020 13:58:29 -0500 Subject: [PATCH 44/81] add preimage serialization --- libraries/chain/htlc_evaluator.cpp | 4 ++-- libraries/protocol/include/graphene/protocol/htlc.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 2bd592302f..db086bba66 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -41,8 +41,8 @@ namespace graphene { FC_ASSERT( !op.extensions.value.memo.valid(), "Memo unavailable until after HARDFORK BSIP64"); // HASH160 added at hardfork BSIP64 - FC_ASSERT( op.preimage_hash.which() != - htlc_hash(fc::hash160()).which(), "HASH160 unavailable until after HARDFORK BSIP64" ); + FC_ASSERT( !op.preimage_hash.is_type(), + "HASH160 unavailable until after HARDFORK BSIP64" ); } else { diff --git a/libraries/protocol/include/graphene/protocol/htlc.hpp b/libraries/protocol/include/graphene/protocol/htlc.hpp index 7d560cee7f..a5510ec092 100644 --- a/libraries/protocol/include/graphene/protocol/htlc.hpp +++ b/libraries/protocol/include/graphene/protocol/htlc.hpp @@ -226,7 +226,7 @@ FC_REFLECT( graphene::protocol::htlc_create_operation, (fee)(from)(to)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)) FC_REFLECT( graphene::protocol::htlc_redeem_operation, (fee)(htlc_id)(redeemer)(preimage)(extensions)) FC_REFLECT( graphene::protocol::htlc_redeemed_operation, - (fee)(htlc_id)(from)(to)(redeemer)(amount)(htlc_preimage_hash)(htlc_preimage_size)) + (fee)(htlc_id)(from)(to)(redeemer)(amount)(htlc_preimage_hash)(htlc_preimage_size)(preimage)) FC_REFLECT( graphene::protocol::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) FC_REFLECT( graphene::protocol::htlc_refund_operation, (fee)(htlc_id)(to)(original_htlc_recipient)(htlc_amount)(htlc_preimage_hash)(htlc_preimage_size)) From b6bb193c38fa80de0c57ed6a087fb0ddf864c5d4 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 18 Apr 2020 14:08:07 -0500 Subject: [PATCH 45/81] adjust transfer_restricted logic --- libraries/chain/htlc_evaluator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index db086bba66..1a1cfbae41 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -48,10 +48,10 @@ namespace graphene { { // this can be moved to the normal non-hf checks after HF_BSIP64 // IF there were no restricted transfers before HF_BSIP64 - FC_ASSERT( !asset_to_transfer.is_transfer_restricted(), + FC_ASSERT( !asset_to_transfer.is_transfer_restricted() + || op.from == asset_to_transfer.issuer || op.to == asset_to_transfer.issuer, "Asset ${asset} cannot be transfered.", ("asset", asset_to_transfer.id) ); } - } void check_htlc_redeem_hf_bsip64(const fc::time_point_sec& block_time, From 1039366ac018fcc71354bf2773cef6a460ada4f5 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 18 Apr 2020 16:07:47 -0500 Subject: [PATCH 46/81] Exclude memo on locked wallets --- libraries/wallet/operation_printer.cpp | 49 ++++++++++++++------------ libraries/wallet/operation_printer.hpp | 3 ++ tests/cli/main.cpp | 25 ++++++++++--- 3 files changed, 50 insertions(+), 27 deletions(-) diff --git a/libraries/wallet/operation_printer.cpp b/libraries/wallet/operation_printer.cpp index 393cb1d63a..cb4d2c9300 100644 --- a/libraries/wallet/operation_printer.cpp +++ b/libraries/wallet/operation_printer.cpp @@ -79,37 +79,45 @@ std::string operation_printer::operator()(const transfer_to_blind_operation& op) << " fee: " << fa.amount_to_pretty_string( op.fee ); return ""; } -string operation_printer::operator()(const transfer_operation& op) const + +string print_memo( const graphene::wallet::detail::wallet_api_impl& wallet, + const fc::optional& memo, ostream& out) { - out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount) - << " from " << wallet.get_account(op.from).name << " to " << wallet.get_account(op.to).name; - std::string memo; - if( op.memo ) + std::string outstr; + if( memo ) { if( wallet.is_locked() ) { out << " -- Unlock wallet to see memo."; } else { try { - FC_ASSERT( wallet._keys.count(op.memo->to) || wallet._keys.count(op.memo->from), + FC_ASSERT( wallet._keys.count(memo->to) || wallet._keys.count(memo->from), "Memo is encrypted to a key ${to} or ${from} not in this wallet.", - ("to", op.memo->to)("from",op.memo->from) ); - if( wallet._keys.count(op.memo->to) ) { - auto my_key = wif_to_key(wallet._keys.at(op.memo->to)); + ("to", memo->to)("from",memo->from) ); + if( wallet._keys.count(memo->to) ) { + auto my_key = wif_to_key(wallet._keys.at(memo->to)); FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); - memo = op.memo->get_message(*my_key, op.memo->from); - out << " -- Memo: " << memo; + outstr = memo->get_message(*my_key, memo->from); + out << " -- Memo: " << outstr; } else { - auto my_key = wif_to_key(wallet._keys.at(op.memo->from)); + auto my_key = wif_to_key(wallet._keys.at(memo->from)); FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); - memo = op.memo->get_message(*my_key, op.memo->to); - out << " -- Memo: " << memo; + outstr = memo->get_message(*my_key, memo->to); + out << " -- Memo: " << outstr; } } catch (const fc::exception& e) { out << " -- could not decrypt memo"; } } - } + } + return outstr; +} + +string operation_printer::operator()(const transfer_operation& op) const +{ + out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount) + << " from " << wallet.get_account(op.from).name << " to " << wallet.get_account(op.to).name; + std::string memo = print_memo( wallet, op.memo, out ); fee(op.fee); return memo; } @@ -178,17 +186,14 @@ std::string operation_printer::operator()(const htlc_create_operation& op) const operation_result_printer rprinter(wallet); std::string database_id = result.visit(rprinter); - out << "Create HTLC to " << to.name - << " with id " << database_id - << " preimage hash: [" - << op.preimage_hash.visit( vtor ) - << "] (Fee: " << fee_asset.amount_to_pretty_string( op.fee ) << ")"; + out << "Create HTLC to " << to.name << " with id " << database_id + << " preimage hash: [" << op.preimage_hash.visit( vtor ) << "] "; + print_memo(wallet, op.extensions.value.memo, out); // determine if the block that the HTLC is in is before or after LIB int32_t pending_blocks = hist.block_num - wallet.get_dynamic_global_properties().last_irreversible_block_num; if (pending_blocks > 0) out << " (pending " << std::to_string(pending_blocks) << " blocks)"; - - return ""; + return fee(op.fee); } std::string operation_result_printer::operator()(const void_result& x) const diff --git a/libraries/wallet/operation_printer.hpp b/libraries/wallet/operation_printer.hpp index c124096a8d..6eb769b983 100644 --- a/libraries/wallet/operation_printer.hpp +++ b/libraries/wallet/operation_printer.hpp @@ -36,6 +36,9 @@ namespace graphene { namespace wallet { namespace detail { +std::string print_memo( const graphene::wallet::detail::wallet_api_impl& wallet, + const fc::optional& memo, ostream& out); + struct operation_result_printer { public: diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 1af768dd1f..626c67661f 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -1739,16 +1739,31 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) auto hist = con.wallet_api_ptr->get_account_history("alice", 10); for(size_t i = 0; i < hist.size(); ++i) { + auto obj = hist[i]; + std::stringstream ss; + ss << "Description: " << obj.description << "\n"; + auto str = ss.str(); + BOOST_TEST_MESSAGE( str ); if (i < 2) { - auto obj = hist[i]; - std::stringstream ss; - ss << "Description: " << obj.description << " Memo: " << obj.memo << "\n"; - auto str = ss.str(); - BOOST_TEST_MESSAGE( str ); BOOST_CHECK( str.find("HASH160 008e") != std::string::npos ); } } + con.wallet_api_ptr->lock(); + hist = con.wallet_api_ptr->get_account_history("alice", 10); + for(size_t i = 0; i < hist.size(); ++i) + { + auto obj = hist[i]; + std::stringstream ss; + ss << "Description: " << obj.description << "\n"; + auto str = ss.str(); + BOOST_TEST_MESSAGE( str ); + if (i < 2) + { + BOOST_CHECK( str.find("HASH160 008e") != std::string::npos ); + } + } + con.wallet_api_ptr->unlock("supersecret"); } // Alice can now look over Bob's HTLC, to see if it is what was agreed to: From 3629d80ed9e0cde229cae0227142c343e81d92d2 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 18 Apr 2020 16:17:04 -0500 Subject: [PATCH 47/81] put print_memo in proper scope --- libraries/wallet/operation_printer.cpp | 51 +++++++++++++------------- libraries/wallet/operation_printer.hpp | 5 +-- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/libraries/wallet/operation_printer.cpp b/libraries/wallet/operation_printer.cpp index cb4d2c9300..c531d923fd 100644 --- a/libraries/wallet/operation_printer.cpp +++ b/libraries/wallet/operation_printer.cpp @@ -58,30 +58,7 @@ std::string operation_printer::fee(const graphene::protocol::asset& a)const { return ""; } -std::string operation_printer::operator()(const transfer_from_blind_operation& op)const -{ - auto a = wallet.get_asset( op.fee.asset_id ); - auto receiver = wallet.get_account( op.to ); - - out << receiver.name - << " received " << a.amount_to_pretty_string( op.amount ) << " from blinded balance"; - return ""; -} -std::string operation_printer::operator()(const transfer_to_blind_operation& op)const -{ - auto fa = wallet.get_asset( op.fee.asset_id ); - auto a = wallet.get_asset( op.amount.asset_id ); - auto sender = wallet.get_account( op.from ); - - out << sender.name - << " sent " << a.amount_to_pretty_string( op.amount ) << " to " << op.outputs.size() - << " blinded balance" << (op.outputs.size()>1?"s":"") - << " fee: " << fa.amount_to_pretty_string( op.fee ); - return ""; -} - -string print_memo( const graphene::wallet::detail::wallet_api_impl& wallet, - const fc::optional& memo, ostream& out) +string operation_printer::print_memo( const fc::optional& memo )const { std::string outstr; if( memo ) @@ -113,11 +90,33 @@ string print_memo( const graphene::wallet::detail::wallet_api_impl& wallet, return outstr; } +std::string operation_printer::operator()(const transfer_from_blind_operation& op)const +{ + auto a = wallet.get_asset( op.fee.asset_id ); + auto receiver = wallet.get_account( op.to ); + + out << receiver.name + << " received " << a.amount_to_pretty_string( op.amount ) << " from blinded balance"; + return ""; +} +std::string operation_printer::operator()(const transfer_to_blind_operation& op)const +{ + auto fa = wallet.get_asset( op.fee.asset_id ); + auto a = wallet.get_asset( op.amount.asset_id ); + auto sender = wallet.get_account( op.from ); + + out << sender.name + << " sent " << a.amount_to_pretty_string( op.amount ) << " to " << op.outputs.size() + << " blinded balance" << (op.outputs.size()>1?"s":"") + << " fee: " << fa.amount_to_pretty_string( op.fee ); + return ""; +} + string operation_printer::operator()(const transfer_operation& op) const { out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount) << " from " << wallet.get_account(op.from).name << " to " << wallet.get_account(op.to).name; - std::string memo = print_memo( wallet, op.memo, out ); + std::string memo = print_memo( op.memo ); fee(op.fee); return memo; } @@ -188,7 +187,7 @@ std::string operation_printer::operator()(const htlc_create_operation& op) const out << "Create HTLC to " << to.name << " with id " << database_id << " preimage hash: [" << op.preimage_hash.visit( vtor ) << "] "; - print_memo(wallet, op.extensions.value.memo, out); + print_memo( op.extensions.value.memo ); // determine if the block that the HTLC is in is before or after LIB int32_t pending_blocks = hist.block_num - wallet.get_dynamic_global_properties().last_irreversible_block_num; if (pending_blocks > 0) diff --git a/libraries/wallet/operation_printer.hpp b/libraries/wallet/operation_printer.hpp index 6eb769b983..a5ce18834f 100644 --- a/libraries/wallet/operation_printer.hpp +++ b/libraries/wallet/operation_printer.hpp @@ -36,9 +36,6 @@ namespace graphene { namespace wallet { namespace detail { -std::string print_memo( const graphene::wallet::detail::wallet_api_impl& wallet, - const fc::optional& memo, ostream& out); - struct operation_result_printer { public: @@ -102,6 +99,8 @@ struct operation_printer std::string operator()(const graphene::protocol::htlc_create_operation& op)const; std::string operator()(const graphene::protocol::htlc_redeem_operation& op)const; std::string operator()(const graphene::protocol::htlc_redeemed_operation& op)const; + protected: + std::string print_memo( const fc::optional& memo)const; }; }}} // namespace graphene::wallet::detail From 5a1dc7268fa8f9fd0395a66f0d2c02912e3e3a08 Mon Sep 17 00:00:00 2001 From: abitmore Date: Mon, 20 Apr 2020 00:16:34 +0000 Subject: [PATCH 48/81] 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 49/81] 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 50/81] 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 133f7c369c972790480ef0d2a62b085fe450bfa8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 20 Apr 2020 08:49:45 -0500 Subject: [PATCH 51/81] truncate long preimages in wallet output --- libraries/wallet/operation_printer.cpp | 56 +++++++++++---------- libraries/wallet/operation_printer.hpp | 4 ++ tests/cli/main.cpp | 67 +++++++++++++------------- 3 files changed, 68 insertions(+), 59 deletions(-) diff --git a/libraries/wallet/operation_printer.cpp b/libraries/wallet/operation_printer.cpp index c531d923fd..975ac2f5b0 100644 --- a/libraries/wallet/operation_printer.cpp +++ b/libraries/wallet/operation_printer.cpp @@ -90,6 +90,32 @@ string operation_printer::print_memo( const fc::optional& preimage)const +{ + if (preimage.size() == 0) + return; + out << " with preimage \""; + // cut it at 50 bytes max + auto flags = out.flags(); + out << std::hex << setw(2) << setfill('0'); + for (size_t i = 0; i < std::min(50, preimage.size()); i++) + out << +preimage[i]; + out.flags(flags); + if (preimage.size() > 50) + out << "...(truncated due to size)"; + out << "\""; +} + +string operation_printer::print_redeem(const graphene::protocol::htlc_id_type& id, + const std::string& redeemer, const std::vector& preimage, + const graphene::protocol::asset& op_fee)const +{ + out << redeemer << " redeemed HTLC with id " + << std::string( static_cast(id)); + print_preimage( preimage ); + return fee(op_fee); +} + std::string operation_printer::operator()(const transfer_from_blind_operation& op)const { auto a = wallet.get_asset( op.fee.asset_id ); @@ -146,34 +172,12 @@ std::string operation_printer::operator()(const asset_create_operation& op) cons std::string operation_printer::operator()(const htlc_redeem_operation& op) const { - auto flags = out.flags(); - out << "Redeem HTLC with database id " - << std::to_string(op.htlc_id.space_id) - << "." << std::to_string(op.htlc_id.type_id) - << "." << std::to_string((uint64_t)op.htlc_id.instance) - << " with preimage \""; - out << std::hex; - for (unsigned char c : op.preimage) - out << c; - out.flags(flags); - out << "\""; - return fee(op.fee); + return print_redeem(op.htlc_id, wallet.get_account(op.redeemer).name, op.preimage, op.fee); } std::string operation_printer::operator()(const htlc_redeemed_operation& op) const { - auto flags = out.flags(); - out << "Redeem HTLC with database id " - << std::to_string(op.htlc_id.space_id) - << "." << std::to_string(op.htlc_id.type_id) - << "." << std::to_string((uint64_t)op.htlc_id.instance) - << " with preimage \""; - out << std::hex; - for (unsigned char c : op.preimage) - out << c; - out.flags(flags); - out << "\""; - return fee(op.fee); + return print_redeem(op.htlc_id, wallet.get_account(op.redeemer).name, op.preimage, op.fee); } std::string operation_printer::operator()(const htlc_create_operation& op) const @@ -182,10 +186,12 @@ std::string operation_printer::operator()(const htlc_create_operation& op) const auto fee_asset = wallet.get_asset( op.fee.asset_id ); auto to = wallet.get_account( op.to ); + auto from = wallet.get_account( op.from ); operation_result_printer rprinter(wallet); std::string database_id = result.visit(rprinter); - out << "Create HTLC to " << to.name << " with id " << database_id + out << "Create HTLC from " << from.name << " to " << to.name + << " with id " << database_id << " preimage hash: [" << op.preimage_hash.visit( vtor ) << "] "; print_memo( op.extensions.value.memo ); // determine if the block that the HTLC is in is before or after LIB diff --git a/libraries/wallet/operation_printer.hpp b/libraries/wallet/operation_printer.hpp index a5ce18834f..428ca469d1 100644 --- a/libraries/wallet/operation_printer.hpp +++ b/libraries/wallet/operation_printer.hpp @@ -101,6 +101,10 @@ struct operation_printer std::string operator()(const graphene::protocol::htlc_redeemed_operation& op)const; protected: std::string print_memo( const fc::optional& memo)const; + void print_preimage( const std::vector& preimage)const; + std::string print_redeem(const graphene::protocol::htlc_id_type& id, + const std::string& redeemer, const std::vector& preimage, + const graphene::protocol::asset& op_fee)const; }; }}} // namespace graphene::wallet::detail diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 626c67661f..8e8e7d3050 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -1686,7 +1686,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) BOOST_TEST_MESSAGE("Alice has agreed to buy 3 BOBCOIN from Bob for 3 BTS. Alice creates an HTLC"); // create an HTLC - std::string preimage_string = "My Secret"; + std::string preimage_string = "My Super Long Secret that is larger than 50 charaters. How do I look?\n"; fc::hash160 preimage_md = fc::hash160::hash(preimage_string); std::stringstream ss; for(size_t i = 0; i < preimage_md.data_size(); i++) @@ -1735,35 +1735,6 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get(); bob_htlc_id_as_string = (std::string)(object_id_type)htlc_id; BOOST_TEST_MESSAGE("Bob shares the HTLC ID with Alice. The HTLC ID is: " + bob_htlc_id_as_string); - // test operation_printer - auto hist = con.wallet_api_ptr->get_account_history("alice", 10); - for(size_t i = 0; i < hist.size(); ++i) - { - auto obj = hist[i]; - std::stringstream ss; - ss << "Description: " << obj.description << "\n"; - auto str = ss.str(); - BOOST_TEST_MESSAGE( str ); - if (i < 2) - { - BOOST_CHECK( str.find("HASH160 008e") != std::string::npos ); - } - } - con.wallet_api_ptr->lock(); - hist = con.wallet_api_ptr->get_account_history("alice", 10); - for(size_t i = 0; i < hist.size(); ++i) - { - auto obj = hist[i]; - std::stringstream ss; - ss << "Description: " << obj.description << "\n"; - auto str = ss.str(); - BOOST_TEST_MESSAGE( str ); - if (i < 2) - { - BOOST_CHECK( str.find("HASH160 008e") != std::string::npos ); - } - } - con.wallet_api_ptr->unlock("supersecret"); } // Alice can now look over Bob's HTLC, to see if it is what was agreed to: @@ -1774,8 +1745,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) // Alice likes what she sees, so uses her preimage to get her BOBCOIN { BOOST_TEST_MESSAGE("Alice uses her preimage to retrieve the BOBCOIN"); - std::string secret = "My Secret"; - con.wallet_api_ptr->htlc_redeem(bob_htlc_id_as_string, "alice", secret, true); + con.wallet_api_ptr->htlc_redeem(bob_htlc_id_as_string, "alice", preimage_string, true); BOOST_TEST_MESSAGE("The system is generating a block"); BOOST_CHECK(generate_block(app1)); } @@ -1784,12 +1754,41 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) // Bob can use the preimage to retrieve his BTS { BOOST_TEST_MESSAGE("Bob uses Alice's preimage to retrieve the BOBCOIN"); - std::string secret = "My Secret"; - con.wallet_api_ptr->htlc_redeem(alice_htlc_id_as_string, "bob", secret, true); + con.wallet_api_ptr->htlc_redeem(alice_htlc_id_as_string, "bob", preimage_string, true); BOOST_TEST_MESSAGE("The system is generating a block"); BOOST_CHECK(generate_block(app1)); } + // test operation_printer + auto hist = con.wallet_api_ptr->get_account_history("alice", 10); + for(size_t i = 0; i < hist.size(); ++i) + { + auto obj = hist[i]; + std::stringstream ss; + ss << "Description: " << obj.description << "\n"; + auto str = ss.str(); + BOOST_TEST_MESSAGE( str ); + if (i == 3 || i == 4) + { + BOOST_CHECK( str.find("HASH160 620e4d5ba") != std::string::npos ); + } + } + con.wallet_api_ptr->lock(); + hist = con.wallet_api_ptr->get_account_history("alice", 10); + for(size_t i = 0; i < hist.size(); ++i) + { + auto obj = hist[i]; + std::stringstream ss; + ss << "Description: " << obj.description << "\n"; + auto str = ss.str(); + BOOST_TEST_MESSAGE( str ); + if (i == 3 || i == 4) + { + BOOST_CHECK( str.find("HASH160 620e4d5ba") != std::string::npos ); + } + } + con.wallet_api_ptr->unlock("supersecret"); + // wait for everything to finish up fc::usleep(fc::seconds(1)); } catch( fc::exception& e ) { From 3e3df5afafaf84a14411e608468a8d649be5c8f8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 20 Apr 2020 10:34:28 -0500 Subject: [PATCH 52/81] standardize exact hardfork time --- libraries/chain/htlc_evaluator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 1a1cfbae41..04bb0fc8ef 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -60,7 +60,7 @@ namespace graphene { // TODO: The hardfork portion of this check can be removed if no HTLC redemptions are // attempted on an HTLC with a 0 preimage size before the hardfork date. if ( htlc_obj->conditions.hash_lock.preimage_size > 0U || - block_time <= HARDFORK_CORE_BSIP64_TIME ) + block_time < HARDFORK_CORE_BSIP64_TIME ) FC_ASSERT(op.preimage.size() == htlc_obj->conditions.hash_lock.preimage_size, "Preimage size mismatch."); } } // end of graphene::chain::details From ec78f4d77ce3c8b74ee4446df825de77893afabf Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 20 Apr 2020 10:34:56 -0500 Subject: [PATCH 53/81] Use current (not default) memo fee --- libraries/protocol/fee_schedule_calc.cpp | 2 +- tests/common/database_fixture.cpp | 6 ++++++ tests/tests/htlc_tests.cpp | 6 +++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libraries/protocol/fee_schedule_calc.cpp b/libraries/protocol/fee_schedule_calc.cpp index 9b62ef4f04..a8815bbfa9 100644 --- a/libraries/protocol/fee_schedule_calc.cpp +++ b/libraries/protocol/fee_schedule_calc.cpp @@ -61,7 +61,7 @@ namespace graphene { namespace protocol { template<> uint64_t calc_fee_visitor::operator()(const htlc_create_operation& op)const { - transfer_operation::fee_parameters_type t; + transfer_operation::fee_parameters_type t = param.get(); return op.calculate_fee( param.get(), t.price_per_kbyte).value; } diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 64e4f0ba7a..e4d021d3c3 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -1457,6 +1457,12 @@ flat_map< uint64_t, graphene::chain::fee_parameters > database_fixture::get_htlc extend_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; ret_val[((operation)htlc_extend_operation()).which()] = extend_param; + // set the transfer kb fee to something other than default, to verify we're looking + // at the correct fee + transfer_operation::fee_parameters_type transfer_param; + transfer_param.price_per_kbyte *= 2; + ret_val[ ((operation)transfer_operation()).which() ] = transfer_param; + return ret_val; } diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 09385b64f2..948adbadc7 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -345,8 +345,8 @@ try { generate_block(); } - // verify funds on hold... 100 - 3 = 97, minus the transaction fee = 91.78907 - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 91.78907 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify funds on hold... 100 - 3 = 97, minus the transaction fee = 90.57813 + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90.57813 * GRAPHENE_BLOCKCHAIN_PRECISION ); // make sure Bob (or anyone) can see the details of the transaction graphene::app::database_api db_api(db); @@ -389,7 +389,7 @@ try { // verify funds end up in Bob's account (3) BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify funds remain out of Alice's acount ( 100 - 3 - transaction fee ) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 91.78907 * GRAPHENE_BLOCKCHAIN_PRECISION ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90.57813 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify all three get notified std::vector history = get_operation_history(alice_id); BOOST_CHECK_EQUAL( history.size(), alice_num_history + 1); From fc158574deef6df05dd67b9fae10685cec84a5aa Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 20 Apr 2020 11:08:14 -0500 Subject: [PATCH 54/81] Detect overflow --- libraries/chain/htlc_evaluator.cpp | 2 +- libraries/protocol/htlc.cpp | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 04bb0fc8ef..3b1238bc9d 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -92,7 +92,7 @@ namespace graphene { ( "asset", asset_to_transfer.id )( "acct", from_account.id ) ); FC_ASSERT( is_authorized_asset( d, to_account, asset_to_transfer ), "Asset ${asset} is not authorized for account ${acct}.", - ( "asset", asset_to_transfer.id )( "acct", to_account.id ) ); + ( "asset", asset_to_transfer.id )( "acct", to_account.id ) ); return void_result(); } diff --git a/libraries/protocol/htlc.cpp b/libraries/protocol/htlc.cpp index fc77e390ef..5da238f571 100644 --- a/libraries/protocol/htlc.cpp +++ b/libraries/protocol/htlc.cpp @@ -38,12 +38,11 @@ namespace graphene { namespace protocol { { uint64_t days = ( claim_period_seconds + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; // multiply with overflow check - share_type per_day_fee = fee_params.fee_per_day * days; - share_type per_kb_fee = 0; + share_type total_fee = fee_params.fee; + total_fee += share_type(fee_params.fee_per_day) * days; if (extensions.value.memo.valid()) - per_kb_fee = calculate_data_fee( fc::raw::pack_size(extensions.value.memo), fee_per_kb); - FC_ASSERT( days == 0 || per_day_fee / days == fee_params.fee_per_day, "Fee calculation overflow" ); - return fee_params.fee + per_day_fee + per_kb_fee; + total_fee += calculate_data_fee( fc::raw::pack_size(extensions.value.memo), fee_per_kb); + return total_fee; } void htlc_redeem_operation::validate()const { From 4d6c20ae2aab6cb73c32582a72306f6048cc8e65 Mon Sep 17 00:00:00 2001 From: abitmore Date: Tue, 21 Apr 2020 12:50:28 +0000 Subject: [PATCH 55/81] 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 cf36230699f57c0b2d4cda66f1293d35c9ec3dcf Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 21 Apr 2020 09:49:14 -0500 Subject: [PATCH 56/81] remove decimal from test --- tests/tests/htlc_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 948adbadc7..27af19e2f8 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -346,7 +346,7 @@ try { } // verify funds on hold... 100 - 3 = 97, minus the transaction fee = 90.57813 - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90.57813 * GRAPHENE_BLOCKCHAIN_PRECISION ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 9057813 ); // make sure Bob (or anyone) can see the details of the transaction graphene::app::database_api db_api(db); @@ -389,7 +389,7 @@ try { // verify funds end up in Bob's account (3) BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify funds remain out of Alice's acount ( 100 - 3 - transaction fee ) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90.57813 * GRAPHENE_BLOCKCHAIN_PRECISION ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 9057813 ); // verify all three get notified std::vector history = get_operation_history(alice_id); BOOST_CHECK_EQUAL( history.size(), alice_num_history + 1); From 96112d4716ab4c182f096acd61fd5d2cf7fbd3ea Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 21 Apr 2020 11:22:02 -0500 Subject: [PATCH 57/81] BOOST_TEST_FAIL no longer part of Boost --- tests/tests/htlc_tests.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 27af19e2f8..97344c3e64 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -275,10 +275,10 @@ try { // this try/catch is just to get some console output to verify the exception try { PUSH_TX(db, trx, ~0); - BOOST_TEST_FAIL("crate_operation should have failed due to the HASH160 hash"); + BOOST_FAIL("crate_operation should have failed due to the HASH160 hash"); } catch(fc::exception& ex) { if ( ex.to_string().find("HASH160 unavailable") == std::string::npos ) - BOOST_TEST_FAIL("create_operation failed but not due to the hash160 function"); + BOOST_FAIL("create_operation failed but not due to the hash160 function"); } trx.clear(); } @@ -303,7 +303,7 @@ try { sign(trx, alice_private_key); try { PUSH_TX(db, trx, ~0); - BOOST_TEST_FAIL("crate_operation should have failed due to the memo field"); + BOOST_FAIL("crate_operation should have failed due to the memo field"); } catch(fc::exception& ex) { if (ex.to_string().find("Memo unavailable") == std::string::npos ) { From f99d81f2d82dd848b43737ed629aa493a8cef4a5 Mon Sep 17 00:00:00 2001 From: Michel Santos Date: Tue, 21 Apr 2020 11:20:18 -0400 Subject: [PATCH 58/81] 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 59/81] 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 60/81] 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 61/81] 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 62/81] 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 63/81] 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 79a7abf2109a832ffe6760601fd1ec283b490c86 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 23 Apr 2020 08:44:22 -0500 Subject: [PATCH 64/81] fee_helper refactor --- libraries/chain/account_evaluator.cpp | 2 +- libraries/chain/db_maint.cpp | 2 +- .../graphene/protocol/fee_schedule.hpp | 86 +++++-------------- tests/tests/fee_tests.cpp | 2 +- 4 files changed, 25 insertions(+), 67 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 4480d48530..93b9f0f7db 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -217,7 +217,7 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio && global_properties.parameters.account_fee_scale_bitshifts != 0 ) { d.modify(global_properties, [](global_property_object& p) { - p.parameters.get_mutable_fees().get().basic_fee <<= p.parameters.account_fee_scale_bitshifts; + p.parameters.get_mutable_fees().mutable_get().basic_fee <<= p.parameters.account_fee_scale_bitshifts; }); } diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 1bdcc67d1a..9e219b1711 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -1183,7 +1183,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g modify(gpo, [&dgpo](global_property_object& p) { // Remove scaling of account registration fee - p.parameters.get_mutable_fees().get().basic_fee >>= p.parameters.account_fee_scale_bitshifts * + p.parameters.get_mutable_fees().mutable_get().basic_fee >>= p.parameters.account_fee_scale_bitshifts * (dgpo.accounts_registered_this_interval / p.parameters.accounts_per_fee_scale); if( p.pending_parameters ) diff --git a/libraries/protocol/include/graphene/protocol/fee_schedule.hpp b/libraries/protocol/include/graphene/protocol/fee_schedule.hpp index 275e87e704..36c31d23d1 100644 --- a/libraries/protocol/include/graphene/protocol/fee_schedule.hpp +++ b/libraries/protocol/include/graphene/protocol/fee_schedule.hpp @@ -37,42 +37,38 @@ namespace graphene { namespace protocol { template class fee_helper { public: - const typename Operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const + const typename Operation::fee_parameters_type& get(const fee_parameters::flat_set_type& parameters)const { auto itr = parameters.find( typename Operation::fee_parameters_type() ); - FC_ASSERT( itr != parameters.end() ); - return itr->template get(); - } - }; + if( itr != parameters.end() ) + return itr->template get(); - template<> - class fee_helper { - public: - const account_create_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const - { - auto itr = parameters.find( account_create_operation::fee_parameters_type() ); - FC_ASSERT( itr != parameters.end() ); - return itr->get(); + static typename Operation::fee_parameters_type dummy; + return dummy; } - typename account_create_operation::fee_parameters_type& get(fee_parameters::flat_set_type& parameters)const + typename Operation::fee_parameters_type& mutable_get(fee_parameters::flat_set_type& parameters)const { - auto itr = parameters.find( account_create_operation::fee_parameters_type() ); - FC_ASSERT( itr != parameters.end() ); - return itr->get(); + auto itr = parameters.find( typename Operation::fee_parameters_type() ); + if( itr != parameters.end() ) + return itr->template get(); + + static typename Operation::fee_parameters_type dummy = get(parameters); + parameters.insert(dummy); + return dummy; } }; template<> class fee_helper { public: - const bid_collateral_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const + const bid_collateral_operation::fee_parameters_type& get(const fee_parameters::flat_set_type& parameters)const { auto itr = parameters.find( bid_collateral_operation::fee_parameters_type() ); if ( itr != parameters.end() ) return itr->get(); static bid_collateral_operation::fee_parameters_type bid_collateral_dummy; - bid_collateral_dummy.fee = fee_helper().cget(parameters).fee; + bid_collateral_dummy.fee = fee_helper().get(parameters).fee; return bid_collateral_dummy; } }; @@ -80,14 +76,14 @@ namespace graphene { namespace protocol { template<> class fee_helper { public: - const asset_update_issuer_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const + const asset_update_issuer_operation::fee_parameters_type& get(const fee_parameters::flat_set_type& parameters)const { auto itr = parameters.find( asset_update_issuer_operation::fee_parameters_type() ); if ( itr != parameters.end() ) return itr->get(); static asset_update_issuer_operation::fee_parameters_type dummy; - dummy.fee = fee_helper().cget(parameters).fee; + dummy.fee = fee_helper().get(parameters).fee; return dummy; } }; @@ -95,58 +91,20 @@ namespace graphene { namespace protocol { template<> class fee_helper { public: - const asset_claim_pool_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const + const asset_claim_pool_operation::fee_parameters_type& get(const fee_parameters::flat_set_type& parameters)const { auto itr = parameters.find( asset_claim_pool_operation::fee_parameters_type() ); if ( itr != parameters.end() ) return itr->get(); static asset_claim_pool_operation::fee_parameters_type asset_claim_pool_dummy; - asset_claim_pool_dummy.fee = fee_helper().cget(parameters).fee; + asset_claim_pool_dummy.fee = fee_helper().get(parameters).fee; return asset_claim_pool_dummy; } }; - template<> - class fee_helper { - public: - const htlc_create_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const - { - auto itr = parameters.find( htlc_create_operation::fee_parameters_type() ); - if ( itr != parameters.end() ) - return itr->get(); - - static htlc_create_operation::fee_parameters_type htlc_create_operation_fee_dummy; - return htlc_create_operation_fee_dummy; - } - }; - - template<> - class fee_helper { - public: - const htlc_redeem_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const - { - auto itr = parameters.find( htlc_redeem_operation::fee_parameters_type() ); - if ( itr != parameters.end() ) - return itr->get(); - static htlc_redeem_operation::fee_parameters_type htlc_redeem_operation_fee_dummy; - return htlc_redeem_operation_fee_dummy; - } - }; - template<> - class fee_helper { - public: - const htlc_extend_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const - { - auto itr = parameters.find( htlc_extend_operation::fee_parameters_type() ); - if ( itr != parameters.end() ) - return itr->get(); - static htlc_extend_operation::fee_parameters_type htlc_extend_operation_fee_dummy; - return htlc_extend_operation_fee_dummy; - } - }; /** * @brief contains all of the parameters necessary to calculate the fee for any operation */ @@ -182,12 +140,12 @@ namespace graphene { namespace protocol { template const typename Operation::fee_parameters_type& get()const { - return fee_helper().cget(parameters); + return fee_helper().get(parameters); } template - typename Operation::fee_parameters_type& get() + typename Operation::fee_parameters_type& mutable_get() { - return fee_helper().get(parameters); + return fee_helper().mutable_get(parameters); } template bool exists()const diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index 83b7557425..8e4a145980 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -690,7 +690,7 @@ BOOST_AUTO_TEST_CASE( account_create_fee_scaling ) db.modify(global_property_id_type()(db), [](global_property_object& gpo) { gpo.parameters.get_mutable_fees() = fee_schedule::get_default(); - gpo.parameters.get_mutable_fees().get().basic_fee = 1; + gpo.parameters.get_mutable_fees().mutable_get().basic_fee = 1; }); for( int i = db.get_dynamic_global_properties().accounts_registered_this_interval; i < accounts_per_scale; ++i ) From 0412a9c7bcf06fe1b0fd394b6e6eebe05093de55 Mon Sep 17 00:00:00 2001 From: Abit Date: Thu, 23 Apr 2020 17:22:41 +0200 Subject: [PATCH 65/81] 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 11f0b8c4feea0f74b26a1a081742d6bf348912ed Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 23 Apr 2020 12:58:24 -0500 Subject: [PATCH 66/81] Prevent exception if fee does not exist --- libraries/protocol/fee_schedule_calc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/protocol/fee_schedule_calc.cpp b/libraries/protocol/fee_schedule_calc.cpp index a8815bbfa9..b417324868 100644 --- a/libraries/protocol/fee_schedule_calc.cpp +++ b/libraries/protocol/fee_schedule_calc.cpp @@ -61,7 +61,9 @@ namespace graphene { namespace protocol { template<> uint64_t calc_fee_visitor::operator()(const htlc_create_operation& op)const { - transfer_operation::fee_parameters_type t = param.get(); + transfer_operation::fee_parameters_type t; + if (param.exists()) + t = param.get(); return op.calculate_fee( param.get(), t.price_per_kbyte).value; } From d02fd01eef81df0903ccf66efe07a113a584f3a3 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 24 Apr 2020 16:24:39 -0500 Subject: [PATCH 67/81] permit restricted transfers in proposals --- libraries/chain/account_evaluator.cpp | 2 +- libraries/chain/db_maint.cpp | 2 +- libraries/chain/proposal_evaluator.cpp | 16 +- libraries/protocol/fee_schedule_calc.cpp | 9 +- .../graphene/protocol/fee_schedule.hpp | 86 +++++-- tests/tests/fee_tests.cpp | 2 +- tests/tests/htlc_tests.cpp | 220 +++++++++++++++++- 7 files changed, 298 insertions(+), 39 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 93b9f0f7db..4480d48530 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -217,7 +217,7 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio && global_properties.parameters.account_fee_scale_bitshifts != 0 ) { d.modify(global_properties, [](global_property_object& p) { - p.parameters.get_mutable_fees().mutable_get().basic_fee <<= p.parameters.account_fee_scale_bitshifts; + p.parameters.get_mutable_fees().get().basic_fee <<= p.parameters.account_fee_scale_bitshifts; }); } diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 9e219b1711..1bdcc67d1a 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -1183,7 +1183,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g modify(gpo, [&dgpo](global_property_object& p) { // Remove scaling of account registration fee - p.parameters.get_mutable_fees().mutable_get().basic_fee >>= p.parameters.account_fee_scale_bitshifts * + p.parameters.get_mutable_fees().get().basic_fee >>= p.parameters.account_fee_scale_bitshifts * (dgpo.accounts_registered_this_interval / p.parameters.accounts_per_fee_scale); if( p.pending_parameters ) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index bb304270e1..2592bdcc49 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -33,9 +33,6 @@ namespace detail { void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options); void check_htlc_create_hf_bsip64(const fc::time_point_sec& block_time, const htlc_create_operation& op, const asset_object& asset); - void check_htlc_redeem_hf_bsip64(const fc::time_point_sec& block_time, - const htlc_redeem_operation& op, const htlc_object* htlc_obj); - } struct proposal_operation_hardfork_visitor @@ -84,12 +81,19 @@ struct proposal_operation_hardfork_visitor } void operator()(const graphene::chain::htlc_create_operation &op) const { FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); - detail::check_htlc_create_hf_bsip64(block_time, op, op.amount.asset_id(db)); + if (block_time < HARDFORK_CORE_BSIP64_TIME) + { + // memo field added at harfork BSIP64 + // NOTE: both of these checks can be removed after hardfork time + FC_ASSERT( !op.extensions.value.memo.valid(), + "Memo unavailable until after HARDFORK BSIP64"); + // HASH160 added at hardfork BSIP64 + FC_ASSERT( !op.preimage_hash.is_type(), + "HASH160 unavailable until after HARDFORK BSIP64" ); + } } void operator()(const graphene::chain::htlc_redeem_operation &op) const { FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); - const auto* htlc_obj = &op.htlc_id(db); - detail::check_htlc_redeem_hf_bsip64(block_time, op, htlc_obj); } void operator()(const graphene::chain::htlc_extend_operation &op) const { FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); diff --git a/libraries/protocol/fee_schedule_calc.cpp b/libraries/protocol/fee_schedule_calc.cpp index a8815bbfa9..eb3e4e92bf 100644 --- a/libraries/protocol/fee_schedule_calc.cpp +++ b/libraries/protocol/fee_schedule_calc.cpp @@ -53,15 +53,12 @@ namespace graphene { namespace protocol { } }; - /** - * Specialization for htlc_create_operation - * - * For HTLCs, after HF_BSIP64, we need to add in the fee per kb - */ template<> uint64_t calc_fee_visitor::operator()(const htlc_create_operation& op)const { - transfer_operation::fee_parameters_type t = param.get(); + transfer_operation::fee_parameters_type t; + if (param.exists()) + t = param.get(); return op.calculate_fee( param.get(), t.price_per_kbyte).value; } diff --git a/libraries/protocol/include/graphene/protocol/fee_schedule.hpp b/libraries/protocol/include/graphene/protocol/fee_schedule.hpp index 36c31d23d1..275e87e704 100644 --- a/libraries/protocol/include/graphene/protocol/fee_schedule.hpp +++ b/libraries/protocol/include/graphene/protocol/fee_schedule.hpp @@ -37,38 +37,42 @@ namespace graphene { namespace protocol { template class fee_helper { public: - const typename Operation::fee_parameters_type& get(const fee_parameters::flat_set_type& parameters)const + const typename Operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const { auto itr = parameters.find( typename Operation::fee_parameters_type() ); - if( itr != parameters.end() ) - return itr->template get(); + FC_ASSERT( itr != parameters.end() ); + return itr->template get(); + } + }; - static typename Operation::fee_parameters_type dummy; - return dummy; + template<> + class fee_helper { + public: + const account_create_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const + { + auto itr = parameters.find( account_create_operation::fee_parameters_type() ); + FC_ASSERT( itr != parameters.end() ); + return itr->get(); } - typename Operation::fee_parameters_type& mutable_get(fee_parameters::flat_set_type& parameters)const + typename account_create_operation::fee_parameters_type& get(fee_parameters::flat_set_type& parameters)const { - auto itr = parameters.find( typename Operation::fee_parameters_type() ); - if( itr != parameters.end() ) - return itr->template get(); - - static typename Operation::fee_parameters_type dummy = get(parameters); - parameters.insert(dummy); - return dummy; + auto itr = parameters.find( account_create_operation::fee_parameters_type() ); + FC_ASSERT( itr != parameters.end() ); + return itr->get(); } }; template<> class fee_helper { public: - const bid_collateral_operation::fee_parameters_type& get(const fee_parameters::flat_set_type& parameters)const + const bid_collateral_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const { auto itr = parameters.find( bid_collateral_operation::fee_parameters_type() ); if ( itr != parameters.end() ) return itr->get(); static bid_collateral_operation::fee_parameters_type bid_collateral_dummy; - bid_collateral_dummy.fee = fee_helper().get(parameters).fee; + bid_collateral_dummy.fee = fee_helper().cget(parameters).fee; return bid_collateral_dummy; } }; @@ -76,14 +80,14 @@ namespace graphene { namespace protocol { template<> class fee_helper { public: - const asset_update_issuer_operation::fee_parameters_type& get(const fee_parameters::flat_set_type& parameters)const + const asset_update_issuer_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const { auto itr = parameters.find( asset_update_issuer_operation::fee_parameters_type() ); if ( itr != parameters.end() ) return itr->get(); static asset_update_issuer_operation::fee_parameters_type dummy; - dummy.fee = fee_helper().get(parameters).fee; + dummy.fee = fee_helper().cget(parameters).fee; return dummy; } }; @@ -91,20 +95,58 @@ namespace graphene { namespace protocol { template<> class fee_helper { public: - const asset_claim_pool_operation::fee_parameters_type& get(const fee_parameters::flat_set_type& parameters)const + const asset_claim_pool_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const { auto itr = parameters.find( asset_claim_pool_operation::fee_parameters_type() ); if ( itr != parameters.end() ) return itr->get(); static asset_claim_pool_operation::fee_parameters_type asset_claim_pool_dummy; - asset_claim_pool_dummy.fee = fee_helper().get(parameters).fee; + asset_claim_pool_dummy.fee = fee_helper().cget(parameters).fee; return asset_claim_pool_dummy; } }; + template<> + class fee_helper { + public: + const htlc_create_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const + { + auto itr = parameters.find( htlc_create_operation::fee_parameters_type() ); + if ( itr != parameters.end() ) + return itr->get(); + + static htlc_create_operation::fee_parameters_type htlc_create_operation_fee_dummy; + return htlc_create_operation_fee_dummy; + } + }; + + template<> + class fee_helper { + public: + const htlc_redeem_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const + { + auto itr = parameters.find( htlc_redeem_operation::fee_parameters_type() ); + if ( itr != parameters.end() ) + return itr->get(); + static htlc_redeem_operation::fee_parameters_type htlc_redeem_operation_fee_dummy; + return htlc_redeem_operation_fee_dummy; + } + }; + template<> + class fee_helper { + public: + const htlc_extend_operation::fee_parameters_type& cget(const fee_parameters::flat_set_type& parameters)const + { + auto itr = parameters.find( htlc_extend_operation::fee_parameters_type() ); + if ( itr != parameters.end() ) + return itr->get(); + static htlc_extend_operation::fee_parameters_type htlc_extend_operation_fee_dummy; + return htlc_extend_operation_fee_dummy; + } + }; /** * @brief contains all of the parameters necessary to calculate the fee for any operation */ @@ -140,12 +182,12 @@ namespace graphene { namespace protocol { template const typename Operation::fee_parameters_type& get()const { - return fee_helper().get(parameters); + return fee_helper().cget(parameters); } template - typename Operation::fee_parameters_type& mutable_get() + typename Operation::fee_parameters_type& get() { - return fee_helper().mutable_get(parameters); + return fee_helper().get(parameters); } template bool exists()const diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index 8e4a145980..83b7557425 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -690,7 +690,7 @@ BOOST_AUTO_TEST_CASE( account_create_fee_scaling ) db.modify(global_property_id_type()(db), [](global_property_object& gpo) { gpo.parameters.get_mutable_fees() = fee_schedule::get_default(); - gpo.parameters.get_mutable_fees().mutable_get().basic_fee = 1; + gpo.parameters.get_mutable_fees().get().basic_fee = 1; }); for( int i = db.get_dynamic_global_properties().accounts_registered_this_interval; i < accounts_per_scale; ++i ) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 97344c3e64..4cd6ab1f57 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -248,6 +248,7 @@ try { transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); transfer( committee_account, joker_id, graphene::chain::asset(init_balance) ); + transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); advance_past_htlc_first_hardfork(this); @@ -259,6 +260,72 @@ try { // cler everything out generate_block(); trx.clear(); + // Alice attempts to put a proposal out with a preimage size of 0 (which is valid) + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = 0; + create_operation.from = alice_id; + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + graphene::chain::proposal_create_operation prop1; + prop1.fee_paying_account = alice_id; + prop1.expiration_time = db.head_block_time() + 100; + prop1.proposed_ops.emplace_back(create_operation); + trx.operations.push_back(prop1); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + // Alice attempts to put a proposal out with a memo (which is not valid) + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = 0; + create_operation.from = alice_id; + create_operation.extensions.value.memo = memo_data(); + create_operation.extensions.value.memo->from = alice_public_key; + create_operation.extensions.value.memo->to = bob_public_key; + create_operation.extensions.value.memo->set_message( + alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + graphene::chain::proposal_create_operation prop1; + prop1.fee_paying_account = alice_id; + prop1.expiration_time = db.head_block_time() + 100; + prop1.proposed_ops.emplace_back(create_operation); + trx.operations.push_back(prop1); + sign(trx, alice_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + trx.clear(); + } + // Alice attempts to put a contract on the blockchain that is correct but has a preimage size of 0 + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = 0; + create_operation.from = alice_id; + create_operation.extensions.value.memo = memo_data(); + create_operation.extensions.value.memo->from = alice_public_key; + create_operation.extensions.value.memo->to = bob_public_key; + create_operation.extensions.value.memo->set_message( + alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + trx.clear(); + } // Alice attempts to put a contract on the blockchain with HASH160 { graphene::chain::htlc_create_operation create_operation; @@ -282,7 +349,6 @@ try { } trx.clear(); } - // Alice attempts to put a contract on the blockchain with a memo { graphene::chain::htlc_create_operation create_operation; @@ -314,7 +380,70 @@ try { } trx.clear(); } + // Alice creates an asset + BOOST_TEST_MESSAGE("Create ALICECOIN"); + const asset_id_type uia_id = create_user_issued_asset( "ALICECOIN", alice, transfer_restricted).id; + // Alice puts transfer restrictions on the asset (allowed to transfer before BSIP64) + /* + { + BOOST_TEST_MESSAGE("Turning on transfer restrictions"); + graphene::chain::asset_update_operation asset_update; + asset_update.issuer = alice_id; + asset_update.asset_to_update = uia_id; + asset_update.new_options = uia_id(db).options; + asset_update.new_options.flags |= transfer_restricted; + asset_update.fee = db.get_global_properties().parameters.current_fees->calculate_fee(asset_update); + trx.operations.push_back(asset_update); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + */ + BOOST_TEST_MESSAGE("Issuing, and then giving some to Bob"); + issue_uia(bob, asset(10000, uia_id) ); + // verify transfer restrictions are in place + GRAPHENE_CHECK_THROW(transfer(bob, joker, asset(1, uia_id)), fc::exception); + trx.operations.clear(); + // Bob attempts to put a contract on the blockchain with currency that cannot be transferred + // Does not fail before HF BSIP64 + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3, uia_id ); + create_operation.to = joker_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = preimage_size; + create_operation.from = bob_id; + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + // Bob attempts to put a proposal on the blockchain of an HTLC with currency that cannot be transferred + // Does not fail before or after HF BSIP64 + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3, uia_id ); + create_operation.to = joker_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = preimage_size; + create_operation.from = bob_id; + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + graphene::chain::proposal_create_operation prop_create; + prop_create.expiration_time = db.head_block_time() + 100; + prop_create.fee_paying_account = bob_id; + prop_create.review_period_seconds = 50; + prop_create.proposed_ops.emplace_back(create_operation); + trx.operations.push_back(prop_create); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + BOOST_TEST_MESSAGE("Fast Forwarding to HF BSIP64"); // fast forward to after the hardfork generate_blocks( HARDFORK_CORE_BSIP64_TIME + 60 ); set_expiration( db, trx ); @@ -348,6 +477,7 @@ try { // verify funds on hold... 100 - 3 = 97, minus the transaction fee = 90.57813 BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 9057813 ); + int64_t bobs_balance = get_balance(bob, asset_id_type()(db)); // make sure Bob (or anyone) can see the details of the transaction graphene::app::database_api db_api(db); auto obj = db_api.get_objects( {alice_htlc_id }).front(); @@ -387,7 +517,7 @@ try { trx.clear(); } // verify funds end up in Bob's account (3) - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), bobs_balance + 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify funds remain out of Alice's acount ( 100 - 3 - transaction fee ) BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 9057813 ); // verify all three get notified @@ -400,6 +530,92 @@ try { history = get_operation_history(joker_id); BOOST_CHECK_EQUAL( history.size(), joker_num_history + 1); display_operation_history{ history, joker }; + + // Alice attempts to put a proposal out with a preimage size of 0 (which is valid) + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = 0; + create_operation.from = alice_id; + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + graphene::chain::proposal_create_operation prop1; + prop1.fee_paying_account = alice_id; + prop1.expiration_time = db.head_block_time() + 100; + prop1.proposed_ops.emplace_back(create_operation); + trx.operations.push_back(prop1); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + // Alice attempts to put a proposal out with a memo (which is valid) + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = 0; + create_operation.from = alice_id; + create_operation.extensions.value.memo = memo_data(); + create_operation.extensions.value.memo->from = alice_public_key; + create_operation.extensions.value.memo->to = bob_public_key; + create_operation.extensions.value.memo->set_message( + alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + graphene::chain::proposal_create_operation prop1; + prop1.fee_paying_account = alice_id; + prop1.expiration_time = db.head_block_time() + 100; + prop1.review_period_seconds = 50; + prop1.proposed_ops.emplace_back(create_operation); + trx.operations.push_back(prop1); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + // Bob attempts to put a contract on the blockchain with currency that cannot be transferred + // Fails after HF BSIP64 + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3, uia_id ); + create_operation.to = joker_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = preimage_size; + create_operation.from = bob_id; + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + trx.clear(); + } + // Bob attempts to put a proposal on the blockchain of an HTLC with currency that cannot be transferred + // Does not fail before or after HF BSIP64 + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); + create_operation.amount = graphene::chain::asset( 3, uia_id ); + create_operation.to = joker_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = preimage_size; + create_operation.from = bob_id; + create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + graphene::chain::proposal_create_operation prop_create; + prop_create.expiration_time = db.head_block_time() + 100; + prop_create.fee_paying_account = bob_id; + prop_create.review_period_seconds = 50; + prop_create.proposed_ops.emplace_back(create_operation); + trx.operations.push_back(prop_create); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } } FC_LOG_AND_RETHROW() } From e4472a936f087242b04919826ffefe5b12dbc271 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 27 Apr 2020 09:46:43 -0500 Subject: [PATCH 68/81] Remove test spaghetti + minor fixes --- libraries/chain/proposal_evaluator.cpp | 3 - libraries/protocol/fee_schedule_calc.cpp | 1 + libraries/protocol/htlc.cpp | 1 + tests/cli/main.cpp | 16 +- tests/tests/htlc_tests.cpp | 579 +++++++++-------------- 5 files changed, 246 insertions(+), 354 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 759fb88a8a..c02d879c66 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -25,14 +25,11 @@ #include #include #include -#include 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_htlc_create_hf_bsip64(const fc::time_point_sec& block_time, const htlc_create_operation& op, - const asset_object& asset); void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options); } diff --git a/libraries/protocol/fee_schedule_calc.cpp b/libraries/protocol/fee_schedule_calc.cpp index eb3e4e92bf..6b21890d1d 100644 --- a/libraries/protocol/fee_schedule_calc.cpp +++ b/libraries/protocol/fee_schedule_calc.cpp @@ -56,6 +56,7 @@ namespace graphene { namespace protocol { template<> uint64_t calc_fee_visitor::operator()(const htlc_create_operation& op)const { + //TODO: refactor for performance (see https://github.com/bitshares/bitshares-core/issues/2150) transfer_operation::fee_parameters_type t; if (param.exists()) t = param.get(); diff --git a/libraries/protocol/htlc.cpp b/libraries/protocol/htlc.cpp index 5da238f571..06bc048dd0 100644 --- a/libraries/protocol/htlc.cpp +++ b/libraries/protocol/htlc.cpp @@ -71,6 +71,7 @@ namespace graphene { namespace protocol { } } GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation::fee_parameters_type ) +GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation::additional_options_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_redeem_operation::fee_parameters_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_extend_operation::fee_parameters_type ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::protocol::htlc_create_operation ) diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 787e152429..351be60e71 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -423,7 +423,7 @@ BOOST_FIXTURE_TEST_CASE( create_new_account, cli_fixture ) BOOST_CHECK(con.wallet_api_ptr->import_key("jmjatlanta", bki.wif_priv_key)); con.wallet_api_ptr->save_wallet_file(con.wallet_filename); - // attempt to give jmjatlanta some bitsahres + // attempt to give jmjatlanta some bitshares BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to jmjatlanta"); signed_transaction transfer_tx = con.wallet_api_ptr->transfer( "nathan", "jmjatlanta", "10000", "1.3.0", "Here are some CORE token for your new account", true @@ -848,7 +848,7 @@ BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture ) { INVOKE(create_new_account); - // attempt to give jmjatlanta some bitsahres + // attempt to give jmjatlanta some bitshares BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to jmjatlanta"); for(int i = 1; i <= 199; i++) { @@ -945,7 +945,7 @@ BOOST_AUTO_TEST_CASE( cli_multisig_transaction ) create_multisig_acct_tx.operations.push_back(account_create_op); con.wallet_api_ptr->sign_transaction(create_multisig_acct_tx, true); - // attempt to give cifer.test some bitsahres + // attempt to give cifer.test some bitshares BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to cifer.test"); signed_transaction transfer_tx1 = con.wallet_api_ptr->transfer("nathan", "cifer.test", "10000", "1.3.0", "Here are some BTS for your new account", true); @@ -1127,7 +1127,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "alice", "nathan", "nathan", true); con.wallet_api_ptr->save_wallet_file(con.wallet_filename); - // attempt to give alice some bitsahres + // attempt to give alice some bitshares BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to alice"); signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "alice", "10000", "1.3.0", "Here are some CORE token for your new account", true); @@ -1141,7 +1141,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) "nathan", "nathan", true); // this should cause resync which will import the keys of alice and bob generate_block(app1); - // attempt to give bob some bitsahres + // attempt to give bob some bitshares BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to Bob"); signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "bob", "10000", "1.3.0", "Here are some CORE token for your new account", true); @@ -1663,7 +1663,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "alice", "nathan", "nathan", true); con.wallet_api_ptr->save_wallet_file(con.wallet_filename); - // attempt to give alice some bitsahres + // attempt to give alice some bitshares BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to alice"); signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "alice", "10000", "1.3.0", "Here are some CORE token for your new account", true); @@ -1677,7 +1677,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) "nathan", "nathan", true); // this should cause resync which will import the keys of alice and bob generate_block(app1); - // attempt to give bob some bitsahres + // attempt to give bob some bitshares BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to Bob"); signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "bob", "10000", "1.3.0", "Here are some CORE token for your new account", true); @@ -1722,7 +1722,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC con.wallet_api_ptr->htlc_create("bob", "alice", - "3", "BOBCOIN", "HASH160", hash_str, preimage_string.size(), timelock, "Bob to Alice", true); + "3", "BOBCOIN", "HASH160", hash_str, preimage_string.size(), fc::hours(12).to_seconds(), "Bob to Alice", true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: std::string bob_htlc_id_as_string; diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 4cd6ab1f57..0d693594f3 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -177,66 +177,37 @@ try { } FC_LOG_AND_RETHROW() } -class display_operation_history -{ - public: - display_operation_history( std::vector< operation_history_object > history, const account_object& acct ); - const account_object& acct; -}; - -class op_printer +/**** + * @brief helper to create htlc_create_operation + */ +htlc_create_operation create_htlc(const database& db, const account_id_type& from, const account_id_type& to, const asset& amount, + const graphene::protocol::htlc_hash& preimage_hash, uint16_t preimage_size, uint64_t seconds, + const fc::optional& memo = fc::optional()) { - public: - op_printer( std::ostream& out, display_operation_history* displayer, const operation_history_object& obj ) - : out(out), caller(displayer), obj(obj) {} - typedef std::string result_type; - - template - std::string operator()(const T& op)const - { - return ""; - } - - std::string operator()(const htlc_redeemed_operation& op)const - { - out << "Called by " << caller->acct.name - << " Redeemer: " << id_to_string(op.redeemer) - << " Preimage: " << preimage_to_string(op.preimage); - return ""; - } - - std::string operator()(const htlc_redeem_operation& op)const - { - out << "Called by " << caller->acct.name - << " Redeemer: " << id_to_string(op.redeemer) - << " Preimage: " << preimage_to_string(op.preimage); - return ""; - } - - private: - std::ostream& out; - display_operation_history* caller; - const operation_history_object& obj; - static std::string id_to_string( object_id_type id) - { return "" + std::to_string(id.space()) + "." + std::to_string(id.type()) + "." + std::to_string(id.instance()); } - static std::string preimage_to_string( std::vector in) { - std::string out; - std::for_each(in.begin(), in.end(), [&out](const char& in) - { out += std::to_string((int)in); }); - return out; - } -}; + htlc_create_operation ret_val; + ret_val.from = from; + ret_val.to = to; + ret_val.amount = amount; + ret_val.preimage_hash = preimage_hash; + ret_val.preimage_size = preimage_size; + ret_val.claim_period_seconds = seconds; + ret_val.extensions.value.memo = memo; + ret_val.fee = db.get_global_properties().parameters.current_fees->calculate_fee(ret_val); + return ret_val; +} -display_operation_history::display_operation_history( std::vector history, const account_object& acct) - : acct(acct) +/**** + * @brief helper to create a proposal + */ +proposal_create_operation create_proposal(const database& db, const account_id_type& from, + const htlc_create_operation& op, const fc::time_point_sec& expiration) { - for( auto itr = history.begin(); itr != history.end(); itr++) - { - std::stringstream ss; - (*itr).op.visit( op_printer(ss, this, *itr) ); - if ( ss.rdbuf()->in_avail() > 0 ) - BOOST_TEST_MESSAGE( ss.str() ); - }; + proposal_create_operation ret_val; + ret_val.fee_paying_account = from; + ret_val.expiration_time = expiration; + ret_val.proposed_ops.emplace_back(op); + ret_val.fee = db.get_global_properties().parameters.current_fees->calculate_fee(ret_val); + return ret_val; } BOOST_AUTO_TEST_CASE( htlc_hf_bsip64 ) @@ -260,362 +231,284 @@ try { // cler everything out generate_block(); trx.clear(); - // Alice attempts to put a proposal out with a preimage size of 0 (which is valid) + + /*** + * Proposals before the hardfork + */ { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); - create_operation.to = bob_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = 0; - create_operation.from = alice_id; - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); - graphene::chain::proposal_create_operation prop1; - prop1.fee_paying_account = alice_id; - prop1.expiration_time = db.head_block_time() + 100; - prop1.proposed_ops.emplace_back(create_operation); + BOOST_TEST_MESSAGE("Alice is creating a proposal with an HTLC that contains a memo before the hardfork (should fail)"); + memo_data data; + data.from = alice_public_key; + data.to = bob_public_key; + data.set_message( alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, + asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it(pre_image), 0, 60, data); + graphene::chain::proposal_create_operation prop1 = create_proposal( + db, alice_id, create_operation, db.head_block_time() + 100); trx.operations.push_back(prop1); sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); + REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), "memo"); + trx.clear(); + } + { + BOOST_TEST_MESSAGE("Alice is creating a proposal with HASH160 (should fail)"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, + asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it(pre_image), 0, 60); + graphene::chain::proposal_create_operation prop1 = create_proposal( + db, alice_id, create_operation, db.head_block_time() + 100); + trx.operations.push_back(prop1); + sign(trx, alice_private_key); + REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), "HASH160"); trx.clear(); } - // Alice attempts to put a proposal out with a memo (which is not valid) { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); - create_operation.to = bob_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = 0; - create_operation.from = alice_id; - create_operation.extensions.value.memo = memo_data(); - create_operation.extensions.value.memo->from = alice_public_key; - create_operation.extensions.value.memo->to = bob_public_key; - create_operation.extensions.value.memo->set_message( - alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); - graphene::chain::proposal_create_operation prop1; - prop1.fee_paying_account = alice_id; - prop1.expiration_time = db.head_block_time() + 100; - prop1.proposed_ops.emplace_back(create_operation); + BOOST_TEST_MESSAGE("Alice is creating a proposal with a preimage size of 0 (should pass)"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, + asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it(pre_image), 0, 60); + graphene::chain::proposal_create_operation prop1 = create_proposal( + db, alice_id, create_operation, db.head_block_time() + 100); trx.operations.push_back(prop1); sign(trx, alice_private_key); - GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + PUSH_TX(db, trx, ~0); trx.clear(); - } - // Alice attempts to put a contract on the blockchain that is correct but has a preimage size of 0 + } + /*** + * HTLC Contracts (non-proposals) before hardfork + */ { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); - create_operation.to = bob_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = 0; - create_operation.from = alice_id; - create_operation.extensions.value.memo = memo_data(); - create_operation.extensions.value.memo->from = alice_public_key; - create_operation.extensions.value.memo->to = bob_public_key; - create_operation.extensions.value.memo->set_message( - alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + BOOST_TEST_MESSAGE("Alice is creating an HTLC that contains a memo before the hardfork (should fail)"); + memo_data data; + data.from = alice_public_key; + data.to = bob_public_key; + data.set_message( alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, + asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it(pre_image), 0, 60, data); trx.operations.push_back(create_operation); sign(trx, alice_private_key); - GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), "memo"); trx.clear(); - } - // Alice attempts to put a contract on the blockchain with HASH160 + } { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); - create_operation.to = bob_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = preimage_size; - create_operation.from = alice_id; - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + BOOST_TEST_MESSAGE("Alice is creating an HTLC with HASH160 (should fail)"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, + asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it(pre_image), 0, 60); trx.operations.push_back(create_operation); sign(trx, alice_private_key); - // this try/catch is just to get some console output to verify the exception - try { - PUSH_TX(db, trx, ~0); - BOOST_FAIL("crate_operation should have failed due to the HASH160 hash"); - } catch(fc::exception& ex) { - if ( ex.to_string().find("HASH160 unavailable") == std::string::npos ) - BOOST_FAIL("create_operation failed but not due to the hash160 function"); - } + REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), "HASH160"); trx.clear(); } - // Alice attempts to put a contract on the blockchain with a memo { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); - create_operation.to = bob_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = preimage_size; - create_operation.from = alice_id; - create_operation.extensions.value.memo = memo_data(); - create_operation.extensions.value.memo->from = alice_public_key; - create_operation.extensions.value.memo->to = bob_public_key; - create_operation.extensions.value.memo->set_message( - alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + BOOST_TEST_MESSAGE("Alice is creating an HTLC with a preimage size of 0 (should pass)"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, + asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it(pre_image), 0, 60); trx.operations.push_back(create_operation); sign(trx, alice_private_key); - try { - PUSH_TX(db, trx, ~0); - BOOST_FAIL("crate_operation should have failed due to the memo field"); - } catch(fc::exception& ex) { - if (ex.to_string().find("Memo unavailable") == std::string::npos ) - { - std::stringstream ss; - ss << "create_operation failed but not due to the memo field. Message was: " << ex.to_detail_string(); - BOOST_FAIL(ss.str()); - } - } + htlc_id_type htlc_id = PUSH_TX(db, trx, ~0).operation_results[0].get(); + trx.clear(); + BOOST_TEST_MESSAGE("Bob attempts to redeem, but can't because preimage size is 0 (should fail)"); + graphene::chain::htlc_redeem_operation redeem; + redeem.htlc_id = htlc_id; + redeem.preimage = pre_image; + redeem.redeemer = bob_id; + redeem.fee = db.current_fee_schedule().calculate_fee(redeem); + trx.operations.push_back(redeem); + REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), "Preimage size mismatch"); trx.clear(); } + // Alice creates an asset - BOOST_TEST_MESSAGE("Create ALICECOIN"); + BOOST_TEST_MESSAGE("Create ALICECOIN so transfer_restricted can be controlled"); const asset_id_type uia_id = create_user_issued_asset( "ALICECOIN", alice, transfer_restricted).id; - // Alice puts transfer restrictions on the asset (allowed to transfer before BSIP64) - /* - { - BOOST_TEST_MESSAGE("Turning on transfer restrictions"); - graphene::chain::asset_update_operation asset_update; - asset_update.issuer = alice_id; - asset_update.asset_to_update = uia_id; - asset_update.new_options = uia_id(db).options; - asset_update.new_options.flags |= transfer_restricted; - asset_update.fee = db.get_global_properties().parameters.current_fees->calculate_fee(asset_update); - trx.operations.push_back(asset_update); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - } - */ - BOOST_TEST_MESSAGE("Issuing, and then giving some to Bob"); + BOOST_TEST_MESSAGE("Issuing ALICECOIN to Bob"); issue_uia(bob, asset(10000, uia_id) ); // verify transfer restrictions are in place - GRAPHENE_CHECK_THROW(transfer(bob, joker, asset(1, uia_id)), fc::exception); + REQUIRE_EXCEPTION_WITH_TEXT(transfer(bob, joker, asset(1, uia_id)), "transfer"); trx.operations.clear(); - // Bob attempts to put a contract on the blockchain with currency that cannot be transferred - // Does not fail before HF BSIP64 + { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3, uia_id ); - create_operation.to = joker_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = preimage_size; - create_operation.from = bob_id; - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + BOOST_TEST_MESSAGE("Bob wants to transfer ALICECOIN, which is transfer_restricted (allowed before HF)"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, bob_id, joker_id, + asset(3, uia_id), hash_it(pre_image), preimage_size, 60); trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); trx.clear(); } - // Bob attempts to put a proposal on the blockchain of an HTLC with currency that cannot be transferred - // Does not fail before or after HF BSIP64 { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3, uia_id ); - create_operation.to = joker_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = preimage_size; - create_operation.from = bob_id; - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); - graphene::chain::proposal_create_operation prop_create; - prop_create.expiration_time = db.head_block_time() + 100; - prop_create.fee_paying_account = bob_id; - prop_create.review_period_seconds = 50; - prop_create.proposed_ops.emplace_back(create_operation); + BOOST_TEST_MESSAGE("Bob wants to transfer ALICECOIN within a proposal, always allowed, although will fail later"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, bob_id, joker_id, asset(3,uia_id), + hash_it(pre_image), preimage_size, 60); + graphene::chain::proposal_create_operation prop_create = create_proposal(db, bob_id, create_operation, db.head_block_time() + 100); trx.operations.push_back(prop_create); - sign(trx, alice_private_key); + sign(trx, bob_private_key); PUSH_TX(db, trx, ~0); trx.clear(); } - BOOST_TEST_MESSAGE("Fast Forwarding to HF BSIP64"); - // fast forward to after the hardfork + BOOST_TEST_MESSAGE("Fast Forwarding beyond HF BSIP64"); generate_blocks( HARDFORK_CORE_BSIP64_TIME + 60 ); set_expiration( db, trx ); - // Alice attempts to put a contract on the blockchain that is correct (and preimage size of 0) + /*** + * Proposals after the hardfork + */ { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); - create_operation.to = bob_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = 0; - create_operation.from = alice_id; - create_operation.extensions.value.memo = memo_data(); - create_operation.extensions.value.memo->from = alice_public_key; - create_operation.extensions.value.memo->to = bob_public_key; - create_operation.extensions.value.memo->set_message( - alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); - trx.operations.push_back(create_operation); + BOOST_TEST_MESSAGE("Alice is creating a proposal with an HTLC that contains a memo (should pass)"); + memo_data data; + data.from = alice_public_key; + data.to = bob_public_key; + data.set_message( alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, + asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it(pre_image), 0, 60, data); + graphene::chain::proposal_create_operation prop1 = create_proposal( + db, alice_id, create_operation, db.head_block_time() + 100); + trx.operations.push_back(prop1); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); trx.clear(); - graphene::chain::signed_block blk = generate_block(); - processed_transaction alice_trx = blk.transactions[0]; - alice_htlc_id = alice_trx.operation_results[0].get(); - generate_block(); - } - - // verify funds on hold... 100 - 3 = 97, minus the transaction fee = 90.57813 - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 9057813 ); - - int64_t bobs_balance = get_balance(bob, asset_id_type()(db)); - // make sure Bob (or anyone) can see the details of the transaction - graphene::app::database_api db_api(db); - auto obj = db_api.get_objects( {alice_htlc_id }).front(); - graphene::chain::htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); - BOOST_CHECK( htlc.memo.valid() ); - - // grab number of history objects to make sure everyone gets notified - size_t alice_num_history = get_operation_history(alice_id).size(); - size_t bob_num_history = get_operation_history(bob_id).size(); - size_t joker_num_history = get_operation_history(joker_id).size(); - - // joker sends a redeem operation with a bad hash - { - std::vector bad_pre_image{ 0x00, 0x01, 0x02 }; - graphene::chain::htlc_redeem_operation update_operation; - update_operation.redeemer = joker_id; - update_operation.htlc_id = alice_htlc_id; - update_operation.preimage = bad_pre_image; - update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation ); - trx.operations.push_back( update_operation ); - sign(trx, joker_private_key); - GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::exception ); - trx.clear(); - } - - // joker sends a redeem operation to claim the funds for bob + } { - graphene::chain::htlc_redeem_operation update_operation; - update_operation.redeemer = joker_id; - update_operation.htlc_id = alice_htlc_id; - update_operation.preimage = pre_image; - update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation ); - trx.operations.push_back( update_operation ); - sign(trx, joker_private_key); - PUSH_TX( db, trx, ~0 ); - generate_block(); + BOOST_TEST_MESSAGE("Alice is creating a proposal with HASH160 (should pass)"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, + asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it(pre_image), 0, 60); + graphene::chain::proposal_create_operation prop1 = create_proposal( + db, alice_id, create_operation, db.head_block_time() + 100); + trx.operations.push_back(prop1); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); trx.clear(); } - // verify funds end up in Bob's account (3) - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), bobs_balance + 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); - // verify funds remain out of Alice's acount ( 100 - 3 - transaction fee ) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 9057813 ); - // verify all three get notified - std::vector history = get_operation_history(alice_id); - BOOST_CHECK_EQUAL( history.size(), alice_num_history + 1); - display_operation_history{ history, alice }; - history = get_operation_history(bob_id); - BOOST_CHECK_EQUAL( history.size(), bob_num_history + 1); - display_operation_history{ history, bob }; - history = get_operation_history(joker_id); - BOOST_CHECK_EQUAL( history.size(), joker_num_history + 1); - display_operation_history{ history, joker }; - - // Alice attempts to put a proposal out with a preimage size of 0 (which is valid) { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); - create_operation.to = bob_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = 0; - create_operation.from = alice_id; - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); - graphene::chain::proposal_create_operation prop1; - prop1.fee_paying_account = alice_id; - prop1.expiration_time = db.head_block_time() + 100; - prop1.proposed_ops.emplace_back(create_operation); + BOOST_TEST_MESSAGE("Alice is creating a proposal with a preimage size of 0 (should pass)"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, + asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it(pre_image), 0, 60); + graphene::chain::proposal_create_operation prop1 = create_proposal( + db, alice_id, create_operation, db.head_block_time() + 100); trx.operations.push_back(prop1); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); trx.clear(); } - // Alice attempts to put a proposal out with a memo (which is valid) + /*** + * HTLC Contracts (non-proposals) after hardfork + */ { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3 * GRAPHENE_BLOCKCHAIN_PRECISION ); - create_operation.to = bob_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = 0; - create_operation.from = alice_id; - create_operation.extensions.value.memo = memo_data(); - create_operation.extensions.value.memo->from = alice_public_key; - create_operation.extensions.value.memo->to = bob_public_key; - create_operation.extensions.value.memo->set_message( - alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); - graphene::chain::proposal_create_operation prop1; - prop1.fee_paying_account = alice_id; - prop1.expiration_time = db.head_block_time() + 100; - prop1.review_period_seconds = 50; - prop1.proposed_ops.emplace_back(create_operation); - trx.operations.push_back(prop1); + BOOST_TEST_MESSAGE("Alice is creating an HTLC that contains a memo after the hardfork (should pass)"); + memo_data data; + data.from = alice_public_key; + data.to = bob_public_key; + data.set_message( alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, + asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it(pre_image), 0, 60, data); + trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); trx.clear(); - } - // Bob attempts to put a contract on the blockchain with currency that cannot be transferred - // Fails after HF BSIP64 + } { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3, uia_id ); - create_operation.to = joker_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = preimage_size; - create_operation.from = bob_id; - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); + BOOST_TEST_MESSAGE("Alice is creating an HTLC with HASH160 (should pass)"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, alice_id, bob_id, + asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), hash_it(pre_image), 0, 60); trx.operations.push_back(create_operation); sign(trx, alice_private_key); - GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + PUSH_TX(db, trx, ~0); trx.clear(); } - // Bob attempts to put a proposal on the blockchain of an HTLC with currency that cannot be transferred - // Does not fail before or after HF BSIP64 { - graphene::chain::htlc_create_operation create_operation; - BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 3 coins to Bob"); - create_operation.amount = graphene::chain::asset( 3, uia_id ); - create_operation.to = joker_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = hash_it( pre_image ); - create_operation.preimage_size = preimage_size; - create_operation.from = bob_id; - create_operation.fee = db.get_global_properties().parameters.current_fees->calculate_fee(create_operation); - graphene::chain::proposal_create_operation prop_create; - prop_create.expiration_time = db.head_block_time() + 100; - prop_create.fee_paying_account = bob_id; - prop_create.review_period_seconds = 50; - prop_create.proposed_ops.emplace_back(create_operation); - trx.operations.push_back(prop_create); + BOOST_TEST_MESSAGE("Bob wants to transfer ALICECOIN, which is transfer_restricted (no longer allowed)"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, bob_id, joker_id, + asset(3, uia_id), hash_it(pre_image), preimage_size, 60); + trx.operations.push_back(create_operation); sign(trx, alice_private_key); + REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), "transfer_restricted"); + trx.clear(); + } + { + BOOST_TEST_MESSAGE("Bob wants to transfer ALICECOIN within a proposal, always allowed, although will fail later"); + graphene::chain::htlc_create_operation create_operation = create_htlc(db, bob_id, joker_id, asset(3,uia_id), + hash_it(pre_image), preimage_size, 60); + graphene::chain::proposal_create_operation prop_create = create_proposal(db, bob_id, create_operation, db.head_block_time() + 100); + trx.operations.push_back(prop_create); + sign(trx, bob_private_key); PUSH_TX(db, trx, ~0); trx.clear(); } + { + BOOST_TEST_MESSAGE("A memo field should include a charge per kb (uses fee from transfer_operation)"); + htlc_create_operation op = create_htlc(db, alice_id, bob_id, asset(3), hash_it(pre_image), preimage_size, 60); + asset no_memo_fee = op.fee; + memo_data data; + data.from = alice_public_key; + data.to = bob_public_key; + data.set_message( alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); + op.extensions.value.memo = data; + asset with_memo_fee = db.current_fee_schedule().calculate_fee(op); + BOOST_CHECK_GT( with_memo_fee.amount.value, no_memo_fee.amount.value ); + } + // After HF 64, a zero in the preimage_size means 2 things, no preimage (can happen, but who would do such a thing), + // or simply skip the size validation. To test, we must attempt to redeem both cases + { + // case 1: 0 preimage with 0 preimage_size + BOOST_TEST_MESSAGE("Attempt to create an HTLC with no preimage (should pass)"); + htlc_create_operation op = create_htlc(db, alice_id, bob_id, asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), + hash_it(std::vector()), 0, 60); + trx.operations.push_back(op); + sign(trx, alice_private_key); + graphene::protocol::processed_transaction results = PUSH_TX(db, trx, ~0); + trx.operations.clear(); + htlc_id_type htlc_id = results.operation_results[0].get(); + BOOST_TEST_MESSAGE("Attempt to redeem HTLC that has no preimage, but include one anyway (should fail)"); + htlc_redeem_operation redeem; + redeem.htlc_id = htlc_id; + redeem.preimage = pre_image; + redeem.redeemer = bob_id; + redeem.fee = db.current_fee_schedule().calculate_fee(redeem); + trx.operations.push_back(redeem); + sign(trx, bob_private_key); + REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), "Provided preimage does not generate"); + trx.operations.clear(); + BOOST_TEST_MESSAGE("Attempt to redeem HTLC that has no preimage (should pass)"); + redeem.preimage = std::vector(); + redeem.fee = db.current_fee_schedule().calculate_fee(redeem); + trx.operations.push_back(redeem); + sign(trx, bob_private_key); + PUSH_TX(db, trx, ~0); + trx.operations.clear(); + } + { + // case 2: a real preimage with 0 preimage size + htlc_create_operation op = create_htlc(db, alice_id, bob_id, asset(3 * GRAPHENE_BLOCKCHAIN_PRECISION), + hash_it(pre_image), 0, 60); + trx.operations.push_back(op); + sign(trx, alice_private_key); + graphene::protocol::processed_transaction results = PUSH_TX(db, trx, ~0); + trx.operations.clear(); + htlc_id_type htlc_id = results.operation_results[0].get(); + BOOST_TEST_MESSAGE("Attempt to redeem with no preimage (should fail)"); + htlc_redeem_operation redeem; + redeem.htlc_id = htlc_id; + redeem.preimage = std::vector(); + redeem.redeemer = bob_id; + redeem.fee = db.current_fee_schedule().calculate_fee(redeem); + trx.operations.push_back(redeem); + sign(trx, bob_private_key); + REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), "Provided preimage does not generate"); + trx.operations.clear(); + BOOST_TEST_MESSAGE("Attempt to redeem with no preimage size, but incorrect preimage"); + redeem.preimage = std::vector{'H','e','l','l','o'}; + redeem.fee = db.current_fee_schedule().calculate_fee(redeem); + trx.operations.push_back(redeem); + sign(trx, bob_private_key); + REQUIRE_EXCEPTION_WITH_TEXT(PUSH_TX(db, trx, ~0), "Provided preimage does not generate"); + trx.operations.clear(); + BOOST_TEST_MESSAGE("Attempt to redeem with no preimage size (should pass)"); + redeem.preimage = pre_image; + redeem.fee = db.current_fee_schedule().calculate_fee(redeem); + trx.operations.push_back(redeem); + sign(trx, bob_private_key); + PUSH_TX(db, trx, ~0); + trx.operations.clear(); + } } FC_LOG_AND_RETHROW() } 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 69/81] 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 70/81] 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 71/81] 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 72/81] 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 73/81] 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 74/81] 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 06b0fe30c25aff4bb931a2e667f90b4ccd6759bf Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 28 Apr 2020 10:15:19 -0500 Subject: [PATCH 75/81] Fix line length, store preimage in virt op --- libraries/chain/htlc_evaluator.cpp | 12 +++-- libraries/chain/proposal_evaluator.cpp | 21 +++++---- libraries/protocol/htlc.cpp | 3 +- .../include/graphene/protocol/htlc.hpp | 5 +- libraries/wallet/operation_printer.cpp | 6 +-- libraries/wallet/wallet_api_impl.hpp | 6 +-- libraries/wallet/wallet_transfer.cpp | 10 ++-- tests/cli/main.cpp | 38 +++++++++++---- tests/tests/htlc_tests.cpp | 46 ++++++++++++------- 9 files changed, 93 insertions(+), 54 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 3b1238bc9d..339c2fc4bb 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -61,7 +61,8 @@ namespace graphene { // attempted on an HTLC with a 0 preimage size before the hardfork date. if ( htlc_obj->conditions.hash_lock.preimage_size > 0U || block_time < HARDFORK_CORE_BSIP64_TIME ) - FC_ASSERT(op.preimage.size() == htlc_obj->conditions.hash_lock.preimage_size, "Preimage size mismatch."); + FC_ASSERT(op.preimage.size() == htlc_obj->conditions.hash_lock.preimage_size, + "Preimage size mismatch."); } } // end of graphene::chain::details @@ -78,9 +79,11 @@ namespace graphene { FC_ASSERT(htlc_options, "HTLC Committee options are not set."); // make sure the expiration is reasonable - FC_ASSERT( o.claim_period_seconds <= htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); + FC_ASSERT( o.claim_period_seconds <= htlc_options->max_timeout_secs, + "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable - FC_ASSERT( o.preimage_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); + FC_ASSERT( o.preimage_size <= htlc_options->max_preimage_size, + "HTLC preimage length exceeds allowed length" ); // make sure the sender has the funds for the HTLC FC_ASSERT( d.get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; const auto& asset_to_transfer = o.amount.asset_id( d ); @@ -154,7 +157,8 @@ namespace graphene { db().adjust_balance(htlc_obj->transfer.to, amount); // notify related parties htlc_redeemed_operation virt_op( htlc_obj->id, htlc_obj->transfer.from, htlc_obj->transfer.to, o.redeemer, - amount, htlc_obj->conditions.hash_lock.preimage_hash, htlc_obj->conditions.hash_lock.preimage_size ); + amount, htlc_obj->conditions.hash_lock.preimage_hash, htlc_obj->conditions.hash_lock.preimage_size, + o.preimage ); db().push_applied_operation( virt_op ); db().remove(*htlc_obj); return void_result(); diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index c02d879c66..1ba6fd1b8f 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -63,7 +63,8 @@ struct proposal_operation_hardfork_visitor 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"); + FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(), + "Unable to set HTLC options before hardfork 1468"); FC_ASSERT(!op.new_parameters.current_fees->exists()); FC_ASSERT(!op.new_parameters.current_fees->exists()); FC_ASSERT(!op.new_parameters.current_fees->exists()); @@ -126,7 +127,8 @@ struct proposal_operation_hardfork_visitor // Do not allow more than 1 proposal_update in a proposal if ( op.op.is_type() ) { - FC_ASSERT( !already_contains_proposal_update, "At most one proposal update can be nested in a proposal!" ); + FC_ASSERT( !already_contains_proposal_update, + "At most one proposal update can be nested in a proposal!" ); already_contains_proposal_update = true; } } @@ -188,8 +190,9 @@ void_result proposal_create_evaluator::do_evaluate( const proposal_create_operat FC_ASSERT( o.expiration_time > block_time, "Proposal has already expired on creation." ); FC_ASSERT( o.expiration_time <= block_time + global_parameters.maximum_proposal_lifetime, "Proposal expiration time is too far in the future." ); - FC_ASSERT( !o.review_period_seconds || fc::seconds( *o.review_period_seconds ) < ( o.expiration_time - block_time ), - "Proposal review period must be less than its overall lifetime." ); + FC_ASSERT( !o.review_period_seconds || + fc::seconds( *o.review_period_seconds ) < ( o.expiration_time - block_time ), + "Proposal review period must be less than its overall lifetime." ); // Find all authorities required by the proposed operations flat_set tmp_required_active_auths; @@ -199,8 +202,8 @@ void_result proposal_create_evaluator::do_evaluate( const proposal_create_operat operation_get_required_authorities( op.op, tmp_required_active_auths, _required_owner_auths, other, MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( block_time ) ); } - // All accounts which must provide both owner and active authority should be omitted from the active authority set; - // owner authority approval implies active authority approval. + // All accounts which must provide both owner and active authority should be omitted from the + // active authority set; owner authority approval implies active authority approval. std::set_difference( tmp_required_active_auths.begin(), tmp_required_active_auths.end(), _required_owner_auths.begin(), _required_owner_auths.end(), std::inserter( _required_active_auths, _required_active_auths.begin() ) ); @@ -295,9 +298,9 @@ void_result proposal_update_evaluator::do_apply(const proposal_update_operation& { try { database& d = db(); - // Potential optimization: if _executed_proposal is true, we can skip the modify step and make push_proposal skip - // signature checks. This isn't done now because I just wrote all the proposals code, and I'm not yet 100% sure the - // required approvals are sufficient to authorize the transaction. + // Potential optimization: if _executed_proposal is true, we can skip the modify step and make push_proposal + // skip signature checks. This isn't done now because I just wrote all the proposals code, and I'm not yet + // 100% sure the required approvals are sufficient to authorize the transaction. d.modify(*_proposal, [&o](proposal_object& p) { p.available_active_approvals.insert(o.active_approvals_to_add.begin(), o.active_approvals_to_add.end()); p.available_owner_approvals.insert(o.owner_approvals_to_add.begin(), o.owner_approvals_to_add.end()); diff --git a/libraries/protocol/htlc.cpp b/libraries/protocol/htlc.cpp index 06bc048dd0..5694ec5a26 100644 --- a/libraries/protocol/htlc.cpp +++ b/libraries/protocol/htlc.cpp @@ -34,7 +34,8 @@ namespace graphene { namespace protocol { FC_ASSERT( amount.amount > 0, "HTLC amount should be greater than zero" ); } - share_type htlc_create_operation::calculate_fee( const fee_parameters_type& fee_params, uint32_t fee_per_kb )const + share_type htlc_create_operation::calculate_fee( const fee_parameters_type& fee_params, + uint32_t fee_per_kb )const { uint64_t days = ( claim_period_seconds + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; // multiply with overflow check diff --git a/libraries/protocol/include/graphene/protocol/htlc.hpp b/libraries/protocol/include/graphene/protocol/htlc.hpp index a5510ec092..606b2f383f 100644 --- a/libraries/protocol/include/graphene/protocol/htlc.hpp +++ b/libraries/protocol/include/graphene/protocol/htlc.hpp @@ -130,9 +130,10 @@ namespace graphene { namespace protocol { htlc_redeemed_operation() {} htlc_redeemed_operation( htlc_id_type htlc_id, account_id_type from, account_id_type to, - account_id_type redeemer, asset amount, const htlc_hash& preimage_hash, uint16_t preimage_size ) : + account_id_type redeemer, asset amount, const htlc_hash& preimage_hash, uint16_t preimage_size, + const std::vector& preimage ) : htlc_id(htlc_id), from(from), to(to), redeemer(redeemer), amount(amount), - htlc_preimage_hash(preimage_hash), htlc_preimage_size(preimage_size) {} + htlc_preimage_hash(preimage_hash), htlc_preimage_size(preimage_size), preimage(preimage) {} account_id_type fee_payer()const { return to; } void validate()const { FC_ASSERT( !"virtual operation" ); } diff --git a/libraries/wallet/operation_printer.cpp b/libraries/wallet/operation_printer.cpp index 975ac2f5b0..d3f950c4e9 100644 --- a/libraries/wallet/operation_printer.cpp +++ b/libraries/wallet/operation_printer.cpp @@ -95,13 +95,13 @@ void operation_printer::print_preimage(const std::vector& preimage)const if (preimage.size() == 0) return; out << " with preimage \""; - // cut it at 50 bytes max + // cut it at 300 bytes max auto flags = out.flags(); out << std::hex << setw(2) << setfill('0'); - for (size_t i = 0; i < std::min(50, preimage.size()); i++) + for (size_t i = 0; i < std::min(300, preimage.size()); i++) out << +preimage[i]; out.flags(flags); - if (preimage.size() > 50) + if (preimage.size() > 300) out << "...(truncated due to size)"; out << "\""; } diff --git a/libraries/wallet/wallet_api_impl.hpp b/libraries/wallet/wallet_api_impl.hpp index 6727723226..2ee1de89d8 100644 --- a/libraries/wallet/wallet_api_impl.hpp +++ b/libraries/wallet/wallet_api_impl.hpp @@ -220,8 +220,7 @@ class wallet_api_impl transaction preview_builder_transaction(transaction_handle_type handle); signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true); signed_transaction sign_builder_transaction2(transaction_handle_type transaction_handle, - const vector& signing_keys = vector(), - bool broadcast = true); + const vector& signing_keys = vector(), bool broadcast = true); pair broadcast_transaction(signed_transaction tx); @@ -302,7 +301,8 @@ class wallet_api_impl string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, const uint32_t claim_period_seconds, const std::string& memo, bool broadcast = false ); - signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ); + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, + bool broadcast ); signed_transaction htlc_extend ( string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast); diff --git a/libraries/wallet/wallet_transfer.cpp b/libraries/wallet/wallet_transfer.cpp index 0b41cefc41..57a8ae4960 100644 --- a/libraries/wallet/wallet_transfer.cpp +++ b/libraries/wallet/wallet_transfer.cpp @@ -83,8 +83,8 @@ namespace graphene { namespace wallet { namespace detail { return sign_transaction(tx, broadcast); } FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) } - signed_transaction wallet_api_impl::htlc_create( string source, string destination, string amount, string asset_symbol, - string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, + signed_transaction wallet_api_impl::htlc_create( string source, string destination, string amount, + string asset_symbol, string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, const uint32_t claim_period_seconds, const std::string& memo, bool broadcast ) { try @@ -122,8 +122,8 @@ namespace graphene { namespace wallet { namespace detail { (preimage_hash)(preimage_size)(claim_period_seconds)(broadcast) ) } - signed_transaction wallet_api_impl::htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, - bool broadcast ) + signed_transaction wallet_api_impl::htlc_redeem( string htlc_id, string issuer, + const std::vector& preimage, bool broadcast ) { try { @@ -147,7 +147,7 @@ namespace graphene { namespace wallet { namespace detail { } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(preimage)(broadcast) ) } - signed_transaction wallet_api_impl::htlc_extend ( string htlc_id, string issuer, const uint32_t seconds_to_add, + signed_transaction wallet_api_impl::htlc_extend ( string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast) { try diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 351be60e71..34775d47d4 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -1601,7 +1601,8 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) int server_port_number = 0; app1 = start_application(app_dir, server_port_number); // set committee parameters - app1->chain_database()->modify(app1->chain_database()->get_global_properties(), [](global_property_object& p) { + app1->chain_database()->modify(app1->chain_database()->get_global_properties(), [](global_property_object& p) + { graphene::chain::htlc_options params; params.max_preimage_size = 1024; params.max_timeout_secs = 60 * 60 * 24 * 28; @@ -1634,7 +1635,8 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) account_object nathan_acct_after_upgrade = con.wallet_api_ptr->get_account("nathan"); // verify that the upgrade was successful - BOOST_CHECK_PREDICATE( std::not_equal_to(), (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch()) + BOOST_CHECK_PREDICATE( std::not_equal_to(), + (nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch()) (nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) ); BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member()); @@ -1660,8 +1662,8 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) { graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key(); BOOST_CHECK(!bki.brain_priv_key.empty()); - signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "alice", - "nathan", "nathan", true); + signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, + "alice", "nathan", "nathan", true); con.wallet_api_ptr->save_wallet_file(con.wallet_filename); // attempt to give alice some bitshares BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to alice"); @@ -1673,8 +1675,8 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) { graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key(); BOOST_CHECK(!bki.brain_priv_key.empty()); - signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, "bob", - "nathan", "nathan", true); + signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(bki.brain_priv_key, + "bob", "nathan", "nathan", true); // this should cause resync which will import the keys of alice and bob generate_block(app1); // attempt to give bob some bitshares @@ -1710,7 +1712,8 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) BOOST_CHECK(generate_block(app1, result_block)); // get the ID: - htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get(); + htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1] + .operation_results[0].get(); alice_htlc_id_as_string = (std::string)(object_id_type)htlc_id; BOOST_TEST_MESSAGE("Alice shares the HTLC ID with Bob. The HTLC ID is: " + alice_htlc_id_as_string); } @@ -1722,7 +1725,8 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) // Bob likes what he sees, so he creates an HTLC, using the info he retrieved from Alice's HTLC con.wallet_api_ptr->htlc_create("bob", "alice", - "3", "BOBCOIN", "HASH160", hash_str, preimage_string.size(), fc::hours(12).to_seconds(), "Bob to Alice", true); + "3", "BOBCOIN", "HASH160", hash_str, preimage_string.size(), fc::hours(12).to_seconds(), + "Bob to Alice", true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: std::string bob_htlc_id_as_string; @@ -1732,7 +1736,8 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) BOOST_CHECK(generate_block(app1, result_block)); // get the ID: - htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1].operation_results[0].get(); + htlc_id_type htlc_id = result_block.transactions[result_block.transactions.size()-1] + .operation_results[0].get(); bob_htlc_id_as_string = (std::string)(object_id_type)htlc_id; BOOST_TEST_MESSAGE("Bob shares the HTLC ID with Alice. The HTLC ID is: " + bob_htlc_id_as_string); } @@ -1750,7 +1755,20 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) BOOST_CHECK(generate_block(app1)); } - // TODO: Bob can look at Alice's history to see her preimage + // Bob can look at Alice's history to see her preimage + { + BOOST_TEST_MESSAGE("Bob can look at the history of Alice to see the preimage"); + std::vector hist = con.wallet_api_ptr->get_account_history("alice", 1); + BOOST_TEST( hist[0].description.find("with preimage \"4d792") != hist[0].description.npos); + } + + // Bob can also look at his own history to see Alice's preimage + { + BOOST_TEST_MESSAGE("Bob can look at his own history to see the preimage"); + std::vector hist = con.wallet_api_ptr->get_account_history("bob", 1); + BOOST_TEST( hist[0].description.find("with preimage \"4d792") != hist[0].description.npos); + } + // Bob can use the preimage to retrieve his BTS { BOOST_TEST_MESSAGE("Bob uses Alice's preimage to retrieve the BOBCOIN"); diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 0d693594f3..1e27e051d1 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -147,7 +147,8 @@ try { { graphene::chain::htlc_extend_operation big_extend; big_extend.htlc_id = alice_htlc_id; - big_extend.seconds_to_add = db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_timeout_secs + 10; + big_extend.seconds_to_add = db.get_global_properties().parameters.extensions.value + .updatable_htlc_options->max_timeout_secs + 10; big_extend.fee = db.get_global_properties().parameters.current_fees->calculate_fee(big_extend); big_extend.update_issuer = alice_id; trx.operations.push_back(big_extend); @@ -180,9 +181,9 @@ try { /**** * @brief helper to create htlc_create_operation */ -htlc_create_operation create_htlc(const database& db, const account_id_type& from, const account_id_type& to, const asset& amount, - const graphene::protocol::htlc_hash& preimage_hash, uint16_t preimage_size, uint64_t seconds, - const fc::optional& memo = fc::optional()) +htlc_create_operation create_htlc(const database& db, const account_id_type& from, const account_id_type& to, + const asset& amount, const graphene::protocol::htlc_hash& preimage_hash, uint16_t preimage_size, + uint64_t seconds, const fc::optional& memo = fc::optional()) { htlc_create_operation ret_val; ret_val.from = from; @@ -236,7 +237,8 @@ try { * Proposals before the hardfork */ { - BOOST_TEST_MESSAGE("Alice is creating a proposal with an HTLC that contains a memo before the hardfork (should fail)"); + BOOST_TEST_MESSAGE( + "Alice is creating a proposal with an HTLC that contains a memo before the hardfork (should fail)"); memo_data data; data.from = alice_public_key; data.to = bob_public_key; @@ -335,10 +337,12 @@ try { trx.clear(); } { - BOOST_TEST_MESSAGE("Bob wants to transfer ALICECOIN within a proposal, always allowed, although will fail later"); + BOOST_TEST_MESSAGE( + "Bob wants to transfer ALICECOIN within a proposal, always allowed, although will fail later"); graphene::chain::htlc_create_operation create_operation = create_htlc(db, bob_id, joker_id, asset(3,uia_id), hash_it(pre_image), preimage_size, 60); - graphene::chain::proposal_create_operation prop_create = create_proposal(db, bob_id, create_operation, db.head_block_time() + 100); + graphene::chain::proposal_create_operation prop_create = create_proposal( + db, bob_id, create_operation, db.head_block_time() + 100); trx.operations.push_back(prop_create); sign(trx, bob_private_key); PUSH_TX(db, trx, ~0); @@ -424,10 +428,12 @@ try { trx.clear(); } { - BOOST_TEST_MESSAGE("Bob wants to transfer ALICECOIN within a proposal, always allowed, although will fail later"); + BOOST_TEST_MESSAGE( + "Bob wants to transfer ALICECOIN within a proposal, always allowed, although will fail later"); graphene::chain::htlc_create_operation create_operation = create_htlc(db, bob_id, joker_id, asset(3,uia_id), hash_it(pre_image), preimage_size, 60); - graphene::chain::proposal_create_operation prop_create = create_proposal(db, bob_id, create_operation, db.head_block_time() + 100); + graphene::chain::proposal_create_operation prop_create = create_proposal( + db, bob_id, create_operation, db.head_block_time() + 100); trx.operations.push_back(prop_create); sign(trx, bob_private_key); PUSH_TX(db, trx, ~0); @@ -435,7 +441,8 @@ try { } { BOOST_TEST_MESSAGE("A memo field should include a charge per kb (uses fee from transfer_operation)"); - htlc_create_operation op = create_htlc(db, alice_id, bob_id, asset(3), hash_it(pre_image), preimage_size, 60); + htlc_create_operation op = create_htlc(db, alice_id, bob_id, asset(3), hash_it(pre_image), + preimage_size, 60); asset no_memo_fee = op.fee; memo_data data; data.from = alice_public_key; @@ -445,8 +452,8 @@ try { asset with_memo_fee = db.current_fee_schedule().calculate_fee(op); BOOST_CHECK_GT( with_memo_fee.amount.value, no_memo_fee.amount.value ); } - // After HF 64, a zero in the preimage_size means 2 things, no preimage (can happen, but who would do such a thing), - // or simply skip the size validation. To test, we must attempt to redeem both cases + // After HF 64, a zero in the preimage_size means 2 things, no preimage (can happen, but who would do such a + // thing), or simply skip the size validation. To test, we must attempt to redeem both cases { // case 1: 0 preimage with 0 preimage_size BOOST_TEST_MESSAGE("Attempt to create an HTLC with no preimage (should pass)"); @@ -749,7 +756,8 @@ BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) new_fee_schedule->parameters.insert( (*itr).second); } } - proposal_create_operation cop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time()); + 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 uop; @@ -780,12 +788,14 @@ BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) } BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u); + BOOST_CHECK_EQUAL( + db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u); BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); generate_blocks(good_proposal_id(db).expiration_time + 5); BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u); + BOOST_CHECK_EQUAL(db.get_global_properties() + .parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u); BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" ); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); @@ -793,8 +803,10 @@ BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); - const graphene::chain::fee_schedule& current_fee_schedule = *(db.get_global_properties().parameters.current_fees); + BOOST_CHECK_EQUAL(db.get_global_properties() + .parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); + const graphene::chain::fee_schedule& current_fee_schedule = + *(db.get_global_properties().parameters.current_fees); const htlc_create_operation::fee_parameters_type& htlc_fee = current_fee_schedule.get(); BOOST_CHECK_EQUAL(htlc_fee.fee, 2 * GRAPHENE_BLOCKCHAIN_PRECISION); From ccef9fdf79910c9d729c781aaf47172eabd19506 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 28 Apr 2020 12:17:46 -0500 Subject: [PATCH 76/81] Replace BOOST_TEST with BOOST_CHECK --- 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 34775d47d4..0615f8dff6 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -1759,14 +1759,14 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc_bsip64 ) { BOOST_TEST_MESSAGE("Bob can look at the history of Alice to see the preimage"); std::vector hist = con.wallet_api_ptr->get_account_history("alice", 1); - BOOST_TEST( hist[0].description.find("with preimage \"4d792") != hist[0].description.npos); + BOOST_CHECK( hist[0].description.find("with preimage \"4d792") != hist[0].description.npos); } // Bob can also look at his own history to see Alice's preimage { BOOST_TEST_MESSAGE("Bob can look at his own history to see the preimage"); std::vector hist = con.wallet_api_ptr->get_account_history("bob", 1); - BOOST_TEST( hist[0].description.find("with preimage \"4d792") != hist[0].description.npos); + BOOST_CHECK( hist[0].description.find("with preimage \"4d792") != hist[0].description.npos); } // Bob can use the preimage to retrieve his BTS From 864cf3e4a326929595887c2bc29c43b155373cf4 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 29 Apr 2020 10:41:12 -0400 Subject: [PATCH 77/81] Move seed nodes processing code to network module Code ported from https://github.com/bitshares/bitshares-core/pull/1764 . The code also fixes a race condition on node startup caused by the premature `connect_to_endpoint` call. --- libraries/app/application.cpp | 77 +-------------------- libraries/app/application_impl.hxx | 2 - libraries/net/include/graphene/net/node.hpp | 23 ++++++ libraries/net/node.cpp | 68 ++++++++++++++++++ libraries/net/node_impl.hxx | 1 + 5 files changed, 95 insertions(+), 76 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 16de823ddd..6263ab1ec8 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -43,7 +43,6 @@ #include #include #include -#include #include #include @@ -125,41 +124,14 @@ void application_impl::reset_p2p_node(const fc::path& data_dir) if( _options->count("seed-node") ) { auto seeds = _options->at("seed-node").as>(); - for( const string& endpoint_string : seeds ) - { - try { - std::vector endpoints = resolve_string_to_ip_endpoints(endpoint_string); - for (const fc::ip::endpoint& endpoint : endpoints) - { - ilog("Adding seed node ${endpoint}", ("endpoint", endpoint)); - _p2p_network->add_node(endpoint); - _p2p_network->connect_to_endpoint(endpoint); - } - } catch( const fc::exception& e ) { - wlog( "caught exception ${e} while adding seed node ${endpoint}", - ("e", e.to_detail_string())("endpoint", endpoint_string) ); - } - } + _p2p_network->add_seed_nodes(seeds); } if( _options->count("seed-nodes") ) { auto seeds_str = _options->at("seed-nodes").as(); auto seeds = fc::json::from_string(seeds_str).as>(2); - for( const string& endpoint_string : seeds ) - { - try { - std::vector endpoints = resolve_string_to_ip_endpoints(endpoint_string); - for (const fc::ip::endpoint& endpoint : endpoints) - { - ilog("Adding seed node ${endpoint}", ("endpoint", endpoint)); - _p2p_network->add_node(endpoint); - } - } catch( const fc::exception& e ) { - wlog( "caught exception ${e} while adding seed node ${endpoint}", - ("e", e.to_detail_string())("endpoint", endpoint_string) ); - } - } + _p2p_network->add_seed_nodes(seeds); } else { @@ -167,20 +139,7 @@ void application_impl::reset_p2p_node(const fc::path& data_dir) vector seeds = { #include "../egenesis/seed-nodes.txt" }; - for( const string& endpoint_string : seeds ) - { - try { - std::vector endpoints = resolve_string_to_ip_endpoints(endpoint_string); - for (const fc::ip::endpoint& endpoint : endpoints) - { - ilog("Adding seed node ${endpoint}", ("endpoint", endpoint)); - _p2p_network->add_node(endpoint); - } - } catch( const fc::exception& e ) { - wlog( "caught exception ${e} while adding seed node ${endpoint}", - ("e", e.to_detail_string())("endpoint", endpoint_string) ); - } - } + _p2p_network->add_seed_nodes(seeds); } if( _options->count("p2p-endpoint") ) @@ -196,36 +155,6 @@ void application_impl::reset_p2p_node(const fc::path& data_dir) std::vector()); } FC_CAPTURE_AND_RETHROW() } -std::vector application_impl::resolve_string_to_ip_endpoints(const std::string& endpoint_string) -{ - try - { - string::size_type colon_pos = endpoint_string.find(':'); - if (colon_pos == std::string::npos) - FC_THROW("Missing required port number in endpoint string \"${endpoint_string}\"", - ("endpoint_string", endpoint_string)); - std::string port_string = endpoint_string.substr(colon_pos + 1); - try - { - uint16_t port = boost::lexical_cast(port_string); - - std::string hostname = endpoint_string.substr(0, colon_pos); - std::vector endpoints = fc::resolve(hostname, port); - if (endpoints.empty()) - FC_THROW_EXCEPTION( fc::unknown_host_exception, - "The host name can not be resolved: ${hostname}", - ("hostname", hostname) ); - return endpoints; - } - catch (const boost::bad_lexical_cast&) - { - FC_THROW("Bad port: ${port}", ("port", port_string)); - } - } - FC_CAPTURE_AND_RETHROW((endpoint_string)) -} - - void application_impl::new_connection( const fc::http::websocket_connection_ptr& c ) { auto wsc = std::make_shared(c, GRAPHENE_NET_MAX_NESTED_OBJECTS); diff --git a/libraries/app/application_impl.hxx b/libraries/app/application_impl.hxx index 175648e10f..accc8fe4f1 100644 --- a/libraries/app/application_impl.hxx +++ b/libraries/app/application_impl.hxx @@ -22,8 +22,6 @@ class application_impl : public net::node_delegate void reset_p2p_node(const fc::path& data_dir); - std::vector resolve_string_to_ip_endpoints(const std::string& endpoint_string); - void new_connection( const fc::http::websocket_connection_ptr& c ); void reset_websocket_server(); diff --git a/libraries/net/include/graphene/net/node.hpp b/libraries/net/include/graphene/net/node.hpp index fe03ac0cb6..43aa94a6e9 100644 --- a/libraries/net/include/graphene/net/node.hpp +++ b/libraries/net/include/graphene/net/node.hpp @@ -211,11 +211,34 @@ namespace graphene { namespace net { */ void add_node( const fc::ip::endpoint& ep ); + /***** + * @brief add a list of nodes to seed the p2p network + * @param seeds a vector of url strings + */ + void add_seed_nodes( std::vector seeds ); + + /**** + * @brief add a node to seed the p2p network + * @param in the url as a string + */ + void add_seed_node( const std::string& in); + /** * Attempt to connect to the specified endpoint immediately. */ virtual void connect_to_endpoint( const fc::ip::endpoint& ep ); + /** + * @brief Helper to convert a string to a collection of endpoints + * + * This converts a string (i.e. "bitshares.eu:665535" to a collection of endpoints. + * NOTE: Throws an exception if not in correct format or was unable to resolve URL. + * + * @param in the incoming string + * @returns a vector of endpoints + */ + static std::vector resolve_string_to_ip_endpoints( const std::string& in ); + /** * Specifies the network interface and port upon which incoming * connections should be accepted. diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index ec2f6a1f80..d787a3029c 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,7 @@ #include #include #include +#include #include #include @@ -4198,6 +4200,25 @@ namespace graphene { namespace net { namespace detail { trigger_p2p_network_connect_loop(); } + void node_impl::add_seed_node(const std::string& endpoint_string) + { + VERIFY_CORRECT_THREAD(); + std::vector endpoints; + try + { + endpoints = graphene::net::node::resolve_string_to_ip_endpoints(endpoint_string); + } + catch(...) + { + wlog( "Unable to resolve endpoint during attempt to add seed node ${ep}", ("ep", endpoint_string) ); + } + for (const fc::ip::endpoint& endpoint : endpoints) + { + ilog("Adding seed node ${endpoint}", ("endpoint", endpoint)); + add_node(endpoint); + } + } + void node_impl::initiate_connect_to(const peer_connection_ptr& new_peer) { new_peer->get_socket().open(); @@ -5104,4 +5125,51 @@ namespace graphene { namespace net { namespace detail { } // end namespace detail + std::vector node::resolve_string_to_ip_endpoints(const std::string& in) + { + try + { + std::string::size_type colon_pos = in.find(':'); + if (colon_pos == std::string::npos) + FC_THROW("Missing required port number in endpoint string \"${endpoint_string}\"", + ("endpoint_string", in)); + std::string port_string = in.substr(colon_pos + 1); + try + { + uint16_t port = boost::lexical_cast(port_string); + + std::string hostname = in.substr(0, colon_pos); + std::vector endpoints = fc::resolve(hostname, port); + if (endpoints.empty()) + FC_THROW_EXCEPTION( fc::unknown_host_exception, + "The host name can not be resolved: ${hostname}", + ("hostname", hostname) ); + return endpoints; + } + catch (const boost::bad_lexical_cast&) + { + FC_THROW("Bad port: ${port}", ("port", port_string)); + } + } + FC_CAPTURE_AND_RETHROW((in)) + } + + void node::add_seed_nodes(std::vector seeds) + { + for(const std::string& endpoint_string : seeds ) + { + try { + add_seed_node(endpoint_string); + } catch( const fc::exception& e ) { + wlog( "caught exception ${e} while adding seed node ${endpoint}", + ("e", e.to_detail_string())("endpoint", endpoint_string) ); + } + } + } + + void node::add_seed_node(const std::string& in) + { + INVOKE_IN_IMPL(add_seed_node, in); + } + } } // end namespace graphene::net diff --git a/libraries/net/node_impl.hxx b/libraries/net/node_impl.hxx index 7d31d16eea..c8bf74ae47 100644 --- a/libraries/net/node_impl.hxx +++ b/libraries/net/node_impl.hxx @@ -482,6 +482,7 @@ class node_impl : public peer_connection_delegate void listen_to_p2p_network(); void connect_to_p2p_network(); void add_node( const fc::ip::endpoint& ep ); + void add_seed_node( const std::string& seed_string ); void initiate_connect_to(const peer_connection_ptr& peer); void connect_to_endpoint(const fc::ip::endpoint& ep); void listen_on_endpoint(const fc::ip::endpoint& ep , bool wait_if_not_available); From 30648f253c7de96341f1995cd14ec5bf50e4232a Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 1 May 2020 09:22:48 -0400 Subject: [PATCH 78/81] Periodically re-check addresses of seed nodes --- libraries/net/node.cpp | 62 +++++++++++++++++++++++++++++++++++++ libraries/net/node_impl.hxx | 9 ++++++ 2 files changed, 71 insertions(+) diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index d787a3029c..5f5227ba98 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -483,6 +483,43 @@ namespace graphene { namespace net { namespace detail { // _retrigger_connect_loop_promise->set_value(); } + void node_impl::update_seed_nodes_task() + { + VERIFY_CORRECT_THREAD(); + + try + { + dlog("Starting an iteration of update_seed_nodes loop."); + for( const std::string& endpoint_string : _seed_nodes ) + { + resolve_seed_node_and_add( endpoint_string ); + } + dlog("Done an iteration of update_seed_nodes loop."); + } + catch (const fc::canceled_exception&) + { + throw; + } + FC_CAPTURE_AND_LOG( (_seed_nodes) ) + + schedule_next_update_seed_nodes_task(); + } + + void node_impl::schedule_next_update_seed_nodes_task() + { + VERIFY_CORRECT_THREAD(); + + if( _node_is_shutting_down ) + return; + + if( _update_seed_nodes_loop_done.valid() && _update_seed_nodes_loop_done.canceled() ) + return; + + _update_seed_nodes_loop_done = fc::schedule( [this]() { update_seed_nodes_task(); }, + fc::time_point::now() + fc::hours(3), + "update_seed_nodes_loop" ); + } + bool node_impl::have_already_received_sync_item( const item_hash_t& item_hash ) { VERIFY_CORRECT_THREAD(); @@ -3762,6 +3799,20 @@ namespace graphene { namespace net { namespace detail { wlog( "Exception thrown while terminating Fetch updated peer lists loop, ignoring" ); } + try + { + _update_seed_nodes_loop_done.cancel_and_wait("node_impl::close()"); + dlog("Update seed nodes loop terminated"); + } + catch ( const fc::exception& e ) + { + wlog( "Exception thrown while terminating Update seed nodes loop, ignoring: ${e}", ("e", e) ); + } + catch (...) + { + wlog( "Exception thrown while terminating Update seed nodes loop, ignoring" ); + } + try { _bandwidth_monitor_loop_done.cancel_and_wait("node_impl::close()"); @@ -4166,6 +4217,7 @@ namespace graphene { namespace net { namespace detail { assert(!_accept_loop_complete.valid() && !_p2p_network_connect_loop_done.valid() && + !_update_seed_nodes_loop_done.valid() && !_fetch_sync_items_loop_done.valid() && !_fetch_item_loop_done.valid() && !_advertise_inventory_loop_done.valid() && @@ -4183,6 +4235,7 @@ namespace graphene { namespace net { namespace detail { _fetch_updated_peer_lists_loop_done = fc::async([=](){ fetch_updated_peer_lists_loop(); }, "fetch_updated_peer_lists_loop"); _bandwidth_monitor_loop_done = fc::async([=](){ bandwidth_monitor_loop(); }, "bandwidth_monitor_loop"); _dump_node_status_task_done = fc::async([=](){ dump_node_status_task(); }, "dump_node_status_task"); + schedule_next_update_seed_nodes_task(); } void node_impl::add_node(const fc::ip::endpoint& ep) @@ -4201,9 +4254,17 @@ namespace graphene { namespace net { namespace detail { } void node_impl::add_seed_node(const std::string& endpoint_string) + { + VERIFY_CORRECT_THREAD(); + _seed_nodes.insert( endpoint_string ); + resolve_seed_node_and_add( endpoint_string ); + } + + void node_impl::resolve_seed_node_and_add(const std::string& endpoint_string) { VERIFY_CORRECT_THREAD(); std::vector endpoints; + ilog("Resolving seed node ${endpoint}", ("endpoint", endpoint_string)); try { endpoints = graphene::net::node::resolve_string_to_ip_endpoints(endpoint_string); @@ -5125,6 +5186,7 @@ namespace graphene { namespace net { namespace detail { } // end namespace detail + // TODO move this function to impl class std::vector node::resolve_string_to_ip_endpoints(const std::string& in) { try diff --git a/libraries/net/node_impl.hxx b/libraries/net/node_impl.hxx index c8bf74ae47..da12718b98 100644 --- a/libraries/net/node_impl.hxx +++ b/libraries/net/node_impl.hxx @@ -337,6 +337,14 @@ class node_impl : public peer_connection_delegate std::list > _handle_message_calls_in_progress; + /// used by the task that checks whether addresses of seed nodes have been updated + // @{ + boost::container::flat_set _seed_nodes; + fc::future _update_seed_nodes_loop_done; + void update_seed_nodes_task(); + void schedule_next_update_seed_nodes_task(); + // @} + node_impl(const std::string& user_agent); virtual ~node_impl(); @@ -483,6 +491,7 @@ class node_impl : public peer_connection_delegate void connect_to_p2p_network(); void add_node( const fc::ip::endpoint& ep ); void add_seed_node( const std::string& seed_string ); + void resolve_seed_node_and_add( const std::string& seed_string ); void initiate_connect_to(const peer_connection_ptr& peer); void connect_to_endpoint(const fc::ip::endpoint& ep); void listen_on_endpoint(const fc::ip::endpoint& ep , bool wait_if_not_available); From 8bff2665b27917d62001c3328ffa5531b39058fb Mon Sep 17 00:00:00 2001 From: crazybits Date: Tue, 28 Apr 2020 23:07:26 +0800 Subject: [PATCH 79/81] return lowest_ask and highest_bid order volume --- libraries/app/api_objects.cpp | 13 ++++++++++++- libraries/app/include/graphene/app/api_objects.hpp | 6 +++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/libraries/app/api_objects.cpp b/libraries/app/api_objects.cpp index e002f061ae..b3637b3388 100644 --- a/libraries/app/api_objects.cpp +++ b/libraries/app/api_objects.cpp @@ -38,8 +38,11 @@ market_ticker::market_ticker(const market_ticker_object& mto, quote = asset_quote.symbol; percent_change = "0"; lowest_ask = "0"; + lowest_ask_base_size = "0"; + lowest_ask_quote_size = "0"; highest_bid = "0"; - + highest_bid_base_size = "0"; + highest_bid_quote_size = "0"; fc::uint128_t bv; fc::uint128_t qv; price latest_price = asset( mto.latest_base, mto.base ) / asset( mto.latest_quote, mto.quote ); @@ -69,8 +72,12 @@ market_ticker::market_ticker(const market_ticker_object& mto, if(!orders.asks.empty()) lowest_ask = orders.asks[0].price; + lowest_ask_base_size =orders.asks[0].base; + lowest_ask_quote_size =orders.asks[0].quote; if(!orders.bids.empty()) highest_bid = orders.bids[0].price; + highest_bid_base_size =orders.bids[0].base; + highest_bid_quote_size =orders.bids[0].quote; } market_ticker::market_ticker(const fc::time_point_sec& now, @@ -82,7 +89,11 @@ market_ticker::market_ticker(const fc::time_point_sec& now, quote = asset_quote.symbol; latest = "0"; lowest_ask = "0"; + lowest_ask_base_size = "0"; + lowest_ask_quote_size = "0"; highest_bid = "0"; + highest_bid_base_size = "0"; + highest_bid_quote_size = "0"; percent_change = "0"; base_volume = "0"; quote_volume = "0"; diff --git a/libraries/app/include/graphene/app/api_objects.hpp b/libraries/app/include/graphene/app/api_objects.hpp index af7a96e1ab..847d2b4bf9 100644 --- a/libraries/app/include/graphene/app/api_objects.hpp +++ b/libraries/app/include/graphene/app/api_objects.hpp @@ -100,7 +100,11 @@ namespace graphene { namespace app { string quote; string latest; string lowest_ask; + string lowest_ask_base_size; + string lowest_ask_quote_size; string highest_bid; + string highest_bid_base_size; + string highest_bid_quote_size; string percent_change; string base_volume; string quote_volume; @@ -178,7 +182,7 @@ FC_REFLECT( graphene::app::full_account, FC_REFLECT( graphene::app::order, (price)(quote)(base) ); FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) ); FC_REFLECT( graphene::app::market_ticker, - (time)(base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) ); + (time)(base)(quote)(latest)(lowest_ask)(lowest_ask_base_size)(lowest_ask_quote_size)(highest_bid)(highest_bid_base_size)(highest_bid_quote_size)(percent_change)(base_volume)(quote_volume) ); FC_REFLECT( graphene::app::market_volume, (time)(base)(quote)(base_volume)(quote_volume) ); FC_REFLECT( graphene::app::market_trade, (sequence)(date)(price)(amount)(value)(side1_account_id)(side2_account_id) ); From 22ab82c7e9ef0e9117ec6787150e10aab7e386de Mon Sep 17 00:00:00 2001 From: crazybits Date: Wed, 29 Apr 2020 09:30:54 +0800 Subject: [PATCH 80/81] formatting updated as advised --- libraries/app/api_objects.cpp | 18 ++++++++++++------ .../app/include/graphene/app/api_objects.hpp | 3 ++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/libraries/app/api_objects.cpp b/libraries/app/api_objects.cpp index b3637b3388..a999ad99ca 100644 --- a/libraries/app/api_objects.cpp +++ b/libraries/app/api_objects.cpp @@ -71,13 +71,19 @@ market_ticker::market_ticker(const market_ticker_object& mto, quote_volume = uint128_amount_to_string( qv, asset_quote.precision ); if(!orders.asks.empty()) - lowest_ask = orders.asks[0].price; - lowest_ask_base_size =orders.asks[0].base; - lowest_ask_quote_size =orders.asks[0].quote; + { + lowest_ask = orders.asks[0].price; + lowest_ask_base_size = orders.asks[0].base; + lowest_ask_quote_size = orders.asks[0].quote; + } + if(!orders.bids.empty()) - highest_bid = orders.bids[0].price; - highest_bid_base_size =orders.bids[0].base; - highest_bid_quote_size =orders.bids[0].quote; + { + highest_bid = orders.bids[0].price; + highest_bid_base_size = orders.bids[0].base; + highest_bid_quote_size = orders.bids[0].quote; + } + } market_ticker::market_ticker(const fc::time_point_sec& now, diff --git a/libraries/app/include/graphene/app/api_objects.hpp b/libraries/app/include/graphene/app/api_objects.hpp index 847d2b4bf9..ff7f027f09 100644 --- a/libraries/app/include/graphene/app/api_objects.hpp +++ b/libraries/app/include/graphene/app/api_objects.hpp @@ -182,7 +182,8 @@ FC_REFLECT( graphene::app::full_account, FC_REFLECT( graphene::app::order, (price)(quote)(base) ); FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) ); FC_REFLECT( graphene::app::market_ticker, - (time)(base)(quote)(latest)(lowest_ask)(lowest_ask_base_size)(lowest_ask_quote_size)(highest_bid)(highest_bid_base_size)(highest_bid_quote_size)(percent_change)(base_volume)(quote_volume) ); + (time)(base)(quote)(latest)(lowest_ask)(lowest_ask_base_size)(lowest_ask_quote_size) + (highest_bid)(highest_bid_base_size)(highest_bid_quote_size)(percent_change)(base_volume)(quote_volume) ); FC_REFLECT( graphene::app::market_volume, (time)(base)(quote)(base_volume)(quote_volume) ); FC_REFLECT( graphene::app::market_trade, (sequence)(date)(price)(amount)(value)(side1_account_id)(side2_account_id) ); 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 81/81] 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;