From 895509a75c75f6a39285a8c23eb5dacc05dfe0a8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 20 Sep 2019 12:55:12 -0500 Subject: [PATCH 01/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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 a59540f9e6c6d0bba735c08b2670bd9770c8b32e Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 11 Apr 2020 09:50:28 -0500 Subject: [PATCH 07/28] 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 08/28] 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 09/28] 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 10/28] 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 11/28] 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 12/28] 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 13/28] 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 14/28] 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 15/28] 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 16/28] 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 17/28] 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 18/28] 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 19/28] 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 20/28] 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 21/28] 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 22/28] 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 23/28] 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 24/28] 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 25/28] 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 11f0b8c4feea0f74b26a1a081742d6bf348912ed Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 23 Apr 2020 12:58:24 -0500 Subject: [PATCH 26/28] 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 27/28] 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 28/28] 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() }