From 895509a75c75f6a39285a8c23eb5dacc05dfe0a8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 20 Sep 2019 12:55:12 -0500 Subject: [PATCH 01/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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 20e43f0646626a7fd97291fc9c69a886406aef48 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 18 Apr 2020 10:09:37 -0500 Subject: [PATCH 14/43] 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 5f68e640ebe4fb6c3fc5fecd99ded26e14496499 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 18 Apr 2020 13:06:41 -0500 Subject: [PATCH 15/43] 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 16/43] 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 17/43] 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 18/43] 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 19/43] 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 133f7c369c972790480ef0d2a62b085fe450bfa8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 20 Apr 2020 08:49:45 -0500 Subject: [PATCH 20/43] 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 21/43] 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 22/43] 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 23/43] 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 cf36230699f57c0b2d4cda66f1293d35c9ec3dcf Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 21 Apr 2020 09:49:14 -0500 Subject: [PATCH 24/43] 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 25/43] 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 79a7abf2109a832ffe6760601fd1ec283b490c86 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 23 Apr 2020 08:44:22 -0500 Subject: [PATCH 26/43] 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 27/43] 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 28/43] 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 29/43] 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 30/43] 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 31/43] 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 32/43] 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 33/43] 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 34/43] 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 35/43] 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 36/43] 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 37/43] 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 38/43] 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 39/43] 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 40/43] 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 41/43] 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 42/43] 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 43/43] 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;