From 84c72184ce4d925911e25fdb152a953f262fe1c6 Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 31 Aug 2018 15:58:01 -0400 Subject: [PATCH 001/161] BitAsset obj: added maintenance_collateralization --- libraries/chain/asset_object.cpp | 11 +++++++++-- libraries/chain/hardfork.d/CORE_1270.hf | 4 ++++ .../chain/include/graphene/chain/asset_object.hpp | 4 ++++ .../include/graphene/chain/protocol/asset.hpp | 14 +++++--------- libraries/chain/protocol/asset.cpp | 6 ++++++ 5 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 libraries/chain/hardfork.d/CORE_1270.hf diff --git a/libraries/chain/asset_object.cpp b/libraries/chain/asset_object.cpp index 47fd3c146b..3eec2a5a27 100644 --- a/libraries/chain/asset_object.cpp +++ b/libraries/chain/asset_object.cpp @@ -73,13 +73,17 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point feed_cer_updated = false; // new median cer is null, won't update asset_object anyway, set to false for better performance current_feed_publication_time = current_time; current_feed = price_feed(); + current_maintenance_collateralization = price(); return; } if( current_feeds.size() == 1 ) { if( current_feed.core_exchange_rate != current_feeds.front().get().core_exchange_rate ) feed_cer_updated = true; - current_feed = std::move(current_feeds.front()); + current_feed = current_feeds.front(); + // Note: 1. perhaps can defer updating current_maintenance_collateralization for better performance; + // 2. can check time before updating for better performance. + current_maintenance_collateralization = current_feed.maintenance_collateralization(); return; } @@ -100,6 +104,9 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point if( current_feed.core_exchange_rate != median_feed.core_exchange_rate ) feed_cer_updated = true; current_feed = median_feed; + // Note: 1. perhaps can defer updating current_maintenance_collateralization for better performance; + // 2. can check time before updating for better performance. + current_maintenance_collateralization = current_feed.maintenance_collateralization(); } @@ -157,7 +164,7 @@ asset asset_object::amount_from_string(string amount_string) const satoshis *= -1; return amount(satoshis); - } FC_CAPTURE_AND_RETHROW( (amount_string) ) } +} FC_CAPTURE_AND_RETHROW( (amount_string) ) } string asset_object::amount_to_string(share_type amount) const { diff --git a/libraries/chain/hardfork.d/CORE_1270.hf b/libraries/chain/hardfork.d/CORE_1270.hf new file mode 100644 index 0000000000..6894635f4e --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_1270.hf @@ -0,0 +1,4 @@ +// bitshares-core issue #1270 Call price is inconsistent when MCR changed +#ifndef HARDFORK_CORE_1270_TIME +#define HARDFORK_CORE_1270_TIME (fc::time_point_sec( 1580000000 )) // a temporary date in the future +#endif diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 5f73e79ddd..a5b90d4613 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -191,6 +191,9 @@ namespace graphene { namespace chain { price_feed current_feed; /// This is the publication time of the oldest feed which was factored into current_feed. time_point_sec current_feed_publication_time; + /// Call orders with collateralization (aka collateral/debt) not greater than this value are in margin call territory. + /// This value is derived from @ref current_feed for better performance and should be kept consistent. + price current_maintenance_collateralization; /// True if this asset implements a @ref prediction_market bool is_prediction_market = false; @@ -305,6 +308,7 @@ FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db:: (feeds) (current_feed) (current_feed_publication_time) + (current_maintenance_collateralization) (options) (force_settled_volume) (is_prediction_market) diff --git a/libraries/chain/include/graphene/chain/protocol/asset.hpp b/libraries/chain/include/graphene/chain/protocol/asset.hpp index 86d17892ff..f7cfc4680d 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset.hpp @@ -193,15 +193,6 @@ namespace graphene { namespace chain { /** Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM */ uint16_t maximum_short_squeeze_ratio = GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO; - /** - * When updating a call order the following condition must be maintained: - * - * debt * maintenance_price() < collateral - * debt * settlement_price < debt * maintenance - * debt * maintenance_price() < debt * max_short_squeeze_price() - price maintenance_price()const; - */ - /** When selling collateral to pay off debt, the least amount of debt to receive should be * min_usd = max_short_squeeze_price() * collateral * @@ -209,6 +200,11 @@ namespace graphene { namespace chain { * must be confirmed by having the max_short_squeeze_price() move below the black swan price. */ price max_short_squeeze_price()const; + + /// Call orders with collateralization (aka collateral/debt) not greater than this value are in margin call territory. + /// Calculation: ~settlement_price * maintenance_collateral_ratio / GRAPHENE_COLLATERAL_RATIO_DENOM + price maintenance_collateralization()const; + ///@} friend bool operator == ( const price_feed& a, const price_feed& b ) diff --git a/libraries/chain/protocol/asset.cpp b/libraries/chain/protocol/asset.cpp index 531ea7f6f6..8f7a63951a 100644 --- a/libraries/chain/protocol/asset.cpp +++ b/libraries/chain/protocol/asset.cpp @@ -271,6 +271,12 @@ namespace graphene { namespace chain { return (asset( cp.numerator().convert_to(), settlement_price.base.asset_id ) / asset( cp.denominator().convert_to(), settlement_price.quote.asset_id )); } + price price_feed::maintenance_collateralization()const + { + if( settlement_price.is_null() ) + return price(); + return ~settlement_price * ratio_type( maintenance_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM ); + } // compile-time table of powers of 10 using template metaprogramming template< int N > From 6254076ecd3e966d03d32bb43c19249f9bfb76df Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 31 Aug 2018 18:13:48 -0400 Subject: [PATCH 002/161] Refactored feed_price::max_short_squeeze_price() --- libraries/chain/db_market.cpp | 14 +++++++--- libraries/chain/db_update.cpp | 5 +++- .../include/graphene/chain/protocol/asset.hpp | 2 ++ libraries/chain/protocol/asset.cpp | 27 ++++++++++++++----- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 6b8f67ea1c..3fb1f76d45 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -427,6 +427,9 @@ bool database::apply_order(const limit_order_object& new_order_object, bool allo // 5. the call order's collateral ratio is below or equals to MCR // 6. the limit order provided a good price + auto maint_time = get_dynamic_global_properties().next_maintenance_time; + bool before_core_hardfork_1270 = ( maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue + bool to_check_call_orders = false; const asset_object& sell_asset = sell_asset_id( *this ); const asset_bitasset_data_object* sell_abd = nullptr; @@ -439,7 +442,10 @@ bool database::apply_order(const limit_order_object& new_order_object, bool allo && !sell_abd->has_settlement() && !sell_abd->current_feed.settlement_price.is_null() ) { - call_match_price = ~sell_abd->current_feed.max_short_squeeze_price(); + if( before_core_hardfork_1270 ) + call_match_price = ~sell_abd->current_feed.max_short_squeeze_price_before_hf_1270(); + else + call_match_price = ~sell_abd->current_feed.max_short_squeeze_price(); if( ~new_order_object.sell_price <= call_match_price ) // new limit order price is good enough to match a call to_check_call_orders = true; } @@ -934,12 +940,14 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa const limit_order_index& limit_index = get_index_type(); const auto& limit_price_index = limit_index.indices().get(); + bool before_core_hardfork_1270 = ( maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue + // looking for limit orders selling the most USD for the least CORE auto max_price = price::max( mia.id, bitasset.options.short_backing_asset ); // stop when limit orders are selling too little USD for too much CORE - auto min_price = bitasset.current_feed.max_short_squeeze_price(); + auto min_price = ( before_core_hardfork_1270 ? bitasset.current_feed.max_short_squeeze_price_before_hf_1270() + : bitasset.current_feed.max_short_squeeze_price() ); - assert( max_price.base.asset_id == min_price.base.asset_id ); // NOTE limit_price_index is sorted from greatest to least auto limit_itr = limit_price_index.lower_bound( max_price ); auto limit_end = limit_price_index.upper_bound( min_price ); diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 90054888bf..5888ea0604 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -204,9 +204,12 @@ bool database::check_for_blackswan( const asset_object& mia, bool enable_black_s const auto& dyn_prop = get_dynamic_global_properties(); auto maint_time = dyn_prop.next_maintenance_time; - if( maint_time > HARDFORK_CORE_338_TIME ) + if( maint_time > HARDFORK_CORE_1270_TIME ) // due to #338, we won't check for black swan on incoming limit order, so need to check with MSSP here highest = bitasset.current_feed.max_short_squeeze_price(); + else if( maint_time > HARDFORK_CORE_338_TIME ) + // due to #338, we won't check for black swan on incoming limit order, so need to check with MSSP here + highest = bitasset.current_feed.max_short_squeeze_price_before_hf_1270(); const limit_order_index& limit_index = get_index_type(); const auto& limit_price_index = limit_index.indices().get(); diff --git a/libraries/chain/include/graphene/chain/protocol/asset.hpp b/libraries/chain/include/graphene/chain/protocol/asset.hpp index f7cfc4680d..b354d5ccac 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset.hpp @@ -200,6 +200,8 @@ namespace graphene { namespace chain { * must be confirmed by having the max_short_squeeze_price() move below the black swan price. */ price max_short_squeeze_price()const; + /// Another implementation of max_short_squeeze_price() before the core-1270 hard fork + price max_short_squeeze_price_before_hf_1270()const; /// Call orders with collateralization (aka collateral/debt) not greater than this value are in margin call territory. /// Calculation: ~settlement_price * maintenance_collateral_ratio / GRAPHENE_COLLATERAL_RATIO_DENOM diff --git a/libraries/chain/protocol/asset.cpp b/libraries/chain/protocol/asset.cpp index 8f7a63951a..968042d21d 100644 --- a/libraries/chain/protocol/asset.cpp +++ b/libraries/chain/protocol/asset.cpp @@ -239,9 +239,11 @@ namespace graphene { namespace chain { FC_ASSERT( maximum_short_squeeze_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO ); FC_ASSERT( maintenance_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO ); FC_ASSERT( maintenance_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO ); - max_short_squeeze_price(); // make sure that it doesn't overflow + // Note: there was code here calling `max_short_squeeze_price();` before core-1270 hard fork, + // in order to make sure that it doesn't overflow, + // but the code doesn't actually check overflow, and it won't overflow, so the code is removed. - //FC_ASSERT( maintenance_collateral_ratio >= maximum_short_squeeze_ratio ); + // Note: not checking `maintenance_collateral_ratio >= maximum_short_squeeze_ratio` since launch } FC_CAPTURE_AND_RETHROW( (*this) ) } bool price_feed::is_for( asset_id_type asset_id ) const @@ -258,17 +260,27 @@ namespace graphene { namespace chain { FC_CAPTURE_AND_RETHROW( (*this) ) } - price price_feed::max_short_squeeze_price()const + // This function is kept here due to potential different behavior in edge cases. + // TODO check after core-1270 hard fork to see if we can safely remove it + price price_feed::max_short_squeeze_price_before_hf_1270()const { - // TODO replace the calculation with new operator*() and/or operator/(), could be a hardfork change due to edge cases - boost::rational sp( settlement_price.base.amount.value, settlement_price.quote.amount.value ); //debt.amount.value,collateral.amount.value); + // settlement price is in debt/collateral + boost::rational sp( settlement_price.base.amount.value, settlement_price.quote.amount.value ); boost::rational ratio( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio ); auto cp = sp * ratio; while( cp.numerator() > GRAPHENE_MAX_SHARE_SUPPLY || cp.denominator() > GRAPHENE_MAX_SHARE_SUPPLY ) - cp = boost::rational( (cp.numerator() >> 1)+(cp.numerator()&1), (cp.denominator() >> 1)+(cp.denominator()&1) ); + cp = boost::rational( (cp.numerator() >> 1)+(cp.numerator()&1), + (cp.denominator() >> 1)+(cp.denominator()&1) ); - return (asset( cp.numerator().convert_to(), settlement_price.base.asset_id ) / asset( cp.denominator().convert_to(), settlement_price.quote.asset_id )); + return ( asset( cp.numerator().convert_to(), settlement_price.base.asset_id ) + / asset( cp.denominator().convert_to(), settlement_price.quote.asset_id ) ); + } + + price price_feed::max_short_squeeze_price()const + { + // settlement price is in debt/collateral + return settlement_price * ratio_type( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio ); } price price_feed::maintenance_collateralization()const @@ -277,6 +289,7 @@ namespace graphene { namespace chain { return price(); return ~settlement_price * ratio_type( maintenance_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM ); } + // compile-time table of powers of 10 using template metaprogramming template< int N > From 9b41a923f0a6ec599f9c91f8ee55963350f7eb2c Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 31 Aug 2018 18:18:43 -0400 Subject: [PATCH 003/161] Updated comment in price::call_price() --- libraries/chain/protocol/asset.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/chain/protocol/asset.cpp b/libraries/chain/protocol/asset.cpp index 968042d21d..a9c1daf502 100644 --- a/libraries/chain/protocol/asset.cpp +++ b/libraries/chain/protocol/asset.cpp @@ -203,10 +203,11 @@ namespace graphene { namespace chain { * never go to 0 and the debt can never go more than GRAPHENE_MAX_SHARE_SUPPLY * * CR * DEBT/COLLAT or DEBT/(COLLAT/CR) + * + * Note: this function is only used before core-1270 hard fork. */ price price::call_price( const asset& debt, const asset& collateral, uint16_t collateral_ratio) { try { - // TODO replace the calculation with new operator*() and/or operator/(), could be a hardfork change due to edge cases boost::rational swan(debt.amount.value,collateral.amount.value); boost::rational ratio( collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM ); auto cp = swan * ratio; From 5308ea0ca1ad87860c8dfb112e67276fa8748066 Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 31 Aug 2018 18:22:18 -0400 Subject: [PATCH 004/161] Iterates by collateral in globally_settle_asset() --- libraries/chain/db_market.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 3fb1f76d45..b31f6b78d6 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -51,15 +51,14 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett const asset_dynamic_data_object& mia_dyn = mia.dynamic_asset_data_id(*this); auto original_mia_supply = mia_dyn.current_supply; - const call_order_index& call_index = get_index_type(); - const auto& call_price_index = call_index.indices().get(); + const auto& call_index = get_index_type().indices().get(); auto maint_time = get_dynamic_global_properties().next_maintenance_time; bool before_core_hardfork_342 = ( maint_time <= HARDFORK_CORE_342_TIME ); // better rounding // cancel all call orders and accumulate it into collateral_gathered - auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) ); - auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) ); + auto call_itr = call_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) ); + auto call_end = call_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) ); asset pays; while( call_itr != call_end ) { @@ -83,8 +82,7 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett FC_ASSERT( fill_call_order( order, pays, order.get_debt(), settlement_price, true ) ); // call order is maker } - modify( bitasset, [&]( asset_bitasset_data_object& obj ){ - assert( collateral_gathered.asset_id == settlement_price.quote.asset_id ); + modify( bitasset, [&mia,original_mia_supply,&collateral_gathered]( asset_bitasset_data_object& obj ){ obj.settlement_price = mia.amount(original_mia_supply) / collateral_gathered; //settlement_price; obj.settlement_fund = collateral_gathered.amount; }); @@ -93,7 +91,7 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett /// that is a lie, the supply didn't change. We need to capture the current supply before /// filling all call orders and then restore it afterward. Then in the force settlement /// evaluator reduce the supply - modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){ + modify( mia_dyn, [original_mia_supply]( asset_dynamic_data_object& obj ){ obj.current_supply = original_mia_supply; }); From c912886957a19dbf8c72c653db540339b7827a38 Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 31 Aug 2018 18:48:18 -0400 Subject: [PATCH 005/161] Iterate call orders by collateral in apply_order() --- libraries/chain/db_market.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index b31f6b78d6..d37dc087e1 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -461,7 +461,32 @@ bool database::apply_order(const limit_order_object& new_order_object, bool allo finished = ( match( new_order_object, *old_limit_itr, old_limit_itr->sell_price ) != 2 ); } - if( !finished ) + if( !finished && !before_core_hardfork_1270 ) // TODO refactor or cleanup duplicate code after core-1270 hard fork + { + // check if there are margin calls + const auto& call_collateral_idx = get_index_type().indices().get(); + auto call_min = price::min( recv_asset_id, sell_asset_id ); + while( !finished ) + { + // hard fork core-343 and core-625 took place at same time, + // always check call order with least collateral ratio + auto call_itr = call_collateral_idx.lower_bound( call_min ); + if( call_itr == call_collateral_idx.end() + || call_itr->debt_type() != sell_asset_id + // feed protected https://github.com/cryptonomex/graphene/issues/436 + || call_itr->collateralization() > sell_abd->current_maintenance_collateralization ) + break; + // hard fork core-338 and core-625 took place at same time, not checking HARDFORK_CORE_338_TIME here. + int match_result = match( new_order_object, *call_itr, call_match_price, + sell_abd->current_feed.settlement_price, + sell_abd->current_feed.maintenance_collateral_ratio ); + // match returns 1 or 3 when the new order was fully filled. In this case, we stop matching; otherwise keep matching. + // since match can return 0 due to BSIP38 (hard fork core-834), we no longer only check if the result is 2. + if( match_result == 1 || match_result == 3 ) + finished = true; + } + } + else if( !finished ) // and before core-1270 hard fork { // check if there are margin calls const auto& call_price_idx = get_index_type().indices().get(); From 469a4998a46ed419738f12cfbb45f2c2f0940d66 Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 31 Aug 2018 20:45:24 -0400 Subject: [PATCH 006/161] Iterate calls by collateral in check_call_orders() --- libraries/chain/db_market.cpp | 38 ++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index d37dc087e1..192f7722ad 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -980,11 +980,26 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa const call_order_index& call_index = get_index_type(); const auto& call_price_index = call_index.indices().get(); + const auto& call_collateral_index = call_index.indices().get(); auto call_min = price::min( bitasset.options.short_backing_asset, mia.id ); auto call_max = price::max( bitasset.options.short_backing_asset, mia.id ); - auto call_itr = call_price_index.lower_bound( call_min ); - auto call_end = call_price_index.upper_bound( call_max ); + + auto call_price_itr = call_price_index.begin(); + auto call_price_end = call_price_itr; + auto call_collateral_itr = call_collateral_index.begin(); + auto call_collateral_end = call_collateral_itr; + + if( before_core_hardfork_1270 ) + { + call_price_itr = call_price_index.lower_bound( call_min ); + call_price_end = call_price_index.upper_bound( call_max ); + } + else + { + call_collateral_itr = call_collateral_index.lower_bound( call_min ); + call_collateral_end = call_collateral_index.upper_bound( call_max ); + } bool filled_limit = false; bool margin_called = false; @@ -1003,15 +1018,18 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa bool before_core_hardfork_834 = ( maint_time <= HARDFORK_CORE_834_TIME ); // target collateral ratio option while( !check_for_blackswan( mia, enable_black_swan, &bitasset ) // TODO perhaps improve performance by passing in iterators - && call_itr != call_end - && limit_itr != limit_end ) + && limit_itr != limit_end + && ( ( !before_core_hardfork_1270 && call_collateral_itr != call_collateral_end ) + || ( before_core_hardfork_1270 && call_price_itr != call_price_end ) ) ) { bool filled_call = false; - const call_order_object& call_order = *call_itr; + const call_order_object& call_order = ( before_core_hardfork_1270 ? *call_price_itr : *call_collateral_itr ); // Feed protected (don't call if CR>MCR) https://github.com/cryptonomex/graphene/issues/436 - if( after_hardfork_436 && bitasset.current_feed.settlement_price > ~call_order.call_price ) + if( ( !before_core_hardfork_1270 && bitasset.current_maintenance_collateralization < call_order.collateralization() ) + || ( before_core_hardfork_1270 + && after_hardfork_436 && bitasset.current_feed.settlement_price > ~call_order.call_price ) ) return margin_called; const limit_order_object& limit_order = *limit_itr; @@ -1102,11 +1120,13 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa order_pays = call_receives; if( filled_call && before_core_hardfork_343 ) - ++call_itr; + ++call_price_itr; // when for_new_limit_order is true, the call order is maker, otherwise the call order is taker fill_call_order( call_order, call_pays, call_receives, match_price, for_new_limit_order ); - if( !before_core_hardfork_343 ) - call_itr = call_price_index.lower_bound( call_min ); + if( !before_core_hardfork_1270 ) + call_collateral_itr = call_collateral_index.lower_bound( call_min ); + else if( !before_core_hardfork_343 ) + call_price_itr = call_price_index.lower_bound( call_min ); auto next_limit_itr = std::next( limit_itr ); // when for_new_limit_order is true, the limit order is taker, otherwise the limit order is maker From cb7bd0c5960ff8956f96f66f803975c5b9e79f85 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 1 Sep 2018 07:06:26 -0400 Subject: [PATCH 007/161] db_market: stop using call_price after hf #1270 --- libraries/chain/db_market.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 192f7722ad..ee710f1e88 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -168,9 +168,15 @@ void database::execute_bid( const collateral_bid_object& bid, share_type debt_co call.borrower = bid.bidder; call.collateral = bid.inv_swan_price.base.amount + collateral_from_fund; call.debt = debt_covered; - call.call_price = price::call_price(asset(debt_covered, bid.inv_swan_price.quote.asset_id), - asset(call.collateral, bid.inv_swan_price.base.asset_id), - current_feed.maintenance_collateral_ratio); + // don't calculate call_price after core-1270 hard fork + if( get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_1270_TIME ) + // bid.inv_swan_price is in collateral / debt + call.call_price = price( asset( 1, bid.inv_swan_price.base.asset_id ), + asset( 1, bid.inv_swan_price.quote.asset_id ) ); + else + call.call_price = price::call_price( asset(debt_covered, bid.inv_swan_price.quote.asset_id), + asset(call.collateral, bid.inv_swan_price.base.asset_id), + current_feed.maintenance_collateral_ratio ); }); // Note: CORE asset in collateral_bid_object is not counted in account_stats.total_core_in_orders @@ -866,9 +872,17 @@ bool database::fill_call_order( const call_order_object& order, const asset& pay collateral_freed = o.get_collateral(); o.collateral = 0; } - else if( get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_343_TIME ) - o.call_price = price::call_price( o.get_debt(), o.get_collateral(), - mia.bitasset_data(*this).current_feed.maintenance_collateral_ratio ); + else + { + auto maint_time = get_dynamic_global_properties().next_maintenance_time; + // update call_price after core-343 hard fork, + // but don't update call_price after core-1270 hard fork + if( maint_time <= HARDFORK_CORE_1270_TIME && maint_time > HARDFORK_CORE_343_TIME ) + { + o.call_price = price::call_price( o.get_debt(), o.get_collateral(), + mia.bitasset_data(*this).current_feed.maintenance_collateral_ratio ); + } + } }); // update current supply From 0ba6cb1385f15c1e20dc2b59e56345ff380bf70b Mon Sep 17 00:00:00 2001 From: abitmore Date: Tue, 4 Sep 2018 11:42:00 -0400 Subject: [PATCH 008/161] Do not update current_maint_collateral before hf --- libraries/chain/asset_evaluator.cpp | 17 ++++++----- libraries/chain/asset_object.cpp | 28 ++++++++----------- libraries/chain/db_maint.cpp | 5 ++-- libraries/chain/db_update.cpp | 5 ++-- .../include/graphene/chain/asset_object.hpp | 14 +++++++++- 5 files changed, 41 insertions(+), 28 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 9194a02d25..6fcbe2e14e 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -638,7 +638,7 @@ static bool update_bitasset_object_options( const asset_update_bitasset_operation& op, database& db, asset_bitasset_data_object& bdo, const asset_object& asset_to_update ) { - const fc::time_point_sec& next_maint_time = db.get_dynamic_global_properties().next_maintenance_time; + const fc::time_point_sec next_maint_time = db.get_dynamic_global_properties().next_maintenance_time; bool after_hf_core_868_890 = ( next_maint_time > HARDFORK_CORE_868_890_TIME ); // If the minimum number of feeds to calculate a median has changed, we need to recalculate the median @@ -689,7 +689,7 @@ static bool update_bitasset_object_options( if( should_update_feeds ) { const auto old_feed = bdo.current_feed; - bdo.update_median_feeds( db.head_block_time() ); + bdo.update_median_feeds( db.head_block_time(), next_maint_time ); // TODO review and refactor / cleanup after hard fork: // 1. if hf_core_868_890 and core-935 occurred at same time @@ -766,8 +766,9 @@ void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_f { try { database& d = db(); const auto head_time = d.head_block_time(); + const auto next_maint_time = d.get_dynamic_global_properties().next_maintenance_time; const asset_bitasset_data_object& bitasset_to_update = asset_to_update->bitasset_data(d); - d.modify( bitasset_to_update, [&o,head_time](asset_bitasset_data_object& a) { + d.modify( bitasset_to_update, [&o,head_time,next_maint_time](asset_bitasset_data_object& a) { //This is tricky because I have a set of publishers coming in, but a map of publisher to feed is stored. //I need to update the map such that the keys match the new publishers, but not munge the old price feeds from //publishers who are being kept. @@ -791,7 +792,7 @@ void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_f { a.feeds[acc]; } - a.update_median_feeds( head_time ); + a.update_median_feeds( head_time, next_maint_time ); }); // Process margin calls, allow black swan, not for a new limit order d.check_call_orders( *asset_to_update, true, false, &bitasset_to_update ); @@ -969,15 +970,17 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope { try { database& d = db(); + const auto head_time = d.head_block_time(); + const auto next_maint_time = d.get_dynamic_global_properties().next_maintenance_time; const asset_object& base = *asset_ptr; const asset_bitasset_data_object& bad = *bitasset_ptr; auto old_feed = bad.current_feed; // Store medians for this asset - d.modify(bad , [&o,&d](asset_bitasset_data_object& a) { - a.feeds[o.publisher] = make_pair(d.head_block_time(), o.feed); - a.update_median_feeds(d.head_block_time()); + d.modify( bad , [&o,head_time,next_maint_time](asset_bitasset_data_object& a) { + a.feeds[o.publisher] = make_pair( head_time, o.feed ); + a.update_median_feeds( head_time, next_maint_time ); }); if( !(old_feed == bad.current_feed) ) diff --git a/libraries/chain/asset_object.cpp b/libraries/chain/asset_object.cpp index 3eec2a5a27..c6b6ca0d82 100644 --- a/libraries/chain/asset_object.cpp +++ b/libraries/chain/asset_object.cpp @@ -23,6 +23,7 @@ */ #include #include +#include #include @@ -43,16 +44,10 @@ share_type asset_bitasset_data_object::max_force_settlement_volume(share_type cu return volume.to_uint64(); } -/****** - * @brief calculate the median feed - * - * This calculates the median feed. It sets the current_feed_publication_time - * and current_feed member variables - * - * @param current_time the time to use in the calculations - */ -void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point_sec current_time) +void graphene::chain::asset_bitasset_data_object::update_median_feeds( time_point_sec current_time, + time_point_sec next_maintenance_time ) { + bool after_core_hardfork_1270 = ( next_maintenance_time > HARDFORK_CORE_1270_TIME ); // call price caching issue current_feed_publication_time = current_time; vector> current_feeds; // find feeds that were alive at current_time @@ -73,7 +68,8 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point feed_cer_updated = false; // new median cer is null, won't update asset_object anyway, set to false for better performance current_feed_publication_time = current_time; current_feed = price_feed(); - current_maintenance_collateralization = price(); + if( after_core_hardfork_1270 ) + current_maintenance_collateralization = price(); return; } if( current_feeds.size() == 1 ) @@ -81,9 +77,9 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point if( current_feed.core_exchange_rate != current_feeds.front().get().core_exchange_rate ) feed_cer_updated = true; current_feed = current_feeds.front(); - // Note: 1. perhaps can defer updating current_maintenance_collateralization for better performance; - // 2. can check time before updating for better performance. - current_maintenance_collateralization = current_feed.maintenance_collateralization(); + // Note: perhaps can defer updating current_maintenance_collateralization for better performance + if( after_core_hardfork_1270 ) + current_maintenance_collateralization = current_feed.maintenance_collateralization(); return; } @@ -104,9 +100,9 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point if( current_feed.core_exchange_rate != median_feed.core_exchange_rate ) feed_cer_updated = true; current_feed = median_feed; - // Note: 1. perhaps can defer updating current_maintenance_collateralization for better performance; - // 2. can check time before updating for better performance. - current_maintenance_collateralization = current_feed.maintenance_collateralization(); + // Note: perhaps can defer updating current_maintenance_collateralization for better performance + if( after_core_hardfork_1270 ) + current_maintenance_collateralization = current_feed.maintenance_collateralization(); } diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 7f1a8a4c6c..f00a7bf61e 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -960,6 +960,7 @@ void database::process_bitassets() // * NOTE: the removal can't be applied to testnet void process_hf_868_890( database& db, bool skip_check_call_orders ) { + const auto next_maint_time = db.get_dynamic_global_properties().next_maintenance_time; const auto head_time = db.head_block_time(); const auto head_num = db.head_block_num(); wlog( "Processing hard fork core-868-890 at block ${n}", ("n",head_num) ); @@ -1019,8 +1020,8 @@ void process_hf_868_890( database& db, bool skip_check_call_orders ) } // always update the median feed due to https://github.com/bitshares/bitshares-core/issues/890 - db.modify( bitasset_data, [&head_time]( asset_bitasset_data_object &obj ) { - obj.update_median_feeds( head_time ); + db.modify( bitasset_data, [head_time,next_maint_time]( asset_bitasset_data_object &obj ) { + obj.update_median_feeds( head_time, next_maint_time ); }); bool median_changed = ( old_feed.settlement_price != bitasset_data.current_feed.settlement_price ); diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 5888ea0604..0c581d0af2 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -461,6 +461,7 @@ void database::clear_expired_orders() void database::update_expired_feeds() { const auto head_time = head_block_time(); + const auto next_maint_time = get_dynamic_global_properties().next_maintenance_time; bool after_hardfork_615 = ( head_time >= HARDFORK_615_TIME ); const auto& idx = get_index_type().indices().get(); @@ -475,9 +476,9 @@ void database::update_expired_feeds() if( after_hardfork_615 || b.feed_is_expired_before_hardfork_615( head_time ) ) { auto old_median_feed = b.current_feed; - modify( b, [head_time,&update_cer]( asset_bitasset_data_object& abdo ) + modify( b, [head_time,next_maint_time,&update_cer]( asset_bitasset_data_object& abdo ) { - abdo.update_median_feeds( head_time ); + abdo.update_median_feeds( head_time, next_maint_time ); if( abdo.need_to_update_cer() ) { update_cer = true; diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index a5b90d4613..38081dc40f 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -244,7 +244,19 @@ namespace graphene { namespace chain { { return feed_expiration_time() >= current_time; } bool feed_is_expired(time_point_sec current_time)const { return feed_expiration_time() <= current_time; } - void update_median_feeds(time_point_sec current_time); + + /****** + * @brief calculate the median feed + * + * This calculates the median feed from @ref feeds, feed_lifetime_sec + * in @ref options, and the given parameters. + * It may update the current_feed_publication_time, current_feed and + * current_maintenance_collateralization member variables. + * + * @param current_time the current time to use in the calculations + * @param next_maintenance_time the next chain maintenance time + */ + void update_median_feeds(time_point_sec current_time, time_point_sec next_maintenance_time); }; // key extractor for short backing asset From 92a3b718ca35077ab82134d63c711b6b8ba6295c Mon Sep 17 00:00:00 2001 From: abitmore Date: Tue, 4 Sep 2018 13:04:56 -0400 Subject: [PATCH 009/161] check revive: stop using call_price after hf #1270 --- libraries/chain/asset_evaluator.cpp | 31 +++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 6fcbe2e14e..1eac891f1a 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -985,14 +985,33 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope if( !(old_feed == bad.current_feed) ) { - if( bad.has_settlement() ) // implies head_block_time > HARDFORK_CORE_216_TIME + // Check whether need to revive the asset and proceed if need + if( bad.has_settlement() // has globally settled, implies head_block_time > HARDFORK_CORE_216_TIME + && !bad.current_feed.settlement_price.is_null() ) // has a valid feed { + bool should_revive = false; const auto& mia_dyn = base.dynamic_asset_data_id(d); - if( !bad.current_feed.settlement_price.is_null() - && ( mia_dyn.current_supply == 0 - || ~price::call_price(asset(mia_dyn.current_supply, o.asset_id), - asset(bad.settlement_fund, bad.options.short_backing_asset), - bad.current_feed.maintenance_collateral_ratio ) < bad.current_feed.settlement_price ) ) + if( mia_dyn.current_supply == 0 ) // if current supply is zero, revive the asset + should_revive = true; + else // if current supply is not zero, when collateral ratio of settlement fund is greater than MCR, revive the asset + { + if( next_maint_time <= HARDFORK_CORE_1270_TIME ) + { + // before core-1270 hard fork, calculate call_price and compare to median feed + if( ~price::call_price( asset(mia_dyn.current_supply, o.asset_id), + asset(bad.settlement_fund, bad.options.short_backing_asset), + bad.current_feed.maintenance_collateral_ratio ) < bad.current_feed.settlement_price ) + should_revive = true; + } + else + { + // after core-1270 hard fork, calculate collateralization and compare to maintenance_collateralization + if( price( asset( bad.settlement_fund, bad.options.short_backing_asset ), + asset( mia_dyn.current_supply, o.asset_id ) ) > bad.current_maintenance_collateralization ) + should_revive = true; + } + } + if( should_revive ) d.revive_bitasset(base); } // Process margin calls, allow black swan, not for a new limit order From 077f4e04ce784e6f4dd3a7bc83e48e5623fc51fd Mon Sep 17 00:00:00 2001 From: abitmore Date: Tue, 4 Sep 2018 15:59:47 -0400 Subject: [PATCH 010/161] update call: stop using call_price after hf #1270 --- libraries/chain/market_evaluator.cpp | 36 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index ed7c55351b..90e7b3a9b0 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -217,6 +217,9 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat } } + const auto next_maint_time = d.get_dynamic_global_properties().next_maintenance_time; + bool before_core_hardfork_1270 = ( next_maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue + auto& call_idx = d.get_index_type().indices().get(); auto itr = call_idx.find( boost::make_tuple(o.funding_account, o.delta_debt.asset_id) ); const call_order_object* call_obj = nullptr; @@ -229,12 +232,15 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat FC_ASSERT( o.delta_collateral.amount > 0, "Delta collateral amount of new debt position should be positive" ); FC_ASSERT( o.delta_debt.amount > 0, "Delta debt amount of new debt position should be positive" ); - call_obj = &d.create( [&o,this]( call_order_object& call ){ + call_obj = &d.create( [&o,this,before_core_hardfork_1270]( call_order_object& call ){ call.borrower = o.funding_account; call.collateral = o.delta_collateral.amount; call.debt = o.delta_debt.amount; - call.call_price = price::call_price(o.delta_debt, o.delta_collateral, - _bitasset_data->current_feed.maintenance_collateral_ratio); + if( before_core_hardfork_1270 ) // before core-1270 hard fork, calculate call_price here and cache it + call.call_price = price::call_price( o.delta_debt, o.delta_collateral, + _bitasset_data->current_feed.maintenance_collateral_ratio ); + else // after core-1270 hard fork, set call_price to 1 + call.call_price = price( asset( 1, o.delta_collateral.asset_id ), asset( 1, o.delta_debt.asset_id ) ); call.target_collateral_ratio = o.extensions.value.target_collateral_ratio; }); } @@ -257,11 +263,14 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat old_collateralization = call_obj->collateralization(); old_debt = call_obj->debt; - d.modify( *call_obj, [&o,new_debt,new_collateral,this]( call_order_object& call ){ + d.modify( *call_obj, [&o,new_debt,new_collateral,this,before_core_hardfork_1270]( call_order_object& call ){ call.collateral = new_collateral; call.debt = new_debt; - call.call_price = price::call_price( call.get_debt(), call.get_collateral(), - _bitasset_data->current_feed.maintenance_collateral_ratio ); + if( before_core_hardfork_1270 ) // don't update call_price after core-1270 hard fork + { + call.call_price = price::call_price(call.get_debt(), call.get_collateral(), + _bitasset_data->current_feed.maintenance_collateral_ratio); + } call.target_collateral_ratio = o.extensions.value.target_collateral_ratio; }); } @@ -286,8 +295,7 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat GRAPHENE_ASSERT( !call_obj, call_order_update_unfilled_margin_call, - "Updating call order would trigger a margin call that cannot be fully filled", - ("a", ~call_obj->call_price )("b", _bitasset_data->current_feed.settlement_price) + "Updating call order would trigger a margin call that cannot be fully filled" ); } else @@ -301,9 +309,11 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat // aren't in margin call territory, or it may be because there // were no matching orders. In the latter case, we throw. GRAPHENE_ASSERT( + // we know core-583 hard fork is before core-1270 hard fork, it's ok to use call_price here ~call_obj->call_price < _bitasset_data->current_feed.settlement_price, call_order_update_unfilled_margin_call, "Updating call order would trigger a margin call that cannot be fully filled", + // we know core-583 hard fork is before core-1270 hard fork, it's ok to use call_price here ("a", ~call_obj->call_price )("b", _bitasset_data->current_feed.settlement_price) ); } @@ -315,13 +325,13 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat // if collateral ratio is not increased or debt is increased, we throw. // be here, we know no margin call was executed, // so call_obj's collateral ratio should be set only by op - FC_ASSERT( ( old_collateralization.valid() && call_obj->debt <= *old_debt - && call_obj->collateralization() > *old_collateralization ) - || ~call_obj->call_price < _bitasset_data->current_feed.settlement_price, + FC_ASSERT( ( !before_core_hardfork_1270 + && call_obj->collateralization() > _bitasset_data->current_maintenance_collateralization ) + || ( before_core_hardfork_1270 && ~call_obj->call_price < _bitasset_data->current_feed.settlement_price ) + || ( old_collateralization.valid() && call_obj->debt <= *old_debt + && call_obj->collateralization() > *old_collateralization ), "Can only increase collateral ratio without increasing debt if would trigger a margin call that " "cannot be fully filled", - ("new_call_price", ~call_obj->call_price ) - ("settlement_price", _bitasset_data->current_feed.settlement_price) ("old_debt", old_debt) ("new_debt", call_obj->debt) ("old_collateralization", old_collateralization) From 68c702b3b6989b294c6d5adfa3e4867386625fae Mon Sep 17 00:00:00 2001 From: abitmore Date: Sun, 9 Sep 2018 07:48:31 -0400 Subject: [PATCH 011/161] Update and match call orders at hf #1270 --- libraries/chain/db_maint.cpp | 60 ++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index f00a7bf61e..dfb3bec2b4 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -867,7 +867,9 @@ void database::process_bids( const asset_bitasset_data_object& bad ) _cancel_bids_and_revive_mpa( to_revive, bad ); } -void update_and_match_call_orders( database& db ) +/// Reset call_price of all call orders according to their remaining collateral and debt. +/// Do not update orders of prediction markets because we're sure they're up to date. +void update_call_orders_hf_343( database& db ) { // Update call_price wlog( "Updating all call orders for hardfork core-343 at block ${n}", ("n",db.head_block_num()) ); @@ -888,7 +890,30 @@ void update_and_match_call_orders( database& db ) abd->current_feed.maintenance_collateral_ratio ); }); } + wlog( "Done updating all call orders for hardfork core-343 at block ${n}", ("n",db.head_block_num()) ); +} + +/// Reset call_price of all call orders to (1,1) since it won't be used in the future. +/// Update PMs as well. +void update_call_orders_hf_1270( database& db ) +{ + // Update call_price + wlog( "Updating all call orders for hardfork core-1270 at block ${n}", ("n",db.head_block_num()) ); + for( const auto& call_obj : db.get_index_type().indices().get() ) + { + db.modify( call_obj, []( call_order_object& call ) { + call.call_price.base.amount = 1; + call.call_price.quote.amount = 1; + }); + } + wlog( "Done updating all call orders for hardfork core-1270 at block ${n}", ("n",db.head_block_num()) ); +} + +/// Match call orders for all bitAssets, including PMs. +void match_call_orders( database& db ) +{ // Match call orders + wlog( "Matching call orders at block ${n}", ("n",db.head_block_num()) ); const auto& asset_idx = db.get_index_type().indices().get(); auto itr = asset_idx.lower_bound( true /** market issued */ ); while( itr != asset_idx.end() ) @@ -898,7 +923,7 @@ void update_and_match_call_orders( database& db ) // be here, next_maintenance_time should have been updated already db.check_call_orders( a, true, false ); // allow black swan, and call orders are taker } - wlog( "Done updating all call orders for hardfork core-343 at block ${n}", ("n",db.head_block_num()) ); + wlog( "Done matching call orders at block ${n}", ("n",db.head_block_num()) ); } void database::process_bitassets() @@ -1232,28 +1257,43 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g if( (dgpo.next_maintenance_time < HARDFORK_613_TIME) && (next_maintenance_time >= HARDFORK_613_TIME) ) deprecate_annual_members(*this); - // To reset call_price of all call orders, then match by new rule - bool to_update_and_match_call_orders = false; + // To reset call_price of all call orders, then match by new rule, for hard fork core-343 + bool to_update_and_match_call_orders_for_hf_343 = false; if( (dgpo.next_maintenance_time <= HARDFORK_CORE_343_TIME) && (next_maintenance_time > HARDFORK_CORE_343_TIME) ) - to_update_and_match_call_orders = true; + to_update_and_match_call_orders_for_hf_343 = true; // Process inconsistent price feeds if( (dgpo.next_maintenance_time <= HARDFORK_CORE_868_890_TIME) && (next_maintenance_time > HARDFORK_CORE_868_890_TIME) ) - process_hf_868_890( *this, to_update_and_match_call_orders ); + process_hf_868_890( *this, to_update_and_match_call_orders_for_hf_343 ); // Explicitly call check_call_orders of all markets if( (dgpo.next_maintenance_time <= HARDFORK_CORE_935_TIME) && (next_maintenance_time > HARDFORK_CORE_935_TIME) - && !to_update_and_match_call_orders ) + && !to_update_and_match_call_orders_for_hf_343 ) process_hf_935( *this ); + // To reset call_price of all call orders, then match by new rule, for hard fork core-1270 + bool to_update_and_match_call_orders_for_hf_1270 = false; + if( (dgpo.next_maintenance_time <= HARDFORK_CORE_1270_TIME) && (next_maintenance_time > HARDFORK_CORE_1270_TIME) ) + to_update_and_match_call_orders_for_hf_1270 = true; + modify(dgpo, [next_maintenance_time](dynamic_global_property_object& d) { d.next_maintenance_time = next_maintenance_time; d.accounts_registered_this_interval = 0; }); - // We need to do it after updated next_maintenance_time, to apply new rules here - if( to_update_and_match_call_orders ) - update_and_match_call_orders(*this); + // We need to do it after updated next_maintenance_time, to apply new rules here, for hard fork core-343 + if( to_update_and_match_call_orders_for_hf_343 ) + { + update_call_orders_hf_343(*this); + match_call_orders(*this); + } + + // We need to do it after updated next_maintenance_time, to apply new rules here, for hard fork core-1270. + if( to_update_and_match_call_orders_for_hf_1270 ) + { + update_call_orders_hf_1270(*this); + match_call_orders(*this); + } process_bitassets(); From 8bcf38d2807d9904f6d6cc9afb4ed814c9d5259e Mon Sep 17 00:00:00 2001 From: abitmore Date: Sun, 9 Sep 2018 07:51:16 -0400 Subject: [PATCH 012/161] Check for blackswan by collateral after hf #1270 --- libraries/chain/db_update.cpp | 38 ++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 0c581d0af2..76d31ab799 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -190,20 +190,34 @@ bool database::check_for_blackswan( const asset_object& mia, bool enable_black_s auto settle_price = bitasset.current_feed.settlement_price; if( settle_price.is_null() ) return false; // no feed - const call_order_index& call_index = get_index_type(); - const auto& call_price_index = call_index.indices().get(); + const call_order_object* call_ptr = nullptr; // place holder for the call order with least collateral ratio - auto call_min = price::min( bitasset.options.short_backing_asset, mia.id ); - auto call_max = price::max( bitasset.options.short_backing_asset, mia.id ); - auto call_itr = call_price_index.lower_bound( call_min ); - auto call_end = call_price_index.upper_bound( call_max ); + asset_id_type debt_asset_id = mia.id; + auto call_min = price::min( bitasset.options.short_backing_asset, debt_asset_id ); - if( call_itr == call_end ) return false; // no call orders + auto maint_time = get_dynamic_global_properties().next_maintenance_time; + bool before_core_hardfork_1270 = ( maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue - price highest = settle_price; + if( before_core_hardfork_1270 ) // before core-1270 hard fork, check with call_price + { + const auto& call_price_index = get_index_type().indices().get(); + auto call_itr = call_price_index.lower_bound( call_min ); + if( call_itr == call_price_index.end() ) // no call order + return false; + call_ptr = &(*call_itr); + } + else // after core-1270 hard fork, check with collateralization + { + const auto& call_collateral_index = get_index_type().indices().get(); + auto call_itr = call_collateral_index.lower_bound( call_min ); + if( call_itr == call_collateral_index.end() ) // no call order + return false; + call_ptr = &(*call_itr); + } + if( call_ptr->debt_type() != debt_asset_id ) // no call order + return false; - const auto& dyn_prop = get_dynamic_global_properties(); - auto maint_time = dyn_prop.next_maintenance_time; + price highest = settle_price; if( maint_time > HARDFORK_CORE_1270_TIME ) // due to #338, we won't check for black swan on incoming limit order, so need to check with MSSP here highest = bitasset.current_feed.max_short_squeeze_price(); @@ -229,10 +243,10 @@ bool database::check_for_blackswan( const asset_object& mia, bool enable_black_s highest = std::max( limit_itr->sell_price, highest ); } - auto least_collateral = call_itr->collateralization(); + auto least_collateral = call_ptr->collateralization(); if( ~least_collateral >= highest ) { - wdump( (*call_itr) ); + wdump( (*call_ptr) ); elog( "Black Swan detected on asset ${symbol} (${id}) at block ${b}: \n" " Least collateralized call: ${lc} ${~lc}\n" // " Highest Bid: ${hb} ${~hb}\n" From fd1c091621efd961af6fb62de872fec306b0fbe6 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sun, 9 Sep 2018 08:35:01 -0400 Subject: [PATCH 013/161] get_call_orders() database API: use by_collateral Note: this should not break compatibility in production because the result should be the same after hard fork #343. --- libraries/app/database_api.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 77407e1045..c5ae203c5e 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1175,13 +1175,13 @@ vector database_api::get_call_orders(asset_id_type a, uint32_ vector database_api_impl::get_call_orders(asset_id_type a, uint32_t limit)const { - const auto& call_index = _db.get_index_type().indices().get(); + const auto& call_index = _db.get_index_type().indices().get(); const asset_object& mia = _db.get(a); - price index_price = price::min(mia.bitasset_data(_db).options.short_backing_asset, mia.get_id()); + price index_price = price::min( mia.bitasset_data(_db).options.short_backing_asset, a ); vector< call_order_object> result; - auto itr_min = call_index.lower_bound(index_price.min()); - auto itr_max = call_index.lower_bound(index_price.max()); + auto itr_min = call_index.lower_bound(index_price); + auto itr_max = call_index.upper_bound(index_price.max()); while( itr_min != itr_max && result.size() < limit ) { result.emplace_back(*itr_min); From 129e89c0aa2b4403b8729fabb1f772be345130f7 Mon Sep 17 00:00:00 2001 From: abitmore Date: Thu, 13 Sep 2018 17:30:07 -0400 Subject: [PATCH 014/161] Minor coding style update --- libraries/chain/market_evaluator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index 90e7b3a9b0..e0dc16e848 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -268,8 +268,8 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat call.debt = new_debt; if( before_core_hardfork_1270 ) // don't update call_price after core-1270 hard fork { - call.call_price = price::call_price(call.get_debt(), call.get_collateral(), - _bitasset_data->current_feed.maintenance_collateral_ratio); + call.call_price = price::call_price( call.get_debt(), call.get_collateral(), + _bitasset_data->current_feed.maintenance_collateral_ratio ); } call.target_collateral_ratio = o.extensions.value.target_collateral_ratio; }); From 9af57a8d5107e1cf67589a89dd75a26d570c5815 Mon Sep 17 00:00:00 2001 From: abitmore Date: Thu, 13 Sep 2018 17:31:46 -0400 Subject: [PATCH 015/161] Bump DB_VERSION due to call_order_object change --- 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 ecdfc00656..1ef28a0c67 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -121,7 +121,7 @@ #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 -#define GRAPHENE_CURRENT_DB_VERSION "BTS2.17" +#define GRAPHENE_CURRENT_DB_VERSION "BTS2.18" #define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT) From b5e63a34473b032143570e297eab0011ba1d88c8 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 22 Sep 2018 08:01:46 -0400 Subject: [PATCH 016/161] Target CR: stop using call_price after hf #1270 --- libraries/chain/db_market.cpp | 29 +++++++--- .../chain/include/graphene/chain/database.hpp | 10 +++- .../include/graphene/chain/market_object.hpp | 16 +++++- libraries/chain/market_object.cpp | 56 +++++++++++++++---- 4 files changed, 90 insertions(+), 21 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index ee710f1e88..eec9ae4826 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -485,7 +485,8 @@ bool database::apply_order(const limit_order_object& new_order_object, bool allo // hard fork core-338 and core-625 took place at same time, not checking HARDFORK_CORE_338_TIME here. int match_result = match( new_order_object, *call_itr, call_match_price, sell_abd->current_feed.settlement_price, - sell_abd->current_feed.maintenance_collateral_ratio ); + sell_abd->current_feed.maintenance_collateral_ratio, + sell_abd->current_maintenance_collateralization ); // match returns 1 or 3 when the new order was fully filled. In this case, we stop matching; otherwise keep matching. // since match can return 0 due to BSIP38 (hard fork core-834), we no longer only check if the result is 2. if( match_result == 1 || match_result == 3 ) @@ -509,7 +510,8 @@ bool database::apply_order(const limit_order_object& new_order_object, bool allo // assume hard fork core-338 and core-625 will take place at same time, not checking HARDFORK_CORE_338_TIME here. int match_result = match( new_order_object, *call_itr, call_match_price, sell_abd->current_feed.settlement_price, - sell_abd->current_feed.maintenance_collateral_ratio ); + sell_abd->current_feed.maintenance_collateral_ratio, + optional() ); // match returns 1 or 3 when the new order was fully filled. In this case, we stop matching; otherwise keep matching. // since match can return 0 due to BSIP38 (hard fork core-834), we no longer only check if the result is 2. if( match_result == 1 || match_result == 3 ) @@ -615,7 +617,8 @@ int database::match( const limit_order_object& usd, const limit_order_object& co } int database::match( const limit_order_object& bid, const call_order_object& ask, const price& match_price, - const price& feed_price, const uint16_t maintenance_collateral_ratio ) + const price& feed_price, const uint16_t maintenance_collateral_ratio, + const optional& maintenance_collateralization ) { FC_ASSERT( bid.sell_asset_id() == ask.debt_type() ); FC_ASSERT( bid.receive_asset_id() == ask.collateral_type() ); @@ -641,7 +644,10 @@ int database::match( const limit_order_object& bid, const call_order_object& ask // TODO if we're sure `before_core_hardfork_834` is always false, remove the check asset usd_to_buy = ( before_core_hardfork_834 ? ask.get_debt() : - asset( ask.get_max_debt_to_cover( match_price, feed_price, maintenance_collateral_ratio ), + asset( ask.get_max_debt_to_cover( match_price, + feed_price, + maintenance_collateral_ratio, + maintenance_collateralization ), ask.debt_type() ) ); asset call_pays, call_receives, order_pays, order_receives; @@ -1067,10 +1073,19 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa return true; } - if( !before_core_hardfork_834 ) + if( !before_core_hardfork_1270 ) + { + usd_to_buy.amount = call_order.get_max_debt_to_cover( match_price, + bitasset.current_feed.settlement_price, + bitasset.current_feed.maintenance_collateral_ratio, + bitasset.current_maintenance_collateralization ); + } + else if( !before_core_hardfork_834 ) + { usd_to_buy.amount = call_order.get_max_debt_to_cover( match_price, - bitasset.current_feed.settlement_price, - bitasset.current_feed.maintenance_collateral_ratio ); + bitasset.current_feed.settlement_price, + bitasset.current_feed.maintenance_collateral_ratio ); + } asset usd_for_sale = limit_order.amount_for_sale(); asset call_pays, call_receives, order_pays, order_receives; diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index f0fb8e11c6..7a4c77adfb 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -352,8 +352,10 @@ namespace graphene { namespace chain { * This function takes a new limit order, and runs the markets attempting to match it with existing orders * already on the books. */ + ///@{ bool apply_order_before_hardfork_625(const limit_order_object& new_order_object, bool allow_black_swan = true); bool apply_order(const limit_order_object& new_order_object, bool allow_black_swan = true); + ///@} /** * Matches the two orders, the first parameter is taker, the second is maker. @@ -368,14 +370,17 @@ namespace graphene { namespace chain { ///@{ int match( const limit_order_object& taker, const limit_order_object& maker, const price& trade_price ); int match( const limit_order_object& taker, const call_order_object& maker, const price& trade_price, - const price& feed_price, const uint16_t maintenance_collateral_ratio ); + const price& feed_price, const uint16_t maintenance_collateral_ratio, + const optional& maintenance_collateralization ); + ///@} + + /// Matches the two orders, the first parameter is taker, the second is maker. /// @return the amount of asset settled asset match(const call_order_object& call, const force_settlement_object& settle, const price& match_price, asset max_settlement, const price& fill_price); - ///@} /** * @return true if the order was completely filled and thus freed. @@ -395,6 +400,7 @@ namespace graphene { namespace chain { asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount); asset pay_market_fees( const asset_object& recv_asset, const asset& receives ); + ///@} ///@{ diff --git a/libraries/chain/include/graphene/chain/market_object.hpp b/libraries/chain/include/graphene/chain/market_object.hpp index 706d5ed313..168e2b6439 100644 --- a/libraries/chain/include/graphene/chain/market_object.hpp +++ b/libraries/chain/include/graphene/chain/market_object.hpp @@ -136,8 +136,20 @@ class call_order_object : public abstract_object return tmp; } - /// Calculate maximum quantity of debt to cover to satisfy @ref target_collateral_ratio. - share_type get_max_debt_to_cover( price match_price, price feed_price, const uint16_t maintenance_collateral_ratio )const; + /** + * Calculate maximum quantity of debt to cover to satisfy @ref target_collateral_ratio. + * + * @param match_price the matching price if this call order is margin called + * @param feed_price median settlement price of debt asset + * @param maintenance_collateral_ratio median maintenance collateral ratio of debt asset + * @param maintenance_collateralization maintenance collateralization of debt asset, + * should only be valid after core-1270 hard fork + * @return maximum amount of debt that can be called + */ + share_type get_max_debt_to_cover( price match_price, + price feed_price, + const uint16_t maintenance_collateral_ratio, + const optional& maintenance_collateralization = optional() )const; }; /** diff --git a/libraries/chain/market_object.cpp b/libraries/chain/market_object.cpp index 993df7924f..ab4726bcbd 100644 --- a/libraries/chain/market_object.cpp +++ b/libraries/chain/market_object.cpp @@ -56,7 +56,8 @@ max_debt_to_cover = max_amount_to_sell * match_price */ share_type call_order_object::get_max_debt_to_cover( price match_price, price feed_price, - const uint16_t maintenance_collateral_ratio )const + const uint16_t maintenance_collateral_ratio, + const optional& maintenance_collateralization )const { try { // be defensive here, make sure feed_price is in collateral / debt format if( feed_price.base.asset_id != call_price.base.asset_id ) @@ -65,7 +66,23 @@ share_type call_order_object::get_max_debt_to_cover( price match_price, FC_ASSERT( feed_price.base.asset_id == call_price.base.asset_id && feed_price.quote.asset_id == call_price.quote.asset_id ); - if( call_price > feed_price ) // feed protected. be defensive here, although this should be guaranteed by caller + bool after_core_hardfork_1270 = maintenance_collateralization.valid(); + + // be defensive here, make sure maintenance_collateralization is in collateral / debt format + if( after_core_hardfork_1270 ) + { + FC_ASSERT( maintenance_collateralization->base.asset_id == call_price.base.asset_id + && maintenance_collateralization->quote.asset_id == call_price.quote.asset_id ); + } + + // According to the feed protection rule (https://github.com/cryptonomex/graphene/issues/436), + // a call order should only be called when its collateral ratio is not higher than required maintenance collateral ratio. + // Although this should be guaranteed by the caller of this function, we still check here to be defensive. + // Theoretically this check can be skipped for better performance. + // + // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization(). + if( ( !after_core_hardfork_1270 && call_price > feed_price ) + || ( after_core_hardfork_1270 && collateralization() > *maintenance_collateralization ) ) return 0; if( !target_collateral_ratio.valid() ) // target cr is not set @@ -73,6 +90,10 @@ share_type call_order_object::get_max_debt_to_cover( price match_price, uint16_t tcr = std::max( *target_collateral_ratio, maintenance_collateral_ratio ); // use mcr if target cr is too small + price target_collateralization = ( after_core_hardfork_1270 ? + ~feed_price * ratio_type( tcr, GRAPHENE_COLLATERAL_RATIO_DENOM ) : + price() ); + // be defensive here, make sure match_price is in collateral / debt format if( match_price.base.asset_id != call_price.base.asset_id ) match_price = ~match_price; @@ -113,9 +134,24 @@ share_type call_order_object::get_max_debt_to_cover( price match_price, return debt; FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt ); - // check collateral ratio after filled, if it's OK, we return - price new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr ); - if( new_call_price > feed_price ) + // Check whether the collateral ratio after filled is high enough + auto result_is_good = [after_core_hardfork_1270,this,&to_cover,&to_pay,tcr,feed_price,target_collateralization]() -> bool + { + // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization(). + if( !after_core_hardfork_1270 ) // before core-1270 hard fork + { + price new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr ); + return ( new_call_price > feed_price ); + } + else // after core-1270 hard fork + { + price new_collateralization = ( get_collateral() - to_pay ) / ( get_debt() - to_cover ); + return ( new_collateralization > target_collateralization ); + } + }; + + // if the result is good, we return. + if( result_is_good() ) return to_cover.amount; // be here, to_cover is too small due to rounding. deal with the fraction @@ -209,8 +245,8 @@ share_type call_order_object::get_max_debt_to_cover( price match_price, return to_cover.amount; FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt ); - new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr ); - if( new_call_price > feed_price ) // good + // Check whether the result is good + if( result_is_good() ) // good { if( to_pay.amount == max_to_pay.amount ) return to_cover.amount; @@ -255,11 +291,11 @@ share_type call_order_object::get_max_debt_to_cover( price match_price, return debt; } - // check + // defensive check FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt ); - new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr ); - if( new_call_price > feed_price ) // good + // Check whether the result is good + if( result_is_good() ) // good return to_cover.amount; } From c6ca2c8d861c8b805fe4bd1f8a198b080c6cf39a Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 5 Sep 2018 10:26:08 -0500 Subject: [PATCH 017/161] Initial HTLC commit --- libraries/chain/CMakeLists.txt | 3 +- libraries/chain/assert_evaluator.cpp | 8 ++ libraries/chain/db_init.cpp | 9 +- libraries/chain/db_maint.cpp | 2 + libraries/chain/db_update.cpp | 11 ++ libraries/chain/hardfork.d/HTLC.hf | 4 + libraries/chain/htlc_evaluator.cpp | 112 ++++++++++++++++++ .../chain/include/graphene/chain/database.hpp | 1 + .../include/graphene/chain/htlc_evaluator.hpp | 49 ++++++++ .../include/graphene/chain/htlc_object.hpp | 77 ++++++++++++ .../graphene/chain/protocol/assert.hpp | 21 +++- .../include/graphene/chain/protocol/htlc.hpp | 75 ++++++++++++ .../graphene/chain/protocol/operations.hpp | 5 +- .../include/graphene/chain/protocol/types.hpp | 7 +- libraries/chain/protocol/htlc.cpp | 36 ++++++ 15 files changed, 415 insertions(+), 5 deletions(-) create mode 100644 libraries/chain/hardfork.d/HTLC.hf create mode 100644 libraries/chain/htlc_evaluator.cpp create mode 100644 libraries/chain/include/graphene/chain/htlc_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/htlc_object.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/htlc.hpp create mode 100644 libraries/chain/protocol/htlc.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 6fe3f47883..71d2fd2d2e 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -58,7 +58,7 @@ add_library( graphene_chain protocol/fee_schedule.cpp protocol/confidential.cpp protocol/vote.cpp - + protocol/htlc.cpp genesis_state.cpp get_config.cpp @@ -77,6 +77,7 @@ add_library( graphene_chain vesting_balance_evaluator.cpp withdraw_permission_evaluator.cpp worker_evaluator.cpp + htlc_evaluator.cpp confidential_evaluator.cpp special_authority.cpp buyback.cpp diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp index 3d1b11d257..febf15aeb3 100644 --- a/libraries/chain/assert_evaluator.cpp +++ b/libraries/chain/assert_evaluator.cpp @@ -49,6 +49,14 @@ struct predicate_evaluator { FC_ASSERT( block_summary_id_type( block_header::num_from_id( p.id ) & 0xffff )(db).block_id == p.id ); } + void operator()(const hash_operation_predicate& p)const + { + // TODO: Implement this method + } + void operator()(const timeout_operation_predicate& p)const + { + // TODO: Implement this method + } }; void_result assert_evaluator::do_evaluate( const assert_operation& o ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 263c5df3e5..63e0738634 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -60,6 +61,7 @@ #include #include #include +#include #include @@ -125,6 +127,9 @@ const uint8_t witness_object::type_id; const uint8_t worker_object::space_id; const uint8_t worker_object::type_id; +const uint8_t htlc_object::space_id; +const uint8_t htlc_object::type_id; + void database::initialize_evaluators() { @@ -173,6 +178,8 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -216,7 +223,7 @@ void database::initialize_indexes() add_index< primary_index< special_authority_index > >(); add_index< primary_index< buyback_index > >(); add_index< primary_index >(); - + add_index< primary_index< htlc_index > >(); add_index< primary_index< simple_index< fba_accumulator_object > > >(); } diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 7f1a8a4c6c..a766084131 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -1259,6 +1259,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // process_budget needs to run at the bottom because // it needs to know the next_maintenance_time process_budget(); + + remove_expired_htlcs(); } } } diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 9a5bcad1eb..bd5b9e3c23 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -558,4 +558,15 @@ void database::update_withdraw_permissions() remove(*permit_index.begin()); } +void database::remove_expired_htlcs() +{ + const auto& htlc_idx = get_index_type().indices().get(); + while ( !htlc_idx.empty() && escrow_idx.begin() != htlc_idx.end() + && htlc_idx.begin()->htlc_expiration <= head_block_time() && !htlc_idx.begin()->disputed ) + { + adjust_balance( htlc_idx.begin()->from, htlc_idx.begin()->amount ); + remove( *htlc_idx.begin()); + } +} + } } diff --git a/libraries/chain/hardfork.d/HTLC.hf b/libraries/chain/hardfork.d/HTLC.hf new file mode 100644 index 0000000000..47df3c9f4a --- /dev/null +++ b/libraries/chain/hardfork.d/HTLC.hf @@ -0,0 +1,4 @@ +// htlc support for bitshares-core +#ifndef HTLC_ESCROW_TIME +#define HTLC_ESCROW_TIME (fc::time_point_sec( 1600000000 )) +#endif \ No newline at end of file diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp new file mode 100644 index 0000000000..781aa0f660 --- /dev/null +++ b/libraries/chain/htlc_evaluator.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include + +namespace graphene { + namespace chain { + + void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) + { + //FC_ASSERT( db().head_block_time() > HARDFORK_ESCROW_TIME, + // "Operation not allowed before HARDFORK_ESCROW_TIME."); // remove after HARDFORK_ESCROW_TIME + + FC_ASSERT( fc::time_point_sec(o.epoch) > db().head_block_time() ); + // TODO: what is the fee denominated in? + FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount + o.fee) ); + return void_result(); + } + + object_id_type htlc_create_evaluator::do_apply(const htlc_create_operation& o) + { + try { + //FC_ASSERT( db().head_block_time() > HARDFORK_ESCROW_TIME, + // "Operation not allowed before HARDFORK_ESCROW_TIME."); // remove after HARDFORK_ESCROW_TIME + + db().adjust_balance( o.source, -o.amount ); + + const htlc_object& esc = db().create([&]( htlc_object& esc ) { + esc.from = o.source; + esc.to = o.destination; + esc.amount = o.amount; + esc.preimage_hash = o.key_hash; + esc.preimage_size = o.key_size; + esc.expiration = o.epoch; + }); + return esc.id; + + } FC_CAPTURE_AND_RETHROW( (o) ) + } + + void_result htlc_update_evaluator::do_evaluate(const htlc_update_operation& o) + { + htlc_obj = &db().get(o.htlc_id); + + // TODO: Use signatures to determine what to do, not whether preimage was provided + if (o.preimage.size() > 0) + { + FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); + FC_ASSERT(fc::time_point::now().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); + + // hash the preimage given by the user + fc::sha256 attempted_hash = fc::sha256::hash(o.preimage); + // put the preimage hash in a format we can compare + std::vector passed_hash(attempted_hash.data_size()); + char* data = attempted_hash.data(); + for(size_t i = 0; i < attempted_hash.data_size(); ++i) + { + passed_hash[i] = data[i]; + } + + FC_ASSERT(passed_hash == htlc_obj->preimage_hash, "Provided preimage does not generate correct hash."); + } + else + { + FC_ASSERT(fc::time_point::now().sec_since_epoch() > htlc_obj->expiration.sec_since_epoch(), "Unable to reclaim until escrow expiration."); + FC_ASSERT(htlc_obj->preimage.size() == 0, "Preimage already provided."); + } + return void_result(); + } + + void_result htlc_update_evaluator::do_apply(const htlc_update_operation& o) + { + if (o.preimage.size() > 0) + { + db().adjust_balance(htlc_obj->to, htlc_obj->amount); + db().modify(*htlc_obj, [&o](htlc_object& obj){ + obj.preimage = o.preimage; + }); + } + else + { + db().adjust_balance(htlc_obj->from, htlc_obj->amount); + db().remove(*htlc_obj); + } + return void_result(); + } + } // namespace chain +} // namespace graphene diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index f0fb8e11c6..202b8bcb05 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -466,6 +466,7 @@ namespace graphene { namespace chain { void update_withdraw_permissions(); bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true, const asset_bitasset_data_object* bitasset_ptr = nullptr ); + void remove_expired_htlcs(); ///Steps performed only at maintenance intervals ///@{ diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp new file mode 100644 index 0000000000..1181541cc7 --- /dev/null +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once +#include + +namespace graphene { + namespace chain { + + class htlc_create_evaluator : public evaluator + { + public: + typedef htlc_create_operation operation_type; + + void_result do_evaluate( const htlc_create_operation& o); + object_id_type do_apply( const htlc_create_operation& o); + }; + + class htlc_update_evaluator : public evaluator + { + public: + typedef htlc_update_operation operation_type; + + void_result do_evaluate( const htlc_update_operation& o); + void_result do_apply( const htlc_update_operation& o); + const htlc_object* htlc_obj = nullptr; + }; + } // namespace graphene +} // namespace graphene diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp new file mode 100644 index 0000000000..47ef6f492c --- /dev/null +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { + + /** + * Temporally save escrow transactions until funds are released or operation expired. + */ + class htlc_object : public graphene::db::abstract_object { + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_htlc_object_type; + + account_id_type from; + account_id_type to; + asset amount; + fc::time_point_sec expiration; + asset pending_fee; + vector preimage_hash; + uint16_t preimage_size; + vector preimage; + }; + + struct by_from_id; + struct by_expiration; + typedef multi_index_container< + htlc_object, + indexed_by< + ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, + + ordered_non_unique< tag< by_expiration >, member< htlc_object, fc::time_point_sec, &htlc_object::expiration > >, + + ordered_non_unique< tag< by_from_id >, + composite_key< htlc_object, + member< htlc_object, account_id_type, &htlc_object::from > + > + > + > + + > htlc_object_index_type; + + typedef generic_index< htlc_object, htlc_object_index_type > htlc_index; + + } } + +FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), + (from)(to)(amount)(expiration)(pending_fee) + (preimage_hash)(preimage_size)(preimage) ); \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/assert.hpp b/libraries/chain/include/graphene/chain/protocol/assert.hpp index c9f3b2774a..2b01ddd262 100644 --- a/libraries/chain/include/graphene/chain/protocol/assert.hpp +++ b/libraries/chain/include/graphene/chain/protocol/assert.hpp @@ -70,6 +70,21 @@ namespace graphene { namespace chain { bool validate()const{ return true; } }; + struct hash_operation_predicate + { + account_id_type pay_to; + std::vector key_hash; + int64_t key_size; + bool validate() const { return true; } + }; + + struct timeout_operation_predicate + { + account_id_type pay_to; + uint64_t epoch; + bool validate() const { return true; } + }; + /** * When defining predicates do not make the protocol dependent upon * implementation details. @@ -77,7 +92,9 @@ namespace graphene { namespace chain { typedef static_variant< account_name_eq_lit_predicate, asset_symbol_eq_lit_predicate, - block_id_predicate + block_id_predicate, + hash_operation_predicate, + timeout_operation_predicate > predicate; @@ -109,6 +126,8 @@ FC_REFLECT( graphene::chain::assert_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::account_name_eq_lit_predicate, (account_id)(name) ) FC_REFLECT( graphene::chain::asset_symbol_eq_lit_predicate, (asset_id)(symbol) ) FC_REFLECT( graphene::chain::block_id_predicate, (id) ) +FC_REFLECT( graphene::chain::hash_operation_predicate, (pay_to)(key_hash)(key_size) ) +FC_REFLECT( graphene::chain::timeout_operation_predicate, (pay_to)(epoch) ) FC_REFLECT_TYPENAME( graphene::chain::predicate ) FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths)(extensions) ) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp new file mode 100644 index 0000000000..107a9a9036 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once +#include +#include +#include +#include +#include + +namespace graphene { + namespace chain { + + struct htlc_create_operation : public base_operation { + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + asset fee; // paid to network + account_id_type source; // where the held monies are to come from + account_id_type destination; // where the held monies will go if the preimage is provided + asset amount; // the amount to hold + std::vector key_hash; // the hash of the preimage + uint16_t key_size; // the size of the preimage + fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed + extensions_type extensions; // for future expansion + + void validate()const; + void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(source); } + account_id_type fee_payer()const { return source; } + + }; + + struct htlc_update_operation : public base_operation + { + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + asset fee; // paid to network + htlc_id_type htlc_id; // the object we are attempting to update + account_id_type update_issuer; // who is attempting to update the transaction + std::vector preimage; // the preimage (not used if after epoch timeout) + extensions_type extensions; // for future expansion + + void validate()const; + void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(update_issuer); } + account_id_type fee_payer()const { return update_issuer; } + }; + } +} + +FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::htlc_update_operation::fee_parameters_type, (fee) ) + +FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)) +FC_REFLECT( graphene::chain::htlc_update_operation, (fee)(update_issuer)(preimage)(extensions)) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index de2cfa7fd9..a666a844ab 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -38,6 +38,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -95,7 +96,9 @@ namespace graphene { namespace chain { bid_collateral_operation, execute_bid_operation, // VIRTUAL asset_claim_pool_operation, - asset_update_issuer_operation + asset_update_issuer_operation, + htlc_create_operation, + htlc_update_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 06ab7ff291..3c334bad44 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -162,7 +162,8 @@ namespace graphene { namespace chain { impl_special_authority_object_type, impl_buyback_object_type, impl_fba_accumulator_object_type, - impl_collateral_bid_object_type + impl_collateral_bid_object_type, + impl_htlc_object_type }; //typedef fc::unsigned_int object_id_type; @@ -214,6 +215,7 @@ namespace graphene { namespace chain { class buyback_object; class fba_accumulator_object; class collateral_bid_object; + class htlc_object; typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type; typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type; @@ -234,6 +236,7 @@ namespace graphene { namespace chain { typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type; typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type; typedef object_id< implementation_ids, impl_collateral_bid_object_type, collateral_bid_object > collateral_bid_id_type; + typedef object_id< implementation_ids, impl_htlc_object_type, htlc_object > htlc_id_type; typedef fc::ripemd160 block_id_type; typedef fc::ripemd160 checksum_type; @@ -364,6 +367,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type, (impl_buyback_object_type) (impl_fba_accumulator_object_type) (impl_collateral_bid_object_type) + (impl_htlc_object_type) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) @@ -395,6 +399,7 @@ FC_REFLECT_TYPENAME( graphene::chain::special_authority_id_type ) FC_REFLECT_TYPENAME( graphene::chain::buyback_id_type ) FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type ) FC_REFLECT_TYPENAME( graphene::chain::collateral_bid_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::htlc_id_type ) FC_REFLECT( graphene::chain::void_t, ) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp new file mode 100644 index 0000000000..1c8aa44dde --- /dev/null +++ b/libraries/chain/protocol/htlc.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include + +namespace graphene { namespace chain { + + void htlc_create_operation::validate()const { + //TODO: Implement + // signer must equal from + } + void htlc_update_operation::validate()const { + //TODO: Implement + // if preimage is provided, size must equal the size stored in the contract + } +} } From bab9fb75e51ef43e66d1ac97ac2f9feb0ca36253 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 5 Sep 2018 11:09:22 -0500 Subject: [PATCH 018/161] Fix compile errors --- libraries/chain/db_notify.cpp | 8 ++++++++ libraries/chain/db_update.cpp | 4 ++-- libraries/chain/htlc_evaluator.cpp | 4 ++-- libraries/chain/include/graphene/chain/protocol/htlc.hpp | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index ff44a177df..24ad3fb342 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -256,6 +256,14 @@ struct get_impacted_account_visitor { _impacted.insert( op.fee_payer() ); // account_id } + void operator()( const htlc_create_operation& op ) + { + _impacted.insert( op.fee_payer() ); + } + void operator()( const htlc_update_operation& op ) + { + _impacted.insert( op.fee_payer() ); + } }; void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index bd5b9e3c23..fc10bd6ad8 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -561,8 +561,8 @@ void database::update_withdraw_permissions() void database::remove_expired_htlcs() { const auto& htlc_idx = get_index_type().indices().get(); - while ( !htlc_idx.empty() && escrow_idx.begin() != htlc_idx.end() - && htlc_idx.begin()->htlc_expiration <= head_block_time() && !htlc_idx.begin()->disputed ) + while ( !htlc_idx.empty() && htlc_idx.begin() != htlc_idx.end() + && htlc_idx.begin()->expiration <= head_block_time() ) { adjust_balance( htlc_idx.begin()->from, htlc_idx.begin()->amount ); remove( *htlc_idx.begin()); diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 781aa0f660..9d806baca3 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -22,8 +22,8 @@ * THE SOFTWARE. */ #include -#include -#include +#include +#include #include #include diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 107a9a9036..607deddb52 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -24,7 +24,7 @@ #pragma once #include #include -#include +#include #include #include From 81996c43b3e16520b4d017d374add017c6e419ff Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 5 Sep 2018 11:23:18 -0500 Subject: [PATCH 019/161] Fix tests --- tests/common/database_fixture.cpp | 12 ++ tests/tests/htlc_tests.cpp | 279 ++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 tests/tests/htlc_tests.cpp diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 534c03de33..2ac16f2106 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -285,6 +285,18 @@ void database_fixture::verify_asset_supplies( const database& db ) BOOST_CHECK_EQUAL(item.first(db).dynamic_asset_data_id(db).current_supply.value, item.second.value); } + // htlc + const auto& htlc_idx = db.get_index_type< htlc_index >().indices().get< by_id >(); + for( auto itr = htlc_idx.begin(); itr != htlc_idx.end(); ++itr ) + { + // do not add in htlc escrow objects that are completed + if (itr->preimage.size() == 0) + { + total_balances[itr->amount.asset_id] += itr->amount.amount; + total_balances[itr->pending_fee.asset_id] += itr->pending_fee.amount; + } + } + for( const asset_object& asset_obj : db.get_index_type().indices() ) { BOOST_CHECK_EQUAL(total_balances[asset_obj.id].value, asset_obj.dynamic_asset_data_id(db).current_supply.value); diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp new file mode 100644 index 0000000000..44b49b199e --- /dev/null +++ b/tests/tests/htlc_tests.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// below are for random bytes for htlc +#include +#include +#include +#include +#include +// for htlc timeout +#include +#include + +#include + +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE( htlc_tests, database_fixture ) + +void generate_random_preimage(uint16_t key_size, std::vector& vec) +{ + std::independent_bits_engine rbe; + std::generate(begin(vec), end(vec), std::ref(rbe)); + return; +} + +std::vector hash_it(std::vector preimage) +{ + fc::sha256 hash = fc::sha256::hash(preimage); + std::vector ret_val(hash.data_size()); + char* data = hash.data(); + for(size_t i = 0; i < hash.data_size(); i++) + { + ret_val[i] = data[i]; + } + return ret_val; +} + +BOOST_AUTO_TEST_CASE( htlc_expires ) +{ + ACTORS((alice)(bob)); + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t key_size = 256; + std::vector pre_image(256); + generate_random_preimage(key_size, pre_image); + std::vector key_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + // Alice puts a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.key_hash = key_hash; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + } catch (fc::exception& ex) + { + BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); + } + trx.clear(); + graphene::chain::signed_block blk = generate_block(); + // can we assume that alice's transaction will be the only one in this block? + processed_transaction alice_trx = blk.transactions[0]; + alice_htlc_id = alice_trx.operation_results[0].get(); + } + + // verify funds on hold (make sure this can cover fees) + BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + //BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) > 98000 ); + + // make sure Alice can't get it back before the timeout + { + graphene::chain::htlc_update_operation update_operation; + update_operation.update_issuer = alice_id; + update_operation.htlc_id = alice_htlc_id; + trx.operations.push_back(update_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + BOOST_FAIL("Should not allow Alice to reclaim funds before timeout"); + } catch (fc::exception& ex) + { + // this should happen + } + generate_block(); + trx.clear(); + } + + // make sure Alice can't spend it. + // make sure Bob (or anyone) can see the details of the transaction + // let it expire (wait for timeout) + std::this_thread::sleep_for(std::chrono::seconds(4)); + // send an update operation to reclaim the funds + { + graphene::chain::htlc_update_operation update_operation; + update_operation.update_issuer = alice_id; + update_operation.htlc_id = alice_htlc_id; + trx.operations.push_back(update_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + } catch (fc::exception& ex) + { + BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); + } + generate_block(); + trx.clear(); + } + // verify funds return (what about fees?) + BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 100000 ); + // verify Bob cannot execute the contract after the fact +} + +BOOST_AUTO_TEST_CASE( htlc_fulfilled ) +{ + ACTORS((alice)(bob)); + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t key_size = 256; + std::vector pre_image(256); + generate_random_preimage(key_size, pre_image); + std::vector key_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + // Alice puts a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.key_hash = key_hash; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + } catch (fc::exception& ex) + { + BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); + } + trx.clear(); + graphene::chain::signed_block blk = generate_block(); + // can we assume that alice's transaction will be the only one in this block? + processed_transaction alice_trx = blk.transactions[0]; + alice_htlc_id = alice_trx.operation_results[0].get(); + } + + // verify funds on hold (make sure this can cover fees) + BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + + // make sure Alice can't get it back before the timeout + { + graphene::chain::htlc_update_operation update_operation; + update_operation.update_issuer = alice_id; + update_operation.htlc_id = alice_htlc_id; + trx.operations.push_back(update_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + BOOST_FAIL("Should not allow Alice to reclaim funds before timeout"); + } catch (fc::exception& ex) + { + // this should happen + } + generate_block(); + trx.clear(); + } + + // balance should not have changed + BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + // make sure Bob (or anyone) can see the details of the transaction + // send an update operation to claim the funds + { + graphene::chain::htlc_update_operation update_operation; + update_operation.update_issuer = bob_id; + update_operation.htlc_id = alice_htlc_id; + update_operation.preimage = pre_image; + trx.operations.push_back(update_operation); + sign(trx, bob_private_key); + try + { + PUSH_TX(db, trx, ~0); + } catch (fc::exception& ex) + { + BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); + } + generate_block(); + trx.clear(); + } + // verify Alice cannot execute the contract after the fact + { + graphene::chain::htlc_update_operation update_operation; + update_operation.update_issuer = alice_id; + update_operation.htlc_id = alice_htlc_id; + trx.operations.push_back(update_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + BOOST_FAIL("Should not allow Alice to reclaim funds after Bob already claimed them."); + } catch (fc::exception& ex) + { + // this should happen + } + generate_block(); + trx.clear(); + } + // verify funds end up in Bob's account + BOOST_TEST_CHECK( get_balance(bob_id, graphene::chain::asset_id_type()) == 10000 ); + BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); +} + +BOOST_AUTO_TEST_SUITE_END() From 3afa87eb04e15a7609098f3d195f6962c92eec38 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 7 Sep 2018 10:42:19 -0500 Subject: [PATCH 020/161] added signature test --- tests/tests/htlc_tests.cpp | 65 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 44b49b199e..723bea78c3 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -276,4 +276,69 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); } +BOOST_AUTO_TEST_CASE( other_peoples_money ) +{ + ACTORS((alice)(bob)); + + fc::time_point_sec starting_time = genesis_state.initial_timestamp + 3000; + + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t key_size = 256; + std::vector pre_image(256); + generate_random_preimage(key_size, pre_image); + std::vector key_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + // Bob attempts to put a contract on the blockchain using Alice's funds + { + graphene::chain::htlc_create_operation create_operation; + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.key_hash = key_hash; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, bob_private_key); + try + { + PUSH_TX(db, trx, database::skip_nothing); + BOOST_FAIL( "Bob stole money from Alice!" ); + } catch (fc::exception& ex) + { + // this is supposed to happen + //BOOST_TEST_MESSAGE("This is the error thrown (expected):"); + //BOOST_TEST_MESSAGE(ex.to_detail_string(fc::log_level(fc::log_level::all))); + } + trx.clear(); + } + // now try the same but with Alice's signature (should work) + { + graphene::chain::htlc_create_operation create_operation; + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.key_hash = key_hash; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, database::skip_nothing); + } catch (fc::exception& ex) + { + BOOST_FAIL( "Alice cannot create a contract!" ); + } + trx.clear(); + } +} + BOOST_AUTO_TEST_SUITE_END() From 6e960efb9316c45d70c422aa936ec15f8a379b38 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 14 Sep 2018 05:54:11 -0500 Subject: [PATCH 021/161] Intermediate commit for adding htlc to wallet api --- libraries/app/database_api.cpp | 21 +++++ .../app/include/graphene/app/database_api.hpp | 10 +++ .../include/graphene/chain/protocol/htlc.hpp | 37 ++++---- .../wallet/include/graphene/wallet/wallet.hpp | 34 +++++++ libraries/wallet/wallet.cpp | 72 +++++++++++++++ tests/cli/main.cpp | 88 +++++++++++++++++++ 6 files changed, 247 insertions(+), 15 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index acb90c8af5..3f053b911f 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -167,6 +167,9 @@ class database_api_impl : public std::enable_shared_from_this vector get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; vector get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; + // Escrow / HTLC + fc::optional get_htlc(const std::string htlc_id) const; + //private: static string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote ); @@ -416,6 +419,10 @@ void database_api_impl::cancel_all_subscriptions( bool reset_callback, bool rese // // ////////////////////////////////////////////////////////////////////// +optional database_api::get_htlc(const std::string& htlc_id) const +{ + return my->get_htlc( htlc_id ); +} optional database_api::get_block_header(uint32_t block_num)const { return my->get_block_header( block_num ); @@ -2288,6 +2295,20 @@ vector database_api_impl::get_withdraw_permissions_b return result; } +////////////////////////////////////////////////////////////////////// +// // +// Escrow / HTLC // +// // +////////////////////////////////////////////////////////////////////// +fc::optional database_api_impl::get_htlc(const std::string htlc_id) const +{ + FC_ASSERT( htlc_id.size() > 0 ); + const htlc_object* htlc = _db.find(fc::variant(htlc_id, 1).as(1)); + if (htlc) + return *htlc; + return {}; +} + ////////////////////////////////////////////////////////////////////// // // // Private methods // diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 506b427597..2bfd27c6d7 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -267,6 +267,13 @@ class database_api */ account_id_type get_account_id_from_string(const std::string& name_or_id) const; + /** + * @brief Get HTLC object from an ID + * @param htlc_id the id of the HTLC + * @return the HTLC object + */ + fc::optional get_htlc( const std::string& htlc_id ) const; + /** * @brief Get a list of accounts by ID * @param account_names_or_ids IDs or names of the accounts to retrieve @@ -791,6 +798,9 @@ FC_API(graphene::app::database_api, (lookup_asset_symbols) (get_asset_count) + // HTLC / Escrow + (get_htlc) + // Markets / feeds (get_order_book) (get_limit_orders) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 607deddb52..7b9f242d60 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -31,22 +31,29 @@ namespace graphene { namespace chain { - struct htlc_create_operation : public base_operation { - struct fee_parameters_type { - uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; - }; - asset fee; // paid to network - account_id_type source; // where the held monies are to come from - account_id_type destination; // where the held monies will go if the preimage is provided - asset amount; // the amount to hold - std::vector key_hash; // the hash of the preimage - uint16_t key_size; // the size of the preimage - fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed - extensions_type extensions; // for future expansion + struct htlc_create_operation : public base_operation + { + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + enum hash_algorithm { + UNKNOWN, + RIPEMD160, + SHA256 + }; + asset fee; // paid to network + account_id_type source; // where the held monies are to come from + account_id_type destination; // where the held monies will go if the preimage is provided + asset amount; // the amount to hold + enum hash_algorithm hash_type = hash_algorithm::UNKNOWN; // hash algorithm used to create key_hash + std::vector key_hash; // the hash of the preimage + uint16_t key_size; // the size of the preimage + fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed + extensions_type extensions; // for future expansion - void validate()const; - void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(source); } - account_id_type fee_payer()const { return source; } + void validate()const; + void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(source); } + account_id_type fee_payer()const { return source; } }; diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index ca13357f0c..97ba29cc05 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -490,6 +490,13 @@ class wallet_api */ asset_bitasset_data_object get_bitasset_data(string asset_name_or_id)const; + /** + * Returns information about the given HTLC object. + * @param htlc_id the id of the HTLC object. + * @returns the information about the HTLC object + */ + htlc_object get_htlc(string htlc_id) const; + /** Lookup the id of a named account. * @param account_name_or_id the name of the account to look up * @returns the id of the named account @@ -1448,6 +1455,33 @@ class wallet_api bool broadcast = false ); + /** + * Create a hashed time lock contract + * + * @param source The account that will reserve the funds (and pay the fee) + * @param destination The account that will receive the funds if the preimage is presented + * @param asset_symbol The asset that is to be traded + * @param amount the amount of the asset that is to be traded + * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160 or SHA256. + * @param preimage_hash the hash of the preimage + * @param preimage_size the size of the preimage in bytes + * @param timelock when the time lock expires + * @param broadcast true if you wish to broadcast the transaction + */ + signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, + string hash_algorithm, std::vector preimage_hash, size_t preimage_size, + fc::time_point_sec timelock, bool broadcast = false ); + + /**** + * Update a hashed time lock contract + * + * @param htlc_id The object identifier of the HTLC on the blockchain + * @param issuer Who is performing this operation (and paying the fee) + * @param preimage the preimage that should evaluate to the preimage_hash + */ + signed_transaction update_htlc( string htlc_id, string issuer, std::vector preimage, + bool broadcast = false ); + /** * Get information about a vesting balance object. * diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index ac33c180e9..ea4d05ec3f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -645,6 +645,13 @@ class wallet_api_impl return *opt; } + htlc_object get_htlc(string htlc_id) const + { + auto obj = _remote_db->get_htlc(htlc_id); + FC_ASSERT(obj, "HTLC not found"); + return *obj; + } + asset_id_type get_asset_id(string asset_symbol_or_id) const { FC_ASSERT( asset_symbol_or_id.size() > 0 ); @@ -1735,6 +1742,63 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } + signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, + string hash_algorithm, std::vector preimage_hash, size_t preimage_size, fc::time_point_sec timelock, bool broadcast = false ) + { + try + { + FC_ASSERT( !self.is_locked() ); + fc::optional asset_obj = get_asset(asset_symbol); + FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); + + htlc_create_operation create_op; + create_op.source = get_account(source).id; + create_op.destination = get_account(destination).id; + create_op.amount = asset_obj->amount_from_string(amount); + create_op.epoch = timelock; + create_op.key_hash = preimage_hash; + create_op.key_size = preimage_size; + if ( "SHA256" == hash_algorithm ) + create_op.hash_type = htlc_create_operation::hash_algorithm::SHA256; + if ( "RIPEMD160" == hash_algorithm ) + create_op.hash_type = htlc_create_operation::hash_algorithm::RIPEMD160; + FC_ASSERT(create_op.hash_type != htlc_create_operation::hash_algorithm::UNKNOWN, + "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); + + signed_transaction tx; + tx.operations.push_back(create_op); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } FC_CAPTURE_AND_RETHROW( (source)(destination)(amount)(asset_symbol)(hash_algorithm) + (preimage_hash)(preimage_size)(timelock)(broadcast) ) + } + + signed_transaction update_htlc( string htlc_id, string issuer, std::vector preimage, bool broadcast ) + { + try + { + FC_ASSERT( !self.is_locked() ); + fc::optional htlc_obj = get_htlc(htlc_id); + FC_ASSERT(htlc_obj, "Could not find HTLC matching ${htlc}", ("htlc", htlc_id)); + + account_object issuer_obj = get_account(issuer); + + htlc_update_operation update_op; + update_op.htlc_id = htlc_obj->id; + update_op.update_issuer = issuer_obj.id; + update_op.preimage = preimage; + + signed_transaction tx; + tx.operations.push_back(update_op); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(preimage)(broadcast) ) + } + vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) { try { fc::optional vbid = maybe_id( account_name ); @@ -3012,6 +3076,14 @@ uint64_t wallet_api::get_asset_count()const return my->_remote_db->get_asset_count(); } +signed_transaction wallet_api::create_htlc( string source, string destination, string asset_symbol, string amount, + string hash_algorithm, std::vector preimage_hash, size_t preimage_size, + fc::time_point_sec timelock, bool broadcast) +{ + return my->create_htlc(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, + timelock, broadcast); +} + vector wallet_api::get_account_history(string name, int limit)const { vector result; diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 393dce4d83..2a9bec5448 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -566,3 +566,91 @@ BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture ) throw; } } + +/////////////////////// +// Start a server and connect using the same calls as the CLI +// Create an HTLC +/////////////////////// +BOOST_AUTO_TEST_CASE( cli_create_htlc ) +{ + 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); + + // connect to the server + client_connection con(app1, app_dir, server_port_number); + + 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 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); + // 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"); + 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 an HTLC + std::string preimage_string = "My Secret"; + fc::ripemd160 preimage = fc::ripemd160::hash(preimage_string); + std::vector hash(preimage.data_size()); + for(size_t i = 0; i < preimage.data_size(); ++i) + hash[i] = preimage.data()[i]; + fc::time_point_sec timelock = fc::time_point::now() + fc::days(1); + graphene::chain::signed_transaction result_tx + = con.wallet_api_ptr->create_htlc("alice", "nathan", + "1.3.0", "1000", "RIPEMD160", hash, preimage.data_size(), timelock, true); + + // share the id with bob + graphene::chain::transaction_id_type htlc_trans_id = con.wallet_api_ptr->get_transaction_id(result_tx); + BOOST_TEST_MESSAGE("The hash is " + std::string(htlc_trans_id)); + + // generate a block to get things started + BOOST_CHECK(generate_block(app1)); + // wait for a maintenance interval + BOOST_CHECK(generate_maintenance_block(app1)); + + // send another block to trigger maintenance interval + BOOST_CHECK(generate_maintenance_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(); +} From f470aa361abc950de3043a9b24504d6a87a3d8f9 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Sep 2018 21:15:17 -0500 Subject: [PATCH 022/161] Improvements to cli_wallet test --- libraries/app/database_api.cpp | 35 ++++++- libraries/chain/db_init.cpp | 2 +- libraries/chain/htlc_evaluator.cpp | 41 ++++---- .../include/graphene/chain/htlc_object.hpp | 12 ++- .../include/graphene/chain/protocol/htlc.hpp | 8 +- .../include/graphene/chain/protocol/types.hpp | 4 +- libraries/wallet/wallet.cpp | 11 ++- tests/cli/main.cpp | 95 +++++++++++++------ tests/common/database_fixture.cpp | 8 +- 9 files changed, 148 insertions(+), 68 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 3f053b911f..6e3115b674 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -2300,13 +2301,37 @@ vector database_api_impl::get_withdraw_permissions_b // Escrow / HTLC // // // ////////////////////////////////////////////////////////////////////// +htlc_id_type htlc_string_to_id(std::string incoming) +{ + boost::replace_all(incoming, "\"", ""); + boost::replace_all(incoming, ".", ","); + std::stringstream ss(incoming); + htlc_id_type id; + int i; + int pos = 0; + while(ss >> i) + { + if (pos == 0 && i != id.space_id) + FC_ASSERT("Invalid HTLC ID"); + if (pos == 1 && i != id.type_id) + FC_ASSERT("Invalid HTLC ID"); + if (pos == 2) + id.instance = i; + pos++; + if (ss.peek() == ',') + ss.ignore(); + } + return id; +} + fc::optional database_api_impl::get_htlc(const std::string htlc_id) const { - FC_ASSERT( htlc_id.size() > 0 ); - const htlc_object* htlc = _db.find(fc::variant(htlc_id, 1).as(1)); - if (htlc) - return *htlc; - return {}; + htlc_id_type id = htlc_string_to_id(htlc_id); + const auto& htlc_idx = _db.get_index_type().indices().get(); + auto itr = htlc_idx.find(id); + if (itr == htlc_idx.end()) + return {}; + return (*itr); } ////////////////////////////////////////////////////////////////////// diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 63e0738634..dd9ac41ebb 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -208,6 +208,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index< htlc_index> >(); //Implementation object indexes add_index< primary_index >(); @@ -223,7 +224,6 @@ void database::initialize_indexes() add_index< primary_index< special_authority_index > >(); add_index< primary_index< buyback_index > >(); add_index< primary_index >(); - add_index< primary_index< htlc_index > >(); add_index< primary_index< simple_index< fba_accumulator_object > > >(); } diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 9d806baca3..155f26c89d 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -56,12 +56,27 @@ namespace graphene { esc.preimage_hash = o.key_hash; esc.preimage_size = o.key_size; esc.expiration = o.epoch; + esc.preimage_hash_algorithm = o.hash_type; }); return esc.id; } FC_CAPTURE_AND_RETHROW( (o) ) } + template + bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) + { + T attempted_hash = T::hash(incoming_preimage); + if (attempted_hash.data_size() != valid_hash.size()) + return false; + for(size_t i = 0; i < attempted_hash.data_size(); ++i) + { + if (attempted_hash.data()[i] != valid_hash[i]) + return false; + } + return true; + } + void_result htlc_update_evaluator::do_evaluate(const htlc_update_operation& o) { htlc_obj = &db().get(o.htlc_id); @@ -69,25 +84,21 @@ namespace graphene { // TODO: Use signatures to determine what to do, not whether preimage was provided if (o.preimage.size() > 0) { - FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); - FC_ASSERT(fc::time_point::now().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); + FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); + FC_ASSERT(fc::time_point::now().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); - // hash the preimage given by the user - fc::sha256 attempted_hash = fc::sha256::hash(o.preimage); - // put the preimage hash in a format we can compare - std::vector passed_hash(attempted_hash.data_size()); - char* data = attempted_hash.data(); - for(size_t i = 0; i < attempted_hash.data_size(); ++i) - { - passed_hash[i] = data[i]; - } + // see if the preimages match + bool match = false; + if (htlc_obj->preimage_hash_algorithm == graphene::chain::htlc_object::hash_algorithm::SHA256) + match = test_hash(o.preimage, htlc_obj->preimage_hash); + if (htlc_obj->preimage_hash_algorithm == graphene::chain::htlc_object::hash_algorithm::RIPEMD160) + match = test_hash(o.preimage, htlc_obj->preimage_hash); - FC_ASSERT(passed_hash == htlc_obj->preimage_hash, "Provided preimage does not generate correct hash."); + FC_ASSERT(match, "Provided preimage does not generate correct hash."); } else { FC_ASSERT(fc::time_point::now().sec_since_epoch() > htlc_obj->expiration.sec_since_epoch(), "Unable to reclaim until escrow expiration."); - FC_ASSERT(htlc_obj->preimage.size() == 0, "Preimage already provided."); } return void_result(); } @@ -97,9 +108,7 @@ namespace graphene { if (o.preimage.size() > 0) { db().adjust_balance(htlc_obj->to, htlc_obj->amount); - db().modify(*htlc_obj, [&o](htlc_object& obj){ - obj.preimage = o.preimage; - }); + db().remove(*htlc_obj); } else { diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 47ef6f492c..b4d3e1d6b8 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -40,14 +40,20 @@ namespace graphene { namespace chain { static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_htlc_object_type; + enum hash_algorithm { + UNKNOWN, + RIPEMD160, + SHA256 + }; + account_id_type from; account_id_type to; asset amount; fc::time_point_sec expiration; asset pending_fee; vector preimage_hash; + enum hash_algorithm preimage_hash_algorithm; uint16_t preimage_size; - vector preimage; }; struct by_from_id; @@ -72,6 +78,8 @@ namespace graphene { namespace chain { } } +FC_REFLECT_ENUM( graphene::chain::htlc_object::hash_algorithm, (UNKNOWN)(RIPEMD160)(SHA256)); + FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), (from)(to)(amount)(expiration)(pending_fee) - (preimage_hash)(preimage_size)(preimage) ); \ No newline at end of file + (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 7b9f242d60..3a661017cd 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -35,17 +35,13 @@ namespace graphene { { struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; - }; - enum hash_algorithm { - UNKNOWN, - RIPEMD160, - SHA256 }; asset fee; // paid to network account_id_type source; // where the held monies are to come from account_id_type destination; // where the held monies will go if the preimage is provided asset amount; // the amount to hold - enum hash_algorithm hash_type = hash_algorithm::UNKNOWN; // hash algorithm used to create key_hash + enum graphene::chain::htlc_object::hash_algorithm hash_type + = graphene::chain::htlc_object::hash_algorithm::UNKNOWN; // hash algorithm used to create key_hash std::vector key_hash; // the hash of the preimage uint16_t key_size; // the size of the preimage fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 3c334bad44..4d04426527 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -163,7 +163,7 @@ namespace graphene { namespace chain { impl_buyback_object_type, impl_fba_accumulator_object_type, impl_collateral_bid_object_type, - impl_htlc_object_type + impl_htlc_object_type, }; //typedef fc::unsigned_int object_id_type; @@ -236,7 +236,7 @@ namespace graphene { namespace chain { typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type; typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type; typedef object_id< implementation_ids, impl_collateral_bid_object_type, collateral_bid_object > collateral_bid_id_type; - typedef object_id< implementation_ids, impl_htlc_object_type, htlc_object > htlc_id_type; + typedef object_id< implementation_ids, impl_htlc_object_type, htlc_object> htlc_id_type; typedef fc::ripemd160 block_id_type; typedef fc::ripemd160 checksum_type; diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index ea4d05ec3f..6f42a44975 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1759,10 +1759,10 @@ class wallet_api_impl create_op.key_hash = preimage_hash; create_op.key_size = preimage_size; if ( "SHA256" == hash_algorithm ) - create_op.hash_type = htlc_create_operation::hash_algorithm::SHA256; + create_op.hash_type = htlc_object::hash_algorithm::SHA256; if ( "RIPEMD160" == hash_algorithm ) - create_op.hash_type = htlc_create_operation::hash_algorithm::RIPEMD160; - FC_ASSERT(create_op.hash_type != htlc_create_operation::hash_algorithm::UNKNOWN, + create_op.hash_type = htlc_object::hash_algorithm::RIPEMD160; + FC_ASSERT(create_op.hash_type != htlc_object::hash_algorithm::UNKNOWN, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); signed_transaction tx; @@ -3084,6 +3084,11 @@ signed_transaction wallet_api::create_htlc( string source, string destination, s timelock, broadcast); } +graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const +{ + return my->get_htlc(htlc_id); +} + vector wallet_api::get_account_history(string name, int limit)const { vector result; diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 2a9bec5448..d93a81aaa9 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -49,6 +49,7 @@ #include #include #endif +#include #include @@ -143,13 +144,15 @@ std::shared_ptr start_application(fc::temp_directory /////////// /// Send a block to the db /// @param app the application +/// @param returned_block the signed block /// @returns true on success /////////// -bool generate_block(std::shared_ptr app) { +bool generate_block(std::shared_ptr app, graphene::chain::signed_block& returned_block) +{ try { fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); auto db = app->chain_database(); - auto block_1 = db->generate_block( db->get_slot_time(1), + returned_block = db->generate_block( db->get_slot_time(1), db->get_scheduled_witness(1), committee_key, database::skip_nothing ); @@ -159,6 +162,12 @@ bool generate_block(std::shared_ptr app) { } } +bool generate_block(std::shared_ptr app) +{ + graphene::chain::signed_block returned_block; + return generate_block(app, returned_block); +} + /////////// /// @brief Skip intermediate blocks, and generate a maintenance block /// @param app the application @@ -609,43 +618,75 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) (nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch()) ); BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member()); - // 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); - // 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); + // 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); + // 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); + } + + // 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); + // 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); + } // 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); + BOOST_TEST_MESSAGE("Creating HTLC"); // create an HTLC std::string preimage_string = "My Secret"; - fc::ripemd160 preimage = fc::ripemd160::hash(preimage_string); - std::vector hash(preimage.data_size()); - for(size_t i = 0; i < preimage.data_size(); ++i) - hash[i] = preimage.data()[i]; + fc::ripemd160 preimage_md = fc::ripemd160::hash(preimage_string); + std::vector hash(preimage_md.data_size()); + for(size_t i = 0; i < preimage_md.data_size(); ++i) + hash[i] = preimage_md.data()[i]; fc::time_point_sec timelock = fc::time_point::now() + fc::days(1); graphene::chain::signed_transaction result_tx - = con.wallet_api_ptr->create_htlc("alice", "nathan", - "1.3.0", "1000", "RIPEMD160", hash, preimage.data_size(), timelock, true); + = con.wallet_api_ptr->create_htlc("alice", "bob", + "1.3.0", "1000", "RIPEMD160", hash, preimage_string.size(), timelock, true); + + // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: + BOOST_TEST_MESSAGE("Generating 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[5].operation_results[0].get(); + std::string htlc_id_as_string = fc::json::to_pretty_string(htlc_id); + BOOST_TEST_MESSAGE("The HTLC ID is: " + htlc_id_as_string); + + // Bob can now look over Alice's HTLC + BOOST_TEST_MESSAGE("Retrieving HTLC Object by ID"); + try + { + //con.wallet_api_ptr->get_object(htlc_id); + graphene::chain::htlc_object alice_htlc = con.wallet_api_ptr->get_htlc(htlc_id_as_string); + BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(alice_htlc)); + } + catch(std::exception& e) + { + BOOST_FAIL(e.what()); + } + catch(...) + { + BOOST_FAIL("Unknown exception attempting to retrieve HTLC"); + } - // share the id with bob - graphene::chain::transaction_id_type htlc_trans_id = con.wallet_api_ptr->get_transaction_id(result_tx); - BOOST_TEST_MESSAGE("The hash is " + std::string(htlc_trans_id)); + // TODO: API for Bob to claim the funds by giving the preimage + // TODO: Clean the HTLC object (no need to have preimage stored there, nor reference to claim tx) - // generate a block to get things started - BOOST_CHECK(generate_block(app1)); - // wait for a maintenance interval - BOOST_CHECK(generate_maintenance_block(app1)); - - // send another block to trigger maintenance interval - BOOST_CHECK(generate_maintenance_block(app1)); - // wait for everything to finish up fc::usleep(fc::seconds(1)); } catch( fc::exception& e ) { diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 2ac16f2106..9bbb22ab51 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -289,12 +289,8 @@ void database_fixture::verify_asset_supplies( const database& db ) const auto& htlc_idx = db.get_index_type< htlc_index >().indices().get< by_id >(); for( auto itr = htlc_idx.begin(); itr != htlc_idx.end(); ++itr ) { - // do not add in htlc escrow objects that are completed - if (itr->preimage.size() == 0) - { - total_balances[itr->amount.asset_id] += itr->amount.amount; - total_balances[itr->pending_fee.asset_id] += itr->pending_fee.amount; - } + total_balances[itr->amount.asset_id] += itr->amount.amount; + total_balances[itr->pending_fee.asset_id] += itr->pending_fee.amount; } for( const asset_object& asset_obj : db.get_index_type().indices() ) From b5510acd9501f172c8a4741b8f0ac3e677834652 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 17 Sep 2018 07:41:19 -0500 Subject: [PATCH 023/161] Added update_htlc to wallet api --- libraries/chain/htlc_evaluator.cpp | 29 +++-- .../include/graphene/chain/htlc_object.hpp | 60 +++++----- .../include/graphene/chain/protocol/htlc.hpp | 8 +- .../wallet/include/graphene/wallet/wallet.hpp | 6 +- libraries/wallet/wallet.cpp | 21 ++-- tests/cli/main.cpp | 110 +++++++++++++----- 6 files changed, 146 insertions(+), 88 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 155f26c89d..d0b684193b 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -36,8 +36,10 @@ namespace graphene { // "Operation not allowed before HARDFORK_ESCROW_TIME."); // remove after HARDFORK_ESCROW_TIME FC_ASSERT( fc::time_point_sec(o.epoch) > db().head_block_time() ); - // TODO: what is the fee denominated in? - FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount + o.fee) ); + // make sure we have the funds for the HTLC + FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount ) ); + // make sure we have the funds for the fee + FC_ASSERT( db().get_balance( o.source, asset().asset_id) > o.fee ); return void_result(); } @@ -66,12 +68,15 @@ namespace graphene { template bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { - T attempted_hash = T::hash(incoming_preimage); + std::string incoming_string(incoming_preimage.begin(), incoming_preimage.end()); + //T attempted_hash = T::hash(incoming_string); + fc::ripemd160 attempted_hash = fc::ripemd160::hash(incoming_string); if (attempted_hash.data_size() != valid_hash.size()) return false; + char* data = attempted_hash.data(); for(size_t i = 0; i < attempted_hash.data_size(); ++i) { - if (attempted_hash.data()[i] != valid_hash[i]) + if ( ((unsigned char)data[i]) != valid_hash[i]) return false; } return true; @@ -89,9 +94,9 @@ namespace graphene { // see if the preimages match bool match = false; - if (htlc_obj->preimage_hash_algorithm == graphene::chain::htlc_object::hash_algorithm::SHA256) + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha256) match = test_hash(o.preimage, htlc_obj->preimage_hash); - if (htlc_obj->preimage_hash_algorithm == graphene::chain::htlc_object::hash_algorithm::RIPEMD160) + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::ripemd160) match = test_hash(o.preimage, htlc_obj->preimage_hash); FC_ASSERT(match, "Provided preimage does not generate correct hash."); @@ -105,16 +110,8 @@ namespace graphene { void_result htlc_update_evaluator::do_apply(const htlc_update_operation& o) { - if (o.preimage.size() > 0) - { - db().adjust_balance(htlc_obj->to, htlc_obj->amount); - db().remove(*htlc_obj); - } - else - { - db().adjust_balance(htlc_obj->from, htlc_obj->amount); - db().remove(*htlc_obj); - } + db().adjust_balance(htlc_obj->to, htlc_obj->amount); + db().remove(*htlc_obj); return void_result(); } } // namespace chain diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index b4d3e1d6b8..3a833d6334 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -32,33 +32,33 @@ namespace graphene { namespace chain { - /** - * Temporally save escrow transactions until funds are released or operation expired. - */ - class htlc_object : public graphene::db::abstract_object { - public: - static const uint8_t space_id = implementation_ids; - static const uint8_t type_id = impl_htlc_object_type; + enum hash_algorithm { + unknown = 0x00, + ripemd160 = 0x01, + sha256 = 0x02 + }; + + /** + * Temporally save escrow transactions until funds are released or operation expired. + */ + class htlc_object : public graphene::db::abstract_object { + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_htlc_object_type; - enum hash_algorithm { - UNKNOWN, - RIPEMD160, - SHA256 - }; + account_id_type from; + account_id_type to; + asset amount; + fc::time_point_sec expiration; + asset pending_fee; + vector preimage_hash; + fc::enum_type preimage_hash_algorithm; + uint16_t preimage_size; + }; - account_id_type from; - account_id_type to; - asset amount; - fc::time_point_sec expiration; - asset pending_fee; - vector preimage_hash; - enum hash_algorithm preimage_hash_algorithm; - uint16_t preimage_size; - }; - - struct by_from_id; - struct by_expiration; - typedef multi_index_container< + struct by_from_id; + struct by_expiration; + typedef multi_index_container< htlc_object, indexed_by< ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, @@ -72,14 +72,14 @@ namespace graphene { namespace chain { > > - > htlc_object_index_type; + > htlc_object_index_type; - typedef generic_index< htlc_object, htlc_object_index_type > htlc_index; + typedef generic_index< htlc_object, htlc_object_index_type > htlc_index; - } } +} } -FC_REFLECT_ENUM( graphene::chain::htlc_object::hash_algorithm, (UNKNOWN)(RIPEMD160)(SHA256)); +FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)); FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), - (from)(to)(amount)(expiration)(pending_fee) + (from)(to)(amount)(expiration)(pending_fee) (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 3a661017cd..5b4e1f0e3b 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -40,8 +40,8 @@ namespace graphene { account_id_type source; // where the held monies are to come from account_id_type destination; // where the held monies will go if the preimage is provided asset amount; // the amount to hold - enum graphene::chain::htlc_object::hash_algorithm hash_type - = graphene::chain::htlc_object::hash_algorithm::UNKNOWN; // hash algorithm used to create key_hash + fc::enum_type hash_type + = graphene::chain::hash_algorithm::unknown; // hash algorithm used to create key_hash std::vector key_hash; // the hash of the preimage uint16_t key_size; // the size of the preimage fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed @@ -74,5 +74,5 @@ namespace graphene { FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::htlc_update_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)) -FC_REFLECT( graphene::chain::htlc_update_operation, (fee)(update_issuer)(preimage)(extensions)) +FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)(hash_type)) +FC_REFLECT( graphene::chain::htlc_update_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 97ba29cc05..89b9bcb4b1 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1469,8 +1469,8 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, std::vector preimage_hash, size_t preimage_size, - fc::time_point_sec timelock, bool broadcast = false ); + string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + const fc::time_point_sec& timelock, bool broadcast = false ); /**** * Update a hashed time lock contract @@ -1479,7 +1479,7 @@ class wallet_api * @param issuer Who is performing this operation (and paying the fee) * @param preimage the preimage that should evaluate to the preimage_hash */ - signed_transaction update_htlc( string htlc_id, string issuer, std::vector preimage, + signed_transaction update_htlc( string htlc_id, string issuer, const std::vector& preimage, bool broadcast = false ); /** diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 6f42a44975..93229d24ed 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1743,7 +1743,8 @@ class wallet_api_impl } signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, std::vector preimage_hash, size_t preimage_size, fc::time_point_sec timelock, bool broadcast = false ) + string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + const fc::time_point_sec& timelock, bool broadcast = false ) { try { @@ -1759,10 +1760,10 @@ class wallet_api_impl create_op.key_hash = preimage_hash; create_op.key_size = preimage_size; if ( "SHA256" == hash_algorithm ) - create_op.hash_type = htlc_object::hash_algorithm::SHA256; + create_op.hash_type = hash_algorithm::sha256; if ( "RIPEMD160" == hash_algorithm ) - create_op.hash_type = htlc_object::hash_algorithm::RIPEMD160; - FC_ASSERT(create_op.hash_type != htlc_object::hash_algorithm::UNKNOWN, + create_op.hash_type = hash_algorithm::ripemd160; + FC_ASSERT(create_op.hash_type != hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); signed_transaction tx; @@ -1775,7 +1776,7 @@ class wallet_api_impl (preimage_hash)(preimage_size)(timelock)(broadcast) ) } - signed_transaction update_htlc( string htlc_id, string issuer, std::vector preimage, bool broadcast ) + signed_transaction update_htlc( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) { try { @@ -3077,8 +3078,8 @@ uint64_t wallet_api::get_asset_count()const } signed_transaction wallet_api::create_htlc( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, std::vector preimage_hash, size_t preimage_size, - fc::time_point_sec timelock, bool broadcast) + string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + const fc::time_point_sec& timelock, bool broadcast) { return my->create_htlc(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, timelock, broadcast); @@ -3089,6 +3090,12 @@ graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const return my->get_htlc(htlc_id); } +signed_transaction wallet_api::update_htlc( std::string htlc_id, std::string issuer, const std::vector& preimage, + bool broadcast) +{ + return my->update_htlc(htlc_id, issuer, preimage, broadcast); +} + vector wallet_api::get_account_history(string name, int limit)const { vector result; diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index d93a81aaa9..80c289a04c 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -618,6 +618,24 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) (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(); @@ -627,6 +645,10 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // 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"); + 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 @@ -638,14 +660,15 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // 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); + // 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); } - // 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); - BOOST_TEST_MESSAGE("Creating HTLC"); + 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::ripemd160 preimage_md = fc::ripemd160::hash(preimage_string); @@ -655,38 +678,69 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) fc::time_point_sec timelock = fc::time_point::now() + fc::days(1); graphene::chain::signed_transaction result_tx = con.wallet_api_ptr->create_htlc("alice", "bob", - "1.3.0", "1000", "RIPEMD160", hash, preimage_string.size(), timelock, true); + "BTS", "3", "RIPEMD160", hash, 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; + { + 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 = fc::json::to_pretty_string(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."); + graphene::chain::htlc_object 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->create_htlc("bob", "alice", + "BOBCOIN", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: - BOOST_TEST_MESSAGE("Generating 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[5].operation_results[0].get(); - std::string htlc_id_as_string = fc::json::to_pretty_string(htlc_id); - BOOST_TEST_MESSAGE("The HTLC ID is: " + htlc_id_as_string); - - // Bob can now look over Alice's HTLC - BOOST_TEST_MESSAGE("Retrieving HTLC Object by ID"); - try + std::string bob_htlc_id_as_string; { - //con.wallet_api_ptr->get_object(htlc_id); - graphene::chain::htlc_object alice_htlc = con.wallet_api_ptr->get_htlc(htlc_id_as_string); - BOOST_TEST_MESSAGE("The HTLC Object is: " + fc::json::to_pretty_string(alice_htlc)); + 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 = fc::json::to_pretty_string(htlc_id); + BOOST_TEST_MESSAGE("Bob shares the HTLC ID with Alice. The HTLC ID is: " + bob_htlc_id_as_string); } - catch(std::exception& e) + + // 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."); + graphene::chain::htlc_object 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_FAIL(e.what()); + BOOST_TEST_MESSAGE("Alice uses her preimage to retrieve the BOBCOIN"); + std::string secret = "My Secret"; + vector secret_vector(secret.begin(), secret.end()); + con.wallet_api_ptr->update_htlc(bob_htlc_id_as_string, "alice", secret_vector, true); + BOOST_TEST_MESSAGE("The system is generating a block"); + BOOST_CHECK(generate_block(app1)); } - catch(...) + + // TODO: Bob can look at Alice's history to see her preimage + // Bob can use the preimage to retrieve his BTS { - BOOST_FAIL("Unknown exception attempting to retrieve HTLC"); + BOOST_TEST_MESSAGE("Bob uses Alice's preimage to retrieve the BOBCOIN"); + std::string secret = "My Secret"; + vector secret_vector(secret.begin(), secret.end()); + con.wallet_api_ptr->update_htlc(alice_htlc_id_as_string, "bob", secret_vector, true); + BOOST_TEST_MESSAGE("The system is generating a block"); + BOOST_CHECK(generate_block(app1)); } - // TODO: API for Bob to claim the funds by giving the preimage - // TODO: Clean the HTLC object (no need to have preimage stored there, nor reference to claim tx) - // wait for everything to finish up fc::usleep(fc::seconds(1)); } catch( fc::exception& e ) { From 40d22aa7b014dd59606c524a8ed8275da6b615ae Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 17 Sep 2018 17:11:08 -0500 Subject: [PATCH 024/161] Removed debugging code --- libraries/chain/htlc_evaluator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index d0b684193b..4aef54fbb5 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -69,8 +69,7 @@ namespace graphene { bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { std::string incoming_string(incoming_preimage.begin(), incoming_preimage.end()); - //T attempted_hash = T::hash(incoming_string); - fc::ripemd160 attempted_hash = fc::ripemd160::hash(incoming_string); + T attempted_hash = T::hash(incoming_string); if (attempted_hash.data_size() != valid_hash.size()) return false; char* data = attempted_hash.data(); From eb1225ecb594167671fdf91df2c89d15fd12af45 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 27 Sep 2018 13:02:20 -0500 Subject: [PATCH 025/161] add htlc to switch-case --- libraries/chain/db_notify.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 24ad3fb342..d8f8c09ba3 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -414,7 +414,9 @@ void get_relevant_accounts( const object* obj, flat_set& accoun FC_ASSERT( aobj != nullptr ); accounts.insert( aobj->bidder ); break; - } + } case impl_htlc_object_type: + break; + } } } // end get_relevant_accounts( const object* obj, flat_set& accounts ) From d367c2c86f959d29733db0d42c828fd80097e252 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 12 Oct 2018 13:49:53 -0500 Subject: [PATCH 026/161] Change method names to match spec --- .../wallet/include/graphene/wallet/wallet.hpp | 25 +++++++++++++++---- libraries/wallet/wallet.cpp | 23 ++++++++++++----- tests/cli/main.cpp | 8 +++--- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 89b9bcb4b1..2fd8ef1cb1 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1468,9 +1468,9 @@ class wallet_api * @param timelock when the time lock expires * @param broadcast true if you wish to broadcast the transaction */ - signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, - const fc::time_point_sec& timelock, bool broadcast = false ); + signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, + string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + const fc::time_point_sec& timelock, bool broadcast = false ); /**** * Update a hashed time lock contract @@ -1479,8 +1479,19 @@ class wallet_api * @param issuer Who is performing this operation (and paying the fee) * @param preimage the preimage that should evaluate to the preimage_hash */ - signed_transaction update_htlc( string htlc_id, string issuer, const std::vector& preimage, - bool broadcast = false ); + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, + bool broadcast = false ); + + /***** + * Set a new timelock on an existing HTLC + * + * @param htlc_id The object identifier of the HTLC on the blockchain + * @param issuer Who is performing this operation (and paying the fee) + * @param timelock the new time of expiry + * @param broadcast true to broadcast to the network + */ + signed_transaction htlc_extend_expiry(string htlc_id, string issuer, const fc::time_point_sec& timelock, + bool broadcast = false); /** * Get information about a vesting balance object. @@ -1811,6 +1822,7 @@ FC_API( graphene::wallet::wallet_api, (update_asset) (update_asset_issuer) (update_bitasset) + (get_htlc) (update_asset_feed_producers) (publish_asset_feed) (issue_asset) @@ -1832,6 +1844,9 @@ FC_API( graphene::wallet::wallet_api, (update_witness) (create_worker) (update_worker_votes) + (htlc_prepare) + (htlc_redeem) + (htlc_extend_expiry) (get_vesting_balances) (withdraw_vesting) (vote_for_committee_member) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 93229d24ed..cd8d15f87d 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1742,7 +1742,7 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } - signed_transaction create_htlc( string source, string destination, string asset_symbol, string amount, + signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, const fc::time_point_sec& timelock, bool broadcast = false ) { @@ -1776,7 +1776,7 @@ class wallet_api_impl (preimage_hash)(preimage_size)(timelock)(broadcast) ) } - signed_transaction update_htlc( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) { try { @@ -1800,6 +1800,11 @@ class wallet_api_impl } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(preimage)(broadcast) ) } + signed_transaction htlc_extend_expiry ( string htlc_id, string issuer, const fc::time_point_sec& timeout, bool broadcast) + { + throw std::logic_error("Not Implemented"); + } + vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) { try { fc::optional vbid = maybe_id( account_name ); @@ -3077,11 +3082,11 @@ uint64_t wallet_api::get_asset_count()const return my->_remote_db->get_asset_count(); } -signed_transaction wallet_api::create_htlc( string source, string destination, string asset_symbol, string amount, +signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, const fc::time_point_sec& timelock, bool broadcast) { - return my->create_htlc(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, + return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, timelock, broadcast); } @@ -3090,10 +3095,16 @@ graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const return my->get_htlc(htlc_id); } -signed_transaction wallet_api::update_htlc( std::string htlc_id, std::string issuer, const std::vector& preimage, +signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::vector& preimage, + bool broadcast) +{ + return my->htlc_redeem(htlc_id, issuer, preimage, broadcast); +} + +signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const fc::time_point_sec& timelock, bool broadcast) { - return my->update_htlc(htlc_id, issuer, preimage, broadcast); + return my->htlc_extend_expiry(htlc_id, issuer, timelock, broadcast); } vector wallet_api::get_account_history(string name, int limit)const diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 80c289a04c..3af43dbc4f 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -677,7 +677,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) hash[i] = preimage_md.data()[i]; fc::time_point_sec timelock = fc::time_point::now() + fc::days(1); graphene::chain::signed_transaction result_tx - = con.wallet_api_ptr->create_htlc("alice", "bob", + = con.wallet_api_ptr->htlc_prepare("alice", "bob", "BTS", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: @@ -699,7 +699,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) 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->create_htlc("bob", "alice", + con.wallet_api_ptr->htlc_prepare("bob", "alice", "BOBCOIN", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); // normally, a wallet would watch block production, and find the transaction. Here, we can cheat: @@ -725,7 +725,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) BOOST_TEST_MESSAGE("Alice uses her preimage to retrieve the BOBCOIN"); std::string secret = "My Secret"; vector secret_vector(secret.begin(), secret.end()); - con.wallet_api_ptr->update_htlc(bob_htlc_id_as_string, "alice", secret_vector, true); + con.wallet_api_ptr->htlc_redeem(bob_htlc_id_as_string, "alice", secret_vector, true); BOOST_TEST_MESSAGE("The system is generating a block"); BOOST_CHECK(generate_block(app1)); } @@ -736,7 +736,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) BOOST_TEST_MESSAGE("Bob uses Alice's preimage to retrieve the BOBCOIN"); std::string secret = "My Secret"; vector secret_vector(secret.begin(), secret.end()); - con.wallet_api_ptr->update_htlc(alice_htlc_id_as_string, "bob", secret_vector, true); + con.wallet_api_ptr->htlc_redeem(alice_htlc_id_as_string, "bob", secret_vector, true); BOOST_TEST_MESSAGE("The system is generating a block"); BOOST_CHECK(generate_block(app1)); } From 6ded68887f025ef401c1419adb364bd408ef87c6 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 12 Oct 2018 20:41:18 -0500 Subject: [PATCH 027/161] Boost 1.58 compatibility --- libraries/wallet/wallet.cpp | 6 +++--- tests/tests/htlc_tests.cpp | 16 ++++++---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index cd8d15f87d..fb9c74d71f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1760,10 +1760,10 @@ class wallet_api_impl create_op.key_hash = preimage_hash; create_op.key_size = preimage_size; if ( "SHA256" == hash_algorithm ) - create_op.hash_type = hash_algorithm::sha256; + create_op.hash_type = graphene::chain::hash_algorithm::sha256; if ( "RIPEMD160" == hash_algorithm ) - create_op.hash_type = hash_algorithm::ripemd160; - FC_ASSERT(create_op.hash_type != hash_algorithm::unknown, + create_op.hash_type = graphene::chain::hash_algorithm::ripemd160; + FC_ASSERT(create_op.hash_type != graphene::chain::hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); signed_transaction tx; diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 723bea78c3..6acebbfaeb 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -119,8 +119,7 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) } // verify funds on hold (make sure this can cover fees) - BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); - //BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) > 98000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); // make sure Alice can't get it back before the timeout { @@ -163,7 +162,7 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) trx.clear(); } // verify funds return (what about fees?) - BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 100000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 100000 ); // verify Bob cannot execute the contract after the fact } @@ -211,7 +210,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) } // verify funds on hold (make sure this can cover fees) - BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); // make sure Alice can't get it back before the timeout { @@ -233,7 +232,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) } // balance should not have changed - BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); // make sure Bob (or anyone) can see the details of the transaction // send an update operation to claim the funds { @@ -272,17 +271,14 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) trx.clear(); } // verify funds end up in Bob's account - BOOST_TEST_CHECK( get_balance(bob_id, graphene::chain::asset_id_type()) == 10000 ); - BOOST_TEST_CHECK( get_balance(alice_id, graphene::chain::asset_id_type()) == 90000 ); + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 10000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); } BOOST_AUTO_TEST_CASE( other_peoples_money ) { ACTORS((alice)(bob)); - fc::time_point_sec starting_time = genesis_state.initial_timestamp + 3000; - - int64_t init_balance(100000); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); From 9a502a35453973da71669371130b5303eb363304 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sun, 14 Oct 2018 21:46:51 -0500 Subject: [PATCH 028/161] add get_typename for enum --- libraries/chain/htlc_evaluator.cpp | 1 - libraries/chain/include/graphene/chain/htlc_object.hpp | 7 ++++++- programs/build_helpers/member_enumerator.cpp | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 4aef54fbb5..89e408c332 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include namespace graphene { diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 3a833d6334..0a3b571e6f 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -76,7 +76,12 @@ namespace graphene { namespace chain { typedef generic_index< htlc_object, htlc_object_index_type > htlc_index; -} } +} } // namespace graphene::chain + +namespace fc +{ + template<> struct get_typename> { static const char* name() { return "fc::enum_type"; } }; +} FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)); diff --git a/programs/build_helpers/member_enumerator.cpp b/programs/build_helpers/member_enumerator.cpp index 16452c5bf8..5dcc6156a3 100644 --- a/programs/build_helpers/member_enumerator.cpp +++ b/programs/build_helpers/member_enumerator.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include From dc826109541fe9c51e0ee00b35267d82223edce4 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 15 Oct 2018 15:16:31 -0500 Subject: [PATCH 029/161] Addition of SHA1 and committee parameters --- libraries/chain/htlc_evaluator.cpp | 26 +++++++++----- .../include/graphene/chain/htlc_object.hpp | 7 ++-- .../chain/protocol/chain_parameters.hpp | 36 +++++++++++++++++-- libraries/wallet/wallet.cpp | 2 ++ 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 89e408c332..cf84f89f3a 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include #include #include #include @@ -31,15 +32,22 @@ namespace graphene { void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) { - //FC_ASSERT( db().head_block_time() > HARDFORK_ESCROW_TIME, - // "Operation not allowed before HARDFORK_ESCROW_TIME."); // remove after HARDFORK_ESCROW_TIME + //FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME, + // "Operation not allowed before HARDFORK_HTLC_TIME."); // remove after HARDFORK_ESCROW_TIME - FC_ASSERT( fc::time_point_sec(o.epoch) > db().head_block_time() ); - // make sure we have the funds for the HTLC - FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount ) ); - // make sure we have the funds for the fee - FC_ASSERT( db().get_balance( o.source, asset().asset_id) > o.fee ); - return void_result(); + const graphene::chain::global_property_object& global_properties = db().get_global_properties(); + // make sure the expiration is reasonable + FC_ASSERT( o.epoch.sec_since_epoch() < fc::time_point::now().sec_since_epoch() + + global_properties.parameters.get_committee_updatable_parameters().htlc_max_timeout_secs, + "HTLC Timeout exceeds allowed length" ); + // make sure the preimage length is reasonable + FC_ASSERT( o.key_size < global_properties.parameters.get_committee_updatable_parameters().htlc_max_preimage_size, + "HTLC preimage length exceeds allowed length" ); + // make sure sender has the funds for the HTLC + FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount ), "Insufficient funds" ); + // make sure sender has the funds for the fee + FC_ASSERT( db().get_balance( o.source, asset().asset_id) > o.fee, "Insufficient funds" ); + return void_result(); } object_id_type htlc_create_evaluator::do_apply(const htlc_create_operation& o) @@ -96,6 +104,8 @@ namespace graphene { match = test_hash(o.preimage, htlc_obj->preimage_hash); if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::ripemd160) match = test_hash(o.preimage, htlc_obj->preimage_hash); + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha1) + match = test_hash(o.preimage, htlc_obj->preimage_hash); FC_ASSERT(match, "Provided preimage does not generate correct hash."); } diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 0a3b571e6f..a2ceac0271 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -35,7 +35,8 @@ namespace graphene { namespace chain { enum hash_algorithm { unknown = 0x00, ripemd160 = 0x01, - sha256 = 0x02 + sha256 = 0x02, + sha1 = 0x03 }; /** @@ -53,7 +54,7 @@ namespace graphene { namespace chain { asset pending_fee; vector preimage_hash; fc::enum_type preimage_hash_algorithm; - uint16_t preimage_size; + uint16_t preimage_size; }; struct by_from_id; @@ -83,7 +84,7 @@ namespace fc template<> struct get_typename> { static const char* name() { return "fc::enum_type"; } }; } -FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)); +FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), (from)(to)(amount)(expiration)(pending_fee) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 695d9541ee..f04fdcea78 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -30,7 +30,6 @@ namespace graphene { namespace chain { struct fee_schedule; } } namespace graphene { namespace chain { - typedef static_variant<> parameter_extension; struct chain_parameters { /** using a smart ref breaks the circular dependency created between operations and the fee schedule */ @@ -63,14 +62,47 @@ namespace graphene { namespace chain { uint16_t accounts_per_fee_scale = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE; ///< number of accounts between fee scalings uint8_t account_fee_scale_bitshifts = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS; ///< number of times to left bitshift account registration fee at each scaling uint8_t max_authority_depth = GRAPHENE_MAX_SIG_CHECK_DEPTH; + + struct ext + { + struct committee_updatable_parameters + { + uint32_t htlc_max_timeout_secs; + uint32_t htlc_max_preimage_size; + }; + }; + typedef static_variant parameter_extension; + typedef flat_set extensions_type; + extensions_type extensions; /** defined in fee_schedule.cpp */ void validate()const; + + const ext::committee_updatable_parameters get_committee_updatable_parameters() const + { + if (extensions.size() > 0) + { + for ( const parameter_extension& e : extensions ) + { + if ( e.which() == parameter_extension::tag::value ) + return e.get(); + } + } + return ext::committee_updatable_parameters(); + } }; } } // graphene::chain +FC_REFLECT( graphene::chain::chain_parameters::ext::committee_updatable_parameters, + (htlc_max_timeout_secs) + (htlc_max_preimage_size) + ) + +FC_REFLECT_TYPENAME( graphene::chain::chain_parameters::parameter_extension ) +FC_REFLECT_TYPENAME( graphene::chain::chain_parameters::extensions_type ) + FC_REFLECT( graphene::chain::chain_parameters, (current_fees) (block_interval) @@ -101,4 +133,4 @@ FC_REFLECT( graphene::chain::chain_parameters, (account_fee_scale_bitshifts) (max_authority_depth) (extensions) - ) + ) \ No newline at end of file diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index fb9c74d71f..ae4fe270f8 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1763,6 +1763,8 @@ class wallet_api_impl create_op.hash_type = graphene::chain::hash_algorithm::sha256; if ( "RIPEMD160" == hash_algorithm ) create_op.hash_type = graphene::chain::hash_algorithm::ripemd160; + if ( "SHA1" == hash_algorithm ) + create_op.hash_type = graphene::chain::hash_algorithm::sha1; FC_ASSERT(create_op.hash_type != graphene::chain::hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); From 49822e18982ab685336ecc6e62f004698d195465 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 24 Oct 2018 12:09:21 -0500 Subject: [PATCH 030/161] Refactor committee parameters --- libraries/chain/db_block.cpp | 1 + libraries/chain/db_maint.cpp | 2 - libraries/chain/db_update.cpp | 2 +- libraries/chain/htlc_evaluator.cpp | 31 +++++++++++----- .../chain/include/graphene/chain/database.hpp | 2 +- .../chain/protocol/chain_parameters.hpp | 37 +++++-------------- 6 files changed, 35 insertions(+), 40 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index efc5562a89..20022ce4c4 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -584,6 +584,7 @@ void database::_apply_block( const signed_block& next_block ) clear_expired_transactions(); clear_expired_proposals(); clear_expired_orders(); + clear_expired_htlcs(); update_expired_feeds(); // this will update expired feeds and some core exchange rates update_core_exchange_rates(); // this will update remaining core exchange rates update_withdraw_permissions(); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index a766084131..7f1a8a4c6c 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -1259,8 +1259,6 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // process_budget needs to run at the bottom because // it needs to know the next_maintenance_time process_budget(); - - remove_expired_htlcs(); } } } diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index fc10bd6ad8..7552bc5528 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -558,7 +558,7 @@ void database::update_withdraw_permissions() remove(*permit_index.begin()); } -void database::remove_expired_htlcs() +void database::clear_expired_htlcs() { const auto& htlc_idx = get_index_type().indices().get(); while ( !htlc_idx.empty() && htlc_idx.begin() != htlc_idx.end() diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index cf84f89f3a..826e0581c2 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -30,19 +30,29 @@ namespace graphene { namespace chain { + optional get_committee_htlc_parameters(graphene::chain::database& db) + { + return db.get_global_properties().parameters.extensions.value.committee_updatable_options; + } + void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) { //FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME, // "Operation not allowed before HARDFORK_HTLC_TIME."); // remove after HARDFORK_ESCROW_TIME - const graphene::chain::global_property_object& global_properties = db().get_global_properties(); + optional committee_parameters = get_committee_htlc_parameters(db()); + + FC_ASSERT(committee_parameters, "HTLC Committee parameters are not set."); + // make sure the expiration is reasonable - FC_ASSERT( o.epoch.sec_since_epoch() < fc::time_point::now().sec_since_epoch() - + global_properties.parameters.get_committee_updatable_parameters().htlc_max_timeout_secs, + FC_ASSERT( o.epoch.sec_since_epoch() < db().head_block_time().sec_since_epoch() + + committee_parameters->htlc_max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable - FC_ASSERT( o.key_size < global_properties.parameters.get_committee_updatable_parameters().htlc_max_preimage_size, + FC_ASSERT( o.key_size < committee_parameters->htlc_max_preimage_size, "HTLC preimage length exceeds allowed length" ); + // make sure we have a hash algorithm set + FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); // make sure sender has the funds for the HTLC FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount ), "Insufficient funds" ); // make sure sender has the funds for the fee @@ -75,8 +85,11 @@ namespace graphene { template bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { - std::string incoming_string(incoming_preimage.begin(), incoming_preimage.end()); - T attempted_hash = T::hash(incoming_string); + // convert incoming_preimage to an array + unsigned char incoming_array[incoming_preimage.size()]; + for(int i = 0; i < incoming_preimage.size(); ++i) + incoming_array[i] = incoming_preimage[i]; + T attempted_hash = T::hash( (char*)incoming_array, incoming_preimage.size()); if (attempted_hash.data_size() != valid_hash.size()) return false; char* data = attempted_hash.data(); @@ -96,7 +109,7 @@ namespace graphene { if (o.preimage.size() > 0) { FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); - FC_ASSERT(fc::time_point::now().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); + FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); // see if the preimages match bool match = false; @@ -107,11 +120,11 @@ namespace graphene { if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha1) match = test_hash(o.preimage, htlc_obj->preimage_hash); - FC_ASSERT(match, "Provided preimage does not generate correct hash."); + FC_ASSERT(match, "Provided preimage does not generate correct hash."); } else { - FC_ASSERT(fc::time_point::now().sec_since_epoch() > htlc_obj->expiration.sec_since_epoch(), "Unable to reclaim until escrow expiration."); + FC_ASSERT(db().head_block_time().sec_since_epoch() > htlc_obj->expiration.sec_since_epoch(), "Unable to reclaim until escrow expiration."); } return void_result(); } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 202b8bcb05..5ad65950bc 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -466,7 +466,7 @@ namespace graphene { namespace chain { void update_withdraw_permissions(); bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true, const asset_bitasset_data_object* bitasset_ptr = nullptr ); - void remove_expired_htlcs(); + void clear_expired_htlcs(); ///Steps performed only at maintenance intervals ///@{ diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index f04fdcea78..1f24fd1c2f 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -30,6 +30,12 @@ namespace graphene { namespace chain { struct fee_schedule; } } namespace graphene { namespace chain { + struct committee_updatable_parameters + { + uint32_t htlc_max_timeout_secs; + uint32_t htlc_max_preimage_size; + }; + struct chain_parameters { /** using a smart ref breaks the circular dependency created between operations and the fee schedule */ @@ -65,43 +71,20 @@ namespace graphene { namespace chain { struct ext { - struct committee_updatable_parameters - { - uint32_t htlc_max_timeout_secs; - uint32_t htlc_max_preimage_size; - }; + optional< committee_updatable_parameters > committee_updatable_options; }; - typedef static_variant parameter_extension; - typedef flat_set extensions_type; - extensions_type extensions; + extension extensions; /** defined in fee_schedule.cpp */ void validate()const; - const ext::committee_updatable_parameters get_committee_updatable_parameters() const - { - if (extensions.size() > 0) - { - for ( const parameter_extension& e : extensions ) - { - if ( e.which() == parameter_extension::tag::value ) - return e.get(); - } - } - return ext::committee_updatable_parameters(); - } }; } } // graphene::chain -FC_REFLECT( graphene::chain::chain_parameters::ext::committee_updatable_parameters, - (htlc_max_timeout_secs) - (htlc_max_preimage_size) - ) - -FC_REFLECT_TYPENAME( graphene::chain::chain_parameters::parameter_extension ) -FC_REFLECT_TYPENAME( graphene::chain::chain_parameters::extensions_type ) +FC_REFLECT( graphene::chain::committee_updatable_parameters, (htlc_max_timeout_secs) (htlc_max_preimage_size) ) +FC_REFLECT( graphene::chain::chain_parameters::ext, (committee_updatable_options)) FC_REFLECT( graphene::chain::chain_parameters, (current_fees) From 3ac8323250c3426f82c338a1f0e632837da869c7 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 24 Oct 2018 12:10:36 -0500 Subject: [PATCH 031/161] Test cleanup. Hardfork protection still needed. --- tests/tests/htlc_tests.cpp | 281 ++++++++++++++++++++----------------- 1 file changed, 151 insertions(+), 130 deletions(-) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 6acebbfaeb..5399f2ff9f 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -65,7 +65,11 @@ void generate_random_preimage(uint16_t key_size, std::vector& vec std::vector hash_it(std::vector preimage) { - fc::sha256 hash = fc::sha256::hash(preimage); + // convert the preimage to a char array + unsigned char char_array[preimage.size()]; + for(unsigned int i = 0; i < preimage.size(); ++i) + char_array[i] = preimage[i]; + fc::sha256 hash = fc::sha256::hash((char*)char_array, preimage.size()); std::vector ret_val(hash.data_size()); char* data = hash.data(); for(size_t i = 0; i < hash.data_size(); i++) @@ -75,8 +79,21 @@ std::vector hash_it(std::vector preimage) return ret_val; } +void set_committee_parameters(database_fixture* db_fixture) +{ + // set the committee parameters + db_fixture->db.modify(db_fixture->db.get_global_properties(), [](global_property_object& p) { + graphene::chain::committee_updatable_parameters params; + params.htlc_max_preimage_size = 1024; + params.htlc_max_timeout_secs = 60 * 60 * 24 * 28; + p.parameters.extensions.value.committee_updatable_options = params; + }); +} + BOOST_AUTO_TEST_CASE( htlc_expires ) { + set_committee_parameters(this); + ACTORS((alice)(bob)); int64_t init_balance(100000); @@ -92,14 +109,16 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) // cler everything out generate_block(); trx.clear(); + fc::time_point_sec expiration = db.head_block_time() + fc::seconds(60); // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.epoch = expiration; create_operation.key_hash = key_hash; + create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); @@ -116,35 +135,94 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) // can we assume that alice's transaction will be the only one in this block? processed_transaction alice_trx = blk.transactions[0]; alice_htlc_id = alice_trx.operation_results[0].get(); + generate_block(); + } + + // verify funds on hold (TODO: make sure this can cover fees) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + + // make sure Bob (or anyone) can see the details of the transaction + graphene::app::database_api db_api(db); + optional htlc = db_api.get_htlc(fc::json::to_pretty_string(alice_htlc_id)); + BOOST_CHECK(htlc); + + // let it expire (wait for timeout) + generate_blocks(expiration + fc::seconds(120) ); + // verify funds return (what about fees?) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 100000 ); + // verify Bob cannot execute the contract after the fact +} + +BOOST_AUTO_TEST_CASE( htlc_fulfilled ) +{ + set_committee_parameters(this); + + ACTORS((alice)(bob)); + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t key_size = 256; + std::vector pre_image(key_size); + generate_random_preimage(key_size, pre_image); + std::vector key_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + // Alice puts a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = db.head_block_time() + fc::seconds(10); + create_operation.key_hash = key_hash; + create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + try + { + PUSH_TX(db, trx, ~0); + } catch (fc::exception& ex) + { + BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); + } + trx.clear(); + graphene::chain::signed_block blk = generate_block(); + // can we assume that alice's transaction will be the only one in this block? + processed_transaction alice_trx = blk.transactions[0]; + alice_htlc_id = alice_trx.operation_results[0].get(); } // verify funds on hold (make sure this can cover fees) BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); - // make sure Alice can't get it back before the timeout + // TODO: make sure Bob (or anyone) can see the details of the transaction + + // send an update operation to claim the funds { graphene::chain::htlc_update_operation update_operation; - update_operation.update_issuer = alice_id; + update_operation.update_issuer = bob_id; update_operation.htlc_id = alice_htlc_id; + update_operation.preimage = pre_image; trx.operations.push_back(update_operation); - sign(trx, alice_private_key); + sign(trx, bob_private_key); try { - PUSH_TX(db, trx, ~0); - BOOST_FAIL("Should not allow Alice to reclaim funds before timeout"); + PUSH_TX(db, trx, ~0); } catch (fc::exception& ex) { - // this should happen + BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); } generate_block(); trx.clear(); } - - // make sure Alice can't spend it. - // make sure Bob (or anyone) can see the details of the transaction - // let it expire (wait for timeout) - std::this_thread::sleep_for(std::chrono::seconds(4)); - // send an update operation to reclaim the funds + // verify Alice cannot execute the contract after the fact { graphene::chain::htlc_update_operation update_operation; update_operation.update_issuer = alice_id; @@ -153,130 +231,24 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) sign(trx, alice_private_key); try { - PUSH_TX(db, trx, ~0); + PUSH_TX(db, trx, ~0); + BOOST_FAIL("Should not allow Alice to reclaim funds after Bob already claimed them."); } catch (fc::exception& ex) { - BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); + // this should happen } generate_block(); trx.clear(); } - // verify funds return (what about fees?) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 100000 ); - // verify Bob cannot execute the contract after the fact -} - -BOOST_AUTO_TEST_CASE( htlc_fulfilled ) -{ - ACTORS((alice)(bob)); - - int64_t init_balance(100000); - - transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - - uint16_t key_size = 256; - std::vector pre_image(256); - generate_random_preimage(key_size, pre_image); - std::vector key_hash = hash_it(pre_image); - - graphene::chain::htlc_id_type alice_htlc_id; - // cler everything out - generate_block(); - trx.clear(); - // Alice puts a contract on the blockchain - { - graphene::chain::htlc_create_operation create_operation; - - create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.destination = bob_id; - create_operation.epoch = fc::time_point::now() + fc::seconds(3); - create_operation.key_hash = key_hash; - create_operation.key_size = key_size; - create_operation.source = alice_id; - trx.operations.push_back(create_operation); - sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - } catch (fc::exception& ex) - { - BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); - } - trx.clear(); - graphene::chain::signed_block blk = generate_block(); - // can we assume that alice's transaction will be the only one in this block? - processed_transaction alice_trx = blk.transactions[0]; - alice_htlc_id = alice_trx.operation_results[0].get(); - } - - // verify funds on hold (make sure this can cover fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); - - // make sure Alice can't get it back before the timeout - { - graphene::chain::htlc_update_operation update_operation; - update_operation.update_issuer = alice_id; - update_operation.htlc_id = alice_htlc_id; - trx.operations.push_back(update_operation); - sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - BOOST_FAIL("Should not allow Alice to reclaim funds before timeout"); - } catch (fc::exception& ex) - { - // this should happen - } - generate_block(); - trx.clear(); - } - - // balance should not have changed - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); - // make sure Bob (or anyone) can see the details of the transaction - // send an update operation to claim the funds - { - graphene::chain::htlc_update_operation update_operation; - update_operation.update_issuer = bob_id; - update_operation.htlc_id = alice_htlc_id; - update_operation.preimage = pre_image; - trx.operations.push_back(update_operation); - sign(trx, bob_private_key); - try - { - PUSH_TX(db, trx, ~0); - } catch (fc::exception& ex) - { - BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); - } - generate_block(); - trx.clear(); - } - // verify Alice cannot execute the contract after the fact - { - graphene::chain::htlc_update_operation update_operation; - update_operation.update_issuer = alice_id; - update_operation.htlc_id = alice_htlc_id; - trx.operations.push_back(update_operation); - sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - BOOST_FAIL("Should not allow Alice to reclaim funds after Bob already claimed them."); - } catch (fc::exception& ex) - { - // this should happen - } - generate_block(); - trx.clear(); - } - // verify funds end up in Bob's account - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 10000 ); - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + // verify funds end up in Bob's account + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 10000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); } BOOST_AUTO_TEST_CASE( other_peoples_money ) { + set_committee_parameters(this); + ACTORS((alice)(bob)); int64_t init_balance(100000); @@ -297,7 +269,7 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.epoch = db.head_block_time() + fc::seconds(3); create_operation.key_hash = key_hash; create_operation.key_size = key_size; create_operation.source = alice_id; @@ -320,8 +292,9 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = fc::time_point::now() + fc::seconds(3); + create_operation.epoch = db.head_block_time() + fc::seconds(3); create_operation.key_hash = key_hash; + create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); @@ -337,4 +310,52 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) } } +BOOST_AUTO_TEST_CASE( set_htlc_params ) +{ try { + set_committee_parameters(this); + + BOOST_TEST_MESSAGE( "Creating a proposal to change the max_preimage_size to 2048" ); + { + 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; + graphene::chain::committee_updatable_parameters new_params; + new_params.htlc_max_preimage_size = 2048; + new_params.htlc_max_timeout_secs = 60 * 60 * 24 * 28; + uop.new_parameters.extensions.value.committee_updatable_options = new_params; + cop.proposed_ops.emplace_back(uop); + trx.operations.push_back(cop); + db.push_transaction(trx); + } + BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); + { + proposal_update_operation uop; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; + trx.operations.push_back(uop); + sign( trx, init_account_priv_key ); + db.push_transaction(trx); + BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); + } + BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); + + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 1024u); + + BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); + generate_blocks(proposal_id_type()(db).expiration_time + 5); + BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 1024u); + + BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" ); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); // get the maintenance skip slots out of the way + + BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 2048u); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() From a30df565ec007b26276d3f6c3417e74a69441cc6 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 26 Oct 2018 12:33:10 -0500 Subject: [PATCH 032/161] modified name of optional committee parameters --- libraries/chain/htlc_evaluator.cpp | 12 +++++----- .../chain/protocol/chain_parameters.hpp | 14 ++++++------ tests/tests/htlc_tests.cpp | 22 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 826e0581c2..b3f43d99ad 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -30,9 +30,9 @@ namespace graphene { namespace chain { - optional get_committee_htlc_parameters(graphene::chain::database& db) + optional get_committee_htlc_options(graphene::chain::database& db) { - return db.get_global_properties().parameters.extensions.value.committee_updatable_options; + return db.get_global_properties().parameters.extensions.value.updatable_htlc_options; } void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) @@ -40,16 +40,16 @@ namespace graphene { //FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME, // "Operation not allowed before HARDFORK_HTLC_TIME."); // remove after HARDFORK_ESCROW_TIME - optional committee_parameters = get_committee_htlc_parameters(db()); + optional htlc_options = get_committee_htlc_options(db()); - FC_ASSERT(committee_parameters, "HTLC Committee parameters are not set."); + FC_ASSERT(htlc_options, "HTLC Committee options are not set."); // make sure the expiration is reasonable FC_ASSERT( o.epoch.sec_since_epoch() < db().head_block_time().sec_since_epoch() - + committee_parameters->htlc_max_timeout_secs, + + htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable - FC_ASSERT( o.key_size < committee_parameters->htlc_max_preimage_size, + FC_ASSERT( o.key_size < htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure we have a hash algorithm set FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 1f24fd1c2f..f155ecafb4 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -30,10 +30,10 @@ namespace graphene { namespace chain { struct fee_schedule; } } namespace graphene { namespace chain { - struct committee_updatable_parameters + struct htlc_options { - uint32_t htlc_max_timeout_secs; - uint32_t htlc_max_preimage_size; + uint32_t max_timeout_secs; + uint32_t max_preimage_size; }; struct chain_parameters @@ -71,7 +71,7 @@ namespace graphene { namespace chain { struct ext { - optional< committee_updatable_parameters > committee_updatable_options; + optional< htlc_options > updatable_htlc_options; }; extension extensions; @@ -83,8 +83,8 @@ namespace graphene { namespace chain { } } // graphene::chain -FC_REFLECT( graphene::chain::committee_updatable_parameters, (htlc_max_timeout_secs) (htlc_max_preimage_size) ) -FC_REFLECT( graphene::chain::chain_parameters::ext, (committee_updatable_options)) +FC_REFLECT( graphene::chain::htlc_options, (max_timeout_secs) (max_preimage_size) ) +FC_REFLECT( graphene::chain::chain_parameters::ext, (updatable_htlc_options)) FC_REFLECT( graphene::chain::chain_parameters, (current_fees) @@ -116,4 +116,4 @@ FC_REFLECT( graphene::chain::chain_parameters, (account_fee_scale_bitshifts) (max_authority_depth) (extensions) - ) \ No newline at end of file + ) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 5399f2ff9f..77a394ca29 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -83,10 +83,10 @@ void set_committee_parameters(database_fixture* db_fixture) { // set the committee parameters db_fixture->db.modify(db_fixture->db.get_global_properties(), [](global_property_object& p) { - graphene::chain::committee_updatable_parameters params; - params.htlc_max_preimage_size = 1024; - params.htlc_max_timeout_secs = 60 * 60 * 24 * 28; - p.parameters.extensions.value.committee_updatable_options = params; + 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; }); } @@ -320,10 +320,10 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) 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; - graphene::chain::committee_updatable_parameters new_params; - new_params.htlc_max_preimage_size = 2048; - new_params.htlc_max_timeout_secs = 60 * 60 * 24 * 28; - uop.new_parameters.extensions.value.committee_updatable_options = new_params; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 2048; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + uop.new_parameters.extensions.value.updatable_htlc_options = new_params; cop.proposed_ops.emplace_back(uop); trx.operations.push_back(cop); db.push_transaction(trx); @@ -343,19 +343,19 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) } BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 1024u); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); generate_blocks(proposal_id_type()(db).expiration_time + 5); BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 1024u); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" ); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); generate_block(); // get the maintenance skip slots out of the way BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.committee_updatable_options->htlc_max_preimage_size, 2048u); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() From b5dae57b8473a1f112d505efccf502ac20c884ba Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 26 Oct 2018 16:34:08 -0500 Subject: [PATCH 033/161] beginning of hardfork protection --- .../chain/committee_member_evaluator.cpp | 4 + libraries/chain/hardfork.d/HTLC.hf | 8 +- libraries/chain/htlc_evaluator.cpp | 2 +- tests/tests/htlc_tests.cpp | 147 +++++++++++------- 4 files changed, 101 insertions(+), 60 deletions(-) diff --git a/libraries/chain/committee_member_evaluator.cpp b/libraries/chain/committee_member_evaluator.cpp index 4e7eb827e5..62e658303d 100644 --- a/libraries/chain/committee_member_evaluator.cpp +++ b/libraries/chain/committee_member_evaluator.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,9 @@ void_result committee_member_update_global_parameters_evaluator::do_evaluate(con { try { FC_ASSERT(trx_state->_is_proposed_trx); + FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME || !o.new_parameters.extensions.value.updatable_htlc_options.valid(), + "Unable to set HTLC parameters until hardfork." ); + return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } diff --git a/libraries/chain/hardfork.d/HTLC.hf b/libraries/chain/hardfork.d/HTLC.hf index 47df3c9f4a..2ab8857d78 100644 --- a/libraries/chain/hardfork.d/HTLC.hf +++ b/libraries/chain/hardfork.d/HTLC.hf @@ -1,4 +1,4 @@ -// htlc support for bitshares-core -#ifndef HTLC_ESCROW_TIME -#define HTLC_ESCROW_TIME (fc::time_point_sec( 1600000000 )) -#endif \ No newline at end of file +// HTLC implementation +#ifndef HARDFORK_HTLC_TIME +#define HARDFORK_HTLC_TIME (fc::time_point_sec( 1600000000 ) ) +#endif diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index b3f43d99ad..4b2bd5cd00 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -87,7 +87,7 @@ namespace graphene { { // convert incoming_preimage to an array unsigned char incoming_array[incoming_preimage.size()]; - for(int i = 0; i < incoming_preimage.size(); ++i) + for(unsigned i = 0; i < incoming_preimage.size(); ++i) incoming_array[i] = incoming_preimage[i]; T attempted_hash = T::hash( (char*)incoming_array, incoming_preimage.size()); if (attempted_hash.data_size() != valid_hash.size()) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 77a394ca29..dc73a28daa 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -90,9 +90,51 @@ void set_committee_parameters(database_fixture* db_fixture) }); } +void advance_past_hardfork(database_fixture* db_fixture) +{ + db_fixture->generate_blocks(HARDFORK_HTLC_TIME); + set_committee_parameters(db_fixture); + set_expiration(db_fixture->db, db_fixture->trx); +} + +BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) +{ + ACTORS((alice)(bob)); + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t key_size = 256; + std::vector pre_image(256); + generate_random_preimage(key_size, pre_image); + std::vector key_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + fc::time_point_sec expiration = db.head_block_time() + fc::seconds(60); + // Alice puts a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.destination = bob_id; + create_operation.epoch = expiration; + create_operation.key_hash = key_hash; + create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.key_size = key_size; + create_operation.source = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + } +} + BOOST_AUTO_TEST_CASE( htlc_expires ) { - set_committee_parameters(this); + advance_past_hardfork(this); ACTORS((alice)(bob)); @@ -123,13 +165,7 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - } catch (fc::exception& ex) - { - BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); - } + PUSH_TX(db, trx, ~0); trx.clear(); graphene::chain::signed_block blk = generate_block(); // can we assume that alice's transaction will be the only one in this block? @@ -155,7 +191,7 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) BOOST_AUTO_TEST_CASE( htlc_fulfilled ) { - set_committee_parameters(this); + advance_past_hardfork(this); ACTORS((alice)(bob)); @@ -185,13 +221,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - } catch (fc::exception& ex) - { - BOOST_FAIL( ex.to_detail_string(fc::log_level(fc::log_level::all)) ); - } + PUSH_TX(db, trx, ~0); trx.clear(); graphene::chain::signed_block blk = generate_block(); // can we assume that alice's transaction will be the only one in this block? @@ -212,13 +242,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) update_operation.preimage = pre_image; trx.operations.push_back(update_operation); sign(trx, bob_private_key); - try - { - PUSH_TX(db, trx, ~0); - } catch (fc::exception& ex) - { - BOOST_FAIL(ex.to_detail_string(fc::log_level(fc::log_level::all))); - } + PUSH_TX(db, trx, ~0); generate_block(); trx.clear(); } @@ -229,14 +253,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) update_operation.htlc_id = alice_htlc_id; trx.operations.push_back(update_operation); sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, ~0); - BOOST_FAIL("Should not allow Alice to reclaim funds after Bob already claimed them."); - } catch (fc::exception& ex) - { - // this should happen - } + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); generate_block(); trx.clear(); } @@ -247,7 +264,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) BOOST_AUTO_TEST_CASE( other_peoples_money ) { - set_committee_parameters(this); + advance_past_hardfork(this); ACTORS((alice)(bob)); @@ -275,16 +292,7 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, bob_private_key); - try - { - PUSH_TX(db, trx, database::skip_nothing); - BOOST_FAIL( "Bob stole money from Alice!" ); - } catch (fc::exception& ex) - { - // this is supposed to happen - //BOOST_TEST_MESSAGE("This is the error thrown (expected):"); - //BOOST_TEST_MESSAGE(ex.to_detail_string(fc::log_level(fc::log_level::all))); - } + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, database::skip_nothing), fc::exception); trx.clear(); } // now try the same but with Alice's signature (should work) @@ -299,21 +307,48 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); - try - { - PUSH_TX(db, trx, database::skip_nothing); - } catch (fc::exception& ex) - { - BOOST_FAIL( "Alice cannot create a contract!" ); - } + PUSH_TX(db, trx, database::skip_nothing); trx.clear(); } } BOOST_AUTO_TEST_CASE( set_htlc_params ) -{ try { - set_committee_parameters(this); +{ + { + // try to set committee parameters before hardfork + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db.get_global_properties().parameters, db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation cmuop; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 2048; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + cmuop.new_parameters.extensions.value.updatable_htlc_options = new_params; + cop.proposed_ops.emplace_back(cmuop); + trx.operations.push_back(cop); + // update with signatures + proposal_update_operation uop; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; + trx.operations.push_back(uop); + sign( trx, init_account_priv_key ); + db.push_transaction(trx); + BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); + generate_blocks(proposal_id_type()(db).expiration_time + 5); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + // the proposal should have failed + BOOST_CHECK(!db.get_global_properties().parameters.extensions.value.updatable_htlc_options.valid()); + trx.clear(); + } + + // now things should start working... + advance_past_hardfork(this); + proposal_id_type good_proposal_id; BOOST_TEST_MESSAGE( "Creating a proposal to change the max_preimage_size to 2048" ); { proposal_create_operation cop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time()); @@ -326,11 +361,13 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) uop.new_parameters.extensions.value.updatable_htlc_options = new_params; cop.proposed_ops.emplace_back(uop); trx.operations.push_back(cop); - db.push_transaction(trx); + graphene::chain::processed_transaction proc_trx =db.push_transaction(trx); + good_proposal_id = proc_trx.operation_results[0].get(); } BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); { proposal_update_operation uop; + uop.proposal = good_proposal_id; uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), get_account("init2").get_id(), get_account("init3").get_id(), @@ -339,14 +376,14 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) trx.operations.push_back(uop); sign( trx, init_account_priv_key ); db.push_transaction(trx); - BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); + BOOST_CHECK(good_proposal_id(db).is_authorized_to_execute(db)); } BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); - generate_blocks(proposal_id_type()(db).expiration_time + 5); + generate_blocks(good_proposal_id(db).expiration_time + 5); BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); @@ -356,6 +393,6 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); -} FC_LOG_AND_RETHROW() } +} BOOST_AUTO_TEST_SUITE_END() From bc48b3632118586a95ef596ac9203c242c3e716b Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 29 Oct 2018 08:56:04 -0500 Subject: [PATCH 034/161] Code cleanup and comments --- libraries/chain/assert_evaluator.cpp | 8 -- libraries/chain/htlc_evaluator.cpp | 4 +- .../include/graphene/chain/htlc_evaluator.hpp | 23 +++-- .../include/graphene/chain/htlc_object.hpp | 35 +++++--- .../graphene/chain/protocol/assert.hpp | 21 +---- .../include/graphene/chain/protocol/htlc.hpp | 85 ++++++++++++++----- libraries/chain/protocol/htlc.cpp | 10 ++- 7 files changed, 110 insertions(+), 76 deletions(-) diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp index febf15aeb3..3d1b11d257 100644 --- a/libraries/chain/assert_evaluator.cpp +++ b/libraries/chain/assert_evaluator.cpp @@ -49,14 +49,6 @@ struct predicate_evaluator { FC_ASSERT( block_summary_id_type( block_header::num_from_id( p.id ) & 0xffff )(db).block_id == p.id ); } - void operator()(const hash_operation_predicate& p)const - { - // TODO: Implement this method - } - void operator()(const timeout_operation_predicate& p)const - { - // TODO: Implement this method - } }; void_result assert_evaluator::do_evaluate( const assert_operation& o ) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 4b2bd5cd00..0fc587c7c5 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -30,7 +30,7 @@ namespace graphene { namespace chain { - optional get_committee_htlc_options(graphene::chain::database& db) + optional htlc_create_evaluator::get_committee_htlc_options(graphene::chain::database& db) { return db.get_global_properties().parameters.extensions.value.updatable_htlc_options; } @@ -83,7 +83,7 @@ namespace graphene { } template - bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) + bool htlc_update_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { // convert incoming_preimage to an array unsigned char incoming_array[incoming_preimage.size()]; diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp index 1181541cc7..0be6fb323a 100644 --- a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -29,21 +29,26 @@ namespace graphene { class htlc_create_evaluator : public evaluator { - public: - typedef htlc_create_operation operation_type; + public: + typedef htlc_create_operation operation_type; - void_result do_evaluate( const htlc_create_operation& o); - object_id_type do_apply( const htlc_create_operation& o); + void_result do_evaluate( const htlc_create_operation& o); + object_id_type do_apply( const htlc_create_operation& o); + private: + optional get_committee_htlc_options(graphene::chain::database& db); }; class htlc_update_evaluator : public evaluator { - public: - typedef htlc_update_operation operation_type; + public: + typedef htlc_update_operation operation_type; - void_result do_evaluate( const htlc_update_operation& o); - void_result do_apply( const htlc_update_operation& o); - const htlc_object* htlc_obj = nullptr; + void_result do_evaluate( const htlc_update_operation& o); + void_result do_apply( const htlc_update_operation& o); + const htlc_object* htlc_obj = nullptr; + private: + template + bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash); }; } // namespace graphene } // namespace graphene diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index a2ceac0271..324dbc706e 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -40,21 +40,25 @@ namespace graphene { namespace chain { }; /** - * Temporally save escrow transactions until funds are released or operation expired. + * @brief database object to store HTLCs + * + * This object is stored in the database while an HTLC is active. The HTLC will + * become inactive at expiration or when unlocked via the preimage. */ class htlc_object : public graphene::db::abstract_object { public: + // uniquely identify this object in the database static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_htlc_object_type; - account_id_type from; - account_id_type to; - asset amount; - fc::time_point_sec expiration; - asset pending_fee; - vector preimage_hash; + account_id_type from; + account_id_type to; + asset amount; + fc::time_point_sec expiration; + asset pending_fee; + vector preimage_hash; fc::enum_type preimage_hash_algorithm; - uint16_t preimage_size; + uint16_t preimage_size; }; struct by_from_id; @@ -64,11 +68,11 @@ namespace graphene { namespace chain { indexed_by< ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag< by_expiration >, member< htlc_object, fc::time_point_sec, &htlc_object::expiration > >, + ordered_non_unique< tag< by_expiration >, member< htlc_object, + fc::time_point_sec, &htlc_object::expiration > >, ordered_non_unique< tag< by_from_id >, - composite_key< htlc_object, - member< htlc_object, account_id_type, &htlc_object::from > + composite_key< htlc_object, member< htlc_object, account_id_type, &htlc_object::from > > > > @@ -81,7 +85,14 @@ namespace graphene { namespace chain { namespace fc { - template<> struct get_typename> { static const char* name() { return "fc::enum_type"; } }; + template<> + struct get_typename> + { + static const char* name() + { + return "fc::enum_type"; + } + }; } FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); diff --git a/libraries/chain/include/graphene/chain/protocol/assert.hpp b/libraries/chain/include/graphene/chain/protocol/assert.hpp index 2b01ddd262..c9f3b2774a 100644 --- a/libraries/chain/include/graphene/chain/protocol/assert.hpp +++ b/libraries/chain/include/graphene/chain/protocol/assert.hpp @@ -70,21 +70,6 @@ namespace graphene { namespace chain { bool validate()const{ return true; } }; - struct hash_operation_predicate - { - account_id_type pay_to; - std::vector key_hash; - int64_t key_size; - bool validate() const { return true; } - }; - - struct timeout_operation_predicate - { - account_id_type pay_to; - uint64_t epoch; - bool validate() const { return true; } - }; - /** * When defining predicates do not make the protocol dependent upon * implementation details. @@ -92,9 +77,7 @@ namespace graphene { namespace chain { typedef static_variant< account_name_eq_lit_predicate, asset_symbol_eq_lit_predicate, - block_id_predicate, - hash_operation_predicate, - timeout_operation_predicate + block_id_predicate > predicate; @@ -126,8 +109,6 @@ FC_REFLECT( graphene::chain::assert_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::account_name_eq_lit_predicate, (account_id)(name) ) FC_REFLECT( graphene::chain::asset_symbol_eq_lit_predicate, (asset_id)(symbol) ) FC_REFLECT( graphene::chain::block_id_predicate, (id) ) -FC_REFLECT( graphene::chain::hash_operation_predicate, (pay_to)(key_hash)(key_size) ) -FC_REFLECT( graphene::chain::timeout_operation_predicate, (pay_to)(epoch) ) FC_REFLECT_TYPENAME( graphene::chain::predicate ) FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths)(extensions) ) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 5b4e1f0e3b..1ba59198c6 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -36,37 +36,80 @@ namespace graphene { struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; - asset fee; // paid to network - account_id_type source; // where the held monies are to come from - account_id_type destination; // where the held monies will go if the preimage is provided - asset amount; // the amount to hold + // paid to network + asset fee; + // where the held monies are to come from + account_id_type source; + // where the held monies will go if the preimage is provided + account_id_type destination; + // the amount to hold + asset amount; + // hash algorithm used to create key_hash fc::enum_type hash_type - = graphene::chain::hash_algorithm::unknown; // hash algorithm used to create key_hash - std::vector key_hash; // the hash of the preimage - uint16_t key_size; // the size of the preimage - fc::time_point_sec epoch; // The time the funds will be returned to the source if not claimed - extensions_type extensions; // for future expansion + = graphene::chain::hash_algorithm::unknown; + // the hash of the preimage + std::vector key_hash; + // the size of the preimage + uint16_t key_size; + // The time the funds will be returned to the source if not claimed + fc::time_point_sec epoch; + // for future expansion + extensions_type extensions; + /*** + * @brief Does simple validation of this object + */ void validate()const; - void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(source); } + + /** + * @brief Determines who is required to sign + */ + void get_required_active_authorities( boost::container::flat_set& a )const + { + a.insert(source); + } + + /** + * @brief who will pay the fee + */ account_id_type fee_payer()const { return source; } }; struct htlc_update_operation : public base_operation { - struct fee_parameters_type { - uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; - }; - asset fee; // paid to network - htlc_id_type htlc_id; // the object we are attempting to update - account_id_type update_issuer; // who is attempting to update the transaction - std::vector preimage; // the preimage (not used if after epoch timeout) - extensions_type extensions; // for future expansion + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + + // paid to network + asset fee; + // the object we are attempting to update + htlc_id_type htlc_id; + // who is attempting to update the transaction + account_id_type update_issuer; + // the preimage (not used if after epoch timeout) + std::vector preimage; + // for future expansion + extensions_type extensions; + + /*** + * @brief Perform obvious checks to validate this object + */ + void validate()const; + + /*** + * @determines who should have signed this object + */ + void get_required_active_authorities( boost::container::flat_set& a )const + { + a.insert(update_issuer); + } - void validate()const; - void get_required_active_authorities( boost::container::flat_set& a )const{ a.insert(update_issuer); } - account_id_type fee_payer()const { return update_issuer; } + /** + * @brief Who is to pay the fee + */ + account_id_type fee_payer()const { return update_issuer; } }; } } diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 1c8aa44dde..0d433c57e2 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -26,11 +26,13 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { - //TODO: Implement - // signer must equal from + FC_ASSERT( source != destination ); + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( amount.amount > 0 ); } + void htlc_update_operation::validate()const { - //TODO: Implement - // if preimage is provided, size must equal the size stored in the contract + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( preimage.size() > 0 ); } } } From 5c5a0d71ad7a536c371686ec19c07eb75a2e2b3f Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 29 Oct 2018 17:23:41 -0500 Subject: [PATCH 035/161] Beginning of fee implementation --- .../include/graphene/chain/htlc_evaluator.hpp | 1 + .../include/graphene/chain/protocol/htlc.hpp | 20 ++++++++++++++++ tests/cli/main.cpp | 7 ++++++ tests/common/database_fixture.hpp | 4 ++++ tests/tests/htlc_tests.cpp | 23 ++++++++++++++----- 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp index 0be6fb323a..01bf87a0c0 100644 --- a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -34,6 +34,7 @@ namespace graphene { void_result do_evaluate( const htlc_create_operation& o); object_id_type do_apply( const htlc_create_operation& o); + private: optional get_committee_htlc_options(graphene::chain::database& db); }; diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 1ba59198c6..a7a63c663a 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -35,6 +35,7 @@ namespace graphene { { struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t price_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; // paid to network asset fee; @@ -74,6 +75,17 @@ namespace graphene { */ account_id_type fee_payer()const { return source; } + /**** + * @brief calculates the fee to be paid for this operation + */ + share_type calculate_fee(const fee_parameters_type& fee_params)const + { + // TODO: somehow base this on head block time instead of fc::time_point::now + uint32_t secs = 86400; // epoch.sec_since_epoch() - fc::time_point::now().sec_since_epoch(); + uint32_t days = secs / (60 * 60 * 24); + return fee_params.fee + (fee_params.price_per_day * days); + } + }; struct htlc_update_operation : public base_operation @@ -110,6 +122,14 @@ namespace graphene { * @brief Who is to pay the fee */ account_id_type fee_payer()const { return update_issuer; } + + /**** + * @brief calculates the fee to be paid for this operation + */ + share_type calculate_fee(const fee_parameters_type& fee_params)const + { + return fee_params.fee; + } }; } } diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 3af43dbc4f..4dd606ea4c 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -590,6 +590,13 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) 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); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 78f7b65d19..7ece7ab41d 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -330,6 +330,10 @@ struct database_fixture { void transfer( account_id_type from, account_id_type to, const asset& amount, const asset& fee = asset() ); void transfer( const account_object& from, const account_object& to, const asset& amount, const asset& fee = asset() ); void fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount ); + /** + * NOTE: This modifies the database directly. You will probably have to call this each time you + * finish creating a block + */ void enable_fees(); void change_fees( const flat_set< fee_parameters >& new_params, uint32_t new_scale = 0 ); void upgrade_to_lifetime_member( account_id_type account ); diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index dc73a28daa..e33cc254c9 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -195,9 +195,10 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) ACTORS((alice)(bob)); - int64_t init_balance(100000); + int64_t init_balance(1000000); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); uint16_t key_size = 256; std::vector pre_image(key_size); @@ -208,17 +209,19 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) // cler everything out generate_block(); trx.clear(); + enable_fees(); // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; - create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.amount = graphene::chain::asset( 100000 ); create_operation.destination = bob_id; create_operation.epoch = db.head_block_time() + fc::seconds(10); create_operation.key_hash = key_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; create_operation.source = alice_id; + create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); @@ -230,7 +233,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) } // verify funds on hold (make sure this can cover fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); // TODO: make sure Bob (or anyone) can see the details of the transaction @@ -240,9 +243,17 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) update_operation.update_issuer = bob_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, bob_private_key); - PUSH_TX(db, trx, ~0); + try + { + PUSH_TX(db, trx, ~0); + } + catch (fc::exception& ex) + { + BOOST_FAIL(ex.what()); + } generate_block(); trx.clear(); } @@ -258,8 +269,8 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) trx.clear(); } // verify funds end up in Bob's account - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 10000 ); - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 1000000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); } BOOST_AUTO_TEST_CASE( other_peoples_money ) From ac29e84c7b62ab9eab7de5b4d6f3c4edb171728b Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 30 Oct 2018 15:08:43 -0500 Subject: [PATCH 036/161] committee fee flexibility and bug fixes Modified method names to more closely match the spec. Fixed a misunderstanding on how fees work. Added template operation for finding the correct fee_parameter so that fees can more easily be adjusted within a proposal. --- libraries/chain/db_init.cpp | 3 +- libraries/chain/db_notify.cpp | 6 +- libraries/chain/htlc_evaluator.cpp | 93 ++++++++++++------- .../include/graphene/chain/htlc_evaluator.hpp | 21 +++-- .../chain/protocol/chain_parameters.hpp | 6 +- .../graphene/chain/protocol/fee_schedule.hpp | 6 ++ .../include/graphene/chain/protocol/htlc.hpp | 67 +++++++++++-- .../graphene/chain/protocol/operations.hpp | 3 +- libraries/chain/protocol/htlc.cpp | 5 +- libraries/wallet/wallet.cpp | 23 ++++- tests/tests/htlc_tests.cpp | 18 +--- 11 files changed, 185 insertions(+), 66 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index dd9ac41ebb..69d0f68f52 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -179,7 +179,8 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); - register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index d8f8c09ba3..ba8ff5f1ac 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -260,7 +260,11 @@ struct get_impacted_account_visitor { _impacted.insert( op.fee_payer() ); } - void operator()( const htlc_update_operation& op ) + void operator()( const htlc_redeem_operation& op ) + { + _impacted.insert( op.fee_payer() ); + } + void operator()( const htlc_extend_operation& op ) { _impacted.insert( op.fee_payer() ); } diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 0fc587c7c5..d84b8a50dd 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -30,7 +30,7 @@ namespace graphene { namespace chain { - optional htlc_create_evaluator::get_committee_htlc_options(graphene::chain::database& db) + optional get_committee_htlc_options(graphene::chain::database& db) { return db.get_global_properties().parameters.extensions.value.updatable_htlc_options; } @@ -83,7 +83,7 @@ namespace graphene { } template - bool htlc_update_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) + bool htlc_redeem_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { // convert incoming_preimage to an array unsigned char incoming_array[incoming_preimage.size()]; @@ -101,39 +101,70 @@ namespace graphene { return true; } - void_result htlc_update_evaluator::do_evaluate(const htlc_update_operation& o) + void_result htlc_redeem_evaluator::do_evaluate(const htlc_redeem_operation& o) { - htlc_obj = &db().get(o.htlc_id); - - // TODO: Use signatures to determine what to do, not whether preimage was provided - if (o.preimage.size() > 0) - { - FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); - FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); - - // see if the preimages match - bool match = false; - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha256) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::ripemd160) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha1) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - - FC_ASSERT(match, "Provided preimage does not generate correct hash."); - } - else - { - FC_ASSERT(db().head_block_time().sec_since_epoch() > htlc_obj->expiration.sec_since_epoch(), "Unable to reclaim until escrow expiration."); - } - return void_result(); + htlc_obj = &db().get(o.htlc_id); + + FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); + FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); + + // see if the preimages match + bool match = false; + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha256) + match = test_hash(o.preimage, htlc_obj->preimage_hash); + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::ripemd160) + match = test_hash(o.preimage, htlc_obj->preimage_hash); + if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha1) + match = test_hash(o.preimage, htlc_obj->preimage_hash); + + FC_ASSERT(match, "Provided preimage does not generate correct hash."); + return void_result(); } - void_result htlc_update_evaluator::do_apply(const htlc_update_operation& o) + void_result htlc_redeem_evaluator::do_apply(const htlc_redeem_operation& o) { - db().adjust_balance(htlc_obj->to, htlc_obj->amount); - db().remove(*htlc_obj); - return void_result(); + db().adjust_balance(htlc_obj->to, htlc_obj->amount); + db().remove(*htlc_obj); + return void_result(); } + + void_result htlc_extend_evaluator::do_evaluate(const htlc_extend_operation& o) + { + htlc_obj = &db().get(o.htlc_id); + + FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "HTLC has already expired."); + + return void_result(); + } + + void_result htlc_extend_evaluator::do_apply(const htlc_extend_operation& o) + { + db().modify(*htlc_obj, [&o](htlc_object& db_obj) { + db_obj.expiration = o.epoch; + }); + + return void_result(); + } + + + + + + + + + + + + + + + + + + + + + } // namespace chain } // namespace graphene diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp index 01bf87a0c0..7c6b641db5 100644 --- a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -34,22 +34,29 @@ namespace graphene { void_result do_evaluate( const htlc_create_operation& o); object_id_type do_apply( const htlc_create_operation& o); - - private: - optional get_committee_htlc_options(graphene::chain::database& db); }; - class htlc_update_evaluator : public evaluator + class htlc_redeem_evaluator : public evaluator { public: - typedef htlc_update_operation operation_type; + typedef htlc_redeem_operation operation_type; - void_result do_evaluate( const htlc_update_operation& o); - void_result do_apply( const htlc_update_operation& o); + void_result do_evaluate( const htlc_redeem_operation& o); + void_result do_apply( const htlc_redeem_operation& o); const htlc_object* htlc_obj = nullptr; private: template bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash); }; + + class htlc_extend_evaluator : public evaluator + { + public: + typedef htlc_extend_operation operation_type; + + void_result do_evaluate( const htlc_extend_operation& o); + void_result do_apply( const htlc_extend_operation& o); + const htlc_object* htlc_obj = nullptr; + }; } // namespace graphene } // namespace graphene diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index f155ecafb4..4d0863f8b5 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -83,7 +83,11 @@ namespace graphene { namespace chain { } } // graphene::chain -FC_REFLECT( graphene::chain::htlc_options, (max_timeout_secs) (max_preimage_size) ) +FC_REFLECT( graphene::chain::htlc_options, + (max_timeout_secs) + (max_preimage_size) +) + FC_REFLECT( graphene::chain::chain_parameters::ext, (updatable_htlc_options)) FC_REFLECT( graphene::chain::chain_parameters, diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index a08ee98a2c..3a054cf2ec 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -43,6 +43,12 @@ namespace graphene { namespace chain { FC_ASSERT( itr != parameters.end() ); return itr->template get(); } + typename Operation::fee_parameters_type& get(flat_set& parameters) + { + auto itr = parameters.find( typename Operation::fee_parameters_type() ); + FC_ASSERT( itr != parameters.end() ); + return itr->template get(); + } }; template<> diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index a7a63c663a..7ab9421aab 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -34,8 +34,8 @@ namespace graphene { struct htlc_create_operation : public base_operation { struct fee_parameters_type { - uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; - uint64_t price_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; // paid to network asset fee; @@ -83,12 +83,12 @@ namespace graphene { // TODO: somehow base this on head block time instead of fc::time_point::now uint32_t secs = 86400; // epoch.sec_since_epoch() - fc::time_point::now().sec_since_epoch(); uint32_t days = secs / (60 * 60 * 24); - return fee_params.fee + (fee_params.price_per_day * days); + return fee_params.fee + (fee_params.fee_per_day * days); } }; - struct htlc_update_operation : public base_operation + struct htlc_redeem_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; @@ -131,11 +131,62 @@ namespace graphene { return fee_params.fee; } }; + + struct htlc_extend_operation : public base_operation + { + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + + // paid to network + asset fee; + // the object we are attempting to update + htlc_id_type htlc_id; + // who is attempting to update the transaction + account_id_type update_issuer; + // the new expiry + fc::time_point_sec epoch; + // for future expansion + extensions_type extensions; + + /*** + * @brief Perform obvious checks to validate this object + */ + void validate()const; + + /*** + * @determines who should have signed this object + */ + void get_required_active_authorities( boost::container::flat_set& a )const + { + a.insert(update_issuer); + } + + /** + * @brief Who is to pay the fee + */ + account_id_type fee_payer()const { return update_issuer; } + + /**** + * @brief calculates the fee to be paid for this operation + */ + share_type calculate_fee(const fee_parameters_type& fee_params)const + { + // TODO: somehow base this on head block time instead of fc::time_point::now + uint32_t secs = 86400; // epoch.sec_since_epoch() - fc::time_point::now().sec_since_epoch(); + uint32_t days = secs / (60 * 60 * 24); + return fee_params.fee + (fee_params.fee_per_day * days); + } + }; } } -FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::htlc_update_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) +FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) -FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)(hash_type)) -FC_REFLECT( graphene::chain::htlc_update_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) +FC_REFLECT( graphene::chain::htlc_create_operation, + (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)(hash_type)) +FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) +FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(epoch)(extensions)) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index a666a844ab..2eded4a707 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -98,7 +98,8 @@ namespace graphene { namespace chain { asset_claim_pool_operation, asset_update_issuer_operation, htlc_create_operation, - htlc_update_operation + htlc_redeem_operation, + htlc_extend_operation > operation; /// @} // operations group diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 0d433c57e2..7d21d7ace4 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -31,8 +31,11 @@ namespace graphene { namespace chain { FC_ASSERT( amount.amount > 0 ); } - void htlc_update_operation::validate()const { + void htlc_redeem_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( preimage.size() > 0 ); } + void htlc_extend_operation::validate()const { + FC_ASSERT( fee.amount >= 0 ); + } } } diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index ae4fe270f8..7e7929b855 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1788,7 +1788,7 @@ class wallet_api_impl account_object issuer_obj = get_account(issuer); - htlc_update_operation update_op; + htlc_redeem_operation update_op; update_op.htlc_id = htlc_obj->id; update_op.update_issuer = issuer_obj.id; update_op.preimage = preimage; @@ -1804,7 +1804,26 @@ class wallet_api_impl signed_transaction htlc_extend_expiry ( string htlc_id, string issuer, const fc::time_point_sec& timeout, bool broadcast) { - throw std::logic_error("Not Implemented"); + try + { + FC_ASSERT( !self.is_locked() ); + fc::optional htlc_obj = get_htlc(htlc_id); + FC_ASSERT(htlc_obj, "Could not find HTLC matching ${htlc}", ("htlc", htlc_id)); + + account_object issuer_obj = get_account(issuer); + + htlc_extend_operation update_op; + update_op.htlc_id = htlc_obj->id; + update_op.update_issuer = issuer_obj.id; + update_op.epoch = timeout; + + signed_transaction tx; + tx.operations.push_back(update_op); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(timeout)(broadcast) ) } vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index e33cc254c9..38c55fd2ec 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -44,7 +44,6 @@ #include #include -#include #include #include @@ -83,10 +82,14 @@ void set_committee_parameters(database_fixture* db_fixture) { // set the committee parameters db_fixture->db.modify(db_fixture->db.get_global_properties(), [](global_property_object& p) { + // htlc options 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; + // htlc operation fees + p.parameters.current_fees->get().fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + p.parameters.current_fees->get().fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; }); } @@ -239,7 +242,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) // send an update operation to claim the funds { - graphene::chain::htlc_update_operation update_operation; + graphene::chain::htlc_redeem_operation update_operation; update_operation.update_issuer = bob_id; update_operation.htlc_id = alice_htlc_id; update_operation.preimage = pre_image; @@ -257,17 +260,6 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) generate_block(); trx.clear(); } - // verify Alice cannot execute the contract after the fact - { - graphene::chain::htlc_update_operation update_operation; - update_operation.update_issuer = alice_id; - update_operation.htlc_id = alice_htlc_id; - trx.operations.push_back(update_operation); - sign(trx, alice_private_key); - GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); - generate_block(); - trx.clear(); - } // verify funds end up in Bob's account BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 1000000 ); BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); From 50f65846ceae12aae10ccca18a9d37eff882a41c Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 30 Oct 2018 16:07:50 -0500 Subject: [PATCH 037/161] Calculate fees based on # days --- libraries/chain/htlc_evaluator.cpp | 11 ++++------ .../include/graphene/chain/protocol/htlc.hpp | 18 +++++++---------- .../wallet/include/graphene/wallet/wallet.hpp | 6 +++--- libraries/wallet/wallet.cpp | 20 +++++++++---------- tests/cli/main.cpp | 2 +- tests/tests/htlc_tests.cpp | 18 ++++++++--------- 6 files changed, 33 insertions(+), 42 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index d84b8a50dd..15eb029bd4 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -45,12 +45,9 @@ namespace graphene { FC_ASSERT(htlc_options, "HTLC Committee options are not set."); // make sure the expiration is reasonable - FC_ASSERT( o.epoch.sec_since_epoch() < db().head_block_time().sec_since_epoch() - + htlc_options->max_timeout_secs, - "HTLC Timeout exceeds allowed length" ); + FC_ASSERT( o.seconds_in_force < htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable - FC_ASSERT( o.key_size < htlc_options->max_preimage_size, - "HTLC preimage length exceeds allowed length" ); + FC_ASSERT( o.key_size < htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure we have a hash algorithm set FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); // make sure sender has the funds for the HTLC @@ -74,7 +71,7 @@ namespace graphene { esc.amount = o.amount; esc.preimage_hash = o.key_hash; esc.preimage_size = o.key_size; - esc.expiration = o.epoch; + esc.expiration = db().head_block_time() + o.seconds_in_force; esc.preimage_hash_algorithm = o.hash_type; }); return esc.id; @@ -140,7 +137,7 @@ namespace graphene { void_result htlc_extend_evaluator::do_apply(const htlc_extend_operation& o) { db().modify(*htlc_obj, [&o](htlc_object& db_obj) { - db_obj.expiration = o.epoch; + db_obj.expiration = fc::time_point_sec(db_obj.expiration.sec_since_epoch() + o.seconds_to_add); }); return void_result(); diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 7ab9421aab..503398ba27 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -53,7 +53,7 @@ namespace graphene { // the size of the preimage uint16_t key_size; // The time the funds will be returned to the source if not claimed - fc::time_point_sec epoch; + uint32_t seconds_in_force; // for future expansion extensions_type extensions; @@ -80,9 +80,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - // TODO: somehow base this on head block time instead of fc::time_point::now - uint32_t secs = 86400; // epoch.sec_since_epoch() - fc::time_point::now().sec_since_epoch(); - uint32_t days = secs / (60 * 60 * 24); + uint32_t days = seconds_in_force / (60 * 60 * 24); return fee_params.fee + (fee_params.fee_per_day * days); } @@ -145,8 +143,8 @@ namespace graphene { htlc_id_type htlc_id; // who is attempting to update the transaction account_id_type update_issuer; - // the new expiry - fc::time_point_sec epoch; + // how much to add + uint32_t seconds_to_add; // for future expansion extensions_type extensions; @@ -173,9 +171,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - // TODO: somehow base this on head block time instead of fc::time_point::now - uint32_t secs = 86400; // epoch.sec_since_epoch() - fc::time_point::now().sec_since_epoch(); - uint32_t days = secs / (60 * 60 * 24); + uint32_t days = seconds_to_add / (60 * 60 * 24); return fee_params.fee + (fee_params.fee_per_day * days); } }; @@ -187,6 +183,6 @@ FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) FC_REFLECT( graphene::chain::htlc_create_operation, - (fee)(source)(destination)(amount)(key_hash)(key_size)(epoch)(extensions)(hash_type)) + (fee)(source)(destination)(amount)(key_hash)(key_size)(seconds_in_force)(extensions)(hash_type)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) -FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(epoch)(extensions)) +FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 2fd8ef1cb1..d8cf519d71 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1465,12 +1465,12 @@ class wallet_api * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160 or SHA256. * @param preimage_hash the hash of the preimage * @param preimage_size the size of the preimage in bytes - * @param timelock when the time lock expires + * @param seconds_in_force when the time lock expires * @param broadcast true if you wish to broadcast the transaction */ signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, - const fc::time_point_sec& timelock, bool broadcast = false ); + const uint32_t seconds_in_force, bool broadcast = false ); /**** * Update a hashed time lock contract @@ -1490,7 +1490,7 @@ class wallet_api * @param timelock the new time of expiry * @param broadcast true to broadcast to the network */ - signed_transaction htlc_extend_expiry(string htlc_id, string issuer, const fc::time_point_sec& timelock, + signed_transaction htlc_extend_expiry(string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast = false); /** diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 7e7929b855..0b709add69 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1744,7 +1744,7 @@ class wallet_api_impl signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, - const fc::time_point_sec& timelock, bool broadcast = false ) + const uint32_t seconds_in_force, bool broadcast = false ) { try { @@ -1756,7 +1756,7 @@ class wallet_api_impl create_op.source = get_account(source).id; create_op.destination = get_account(destination).id; create_op.amount = asset_obj->amount_from_string(amount); - create_op.epoch = timelock; + create_op.seconds_in_force = seconds_in_force; create_op.key_hash = preimage_hash; create_op.key_size = preimage_size; if ( "SHA256" == hash_algorithm ) @@ -1775,7 +1775,7 @@ class wallet_api_impl return sign_transaction(tx, broadcast); } FC_CAPTURE_AND_RETHROW( (source)(destination)(amount)(asset_symbol)(hash_algorithm) - (preimage_hash)(preimage_size)(timelock)(broadcast) ) + (preimage_hash)(preimage_size)(seconds_in_force)(broadcast) ) } signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) @@ -1802,7 +1802,7 @@ class wallet_api_impl } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(preimage)(broadcast) ) } - signed_transaction htlc_extend_expiry ( string htlc_id, string issuer, const fc::time_point_sec& timeout, bool broadcast) + signed_transaction htlc_extend_expiry ( string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast) { try { @@ -1815,7 +1815,7 @@ class wallet_api_impl htlc_extend_operation update_op; update_op.htlc_id = htlc_obj->id; update_op.update_issuer = issuer_obj.id; - update_op.epoch = timeout; + update_op.seconds_to_add = seconds_to_add; signed_transaction tx; tx.operations.push_back(update_op); @@ -1823,7 +1823,7 @@ class wallet_api_impl tx.validate(); return sign_transaction(tx, broadcast); - } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(timeout)(broadcast) ) + } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(seconds_to_add)(broadcast) ) } vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) @@ -3105,10 +3105,10 @@ uint64_t wallet_api::get_asset_count()const signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, - const fc::time_point_sec& timelock, bool broadcast) + const uint32_t seconds_in_force, bool broadcast) { return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, - timelock, broadcast); + seconds_in_force, broadcast); } graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const @@ -3122,10 +3122,10 @@ signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string iss return my->htlc_redeem(htlc_id, issuer, preimage, broadcast); } -signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const fc::time_point_sec& timelock, +signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, bool broadcast) { - return my->htlc_extend_expiry(htlc_id, issuer, timelock, broadcast); + return my->htlc_extend_expiry(htlc_id, issuer, seconds_to_add, broadcast); } vector wallet_api::get_account_history(string name, int limit)const diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 4dd606ea4c..6af62e98fe 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -682,7 +682,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) std::vector hash(preimage_md.data_size()); for(size_t i = 0; i < preimage_md.data_size(); ++i) hash[i] = preimage_md.data()[i]; - fc::time_point_sec timelock = fc::time_point::now() + fc::days(1); + uint32_t timelock = fc::days(1).to_seconds(); graphene::chain::signed_transaction result_tx = con.wallet_api_ptr->htlc_prepare("alice", "bob", "BTS", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 38c55fd2ec..13b0d49390 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -117,14 +117,13 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) // cler everything out generate_block(); trx.clear(); - fc::time_point_sec expiration = db.head_block_time() + fc::seconds(60); // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = expiration; + create_operation.seconds_in_force = 60; create_operation.key_hash = key_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; @@ -154,14 +153,13 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) // cler everything out generate_block(); trx.clear(); - fc::time_point_sec expiration = db.head_block_time() + fc::seconds(60); // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = expiration; + create_operation.seconds_in_force = 60; create_operation.key_hash = key_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; @@ -186,9 +184,9 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) BOOST_CHECK(htlc); // let it expire (wait for timeout) - generate_blocks(expiration + fc::seconds(120) ); - // verify funds return (what about fees?) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 100000 ); + generate_blocks(fc::time_point_sec(120) ); + // verify funds return (minus the fees) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); // verify Bob cannot execute the contract after the fact } @@ -219,7 +217,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.amount = graphene::chain::asset( 100000 ); create_operation.destination = bob_id; - create_operation.epoch = db.head_block_time() + fc::seconds(10); + create_operation.seconds_in_force = 86400; create_operation.key_hash = key_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; @@ -289,7 +287,7 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = db.head_block_time() + fc::seconds(3); + create_operation.seconds_in_force = 3; create_operation.key_hash = key_hash; create_operation.key_size = key_size; create_operation.source = alice_id; @@ -303,7 +301,7 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.epoch = db.head_block_time() + fc::seconds(3); + create_operation.seconds_in_force = 3; create_operation.key_hash = key_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.key_size = key_size; From b80feab5154848144b97f32689139516dc843861 Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Tue, 6 Nov 2018 16:07:46 +0300 Subject: [PATCH 038/161] Distribute Asset Market Fees to Referral Program - initial implementation - unit tests: added asset_rewards_test, modified create_advanced_uia --- libraries/app/database_api.cpp | 27 ++ .../app/include/graphene/app/database_api.hpp | 3 + libraries/chain/asset_evaluator.cpp | 12 + libraries/chain/db_balance.cpp | 35 +++ libraries/chain/db_market.cpp | 74 +++++- libraries/chain/hardfork.d/CORE_1268.hf | 4 + .../chain/include/graphene/chain/database.hpp | 3 + .../graphene/chain/protocol/asset_ops.hpp | 13 +- .../graphene/chain/vesting_balance_object.hpp | 18 +- .../wallet/include/graphene/wallet/wallet.hpp | 8 + libraries/wallet/wallet.cpp | 23 ++ tests/common/database_fixture.cpp | 33 ++- tests/common/database_fixture.hpp | 12 +- tests/tests/market_fee_sharing_tests.cpp | 251 ++++++++++++++++++ tests/tests/uia_tests.cpp | 11 + 15 files changed, 510 insertions(+), 17 deletions(-) create mode 100644 libraries/chain/hardfork.d/CORE_1268.hf create mode 100644 tests/tests/market_fee_sharing_tests.cpp diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index acb90c8af5..59a46fac79 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -96,6 +96,7 @@ class database_api_impl : public std::enable_shared_from_this vector get_balance_objects( const vector
& addrs )const; vector get_vested_balances( const vector& objs )const; vector get_vesting_balances( const std::string account_id_or_name )const; + vector get_mfs_vesting_balances( const std::string account_id_or_name )const; // Assets vector> get_assets(const vector& asset_ids)const; @@ -1026,6 +1027,11 @@ vector database_api::get_vesting_balances( const std::st return my->get_vesting_balances( account_id_or_name ); } +vector database_api::get_mfs_vesting_balances( const std::string account_id_or_name )const +{ + return my->get_mfs_vesting_balances( account_id_or_name ); +} + vector database_api_impl::get_vesting_balances( const std::string account_id_or_name )const { try @@ -1042,6 +1048,27 @@ vector database_api_impl::get_vesting_balances( const st FC_CAPTURE_AND_RETHROW( (account_id_or_name) ); } +vector database_api_impl::get_mfs_vesting_balances( const std::string account_id_or_name )const +{ + try + { + const account_id_type account_id = get_account_from_string(account_id_or_name)->id; + vector result; + + auto& vesting_balances = _db.get_index_type().indices().get(); + auto key = boost::make_tuple(account_id, vesting_balance_type::market_fee_sharing); + auto mfs_vesting_range = vesting_balances.equal_range(key); + + std::for_each(mfs_vesting_range.first, mfs_vesting_range.second, + [&result](const vesting_balance_object& balance) { + result.emplace_back(balance); + }); + + return result; + } + FC_CAPTURE_AND_RETHROW( (account_id_or_name) ); +} + ////////////////////////////////////////////////////////////////////// // // // Assets // diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 506b427597..601f6d67ed 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -364,6 +364,8 @@ class database_api vector get_vesting_balances( const std::string account_id_or_name )const; + vector get_mfs_vesting_balances( const std::string account_id_or_name )const; + /** * @brief Get the total number of accounts registered with the blockchain */ @@ -784,6 +786,7 @@ FC_API(graphene::app::database_api, (get_balance_objects) (get_vested_balances) (get_vesting_balances) + (get_mfs_vesting_balances) // Assets (get_assets) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 9194a02d25..deb838a347 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -45,6 +45,12 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o FC_ASSERT( op.common_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); FC_ASSERT( op.common_options.blacklist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); + if( d.head_block_time() < HARDFORK_1268_TIME ) + { + FC_ASSERT(!op.common_options.additional_options.value.null_ext.valid()); + FC_ASSERT(!op.common_options.additional_options.value.reward_percent.valid()); + } + // Check that all authorities do exist for( auto id : op.common_options.whitelist_authorities ) d.get_object(id); @@ -277,6 +283,12 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) validate_new_issuer( d, a, *o.new_issuer ); } + if ( o.new_options.additional_options.value.reward_percent.valid() ) + { + FC_ASSERT( d.head_block_time() >= HARDFORK_1268_TIME, + "Referrer percent is only available after HARDFORK_1268_TIME!"); + } + if( (d.head_block_time() < HARDFORK_572_TIME) || (a.dynamic_asset_data_id(d).current_supply != 0) ) { // new issuer_permissions must be subset of old issuer permissions diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index cf58cb432d..d28c0f51a0 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -80,6 +81,40 @@ void database::adjust_balance(account_id_type account, asset delta ) } FC_CAPTURE_AND_RETHROW( (account)(delta) ) } +void database::deposit_market_fee_vesting_balance(const account_id_type &account_id, const asset &delta) +{ try { + FC_ASSERT( delta.amount > 0, "Invalid negative value for balance"); + + if( delta.amount == 0 ) + return; + + auto& vesting_balances = get_index_type().indices().get(); + auto market_vesting_balances = vesting_balances.equal_range(boost::make_tuple(account_id, vesting_balance_type::market_fee_sharing)); + auto market_balance = boost::range::find_if(market_vesting_balances, + [&delta](const vesting_balance_object& vbo) { return vbo.balance.asset_id == delta.asset_id;} + ); + + if(market_balance == boost::end(market_vesting_balances) ) + { + create([&](vesting_balance_object &vbo) { + vbo.owner = account_id; + vbo.balance = delta; + vbo.balance_type = vesting_balance_type::market_fee_sharing; + cdd_vesting_policy policy; + policy.vesting_seconds = { 0 }; + policy.coin_seconds_earned = vbo.balance.amount.value; + policy.coin_seconds_earned_last_update = head_block_time(); + vbo.policy = policy; + }); + } else { + modify( *market_balance, [&]( vesting_balance_object& vbo ) + { + vbo.deposit_vested(head_block_time(), delta); + }); + } + +} FC_CAPTURE_AND_RETHROW( (account_id)(delta) ) } + optional< vesting_balance_id_type > database::deposit_lazy_vesting( const optional< vesting_balance_id_type >& ovbid, share_type amount, uint32_t req_vesting_seconds, diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 6b8f67ea1c..e56ae2ad77 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -31,7 +31,17 @@ #include -namespace graphene { namespace chain { +namespace graphene { namespace chain { namespace detail { + + fc::uint128 calculate_percent(const share_type& value, uint16_t percent) + { + fc::uint128 a(value.value); + a *= percent; + a /= GRAPHENE_100_PERCENT; + return a; + } + +} //detail /** * All margin positions are force closed at the swan price @@ -775,7 +785,12 @@ bool database::fill_limit_order( const limit_order_object& order, const asset& p const account_object& seller = order.seller(*this); const asset_object& recv_asset = receives.asset_id(*this); - auto issuer_fees = pay_market_fees( recv_asset, receives ); + //auto issuer_fees = pay_market_fees( recv_asset, receives ); + //auto issuer_fees = pay_market_fees(seller, recv_asset, receives); + auto issuer_fees = ( head_block_time() < HARDFORK_1268_TIME ) ? + pay_market_fees(recv_asset, receives) : + pay_market_fees(seller, recv_asset, receives); + pay_order( seller, receives - issuer_fees, pays ); assert( pays.asset_id != receives.asset_id ); @@ -1109,10 +1124,8 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass if( trade_asset.options.market_fee_percent == 0 ) return trade_asset.amount(0); - fc::uint128 a(trade_amount.amount.value); - a *= trade_asset.options.market_fee_percent; - a /= GRAPHENE_100_PERCENT; - asset percent_fee = trade_asset.amount(a.to_uint64()); + auto value = detail::calculate_percent(trade_amount.amount, trade_asset.options.market_fee_percent); + asset percent_fee = trade_asset.amount(value.to_uint64()); if( percent_fee.amount > trade_asset.options.max_market_fee ) percent_fee.amount = trade_asset.options.max_market_fee; @@ -1138,4 +1151,53 @@ asset database::pay_market_fees( const asset_object& recv_asset, const asset& re return issuer_fees; } +asset database::pay_market_fees(const account_object& seller, const asset_object& recv_asset, const asset& receives ) +{ + FC_ASSERT( head_block_time() >= HARDFORK_1268_TIME ); + + const auto issuer_fees = calculate_market_fee( recv_asset, receives ); + + assert(issuer_fees <= receives ); + + //Don't dirty undo state if not actually collecting any fees + if( issuer_fees.amount > 0 ) + { + // calculate and pay rewards + asset reward = recv_asset.amount(0); + + const auto reward_percent = recv_asset.options.additional_options.value.reward_percent; + + if ( reward_percent && *reward_percent ) + { + const auto reward_value = detail::calculate_percent(issuer_fees.amount, *reward_percent); + + if ( reward_value > 0 ) + { + reward = recv_asset.amount(reward_value.to_uint64()); + + assert( reward < issuer_fees ); + // cut referrer percent from reward + const auto referrer_rewards_percentage = seller.referrer_rewards_percentage; + const auto referrer_rewards_value = detail::calculate_percent(reward.amount, referrer_rewards_percentage); + + auto registrar_reward = reward; + if ( referrer_rewards_value > 0 ) + { + const asset referrer_reward = recv_asset.amount(referrer_rewards_value.to_uint64()); + registrar_reward -= referrer_reward; + deposit_market_fee_vesting_balance(seller.referrer, referrer_reward); + } + deposit_market_fee_vesting_balance(seller.registrar, registrar_reward); + } + } + + const auto& recv_dyn_data = recv_asset.dynamic_asset_data_id(*this); + modify( recv_dyn_data, [&]( asset_dynamic_data_object& obj ){ + obj.accumulated_fees += issuer_fees.amount - reward.amount; + }); + } + + return issuer_fees; +} + } } diff --git a/libraries/chain/hardfork.d/CORE_1268.hf b/libraries/chain/hardfork.d/CORE_1268.hf new file mode 100644 index 0000000000..faae771f56 --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_1268.hf @@ -0,0 +1,4 @@ +// #1268 Distribute Asset Market Fees to Referral Program +#ifndef HARDFORK_1268_TIME +#define HARDFORK_1268_TIME (fc::time_point_sec( 1530705600 )) // Wednesday, July 4, 2018 12:00:00 PM +#endif \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index f0fb8e11c6..f1d2382ac1 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -303,6 +303,8 @@ namespace graphene { namespace chain { */ void adjust_balance(account_id_type account, asset delta); + void deposit_market_fee_vesting_balance(const account_id_type &account, const asset &delta); + /** * @brief Helper to make lazy deposit to CDD VBO. * @@ -395,6 +397,7 @@ namespace graphene { namespace chain { asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount); asset pay_market_fees( const asset_object& recv_asset, const asset& receives ); + asset pay_market_fees( const account_object& seller, const asset_object& recv_asset, const asset& receives ); ///@{ diff --git a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp index 3a045a30c9..a36a4e6446 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp @@ -27,6 +27,13 @@ namespace graphene { namespace chain { + struct additional_asset_options + { + fc::optional null_ext; + fc::optional reward_percent; + }; + typedef extension additional_asset_options_t; + bool is_valid_symbol( const string& symbol ); /** @@ -75,7 +82,7 @@ namespace graphene { namespace chain { * size of description. */ string description; - extensions_type extensions; + additional_asset_options_t additional_options; /// Perform internal consistency checks. /// @throws fc::exception if any check fails @@ -523,7 +530,7 @@ FC_REFLECT( graphene::chain::asset_options, (whitelist_markets) (blacklist_markets) (description) - (extensions) + (additional_options) ) FC_REFLECT( graphene::chain::bitasset_options, (feed_lifetime_sec) @@ -535,7 +542,7 @@ FC_REFLECT( graphene::chain::bitasset_options, (extensions) ) - +FC_REFLECT( graphene::chain::additional_asset_options, (null_ext)(reward_percent) ) FC_REFLECT( graphene::chain::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) ) FC_REFLECT( graphene::chain::asset_global_settle_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::asset_settle_operation::fee_parameters_type, (fee) ) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index 210c6c5870..fc5883994b 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -124,6 +125,9 @@ namespace graphene { namespace chain { cdd_vesting_policy > vesting_policy; + enum class vesting_balance_type { unspecified, + worker, + market_fee_sharing }; /** * Vesting balance object is a balance that is locked by the blockchain for a period of time. */ @@ -140,6 +144,8 @@ namespace graphene { namespace chain { asset balance; /// The vesting policy stores details on when funds vest, and controls when they may be withdrawn vesting_policy policy; + /// type of the vesting balance + vesting_balance_type balance_type = vesting_balance_type::unspecified; vesting_balance_object() {} @@ -171,12 +177,22 @@ namespace graphene { namespace chain { * @ingroup object_index */ struct by_account; + struct by_vesting_type; + typedef multi_index_container< vesting_balance_object, indexed_by< - ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, member< object, object_id_type, &object::id > + >, ordered_non_unique< tag, member + >, + ordered_non_unique< tag, + composite_key< + vesting_balance_object, + member, + member + > > > > vesting_balance_multi_index_type; diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index ca13357f0c..4f059b386d 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1455,6 +1455,13 @@ class wallet_api */ vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ); + /** List account's market fee sharing vesting balances. + * Each account can have multiple market fee sharing vesting balances. + * @param account_name_or_id the name or id of the account whose balances you want + * @returns a list of the given account's market fee sharing vesting balances + */ + vector< vesting_balance_object_with_info > get_mfs_vesting_balances(string account_name_or_id); + /** * Withdraw a vesting balance. * @@ -1799,6 +1806,7 @@ FC_API( graphene::wallet::wallet_api, (create_worker) (update_worker_votes) (get_vesting_balances) + (get_mfs_vesting_balances) (withdraw_vesting) (vote_for_committee_member) (vote_for_witness) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index ac33c180e9..04027083ef 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1765,6 +1765,24 @@ class wallet_api_impl } FC_CAPTURE_AND_RETHROW( (account_name) ) } + vector get_mfs_vesting_balances(string account_name_or_id) const + { try { + fc::time_point_sec now = _remote_db->get_dynamic_global_properties().time; + + auto account = get_account(account_name_or_id); + auto always_id = account_id_to_string(account.id); + + vector vbos = _remote_db->get_mfs_vesting_balances(always_id); + + std::vector result; + for (const vesting_balance_object& vbo : vbos) + { + result.emplace_back(vbo, now); + } + return result; + } FC_CAPTURE_AND_RETHROW( (account_name_or_id) ) + } + signed_transaction withdraw_vesting( string witness_name, string amount, @@ -3694,6 +3712,11 @@ vector< vesting_balance_object_with_info > wallet_api::get_vesting_balances( str return my->get_vesting_balances( account_name ); } +vector wallet_api::get_mfs_vesting_balances(string account_name_or_id) +{ + return my->get_mfs_vesting_balances(account_name_or_id); +} + signed_transaction wallet_api::withdraw_vesting( string witness_name, string amount, diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 534c03de33..e50cf19cdb 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -23,6 +23,7 @@ */ #include #include +#include #include #include @@ -55,7 +56,7 @@ namespace graphene { namespace chain { using std::cout; using std::cerr; -database_fixture::database_fixture() +database_fixture::database_fixture(const fc::time_point_sec &initial_timestamp) : app(), db( *app.chain_database() ) { try { @@ -75,7 +76,7 @@ database_fixture::database_fixture() boost::program_options::variables_map options; - genesis_state.initial_timestamp = time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP ); + genesis_state.initial_timestamp = initial_timestamp; genesis_state.initial_active_witnesses = 10; for( unsigned int i = 0; i < genesis_state.initial_active_witnesses; ++i ) @@ -509,8 +510,9 @@ const asset_object& database_fixture::create_user_issued_asset( const string& na return db.get(ptx.operation_results[0].get()); } -const asset_object& database_fixture::create_user_issued_asset( const string& name, const account_object& issuer, uint16_t flags, - const price& core_exchange_rate, uint16_t precision) +const asset_object& database_fixture::create_user_issued_asset( const string& name, const account_object& issuer, + uint16_t flags, const price& core_exchange_rate, + uint16_t precision, uint16_t market_fee_percent) { asset_create_operation creator; creator.issuer = issuer.id; @@ -522,6 +524,7 @@ const asset_object& database_fixture::create_user_issued_asset( const string& na creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; creator.common_options.flags = flags; creator.common_options.issuer_permissions = flags; + creator.common_options.market_fee_percent = market_fee_percent; trx.operations.clear(); trx.operations.push_back(std::move(creator)); set_expiration( db, trx ); @@ -723,6 +726,9 @@ const limit_order_object* database_fixture::create_sell_order( const account_obj const time_point_sec order_expiration, const price& fee_core_exchange_rate ) { + set_expiration( db, trx ); + trx.operations.clear(); + limit_order_create_operation buy_order; buy_order.seller = user.id; buy_order.amount_to_sell = amount; @@ -1120,6 +1126,25 @@ int64_t database_fixture::get_balance( const account_object& account, const asse return db.get_balance(account.get_id(), a.get_id()).amount.value; } +int64_t database_fixture::get_market_fee_reward( account_id_type account, asset_id_type asset_type)const +{ + auto& vesting_balances = db.get_index_type().indices().get(); + auto market_vesting_balances = vesting_balances.equal_range(boost::make_tuple(account, vesting_balance_type::market_fee_sharing)); + auto market_balance = boost::range::find_if(market_vesting_balances, + [&asset_type](const vesting_balance_object& vbo) { return vbo.balance.asset_id == asset_type;} + ); + + FC_ASSERT( market_balance != boost::end(market_vesting_balances) ); + + auto allowed_to_withdraw = market_balance->get_allowed_withdraw(db.head_block_time()); + return allowed_to_withdraw.amount.value; +} + +int64_t database_fixture::get_market_fee_reward( const account_object& account, const asset_object& a )const +{ + return get_market_fee_reward(account.get_id(), a.get_id()); +} + vector< operation_history_object > database_fixture::get_operation_history( account_id_type account_id )const { vector< operation_history_object > result; diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 78f7b65d19..109df46cc8 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -156,7 +156,7 @@ extern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP; #define ACTOR(name) \ PREP_ACTOR(name) \ - const auto& name = create_account(BOOST_PP_STRINGIZE(name), name ## _public_key); \ + const auto name = create_account(BOOST_PP_STRINGIZE(name), name ## _public_key); \ graphene::chain::account_id_type name ## _id = name.id; (void)name ## _id; #define GET_ACTOR(name) \ @@ -187,7 +187,8 @@ struct database_fixture { bool skip_key_index_test = false; uint32_t anon_acct_count; - database_fixture(); + database_fixture(const fc::time_point_sec &initial_timestamp = + fc::time_point_sec(GRAPHENE_TESTING_GENESIS_TIMESTAMP)); ~database_fixture(); static fc::ecc::private_key generate_private_key(string seed); @@ -284,7 +285,8 @@ struct database_fixture { const account_object& issuer, uint16_t flags, const price& core_exchange_rate = price(asset(1, asset_id_type(1)), asset(1)), - uint16_t precision = 2 /* traditional precision for tests */); + uint16_t precision = 2 /* traditional precision for tests */, + uint16_t market_fee_percent = 0); void issue_uia( const account_object& recipient, asset amount ); void issue_uia( account_id_type recipient_id, asset amount ); @@ -343,6 +345,10 @@ struct database_fixture { void print_joint_market( const string& syma, const string& symb )const; int64_t get_balance( account_id_type account, asset_id_type a )const; int64_t get_balance( const account_object& account, const asset_object& a )const; + + int64_t get_market_fee_reward( account_id_type account, asset_id_type asset )const; + int64_t get_market_fee_reward( const account_object& account, const asset_object& asset )const; + vector< operation_history_object > get_operation_history( account_id_type account_id )const; vector< graphene::market_history::order_history_object > get_market_order_history( asset_id_type a, asset_id_type b )const; }; diff --git a/tests/tests/market_fee_sharing_tests.cpp b/tests/tests/market_fee_sharing_tests.cpp new file mode 100644 index 0000000000..a36f8c7e30 --- /dev/null +++ b/tests/tests/market_fee_sharing_tests.cpp @@ -0,0 +1,251 @@ +#include +//#include +//#include + +#include +#include +#include +#include + + +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; + +namespace fc +{ + template + std::basic_ostream& operator<<(std::basic_ostream& os, safe const& sf) + { + os << sf.value; + return os; + } +} + +struct reward_database_fixture : database_fixture +{ + reward_database_fixture() + : database_fixture(HARDFORK_1268_TIME - 100) + { + } + + void update_asset( const account_id_type& issuer_id, + const fc::ecc::private_key& private_key, + const asset_id_type& asset_id, + uint16_t reward_percent ) + { + asset_update_operation op; + op.issuer = issuer_id; + op.asset_to_update = asset_id; + op.new_options = asset_id(db).options; + op.new_options.additional_options.value.reward_percent = reward_percent; + + signed_transaction tx; + tx.operations.push_back( op ); + db.current_fee_schedule().set_fee( tx.operations.back() ); + set_expiration( db, tx ); + sign( tx, private_key ); + PUSH_TX( db, tx ); + } + + void generate_blocks_past_reward_hardfork() + { + database_fixture::generate_blocks( HARDFORK_1268_TIME ); + database_fixture::generate_block(); + } +}; + +BOOST_FIXTURE_TEST_SUITE( reward_tests, reward_database_fixture ) + +BOOST_AUTO_TEST_CASE(asset_rewards_test) +{ + try + { + ACTORS((registrar)(alicereferrer)(bobreferrer)(izzy)(jill)); + + auto register_account = [&](const string& name, const account_object& referrer) -> const account_object& + { + uint8_t referrer_percent = 100; + fc::ecc::private_key _private_key = generate_private_key(name); + public_key_type _public_key = _private_key.get_public_key(); + return create_account(name, registrar, referrer, referrer_percent, _public_key); + }; + + // Izzy issues asset to Alice + // Jill issues asset to Bob + // Alice and Bob trade in the market and pay fees + // Bob's and Alice's referrers can get reward + upgrade_to_lifetime_member(registrar); + upgrade_to_lifetime_member(alicereferrer); + upgrade_to_lifetime_member(bobreferrer); + + auto alice = register_account("alice", alicereferrer); + auto bob = register_account("bob", bobreferrer); + + const share_type core_prec = asset::scaled_precision( asset_id_type()(db).precision ); + + // Return number of core shares (times precision) + auto _core = [&]( int64_t x ) -> asset + { return asset( x*core_prec ); }; + + transfer( committee_account, alice.id, _core(1000000) ); + transfer( committee_account, bob.id, _core(1000000) ); + transfer( committee_account, izzy_id, _core(1000000) ); + transfer( committee_account, jill_id, _core(1000000) ); + + constexpr auto izzycoin_reward_percent = 10*GRAPHENE_1_PERCENT; + constexpr auto jillcoin_reward_percent = 20*GRAPHENE_1_PERCENT; + + constexpr auto izzycoin_market_percent = 10*GRAPHENE_1_PERCENT; + constexpr auto jillcoin_market_percent = 20*GRAPHENE_1_PERCENT; + + asset_id_type izzycoin_id = create_bitasset( "IZZYCOIN", izzy_id, izzycoin_market_percent ).id; + asset_id_type jillcoin_id = create_bitasset( "JILLCOIN", jill_id, jillcoin_market_percent ).id; + + GRAPHENE_REQUIRE_THROW( update_asset(izzy_id, izzy_private_key, izzycoin_id, izzycoin_reward_percent), fc::exception ); + generate_blocks_past_reward_hardfork(); + update_asset(izzy_id, izzy_private_key, izzycoin_id, izzycoin_reward_percent); + + update_asset(jill_id, jill_private_key, jillcoin_id, jillcoin_reward_percent); + + const share_type izzy_prec = asset::scaled_precision( asset_id_type(izzycoin_id)(db).precision ); + const share_type jill_prec = asset::scaled_precision( asset_id_type(jillcoin_id)(db).precision ); + + auto _izzy = [&]( int64_t x ) -> asset + { return asset( x*izzy_prec, izzycoin_id ); }; + auto _jill = [&]( int64_t x ) -> asset + { return asset( x*jill_prec, jillcoin_id ); }; + + update_feed_producers( izzycoin_id(db), { izzy_id } ); + update_feed_producers( jillcoin_id(db), { jill_id } ); + + // Izzycoin is worth 100 BTS + price_feed feed; + feed.settlement_price = price( _izzy(1), _core(100) ); + feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; + feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; + publish_feed( izzycoin_id(db), izzy, feed ); + + // Jillcoin is worth 30 BTS + feed.settlement_price = price( _jill(1), _core(30) ); + feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; + feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; + publish_feed( jillcoin_id(db), jill, feed ); + + enable_fees(); + + // Alice and Bob create some coins + borrow( alice.id, _izzy( 1500), _core( 600000) ); + borrow( bob.id, _jill(2000), _core(180000) ); + + // Alice and Bob place orders which match + create_sell_order( alice.id, _izzy(1000), _jill(1500) ); // Alice is willing to sell her 1000 Izzy's for 1.5 Jill + create_sell_order( bob.id, _jill(1500), _izzy(1000) ); // Bob is buying up to 1500 Izzy's for up to 0.6 Jill + + // 1000 Izzys and 1500 Jills are matched, so the fees should be + // 100 Izzy (10%) and 300 Jill (20%). + // Bob's and Alice's referrers should get rewards + + share_type bob_refereer_reward = get_market_fee_reward( bob.referrer, izzycoin_id ); + share_type alice_refereer_reward = get_market_fee_reward( alice.referrer, jillcoin_id ); + + // Bob's and Alice's registrars should get rewards + share_type bob_rgistrar_reward = get_market_fee_reward( bob.registrar, izzycoin_id ); + share_type alice_registrar_reward = get_market_fee_reward( alice.registrar, jillcoin_id ); + + auto calculate_percent = [](const share_type& value, uint16_t percent) + { + auto a(value.value); + a *= percent; + a /= GRAPHENE_100_PERCENT; + return a; + }; + + BOOST_CHECK_GT( bob_refereer_reward, 0 ); + BOOST_CHECK_GT( alice_refereer_reward, 0 ); + BOOST_CHECK_GT( bob_rgistrar_reward, 0 ); + BOOST_CHECK_GT( alice_registrar_reward, 0 ); + + const auto izzycoin_market_fee = calculate_percent(_izzy(1000).amount, izzycoin_market_percent); + const auto izzycoin_reward = calculate_percent(izzycoin_market_fee, izzycoin_reward_percent); + BOOST_CHECK_EQUAL( izzycoin_reward, bob_refereer_reward + bob_rgistrar_reward ); + BOOST_CHECK_EQUAL( calculate_percent(izzycoin_reward, bob.referrer_rewards_percentage), bob_refereer_reward ); + + const auto jillcoin_market_fee = calculate_percent(_jill(1500).amount, jillcoin_market_percent); + const auto jillcoin_reward = calculate_percent(jillcoin_market_fee, jillcoin_reward_percent); + BOOST_CHECK_EQUAL( jillcoin_reward, alice_refereer_reward + alice_registrar_reward ); + BOOST_CHECK_EQUAL( calculate_percent(jillcoin_reward, alice.referrer_rewards_percentage), alice_refereer_reward ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(asset_claim_reward_test) +{ + try + { + ACTORS((jill)(izzy)); + constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT; + + upgrade_to_lifetime_member(izzy); + + price price(asset(1, asset_id_type(1)), asset(1)); + uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT; + auto obj = jill_id(db); + const asset_object jillcoin = create_user_issued_asset( "JCOIN", jill, charge_market_fee, price, 2, market_fee_percent ); + + const share_type core_prec = asset::scaled_precision( asset_id_type()(db).precision ); + + // return number of core shares (times precision) + auto _core = [&core_prec]( int64_t x ) -> asset { return asset( x*core_prec ); }; + + const account_object alice = create_account("alice", izzy, izzy, 50); + const account_object bob = create_account("bob", izzy, izzy, 50); + + // prepare users' balance + issue_uia( alice, jillcoin.amount( 20000000 ) ); + + transfer( committee_account, alice.get_id(), _core(1000) ); + transfer( committee_account, bob.get_id(), _core(1000) ); + transfer( committee_account, izzy.get_id(), _core(1000) ); + + auto claim_reward = [&]( account_object referrer, asset amount_to_claim, fc::ecc::private_key private_key ) + { + vesting_balance_withdraw_operation op; + op.vesting_balance = vesting_balance_id_type(0); + op.owner = referrer.get_id(); + op.amount = amount_to_claim; + + signed_transaction tx; + tx.operations.push_back( op ); + db.current_fee_schedule().set_fee( tx.operations.back() ); + set_expiration( db, tx ); + sign( tx, private_key ); + PUSH_TX( db, tx ); + }; + + const int64_t amount_to_claim = 3; + //GRAPHENE_REQUIRE_THROW( claim_reward( izzy, jillcoin.amount(amount_to_claim), izzy_private_key ), fc::exception ); + //GRAPHENE_REQUIRE_THROW( update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent), fc::exception ); + generate_blocks_past_reward_hardfork(); + // update_asset: set referrer percent + update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent); + + // Alice and Bob place orders which match + create_sell_order( alice, jillcoin.amount(200000), _core(1) ); + create_sell_order( bob, _core(1), jillcoin.amount(100000) ); + + const int64_t izzy_reward = get_market_fee_reward( izzy, jillcoin ); + const int64_t izzy_balance = get_balance( izzy, jillcoin ); + + BOOST_CHECK_GT(izzy_reward, 0); + + + claim_reward( izzy, jillcoin.amount(amount_to_claim), izzy_private_key ); + + BOOST_CHECK_EQUAL(get_balance( izzy, jillcoin ), izzy_balance + amount_to_claim); + BOOST_CHECK_EQUAL(get_market_fee_reward( izzy, jillcoin ), izzy_reward - amount_to_claim); + } + FC_LOG_AND_RETHROW() +} +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/tests/tests/uia_tests.cpp b/tests/tests/uia_tests.cpp index 7b44ef4c04..997cb672b0 100644 --- a/tests/tests/uia_tests.cpp +++ b/tests/tests/uia_tests.cpp @@ -58,6 +58,12 @@ BOOST_AUTO_TEST_CASE( create_advanced_uia ) creator.common_options.flags = charge_market_fee|white_list|override_authority|disable_confidential; creator.common_options.core_exchange_rate = price(asset(2),asset(1,asset_id_type(1))); creator.common_options.whitelist_authorities = creator.common_options.blacklist_authorities = {account_id_type()}; + + if( db.head_block_time() >= HARDFORK_1268_TIME ) + { + creator.common_options.additional_options.value.reward_percent = 12; + } + trx.operations.push_back(std::move(creator)); PUSH_TX( db, trx, ~0 ); @@ -73,6 +79,11 @@ BOOST_AUTO_TEST_CASE( create_advanced_uia ) BOOST_CHECK(test_asset_dynamic_data.current_supply == 0); BOOST_CHECK(test_asset_dynamic_data.accumulated_fees == 0); BOOST_CHECK(test_asset_dynamic_data.fee_pool == 0); + + if( db.head_block_time() >= HARDFORK_1268_TIME ) + { + BOOST_CHECK(test_asset.options.additional_options.value.reward_percent == 12); + } } catch(fc::exception& e) { edump((e.to_detail_string())); throw; From e8faf8115455d451c843514cec4137c44de07f75 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 7 Nov 2018 13:16:46 -0500 Subject: [PATCH 039/161] Added hardfork protection to proposal_create --- libraries/chain/proposal_evaluator.cpp | 6 ++++++ tests/tests/htlc_tests.cpp | 21 +++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 8124cc4fda..650f360e7e 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -144,6 +144,12 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati for( auto& op : o.proposed_ops ) { operation_get_required_authorities(op.op, auths, auths, other); + // If we are attempting to set HTLC parameters, make sure the HTLC hardfork date has passed. + if (op.op.which() == operation::tag::value) + if (d.head_block_time() < HARDFORK_HTLC_TIME) + if (op.op.get().new_parameters.extensions + .value.updatable_htlc_options.valid()) + FC_ASSERT(false, "Unable to set HTLC parameters before hardfork."); } FC_ASSERT( other.size() == 0 ); // TODO: what about other??? diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 13b0d49390..9414ef1ce3 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -117,7 +117,7 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) // cler everything out generate_block(); trx.clear(); - // Alice puts a contract on the blockchain + // Alice tries to put a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; @@ -132,6 +132,17 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) sign(trx, alice_private_key); GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); } + // attempt to create a proposal that contains htlc stuff + { + + graphene::chain::committee_member_update_global_parameters_operation param_op; + + graphene::chain::proposal_create_operation create_operation; + + create_operation.fee_paying_account = committee_account; + create_operation.review_period_seconds = 60 * 60 * 48; + create_operation.proposed_ops.emplace_back(param_op); + } } BOOST_AUTO_TEST_CASE( htlc_expires ) @@ -337,11 +348,9 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) get_account("init6").get_id(), get_account("init7").get_id()}; trx.operations.push_back(uop); sign( trx, init_account_priv_key ); - db.push_transaction(trx); - BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); - generate_blocks(proposal_id_type()(db).expiration_time + 5); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - // the proposal should have failed + BOOST_TEST_MESSAGE("Sending proposal."); + GRAPHENE_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_TEST_MESSAGE("Verifying that proposal did not succeeed."); BOOST_CHECK(!db.get_global_properties().parameters.extensions.value.updatable_htlc_options.valid()); trx.clear(); } From 2c7f7f99e0491377b310804293e917c8dc5f061c Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 7 Nov 2018 16:50:34 -0500 Subject: [PATCH 040/161] Removed debugging code --- libraries/chain/proposal_evaluator.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 650f360e7e..23c83199e2 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -145,11 +145,10 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati { operation_get_required_authorities(op.op, auths, auths, other); // If we are attempting to set HTLC parameters, make sure the HTLC hardfork date has passed. - if (op.op.which() == operation::tag::value) - if (d.head_block_time() < HARDFORK_HTLC_TIME) - if (op.op.get().new_parameters.extensions - .value.updatable_htlc_options.valid()) - FC_ASSERT(false, "Unable to set HTLC parameters before hardfork."); + FC_ASSERT( op.op.which() != operation::tag::value + || d.head_block_time() >= HARDFORK_HTLC_TIME + || !op.op.get().new_parameters.extensions + .value.updatable_htlc_options.valid(), "Unable to set HTLC parameters before hardfork."); } FC_ASSERT( other.size() == 0 ); // TODO: what about other??? From 558f1ec87191652ed5d8de91db7025ca5bd6f0e6 Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Fri, 9 Nov 2018 14:58:00 +0300 Subject: [PATCH 041/161] - prepare vesting balance object for hardfork - fix PR notes --- libraries/chain/asset_evaluator.cpp | 6 ++-- libraries/chain/db_balance.cpp | 18 +++++++--- libraries/chain/db_market.cpp | 26 +++++++------- libraries/chain/hardfork.d/CORE_1268.hf | 2 +- .../chain/include/graphene/chain/config.hpp | 2 +- .../chain/include/graphene/chain/database.hpp | 2 ++ .../graphene/chain/protocol/asset_ops.hpp | 7 ++-- .../graphene/chain/vesting_balance_object.hpp | 12 +++++-- libraries/chain/worker_evaluator.cpp | 1 + tests/tests/market_fee_sharing_tests.cpp | 36 ++++++++----------- tests/tests/uia_tests.cpp | 9 ----- 11 files changed, 60 insertions(+), 61 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index deb838a347..636626e50d 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -47,8 +47,8 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o if( d.head_block_time() < HARDFORK_1268_TIME ) { - FC_ASSERT(!op.common_options.additional_options.value.null_ext.valid()); - FC_ASSERT(!op.common_options.additional_options.value.reward_percent.valid()); + FC_ASSERT( !op.common_options.extensions.value.reward_percent.valid(), + "Asset additional options reward percent is only available after HARDFORK_1268_TIME!"); } // Check that all authorities do exist @@ -283,7 +283,7 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) validate_new_issuer( d, a, *o.new_issuer ); } - if ( o.new_options.additional_options.value.reward_percent.valid() ) + if ( o.new_options.extensions.value.reward_percent.valid() ) { FC_ASSERT( d.head_block_time() >= HARDFORK_1268_TIME, "Referrer percent is only available after HARDFORK_1268_TIME!"); diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index d28c0f51a0..6fa85cf8c7 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -83,7 +83,7 @@ void database::adjust_balance(account_id_type account, asset delta ) void database::deposit_market_fee_vesting_balance(const account_id_type &account_id, const asset &delta) { try { - FC_ASSERT( delta.amount > 0, "Invalid negative value for balance"); + FC_ASSERT( delta.amount >= 0, "Invalid negative value for balance"); if( delta.amount == 0 ) return; @@ -94,22 +94,26 @@ void database::deposit_market_fee_vesting_balance(const account_id_type &account [&delta](const vesting_balance_object& vbo) { return vbo.balance.asset_id == delta.asset_id;} ); + auto block_time = head_block_time(); + if(market_balance == boost::end(market_vesting_balances) ) { - create([&](vesting_balance_object &vbo) { + create([&account_id, &delta, &block_time](vesting_balance_object &vbo) { vbo.owner = account_id; vbo.balance = delta; vbo.balance_type = vesting_balance_type::market_fee_sharing; + cdd_vesting_policy policy; policy.vesting_seconds = { 0 }; policy.coin_seconds_earned = vbo.balance.amount.value; - policy.coin_seconds_earned_last_update = head_block_time(); + policy.coin_seconds_earned_last_update = block_time; + vbo.policy = policy; }); } else { - modify( *market_balance, [&]( vesting_balance_object& vbo ) + modify( *market_balance, [&block_time, &delta]( vesting_balance_object& vbo ) { - vbo.deposit_vested(head_block_time(), delta); + vbo.deposit_vested(block_time, delta); }); } @@ -118,6 +122,7 @@ void database::deposit_market_fee_vesting_balance(const account_id_type &account optional< vesting_balance_id_type > database::deposit_lazy_vesting( const optional< vesting_balance_id_type >& ovbid, share_type amount, uint32_t req_vesting_seconds, + vesting_balance_type balance_type, account_id_type req_owner, bool require_vesting ) { @@ -151,6 +156,7 @@ optional< vesting_balance_id_type > database::deposit_lazy_vesting( { _vbo.owner = req_owner; _vbo.balance = amount; + _vbo.balance_type = balance_type; cdd_vesting_policy policy; policy.vesting_seconds = req_vesting_seconds; @@ -186,6 +192,7 @@ void database::deposit_cashback(const account_object& acct, share_type amount, b acct.cashback_vb, amount, get_global_properties().parameters.cashback_vesting_period_seconds, + vesting_balance_type::cashback, acct.id, require_vesting ); @@ -213,6 +220,7 @@ void database::deposit_witness_pay(const witness_object& wit, share_type amount) wit.pay_vb, amount, get_global_properties().parameters.witness_pay_vesting_seconds, + vesting_balance_type::witness, wit.witness_account, true ); diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index e56ae2ad77..9b9d0589a0 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -33,12 +33,12 @@ namespace graphene { namespace chain { namespace detail { - fc::uint128 calculate_percent(const share_type& value, uint16_t percent) + uint64_t calculate_percent(const share_type& value, uint16_t percent) { fc::uint128 a(value.value); a *= percent; a /= GRAPHENE_100_PERCENT; - return a; + return a.to_uint64(); } } //detail @@ -785,8 +785,6 @@ bool database::fill_limit_order( const limit_order_object& order, const asset& p const account_object& seller = order.seller(*this); const asset_object& recv_asset = receives.asset_id(*this); - //auto issuer_fees = pay_market_fees( recv_asset, receives ); - //auto issuer_fees = pay_market_fees(seller, recv_asset, receives); auto issuer_fees = ( head_block_time() < HARDFORK_1268_TIME ) ? pay_market_fees(recv_asset, receives) : pay_market_fees(seller, recv_asset, receives); @@ -1125,7 +1123,7 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass return trade_asset.amount(0); auto value = detail::calculate_percent(trade_amount.amount, trade_asset.options.market_fee_percent); - asset percent_fee = trade_asset.amount(value.to_uint64()); + asset percent_fee = trade_asset.amount(value); if( percent_fee.amount > trade_asset.options.max_market_fee ) percent_fee.amount = trade_asset.options.max_market_fee; @@ -1136,7 +1134,7 @@ asset database::calculate_market_fee( const asset_object& trade_asset, const ass asset database::pay_market_fees( const asset_object& recv_asset, const asset& receives ) { auto issuer_fees = calculate_market_fee( recv_asset, receives ); - assert(issuer_fees <= receives ); + FC_ASSERT( issuer_fees <= receives, "Market fee shouldn't be greater than receives"); //Don't dirty undo state if not actually collecting any fees if( issuer_fees.amount > 0 ) @@ -1153,11 +1151,9 @@ asset database::pay_market_fees( const asset_object& recv_asset, const asset& re asset database::pay_market_fees(const account_object& seller, const asset_object& recv_asset, const asset& receives ) { - FC_ASSERT( head_block_time() >= HARDFORK_1268_TIME ); - const auto issuer_fees = calculate_market_fee( recv_asset, receives ); - assert(issuer_fees <= receives ); + FC_ASSERT( issuer_fees <= receives, "Market fee shouldn't be greater than receives"); //Don't dirty undo state if not actually collecting any fees if( issuer_fees.amount > 0 ) @@ -1165,7 +1161,7 @@ asset database::pay_market_fees(const account_object& seller, const asset_object // calculate and pay rewards asset reward = recv_asset.amount(0); - const auto reward_percent = recv_asset.options.additional_options.value.reward_percent; + const auto reward_percent = recv_asset.options.extensions.value.reward_percent; if ( reward_percent && *reward_percent ) { @@ -1173,9 +1169,9 @@ asset database::pay_market_fees(const account_object& seller, const asset_object if ( reward_value > 0 ) { - reward = recv_asset.amount(reward_value.to_uint64()); + reward = recv_asset.amount(reward_value); - assert( reward < issuer_fees ); + FC_ASSERT( reward < issuer_fees, "Market reward should be less than issuer fees"); // cut referrer percent from reward const auto referrer_rewards_percentage = seller.referrer_rewards_percentage; const auto referrer_rewards_value = detail::calculate_percent(reward.amount, referrer_rewards_percentage); @@ -1183,7 +1179,9 @@ asset database::pay_market_fees(const account_object& seller, const asset_object auto registrar_reward = reward; if ( referrer_rewards_value > 0 ) { - const asset referrer_reward = recv_asset.amount(referrer_rewards_value.to_uint64()); + FC_ASSERT ( referrer_rewards_value <= reward.amount, "Referrer reward shouldn't be greater than total reward" ); + + const asset referrer_reward = recv_asset.amount(referrer_rewards_value); registrar_reward -= referrer_reward; deposit_market_fee_vesting_balance(seller.referrer, referrer_reward); } @@ -1192,7 +1190,7 @@ asset database::pay_market_fees(const account_object& seller, const asset_object } const auto& recv_dyn_data = recv_asset.dynamic_asset_data_id(*this); - modify( recv_dyn_data, [&]( asset_dynamic_data_object& obj ){ + modify( recv_dyn_data, [&issuer_fees, &reward]( asset_dynamic_data_object& obj ){ obj.accumulated_fees += issuer_fees.amount - reward.amount; }); } diff --git a/libraries/chain/hardfork.d/CORE_1268.hf b/libraries/chain/hardfork.d/CORE_1268.hf index faae771f56..6872db2fb2 100644 --- a/libraries/chain/hardfork.d/CORE_1268.hf +++ b/libraries/chain/hardfork.d/CORE_1268.hf @@ -1,4 +1,4 @@ // #1268 Distribute Asset Market Fees to Referral Program #ifndef HARDFORK_1268_TIME #define HARDFORK_1268_TIME (fc::time_point_sec( 1530705600 )) // Wednesday, July 4, 2018 12:00:00 PM -#endif \ No newline at end of file +#endif diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 1ef28a0c67..3ab5f47f19 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -121,7 +121,7 @@ #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 -#define GRAPHENE_CURRENT_DB_VERSION "BTS2.18" +#define GRAPHENE_CURRENT_DB_VERSION "BTS2.19" #define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT) diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index f1d2382ac1..4e2915a14b 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -47,6 +47,7 @@ namespace graphene { namespace chain { class transaction_evaluation_state; struct budget_record; + enum class vesting_balance_type; /** * @class database @@ -322,6 +323,7 @@ namespace graphene { namespace chain { const optional< vesting_balance_id_type >& ovbid, share_type amount, uint32_t req_vesting_seconds, + vesting_balance_type balance_type, account_id_type req_owner, bool require_vesting ); diff --git a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp index a36a4e6446..716c14a458 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp @@ -29,7 +29,6 @@ namespace graphene { namespace chain { struct additional_asset_options { - fc::optional null_ext; fc::optional reward_percent; }; typedef extension additional_asset_options_t; @@ -82,7 +81,7 @@ namespace graphene { namespace chain { * size of description. */ string description; - additional_asset_options_t additional_options; + additional_asset_options_t extensions; /// Perform internal consistency checks. /// @throws fc::exception if any check fails @@ -530,7 +529,7 @@ FC_REFLECT( graphene::chain::asset_options, (whitelist_markets) (blacklist_markets) (description) - (additional_options) + (extensions) ) FC_REFLECT( graphene::chain::bitasset_options, (feed_lifetime_sec) @@ -542,7 +541,7 @@ FC_REFLECT( graphene::chain::bitasset_options, (extensions) ) -FC_REFLECT( graphene::chain::additional_asset_options, (null_ext)(reward_percent) ) +FC_REFLECT( graphene::chain::additional_asset_options, (reward_percent) ) FC_REFLECT( graphene::chain::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) ) FC_REFLECT( graphene::chain::asset_global_settle_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::asset_settle_operation::fee_parameters_type, (fee) ) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index fc5883994b..afe7aeb75a 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -125,9 +125,11 @@ namespace graphene { namespace chain { cdd_vesting_policy > vesting_policy; - enum class vesting_balance_type { unspecified, - worker, - market_fee_sharing }; + enum class vesting_balance_type { unspecified, + cashback, + worker, + witness, + market_fee_sharing }; /** * Vesting balance object is a balance that is locked by the blockchain for a period of time. */ @@ -223,4 +225,8 @@ FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::objec (owner) (balance) (policy) + (balance_type) ) + +FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (unspecified)(cashback)(worker)(witness)(market_fee_sharing) ) + diff --git a/libraries/chain/worker_evaluator.cpp b/libraries/chain/worker_evaluator.cpp index b5aea8f3b4..240f9723fa 100644 --- a/libraries/chain/worker_evaluator.cpp +++ b/libraries/chain/worker_evaluator.cpp @@ -58,6 +58,7 @@ struct worker_init_visitor w.balance = db.create([&](vesting_balance_object& b) { b.owner = worker.worker_account; b.balance = asset(0); + b.balance_type = vesting_balance_type::worker; cdd_vesting_policy policy; policy.vesting_seconds = fc::days(i.pay_vesting_period_days).to_seconds(); diff --git a/tests/tests/market_fee_sharing_tests.cpp b/tests/tests/market_fee_sharing_tests.cpp index a36f8c7e30..e7fc05e078 100644 --- a/tests/tests/market_fee_sharing_tests.cpp +++ b/tests/tests/market_fee_sharing_tests.cpp @@ -1,6 +1,4 @@ #include -//#include -//#include #include #include @@ -39,7 +37,7 @@ struct reward_database_fixture : database_fixture op.issuer = issuer_id; op.asset_to_update = asset_id; op.new_options = asset_id(db).options; - op.new_options.additional_options.value.reward_percent = reward_percent; + op.new_options.extensions.value.reward_percent = reward_percent; signed_transaction tx; tx.operations.push_back( op ); @@ -146,7 +144,6 @@ BOOST_AUTO_TEST_CASE(asset_rewards_test) // 1000 Izzys and 1500 Jills are matched, so the fees should be // 100 Izzy (10%) and 300 Jill (20%). // Bob's and Alice's referrers should get rewards - share_type bob_refereer_reward = get_market_fee_reward( bob.referrer, izzycoin_id ); share_type alice_refereer_reward = get_market_fee_reward( alice.referrer, jillcoin_id ); @@ -209,6 +206,19 @@ BOOST_AUTO_TEST_CASE(asset_claim_reward_test) transfer( committee_account, bob.get_id(), _core(1000) ); transfer( committee_account, izzy.get_id(), _core(1000) ); + generate_blocks_past_reward_hardfork(); + // update_asset: set referrer percent + update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent); + + // Alice and Bob place orders which match + create_sell_order( alice, jillcoin.amount(200000), _core(1) ); + create_sell_order( bob, _core(1), jillcoin.amount(100000) ); + + const int64_t izzy_reward = get_market_fee_reward( izzy, jillcoin ); + const int64_t izzy_balance = get_balance( izzy, jillcoin ); + + BOOST_CHECK_GT(izzy_reward, 0); + auto claim_reward = [&]( account_object referrer, asset amount_to_claim, fc::ecc::private_key private_key ) { vesting_balance_withdraw_operation op; @@ -225,22 +235,6 @@ BOOST_AUTO_TEST_CASE(asset_claim_reward_test) }; const int64_t amount_to_claim = 3; - //GRAPHENE_REQUIRE_THROW( claim_reward( izzy, jillcoin.amount(amount_to_claim), izzy_private_key ), fc::exception ); - //GRAPHENE_REQUIRE_THROW( update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent), fc::exception ); - generate_blocks_past_reward_hardfork(); - // update_asset: set referrer percent - update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent); - - // Alice and Bob place orders which match - create_sell_order( alice, jillcoin.amount(200000), _core(1) ); - create_sell_order( bob, _core(1), jillcoin.amount(100000) ); - - const int64_t izzy_reward = get_market_fee_reward( izzy, jillcoin ); - const int64_t izzy_balance = get_balance( izzy, jillcoin ); - - BOOST_CHECK_GT(izzy_reward, 0); - - claim_reward( izzy, jillcoin.amount(amount_to_claim), izzy_private_key ); BOOST_CHECK_EQUAL(get_balance( izzy, jillcoin ), izzy_balance + amount_to_claim); @@ -248,4 +242,4 @@ BOOST_AUTO_TEST_CASE(asset_claim_reward_test) } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/uia_tests.cpp b/tests/tests/uia_tests.cpp index 997cb672b0..95b4fdf4ec 100644 --- a/tests/tests/uia_tests.cpp +++ b/tests/tests/uia_tests.cpp @@ -59,11 +59,6 @@ BOOST_AUTO_TEST_CASE( create_advanced_uia ) creator.common_options.core_exchange_rate = price(asset(2),asset(1,asset_id_type(1))); creator.common_options.whitelist_authorities = creator.common_options.blacklist_authorities = {account_id_type()}; - if( db.head_block_time() >= HARDFORK_1268_TIME ) - { - creator.common_options.additional_options.value.reward_percent = 12; - } - trx.operations.push_back(std::move(creator)); PUSH_TX( db, trx, ~0 ); @@ -80,10 +75,6 @@ BOOST_AUTO_TEST_CASE( create_advanced_uia ) BOOST_CHECK(test_asset_dynamic_data.accumulated_fees == 0); BOOST_CHECK(test_asset_dynamic_data.fee_pool == 0); - if( db.head_block_time() >= HARDFORK_1268_TIME ) - { - BOOST_CHECK(test_asset.options.additional_options.value.reward_percent == 12); - } } catch(fc::exception& e) { edump((e.to_detail_string())); throw; From 45e11592839366ba905ff25c67f48b04c9c7d140 Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Mon, 12 Nov 2018 15:46:51 +0300 Subject: [PATCH 042/161] Unit tests for asset - added market_sharing_whitelist option - added tests for asset extensions before and after the hardfork 1268 --- .../graphene/chain/protocol/asset_ops.hpp | 5 +- tests/common/database_fixture.cpp | 4 +- tests/common/database_fixture.hpp | 5 +- tests/tests/market_fee_sharing_tests.cpp | 101 +++++++++++++++++- 4 files changed, 107 insertions(+), 8 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp index 716c14a458..9c6fca3c9c 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp @@ -29,7 +29,8 @@ namespace graphene { namespace chain { struct additional_asset_options { - fc::optional reward_percent; + fc::optional reward_percent; + fc::optional> whitelist_market_fee_sharing; }; typedef extension additional_asset_options_t; @@ -541,7 +542,7 @@ FC_REFLECT( graphene::chain::bitasset_options, (extensions) ) -FC_REFLECT( graphene::chain::additional_asset_options, (reward_percent) ) +FC_REFLECT( graphene::chain::additional_asset_options, (reward_percent)(whitelist_market_fee_sharing) ) FC_REFLECT( graphene::chain::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) ) FC_REFLECT( graphene::chain::asset_global_settle_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::asset_settle_operation::fee_parameters_type, (fee) ) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index e50cf19cdb..6c633c4729 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -512,7 +512,8 @@ const asset_object& database_fixture::create_user_issued_asset( const string& na const asset_object& database_fixture::create_user_issued_asset( const string& name, const account_object& issuer, uint16_t flags, const price& core_exchange_rate, - uint16_t precision, uint16_t market_fee_percent) + uint8_t precision, uint16_t market_fee_percent, + additional_asset_options_t additional_options) { asset_create_operation creator; creator.issuer = issuer.id; @@ -525,6 +526,7 @@ const asset_object& database_fixture::create_user_issued_asset( const string& na creator.common_options.flags = flags; creator.common_options.issuer_permissions = flags; creator.common_options.market_fee_percent = market_fee_percent; + creator.common_options.extensions = std::move(additional_options); trx.operations.clear(); trx.operations.push_back(std::move(creator)); set_expiration( db, trx ); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 109df46cc8..651d5f29cc 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -285,8 +285,9 @@ struct database_fixture { const account_object& issuer, uint16_t flags, const price& core_exchange_rate = price(asset(1, asset_id_type(1)), asset(1)), - uint16_t precision = 2 /* traditional precision for tests */, - uint16_t market_fee_percent = 0); + uint8_t precision = 2 /* traditional precision for tests */, + uint16_t market_fee_percent = 0, + additional_asset_options_t options = additional_asset_options_t()); void issue_uia( const account_object& recipient, asset amount ); void issue_uia( account_id_type recipient_id, asset amount ); diff --git a/tests/tests/market_fee_sharing_tests.cpp b/tests/tests/market_fee_sharing_tests.cpp index e7fc05e078..a04862fc71 100644 --- a/tests/tests/market_fee_sharing_tests.cpp +++ b/tests/tests/market_fee_sharing_tests.cpp @@ -31,13 +31,15 @@ struct reward_database_fixture : database_fixture void update_asset( const account_id_type& issuer_id, const fc::ecc::private_key& private_key, const asset_id_type& asset_id, - uint16_t reward_percent ) + uint16_t reward_percent, + flat_set whitelist_market_fee_sharing = flat_set()) { asset_update_operation op; op.issuer = issuer_id; op.asset_to_update = asset_id; op.new_options = asset_id(db).options; op.new_options.extensions.value.reward_percent = reward_percent; + op.new_options.extensions.value.whitelist_market_fee_sharing = whitelist_market_fee_sharing; signed_transaction tx; tx.operations.push_back( op ); @@ -56,6 +58,100 @@ struct reward_database_fixture : database_fixture BOOST_FIXTURE_TEST_SUITE( reward_tests, reward_database_fixture ) +BOOST_AUTO_TEST_CASE(cannot_create_asset_with_additional_options_before_hf) +{ + try + { + ACTOR(issuer); + + price price(asset(1, asset_id_type(1)), asset(1)); + uint16_t market_fee_percent = 100; + + additional_asset_options_t options; + options.value.reward_percent = 100; + options.value.whitelist_market_fee_sharing = flat_set{issuer_id}; + + GRAPHENE_CHECK_THROW(create_user_issued_asset("USD", + issuer, + charge_market_fee, + price, + 2, + market_fee_percent, + options), + fc::assert_exception); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(create_asset_with_additional_options_after_hf) +{ + try + { + ACTOR(issuer); + + generate_blocks_past_reward_hardfork(); + + uint16_t reward_percent = 100; + flat_set whitelist = {issuer_id}; + price price(asset(1, asset_id_type(1)), asset(1)); + uint16_t market_fee_percent = 100; + + additional_asset_options_t options; + options.value.reward_percent = reward_percent; + options.value.whitelist_market_fee_sharing = whitelist; + + asset_object usd_asset = create_user_issued_asset("USD", + issuer, + charge_market_fee, + price, + 2, + market_fee_percent, + options); + + additional_asset_options usd_options = usd_asset.options.extensions.value; + BOOST_CHECK_EQUAL(reward_percent, *usd_options.reward_percent); + BOOST_CHECK(whitelist == *usd_options.whitelist_market_fee_sharing); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(cannot_update_additional_options_before_hf) +{ + try + { + ACTOR(issuer); + + asset_object usd_asset = create_user_issued_asset("USD", issuer, charge_market_fee); + + GRAPHENE_CHECK_THROW( + update_asset(issuer_id, issuer_private_key, usd_asset.get_id(), 40, {issuer_id}), + fc::assert_exception ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(update_additional_options_after_hf) +{ + try + { + ACTOR(issuer); + + asset_object usd_asset = create_user_issued_asset("USD", issuer, charge_market_fee); + + generate_blocks_past_reward_hardfork(); + + uint16_t reward_percent = 40; + flat_set whitelist = {issuer_id}; + update_asset(issuer_id, issuer_private_key, usd_asset.get_id(), reward_percent, whitelist); + + asset_object updated_asset = usd_asset.get_id()(db); + additional_asset_options options = updated_asset.options.extensions.value; + BOOST_CHECK_EQUAL(reward_percent, *options.reward_percent); + BOOST_CHECK(whitelist == *options.whitelist_market_fee_sharing); + } + FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_CASE(asset_rewards_test) { try @@ -101,10 +197,9 @@ BOOST_AUTO_TEST_CASE(asset_rewards_test) asset_id_type izzycoin_id = create_bitasset( "IZZYCOIN", izzy_id, izzycoin_market_percent ).id; asset_id_type jillcoin_id = create_bitasset( "JILLCOIN", jill_id, jillcoin_market_percent ).id; - GRAPHENE_REQUIRE_THROW( update_asset(izzy_id, izzy_private_key, izzycoin_id, izzycoin_reward_percent), fc::exception ); generate_blocks_past_reward_hardfork(); - update_asset(izzy_id, izzy_private_key, izzycoin_id, izzycoin_reward_percent); + update_asset(izzy_id, izzy_private_key, izzycoin_id, izzycoin_reward_percent); update_asset(jill_id, jill_private_key, jillcoin_id, jillcoin_reward_percent); const share_type izzy_prec = asset::scaled_precision( asset_id_type(izzycoin_id)(db).precision ); From 44049b0f3462e73e7ab80e0362567ca51f102dc4 Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Fri, 16 Nov 2018 15:08:15 +0300 Subject: [PATCH 043/161] Apply market fee sharing whitelist --- libraries/chain/db_market.cpp | 48 ++++--- tests/tests/market_fee_sharing_tests.cpp | 169 +++++++++++++++++++---- 2 files changed, 168 insertions(+), 49 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 9b9d0589a0..08ebad0ade 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -1152,40 +1152,42 @@ asset database::pay_market_fees( const asset_object& recv_asset, const asset& re asset database::pay_market_fees(const account_object& seller, const asset_object& recv_asset, const asset& receives ) { const auto issuer_fees = calculate_market_fee( recv_asset, receives ); - FC_ASSERT( issuer_fees <= receives, "Market fee shouldn't be greater than receives"); - //Don't dirty undo state if not actually collecting any fees - if( issuer_fees.amount > 0 ) + if ( issuer_fees.amount > 0 ) { // calculate and pay rewards asset reward = recv_asset.amount(0); - const auto reward_percent = recv_asset.options.extensions.value.reward_percent; + auto is_rewards_allowed = [&recv_asset, &seller]() { + const auto &white_list = recv_asset.options.extensions.value.whitelist_market_fee_sharing; + return ( !white_list || (*white_list).empty() || ( (*white_list).find(seller.referrer) != (*white_list).end() ) ); + }; - if ( reward_percent && *reward_percent ) + if ( is_rewards_allowed() ) { - const auto reward_value = detail::calculate_percent(issuer_fees.amount, *reward_percent); - - if ( reward_value > 0 ) + const auto reward_percent = recv_asset.options.extensions.value.reward_percent; + if ( reward_percent && *reward_percent ) { - reward = recv_asset.amount(reward_value); - - FC_ASSERT( reward < issuer_fees, "Market reward should be less than issuer fees"); - // cut referrer percent from reward - const auto referrer_rewards_percentage = seller.referrer_rewards_percentage; - const auto referrer_rewards_value = detail::calculate_percent(reward.amount, referrer_rewards_percentage); - - auto registrar_reward = reward; - if ( referrer_rewards_value > 0 ) + const auto reward_value = detail::calculate_percent(issuer_fees.amount, *reward_percent); + if ( reward_value > 0 ) { - FC_ASSERT ( referrer_rewards_value <= reward.amount, "Referrer reward shouldn't be greater than total reward" ); - - const asset referrer_reward = recv_asset.amount(referrer_rewards_value); - registrar_reward -= referrer_reward; - deposit_market_fee_vesting_balance(seller.referrer, referrer_reward); + reward = recv_asset.amount(reward_value); + FC_ASSERT( reward < issuer_fees, "Market reward should be less than issuer fees"); + // cut referrer percent from reward + const auto referrer_rewards_percentage = seller.referrer_rewards_percentage; + const auto referrer_rewards_value = detail::calculate_percent(reward.amount, referrer_rewards_percentage); + auto registrar_reward = reward; + + if ( referrer_rewards_value > 0 ) + { + FC_ASSERT ( referrer_rewards_value <= reward.amount, "Referrer reward shouldn't be greater than total reward" ); + const asset referrer_reward = recv_asset.amount(referrer_rewards_value); + registrar_reward -= referrer_reward; + deposit_market_fee_vesting_balance(seller.referrer, referrer_reward); + } + deposit_market_fee_vesting_balance(seller.registrar, registrar_reward); } - deposit_market_fee_vesting_balance(seller.registrar, registrar_reward); } } diff --git a/tests/tests/market_fee_sharing_tests.cpp b/tests/tests/market_fee_sharing_tests.cpp index a04862fc71..4b5d984d25 100644 --- a/tests/tests/market_fee_sharing_tests.cpp +++ b/tests/tests/market_fee_sharing_tests.cpp @@ -23,6 +23,8 @@ namespace fc struct reward_database_fixture : database_fixture { + using whitelist_market_fee_sharing_t = fc::optional>; + reward_database_fixture() : database_fixture(HARDFORK_1268_TIME - 100) { @@ -32,7 +34,7 @@ struct reward_database_fixture : database_fixture const fc::ecc::private_key& private_key, const asset_id_type& asset_id, uint16_t reward_percent, - flat_set whitelist_market_fee_sharing = flat_set()) + const whitelist_market_fee_sharing_t &whitelist_market_fee_sharing = whitelist_market_fee_sharing_t{}) { asset_update_operation op; op.issuer = issuer_id; @@ -54,6 +56,13 @@ struct reward_database_fixture : database_fixture database_fixture::generate_blocks( HARDFORK_1268_TIME ); database_fixture::generate_block(); } + + asset core_asset(int64_t x ) + { + return asset( x*core_precision ); + }; + + const share_type core_precision = asset::scaled_precision( asset_id_type()(db).precision ); }; BOOST_FIXTURE_TEST_SUITE( reward_tests, reward_database_fixture ) @@ -123,8 +132,9 @@ BOOST_AUTO_TEST_CASE(cannot_update_additional_options_before_hf) asset_object usd_asset = create_user_issued_asset("USD", issuer, charge_market_fee); + flat_set whitelist = {issuer_id}; GRAPHENE_CHECK_THROW( - update_asset(issuer_id, issuer_private_key, usd_asset.get_id(), 40, {issuer_id}), + update_asset(issuer_id, issuer_private_key, usd_asset.get_id(), 40, whitelist), fc::assert_exception ); } FC_LOG_AND_RETHROW() @@ -177,16 +187,10 @@ BOOST_AUTO_TEST_CASE(asset_rewards_test) auto alice = register_account("alice", alicereferrer); auto bob = register_account("bob", bobreferrer); - const share_type core_prec = asset::scaled_precision( asset_id_type()(db).precision ); - - // Return number of core shares (times precision) - auto _core = [&]( int64_t x ) -> asset - { return asset( x*core_prec ); }; - - transfer( committee_account, alice.id, _core(1000000) ); - transfer( committee_account, bob.id, _core(1000000) ); - transfer( committee_account, izzy_id, _core(1000000) ); - transfer( committee_account, jill_id, _core(1000000) ); + transfer( committee_account, alice.id, core_asset(1000000) ); + transfer( committee_account, bob.id, core_asset(1000000) ); + transfer( committee_account, izzy_id, core_asset(1000000) ); + transfer( committee_account, jill_id, core_asset(1000000) ); constexpr auto izzycoin_reward_percent = 10*GRAPHENE_1_PERCENT; constexpr auto jillcoin_reward_percent = 20*GRAPHENE_1_PERCENT; @@ -215,13 +219,13 @@ BOOST_AUTO_TEST_CASE(asset_rewards_test) // Izzycoin is worth 100 BTS price_feed feed; - feed.settlement_price = price( _izzy(1), _core(100) ); + feed.settlement_price = price( _izzy(1), core_asset(100) ); feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; publish_feed( izzycoin_id(db), izzy, feed ); // Jillcoin is worth 30 BTS - feed.settlement_price = price( _jill(1), _core(30) ); + feed.settlement_price = price( _jill(1), core_asset(30) ); feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100; publish_feed( jillcoin_id(db), jill, feed ); @@ -229,8 +233,8 @@ BOOST_AUTO_TEST_CASE(asset_rewards_test) enable_fees(); // Alice and Bob create some coins - borrow( alice.id, _izzy( 1500), _core( 600000) ); - borrow( bob.id, _jill(2000), _core(180000) ); + borrow( alice.id, _izzy( 1500), core_asset( 600000) ); + borrow( bob.id, _jill(2000), core_asset(180000) ); // Alice and Bob place orders which match create_sell_order( alice.id, _izzy(1000), _jill(1500) ); // Alice is willing to sell her 1000 Izzy's for 1.5 Jill @@ -286,28 +290,23 @@ BOOST_AUTO_TEST_CASE(asset_claim_reward_test) auto obj = jill_id(db); const asset_object jillcoin = create_user_issued_asset( "JCOIN", jill, charge_market_fee, price, 2, market_fee_percent ); - const share_type core_prec = asset::scaled_precision( asset_id_type()(db).precision ); - - // return number of core shares (times precision) - auto _core = [&core_prec]( int64_t x ) -> asset { return asset( x*core_prec ); }; - const account_object alice = create_account("alice", izzy, izzy, 50); const account_object bob = create_account("bob", izzy, izzy, 50); // prepare users' balance issue_uia( alice, jillcoin.amount( 20000000 ) ); - transfer( committee_account, alice.get_id(), _core(1000) ); - transfer( committee_account, bob.get_id(), _core(1000) ); - transfer( committee_account, izzy.get_id(), _core(1000) ); + transfer( committee_account, alice.get_id(), core_asset(1000) ); + transfer( committee_account, bob.get_id(), core_asset(1000) ); + transfer( committee_account, izzy.get_id(), core_asset(1000) ); generate_blocks_past_reward_hardfork(); // update_asset: set referrer percent update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent); // Alice and Bob place orders which match - create_sell_order( alice, jillcoin.amount(200000), _core(1) ); - create_sell_order( bob, _core(1), jillcoin.amount(100000) ); + create_sell_order( alice, jillcoin.amount(200000), core_asset(1) ); + create_sell_order( bob, core_asset(1), jillcoin.amount(100000) ); const int64_t izzy_reward = get_market_fee_reward( izzy, jillcoin ); const int64_t izzy_balance = get_balance( izzy, jillcoin ); @@ -337,4 +336,122 @@ BOOST_AUTO_TEST_CASE(asset_claim_reward_test) } FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(create_actors) +{ + try + { + ACTORS((jill)(izzy)); + + upgrade_to_lifetime_member(izzy); + + price price(asset(1, asset_id_type(1)), asset(1)); + uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT; + auto obj = jill_id(db); + const asset_object jillcoin = create_user_issued_asset( "JCOIN", jill, charge_market_fee, price, 2, market_fee_percent ); + + const account_object alice = create_account("alice", izzy, izzy, 50); + const account_object bob = create_account("bob", izzy, izzy, 50); + + // prepare users' balance + issue_uia( alice, jillcoin.amount( 20000000 ) ); + + transfer( committee_account, alice.get_id(), core_asset(1000) ); + transfer( committee_account, bob.get_id(), core_asset(1000) ); + transfer( committee_account, izzy.get_id(), core_asset(1000) ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(white_list_is_empty_test) +{ + try + { + INVOKE(create_actors); + + generate_blocks_past_reward_hardfork(); + GET_ACTOR(jill); + + constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT; + const asset_object &jillcoin = get_asset("JCOIN"); + + flat_set whitelist; + update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist); + + GET_ACTOR(izzy); + BOOST_CHECK_THROW( get_market_fee_reward( izzy, jillcoin ), fc::exception ); + + GET_ACTOR(alice); + GET_ACTOR(bob); + // Alice and Bob place orders which match + create_sell_order( alice, jillcoin.amount(200000), core_asset(1) ); + create_sell_order( bob, core_asset(1), jillcoin.amount(100000) ); + + const auto izzy_reward = get_market_fee_reward( izzy, jillcoin ); + BOOST_CHECK_GT(izzy_reward , 0); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(white_list_contains_registrar_test) +{ + try + { + INVOKE(create_actors); + + generate_blocks_past_reward_hardfork(); + GET_ACTOR(jill); + + constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT; + const asset_object &jillcoin = get_asset("JCOIN"); + + GET_ACTOR(izzy); + flat_set whitelist = {jill_id, izzy_id}; + + update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist); + + BOOST_CHECK_THROW( get_market_fee_reward( izzy, jillcoin ), fc::exception ); + + GET_ACTOR(alice); + GET_ACTOR(bob); + // Alice and Bob place orders which match + create_sell_order( alice, jillcoin.amount(200000), core_asset(1) ); + create_sell_order( bob, core_asset(1), jillcoin.amount(100000) ); + + const auto izzy_reward = get_market_fee_reward( izzy, jillcoin ); + BOOST_CHECK_GT(izzy_reward , 0); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(white_list_doesnt_contain_registrar_test) +{ + try + { + INVOKE(create_actors); + + generate_blocks_past_reward_hardfork(); + GET_ACTOR(jill); + + constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT; + const asset_object &jillcoin = get_asset("JCOIN"); + + GET_ACTOR(alice); + flat_set whitelist = {jill_id, alice_id}; + + update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist); + + GET_ACTOR(izzy); + BOOST_CHECK_THROW( get_market_fee_reward( izzy, jillcoin ), fc::exception ); + + GET_ACTOR(bob); + // Alice and Bob place orders which match + create_sell_order( alice, jillcoin.amount(200000), core_asset(1) ); + create_sell_order( bob, core_asset(1), jillcoin.amount(100000) ); + + BOOST_CHECK_THROW( get_market_fee_reward( izzy, jillcoin ), fc::exception ); + } + FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_SUITE_END() From b6a813a4889c7dd7c20cd6a6c71a243b4d245cac Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Fri, 16 Nov 2018 15:10:38 +0300 Subject: [PATCH 044/161] Add instant_vesting_policy --- libraries/chain/db_balance.cpp | 8 +-- .../graphene/chain/protocol/vesting.hpp | 10 ++- .../graphene/chain/vesting_balance_object.hpp | 22 ++++++- libraries/chain/vesting_balance_evaluator.cpp | 6 ++ libraries/chain/vesting_balance_object.cpp | 30 +++++++++ tests/tests/operation_tests.cpp | 64 +++++++++++++++++++ 6 files changed, 131 insertions(+), 9 deletions(-) diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index 6fa85cf8c7..610ba8fae0 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -102,13 +102,7 @@ void database::deposit_market_fee_vesting_balance(const account_id_type &account vbo.owner = account_id; vbo.balance = delta; vbo.balance_type = vesting_balance_type::market_fee_sharing; - - cdd_vesting_policy policy; - policy.vesting_seconds = { 0 }; - policy.coin_seconds_earned = vbo.balance.amount.value; - policy.coin_seconds_earned_last_update = block_time; - - vbo.policy = policy; + vbo.policy = instant_vesting_policy{}; }); } else { modify( *market_balance, [&block_time, &delta]( vesting_balance_object& vbo ) diff --git a/libraries/chain/include/graphene/chain/protocol/vesting.hpp b/libraries/chain/include/graphene/chain/protocol/vesting.hpp index 4915b62ec6..b5d03585de 100644 --- a/libraries/chain/include/graphene/chain/protocol/vesting.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vesting.hpp @@ -42,8 +42,15 @@ namespace graphene { namespace chain { cdd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){} }; - typedef fc::static_variant vesting_policy_initializer; + struct instant_vesting_policy_initializer + { + }; + typedef fc::static_variant< + linear_vesting_policy_initializer, + cdd_vesting_policy_initializer, + instant_vesting_policy_initializer + > vesting_policy_initializer; /** @@ -117,4 +124,5 @@ FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_b FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) ) FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vesting_seconds) ) +FC_REFLECT_EMPTY( graphene::chain::instant_vesting_policy_initializer ) FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer ) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index afe7aeb75a..1fba587dff 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -120,9 +120,27 @@ namespace graphene { namespace chain { void on_withdraw(const vesting_policy_context& ctx); }; + /** + * @brief instant vesting policy + * + * This policy allows to withdraw everything that is on a balance immediately + * + */ + struct instant_vesting_policy + { + asset get_allowed_withdraw(const vesting_policy_context& ctx)const; + bool is_deposit_allowed(const vesting_policy_context& ctx)const; + bool is_deposit_vested_allowed(const vesting_policy_context&)const { return false; } + bool is_withdraw_allowed(const vesting_policy_context& ctx)const; + void on_deposit(const vesting_policy_context& ctx); + void on_deposit_vested(const vesting_policy_context&); + void on_withdraw(const vesting_policy_context& ctx); + }; + typedef fc::static_variant< linear_vesting_policy, - cdd_vesting_policy + cdd_vesting_policy, + instant_vesting_policy > vesting_policy; enum class vesting_balance_type { unspecified, @@ -219,6 +237,8 @@ FC_REFLECT(graphene::chain::cdd_vesting_policy, (coin_seconds_earned_last_update) ) +FC_REFLECT_EMPTY( graphene::chain::instant_vesting_policy ) + FC_REFLECT_TYPENAME( graphene::chain::vesting_policy ) FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::object), diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index ee918fd16d..b0bf34f12e 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -76,6 +76,12 @@ struct init_policy_visitor policy.coin_seconds_earned_last_update = now; p = policy; } + + void operator()( const instant_vesting_policy_initializer& i )const + { + p = instant_vesting_policy{}; + } + }; object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance_create_operation& op ) diff --git a/libraries/chain/vesting_balance_object.cpp b/libraries/chain/vesting_balance_object.cpp index 73448e04c8..8735a674f5 100644 --- a/libraries/chain/vesting_balance_object.cpp +++ b/libraries/chain/vesting_balance_object.cpp @@ -157,6 +157,36 @@ bool cdd_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)c return (ctx.amount <= get_allowed_withdraw(ctx)); } +asset instant_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const +{ + return ctx.balance; +} + +void instant_vesting_policy::on_deposit(const vesting_policy_context& ctx) +{ +} + +void instant_vesting_policy::on_deposit_vested(const vesting_policy_context&) +{ + +} + +bool instant_vesting_policy::is_deposit_allowed(const vesting_policy_context& ctx)const +{ + return (ctx.amount.asset_id == ctx.balance.asset_id) + && sum_below_max_shares(ctx.amount, ctx.balance); +} + +void instant_vesting_policy::on_withdraw(const vesting_policy_context& ctx) +{ +} + +bool instant_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const +{ + return (ctx.amount.asset_id == ctx.balance.asset_id) + && (ctx.amount <= get_allowed_withdraw(ctx)); +} + #define VESTING_VISITOR(NAME, MAYBE_CONST) \ struct NAME ## _visitor \ { \ diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 5e4168562d..2afab2e489 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -2359,4 +2359,68 @@ BOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test ) // TODO: Write linear VBO tests +BOOST_AUTO_TEST_CASE( create_vesting_balance_with_instant_vesting_policy_test ) +{ try { + ACTOR(alice); + + INVOKE( create_uia ); + + const asset_object& core = asset_id_type()(db); + const asset_object& test_asset = get_asset(UIA_TEST_SYMBOL); + + vesting_balance_create_operation op; + op.fee = core.amount( 0 ); + op.creator = account_id_type(); + op.owner = account_id_type(); + op.amount = test_asset.amount( 100 ); + op.policy = instant_vesting_policy_initializer{}; + + // Fee must be non-negative + REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(1) ); + REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(0) ); + REQUIRE_OP_VALIDATION_FAILURE( op, fee, core.amount(-1) ); + + // Amount must be positive + REQUIRE_OP_VALIDATION_SUCCESS( op, amount, core.amount(1) ); + REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount(0) ); + REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount(-1) ); + + transfer(committee_account(db), alice, core.amount(100000)); + + op.creator = alice_id; + op.owner = alice_id; + + account_id_type nobody = account_id_type(1234); + + trx.operations.push_back(op); + // Invalid account_id's + REQUIRE_THROW_WITH_VALUE( op, creator, nobody ); + REQUIRE_THROW_WITH_VALUE( op, owner, nobody ); + + // Insufficient funds + REQUIRE_THROW_WITH_VALUE( op, amount, core.amount(999999999) ); + // Alice can fund a bond to herself or to Bob + op.amount = core.amount( 1000 ); + + trx.operations.back() = op; + set_expiration( db, trx ); + sign(trx, alice_private_key); + + processed_transaction ptx = PUSH_TX( db, trx, ~0 ); + const vesting_balance_id_type& vbid = ptx.operation_results.back().get(); + + { + vesting_balance_withdraw_operation withdraw_op; + withdraw_op.vesting_balance = vbid; + withdraw_op.owner = alice_id; + withdraw_op.amount = op.amount; + + signed_transaction withdraw_tx; + withdraw_tx.operations.push_back( withdraw_op ); + set_expiration( db, withdraw_tx ); + sign(withdraw_tx, alice_private_key); + PUSH_TX( db, withdraw_tx ); + } +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() From 2429cb64cb98d7dc71bf30d211f27c823a272f49 Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Fri, 16 Nov 2018 15:16:54 +0300 Subject: [PATCH 045/161] Add check for hf 1268 to proposal_create_evaluator --- libraries/chain/asset_evaluator.cpp | 25 +++--- libraries/chain/proposal_evaluator.cpp | 9 ++ tests/tests/market_fee_sharing_tests.cpp | 104 +++++++++++++++++++++++ 3 files changed, 128 insertions(+), 10 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 636626e50d..aa6688f6eb 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -35,6 +35,19 @@ #include namespace graphene { namespace chain { +namespace detail { + void check_asset_options_hf_1268(const fc::time_point_sec& block_time, const asset_options& options) + { + if( block_time < HARDFORK_1268_TIME ) + { + FC_ASSERT( !options.extensions.value.reward_percent.valid(), + "Asset extension reward percent is only available after HARDFORK_1268_TIME!"); + + FC_ASSERT( !options.extensions.value.whitelist_market_fee_sharing.valid(), + "Asset extension whitelist_market_fee_sharing is only available after HARDFORK_1268_TIME!"); + } + } +} void_result asset_create_evaluator::do_evaluate( const asset_create_operation& op ) { try { @@ -45,11 +58,7 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o FC_ASSERT( op.common_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); FC_ASSERT( op.common_options.blacklist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); - if( d.head_block_time() < HARDFORK_1268_TIME ) - { - FC_ASSERT( !op.common_options.extensions.value.reward_percent.valid(), - "Asset additional options reward percent is only available after HARDFORK_1268_TIME!"); - } + detail::check_asset_options_hf_1268(d.head_block_time(), op.common_options); // Check that all authorities do exist for( auto id : op.common_options.whitelist_authorities ) @@ -283,11 +292,7 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) validate_new_issuer( d, a, *o.new_issuer ); } - if ( o.new_options.extensions.value.reward_percent.valid() ) - { - FC_ASSERT( d.head_block_time() >= HARDFORK_1268_TIME, - "Referrer percent is only available after HARDFORK_1268_TIME!"); - } + detail::check_asset_options_hf_1268(d.head_block_time(), o.new_options); if( (d.head_block_time() < HARDFORK_572_TIME) || (a.dynamic_asset_data_id(d).current_supply != 0) ) { diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 8124cc4fda..99dd87916a 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -30,6 +30,9 @@ namespace graphene { namespace chain { +namespace detail { + void check_asset_options_hf_1268(const fc::time_point_sec& block_time, const asset_options& options); +} struct proposal_operation_hardfork_visitor { @@ -57,6 +60,12 @@ struct proposal_operation_hardfork_visitor static const std::locale &loc = std::locale::classic(); FC_ASSERT(isalpha(v.symbol.back(), loc), "Asset ${s} must end with alpha character before hardfork 620", ("s", v.symbol)); } + + detail::check_asset_options_hf_1268(block_time, v.common_options); + } + // hf_1268 + void operator()(const graphene::chain::asset_update_operation &v) const { + detail::check_asset_options_hf_1268(block_time, v.new_options); } // hf_199 void operator()(const graphene::chain::asset_update_issuer_operation &v) const { diff --git a/tests/tests/market_fee_sharing_tests.cpp b/tests/tests/market_fee_sharing_tests.cpp index 4b5d984d25..80d72cc414 100644 --- a/tests/tests/market_fee_sharing_tests.cpp +++ b/tests/tests/market_fee_sharing_tests.cpp @@ -454,4 +454,108 @@ BOOST_AUTO_TEST_CASE(white_list_doesnt_contain_registrar_test) FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(create_asset_via_proposal_test) +{ + try + { + ACTOR(issuer); + price core_exchange_rate(asset(1, asset_id_type(1)), asset(1)); + + asset_create_operation create_op; + create_op.issuer = issuer.id; + create_op.fee = asset(); + create_op.symbol = "ASSET"; + create_op.common_options.max_supply = 0; + create_op.precision = 2; + create_op.common_options.core_exchange_rate = core_exchange_rate; + create_op.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + create_op.common_options.flags = charge_market_fee; + + additional_asset_options_t options; + options.value.reward_percent = 100; + options.value.whitelist_market_fee_sharing = flat_set{issuer_id}; + create_op.common_options.extensions = std::move(options);; + + const auto& curfees = *db.get_global_properties().parameters.current_fees; + const auto& proposal_create_fees = curfees.get(); + proposal_create_operation prop; + prop.fee_paying_account = issuer_id; + prop.proposed_ops.emplace_back( create_op ); + prop.expiration_time = db.head_block_time() + fc::days(1); + prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte ); + + { + signed_transaction tx; + tx.operations.push_back( prop ); + db.current_fee_schedule().set_fee( tx.operations.back() ); + set_expiration( db, tx ); + sign( tx, issuer_private_key ); + GRAPHENE_CHECK_THROW(PUSH_TX( db, tx ), fc::exception); + } + + generate_blocks_past_reward_hardfork(); + + { + prop.expiration_time = db.head_block_time() + fc::days(1); + signed_transaction tx; + tx.operations.push_back( prop ); + db.current_fee_schedule().set_fee( tx.operations.back() ); + set_expiration( db, tx ); + sign( tx, issuer_private_key ); + PUSH_TX( db, tx ); + } + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(update_asset_via_proposal_test) +{ + try + { + ACTOR(issuer); + asset_object usd_asset = create_user_issued_asset("USD", issuer, charge_market_fee); + + additional_asset_options_t options; + options.value.reward_percent = 100; + options.value.whitelist_market_fee_sharing = flat_set{issuer_id}; + + asset_update_operation update_op; + update_op.issuer = issuer_id; + update_op.asset_to_update = usd_asset.get_id(); + asset_options new_options; + update_op.new_options = usd_asset.options; + update_op.new_options.extensions = std::move(options); + + const auto& curfees = *db.get_global_properties().parameters.current_fees; + const auto& proposal_create_fees = curfees.get(); + proposal_create_operation prop; + prop.fee_paying_account = issuer_id; + prop.proposed_ops.emplace_back( update_op ); + prop.expiration_time = db.head_block_time() + fc::days(1); + prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte ); + + { + signed_transaction tx; + tx.operations.push_back( prop ); + db.current_fee_schedule().set_fee( tx.operations.back() ); + set_expiration( db, tx ); + sign( tx, issuer_private_key ); + GRAPHENE_CHECK_THROW(PUSH_TX( db, tx ), fc::exception); + } + + generate_blocks_past_reward_hardfork(); + + { + prop.expiration_time = db.head_block_time() + fc::days(1); + signed_transaction tx; + tx.operations.push_back( prop ); + db.current_fee_schedule().set_fee( tx.operations.back() ); + set_expiration( db, tx ); + sign( tx, issuer_private_key ); + PUSH_TX( db, tx ); + } + } + FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_SUITE_END() From c7d8a5b155824f68d9db955823196de3c03319cc Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Tue, 20 Nov 2018 13:11:16 +0300 Subject: [PATCH 046/161] Check accumulated fees before/after HF_1268 --- tests/tests/market_fee_sharing_tests.cpp | 109 +++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/tests/tests/market_fee_sharing_tests.cpp b/tests/tests/market_fee_sharing_tests.cpp index 80d72cc414..df8c425eee 100644 --- a/tests/tests/market_fee_sharing_tests.cpp +++ b/tests/tests/market_fee_sharing_tests.cpp @@ -558,4 +558,113 @@ BOOST_AUTO_TEST_CASE(update_asset_via_proposal_test) FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(issue_asset){ + try + { + ACTORS((alice)(bob)(izzy)(jill)); + // Izzy issues asset to Alice (Izzycoin market percent - 10%) + // Jill issues asset to Bob (Jillcoin market percent - 20%) + + fund( alice, core_asset(1000000) ); + fund( bob, core_asset(1000000) ); + fund( izzy, core_asset(1000000) ); + fund( jill, core_asset(1000000) ); + + price price(asset(1, asset_id_type(1)), asset(1)); + constexpr auto izzycoin_market_percent = 10*GRAPHENE_1_PERCENT; + asset_object izzycoin = create_user_issued_asset( "IZZYCOIN", izzy, charge_market_fee, price, 2, izzycoin_market_percent ); + + constexpr auto jillcoin_market_percent = 20*GRAPHENE_1_PERCENT; + asset_object jillcoin = create_user_issued_asset( "JILLCOIN", jill, charge_market_fee, price, 2, jillcoin_market_percent ); + + // Alice and Bob create some coins + issue_uia( alice, izzycoin.amount( 100000 ) ); + issue_uia( bob, jillcoin.amount( 100000 ) ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(accumulated_fees_before_hf_test) +{ + try + { + INVOKE(issue_asset); + + const asset_object &jillcoin = get_asset("JILLCOIN"); + const asset_object &izzycoin = get_asset("IZZYCOIN"); + + GET_ACTOR(alice); + GET_ACTOR(bob); + + // Alice and Bob place orders which match + create_sell_order( alice_id, izzycoin.amount(100), jillcoin.amount(300) ); // Alice is willing to sell her Izzy's for 3 Jill + create_sell_order( bob_id, jillcoin.amount(700), izzycoin.amount(200) ); // Bob is buying up to 200 Izzy's for up to 3.5 Jill + + // 100 Izzys and 300 Jills are matched, so the fees should be + // 10 Izzy (10%) and 60 Jill (20%). + BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == izzycoin.amount(10).amount ); + BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == jillcoin.amount(60).amount ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(accumulated_fees_after_hf_test) +{ + try + { + INVOKE(issue_asset); + + generate_blocks_past_reward_hardfork(); + + const asset_object &jillcoin = get_asset("JILLCOIN"); + const asset_object &izzycoin = get_asset("IZZYCOIN"); + + GET_ACTOR(alice); + GET_ACTOR(bob); + + // Alice and Bob place orders which match + create_sell_order( alice_id, izzycoin.amount(100), jillcoin.amount(300) ); // Alice is willing to sell her Izzy's for 3 Jill + create_sell_order( bob_id, jillcoin.amount(700), izzycoin.amount(200) ); // Bob is buying up to 200 Izzy's for up to 3.5 Jill + + // 100 Izzys and 300 Jills are matched, so the fees should be + // 10 Izzy (10%) and 60 Jill (20%). + BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == izzycoin.amount(10).amount ); + BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == jillcoin.amount(60).amount ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(accumulated_fees_with_additional_options_after_hf_test) +{ + try + { + INVOKE(issue_asset); + + generate_blocks_past_reward_hardfork(); + + GET_ACTOR(jill); + GET_ACTOR(izzy); + + const asset_object &jillcoin = get_asset("JILLCOIN"); + const asset_object &izzycoin = get_asset("IZZYCOIN"); + + uint16_t reward_percent = 0; + update_asset(jill_id, jill_private_key, jillcoin.get_id(), reward_percent); + update_asset(izzy_id, izzy_private_key, izzycoin.get_id(), reward_percent); + + GET_ACTOR(alice); + GET_ACTOR(bob); + + // Alice and Bob place orders which match + create_sell_order( alice_id, izzycoin.amount(100), jillcoin.amount(300) ); // Alice is willing to sell her Izzy's for 3 Jill + create_sell_order( bob_id, jillcoin.amount(700), izzycoin.amount(200) ); // Bob is buying up to 200 Izzy's for up to 3.5 Jill + + // 100 Izzys and 300 Jills are matched, so the fees should be + // 10 Izzy (10%) and 60 Jill (20%). + BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == izzycoin.amount(10).amount ); + BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == jillcoin.amount(60).amount ); + } + FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_SUITE_END() From b52ea7123a0d96bbfe66dd2519178d9f386f7e5e Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Tue, 20 Nov 2018 13:16:31 +0300 Subject: [PATCH 047/161] Add hardfork protection to prevent vesting_balance_create operation with instant_vesting_policy_initializer --- libraries/chain/proposal_evaluator.cpp | 5 + libraries/chain/vesting_balance_evaluator.cpp | 13 ++ tests/tests/market_fee_sharing_tests.cpp | 138 ++++++++++++++++-- tests/tests/operation_tests.cpp | 64 -------- 4 files changed, 143 insertions(+), 77 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 99dd87916a..975d14cb54 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -32,6 +32,7 @@ namespace graphene { namespace chain { namespace detail { void check_asset_options_hf_1268(const fc::time_point_sec& block_time, const asset_options& options); + void check_vesting_balance_policy_hf_1268(const fc::time_point_sec& block_time, const vesting_policy_initializer& policy); } struct proposal_operation_hardfork_visitor @@ -67,6 +68,10 @@ struct proposal_operation_hardfork_visitor void operator()(const graphene::chain::asset_update_operation &v) const { detail::check_asset_options_hf_1268(block_time, v.new_options); } + // hf_1268 + void operator()(const graphene::chain::vesting_balance_create_operation &v) const { + detail::check_vesting_balance_policy_hf_1268(block_time, v.policy); + } // hf_199 void operator()(const graphene::chain::asset_update_issuer_operation &v) const { if (block_time < HARDFORK_CORE_199_TIME) { diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index b0bf34f12e..6f05343c24 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -26,8 +26,19 @@ #include #include #include +#include namespace graphene { namespace chain { +namespace detail { + void check_vesting_balance_policy_hf_1268(const fc::time_point_sec& block_time, const vesting_policy_initializer& policy) + { + if( block_time < HARDFORK_1268_TIME ) + { + FC_ASSERT( policy.which() != vesting_policy_initializer::tag::value, + "Instant vesting policy is only available after HARDFORK_1268_TIME!"); + } + } +} void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance_create_operation& op ) { try { @@ -42,6 +53,8 @@ void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance FC_ASSERT( d.get_balance( creator_account.id, op.amount.asset_id ) >= op.amount ); FC_ASSERT( !op.amount.asset_id(d).is_transfer_restricted() ); + detail::check_vesting_balance_policy_hf_1268(d.head_block_time(), op.policy); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/tests/tests/market_fee_sharing_tests.cpp b/tests/tests/market_fee_sharing_tests.cpp index df8c425eee..d0544b64ae 100644 --- a/tests/tests/market_fee_sharing_tests.cpp +++ b/tests/tests/market_fee_sharing_tests.cpp @@ -51,7 +51,7 @@ struct reward_database_fixture : database_fixture PUSH_TX( db, tx ); } - void generate_blocks_past_reward_hardfork() + void generate_blocks_past_hf1268() { database_fixture::generate_blocks( HARDFORK_1268_TIME ); database_fixture::generate_block(); @@ -65,7 +65,7 @@ struct reward_database_fixture : database_fixture const share_type core_precision = asset::scaled_precision( asset_id_type()(db).precision ); }; -BOOST_FIXTURE_TEST_SUITE( reward_tests, reward_database_fixture ) +BOOST_FIXTURE_TEST_SUITE( fee_sharing_tests, reward_database_fixture ) BOOST_AUTO_TEST_CASE(cannot_create_asset_with_additional_options_before_hf) { @@ -98,7 +98,7 @@ BOOST_AUTO_TEST_CASE(create_asset_with_additional_options_after_hf) { ACTOR(issuer); - generate_blocks_past_reward_hardfork(); + generate_blocks_past_hf1268(); uint16_t reward_percent = 100; flat_set whitelist = {issuer_id}; @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(update_additional_options_after_hf) asset_object usd_asset = create_user_issued_asset("USD", issuer, charge_market_fee); - generate_blocks_past_reward_hardfork(); + generate_blocks_past_hf1268(); uint16_t reward_percent = 40; flat_set whitelist = {issuer_id}; @@ -201,7 +201,7 @@ BOOST_AUTO_TEST_CASE(asset_rewards_test) asset_id_type izzycoin_id = create_bitasset( "IZZYCOIN", izzy_id, izzycoin_market_percent ).id; asset_id_type jillcoin_id = create_bitasset( "JILLCOIN", jill_id, jillcoin_market_percent ).id; - generate_blocks_past_reward_hardfork(); + generate_blocks_past_hf1268(); update_asset(izzy_id, izzy_private_key, izzycoin_id, izzycoin_reward_percent); update_asset(jill_id, jill_private_key, jillcoin_id, jillcoin_reward_percent); @@ -300,7 +300,7 @@ BOOST_AUTO_TEST_CASE(asset_claim_reward_test) transfer( committee_account, bob.get_id(), core_asset(1000) ); transfer( committee_account, izzy.get_id(), core_asset(1000) ); - generate_blocks_past_reward_hardfork(); + generate_blocks_past_hf1268(); // update_asset: set referrer percent update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent); @@ -369,7 +369,7 @@ BOOST_AUTO_TEST_CASE(white_list_is_empty_test) { INVOKE(create_actors); - generate_blocks_past_reward_hardfork(); + generate_blocks_past_hf1268(); GET_ACTOR(jill); constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT; @@ -399,7 +399,7 @@ BOOST_AUTO_TEST_CASE(white_list_contains_registrar_test) { INVOKE(create_actors); - generate_blocks_past_reward_hardfork(); + generate_blocks_past_hf1268(); GET_ACTOR(jill); constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT; @@ -430,7 +430,7 @@ BOOST_AUTO_TEST_CASE(white_list_doesnt_contain_registrar_test) { INVOKE(create_actors); - generate_blocks_past_reward_hardfork(); + generate_blocks_past_hf1268(); GET_ACTOR(jill); constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT; @@ -493,7 +493,7 @@ BOOST_AUTO_TEST_CASE(create_asset_via_proposal_test) GRAPHENE_CHECK_THROW(PUSH_TX( db, tx ), fc::exception); } - generate_blocks_past_reward_hardfork(); + generate_blocks_past_hf1268(); { prop.expiration_time = db.head_block_time() + fc::days(1); @@ -543,7 +543,7 @@ BOOST_AUTO_TEST_CASE(update_asset_via_proposal_test) GRAPHENE_CHECK_THROW(PUSH_TX( db, tx ), fc::exception); } - generate_blocks_past_reward_hardfork(); + generate_blocks_past_hf1268(); { prop.expiration_time = db.head_block_time() + fc::days(1); @@ -614,7 +614,7 @@ BOOST_AUTO_TEST_CASE(accumulated_fees_after_hf_test) { INVOKE(issue_asset); - generate_blocks_past_reward_hardfork(); + generate_blocks_past_hf1268(); const asset_object &jillcoin = get_asset("JILLCOIN"); const asset_object &izzycoin = get_asset("IZZYCOIN"); @@ -640,7 +640,7 @@ BOOST_AUTO_TEST_CASE(accumulated_fees_with_additional_options_after_hf_test) { INVOKE(issue_asset); - generate_blocks_past_reward_hardfork(); + generate_blocks_past_hf1268(); GET_ACTOR(jill); GET_ACTOR(izzy); @@ -667,4 +667,116 @@ BOOST_AUTO_TEST_CASE(accumulated_fees_with_additional_options_after_hf_test) FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( create_vesting_balance_with_instant_vesting_policy_before_hf1268_test ) +{ try { + + ACTOR(alice); + fund(alice); + + const asset_object& core = asset_id_type()(db); + + vesting_balance_create_operation op; + op.fee = core.amount( 0 ); + op.creator = alice_id; + op.owner = alice_id; + op.amount = core.amount( 100 ); + op.policy = instant_vesting_policy_initializer{}; + + trx.operations.push_back(op); + set_expiration( db, trx ); + sign(trx, alice_private_key); + + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( create_vesting_balance_with_instant_vesting_policy_after_hf1268_test ) +{ try { + + ACTOR(alice); + fund(alice); + + generate_blocks_past_hf1268(); + + const asset_object& core = asset_id_type()(db); + + vesting_balance_create_operation op; + op.fee = core.amount( 0 ); + op.creator = alice_id; + op.owner = alice_id; + op.amount = core.amount( 100 ); + op.policy = instant_vesting_policy_initializer{}; + + trx.operations.push_back(op); + set_expiration( db, trx ); + + processed_transaction ptx = PUSH_TX( db, trx, ~0 ); + const vesting_balance_id_type& vbid = ptx.operation_results.back().get(); + + auto withdraw = [&](const asset& amount) { + vesting_balance_withdraw_operation withdraw_op; + withdraw_op.vesting_balance = vbid; + withdraw_op.owner = alice_id; + withdraw_op.amount = amount; + + signed_transaction withdraw_tx; + withdraw_tx.operations.push_back( withdraw_op ); + set_expiration( db, withdraw_tx ); + sign(withdraw_tx, alice_private_key); + PUSH_TX( db, withdraw_tx ); + }; + // try to withdraw more then it is on the balance + GRAPHENE_REQUIRE_THROW(withdraw(op.amount.amount + 1), fc::exception); + //to withdraw all that is on the balance + withdraw(op.amount); + // try to withdraw more then it is on the balance + GRAPHENE_REQUIRE_THROW(withdraw( core.amount(1) ), fc::exception); +} FC_LOG_AND_RETHROW() } + + +BOOST_AUTO_TEST_CASE( create_vesting_balance_with_instant_vesting_policy_via_proposal_test ) +{ try { + + ACTOR(actor); + fund(actor); + + const asset_object& core = asset_id_type()(db); + + vesting_balance_create_operation create_op; + create_op.fee = core.amount( 0 ); + create_op.creator = actor_id; + create_op.owner = actor_id; + create_op.amount = core.amount( 100 ); + create_op.policy = instant_vesting_policy_initializer{}; + + const auto& curfees = *db.get_global_properties().parameters.current_fees; + const auto& proposal_create_fees = curfees.get(); + proposal_create_operation prop; + prop.fee_paying_account = actor_id; + prop.proposed_ops.emplace_back( create_op ); + prop.expiration_time = db.head_block_time() + fc::days(1); + prop.fee = asset( proposal_create_fees.fee + proposal_create_fees.price_per_kbyte ); + + { + signed_transaction tx; + tx.operations.push_back( prop ); + db.current_fee_schedule().set_fee( tx.operations.back() ); + set_expiration( db, tx ); + sign( tx, actor_private_key ); + GRAPHENE_CHECK_THROW(PUSH_TX( db, tx ), fc::exception); + } + + generate_blocks_past_hf1268(); + + { + prop.expiration_time = db.head_block_time() + fc::days(1); + signed_transaction tx; + tx.operations.push_back( prop ); + db.current_fee_schedule().set_fee( tx.operations.back() ); + set_expiration( db, tx ); + sign( tx, actor_private_key ); + PUSH_TX( db, tx ); + } +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 2afab2e489..5e4168562d 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -2359,68 +2359,4 @@ BOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test ) // TODO: Write linear VBO tests -BOOST_AUTO_TEST_CASE( create_vesting_balance_with_instant_vesting_policy_test ) -{ try { - ACTOR(alice); - - INVOKE( create_uia ); - - const asset_object& core = asset_id_type()(db); - const asset_object& test_asset = get_asset(UIA_TEST_SYMBOL); - - vesting_balance_create_operation op; - op.fee = core.amount( 0 ); - op.creator = account_id_type(); - op.owner = account_id_type(); - op.amount = test_asset.amount( 100 ); - op.policy = instant_vesting_policy_initializer{}; - - // Fee must be non-negative - REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(1) ); - REQUIRE_OP_VALIDATION_SUCCESS( op, fee, core.amount(0) ); - REQUIRE_OP_VALIDATION_FAILURE( op, fee, core.amount(-1) ); - - // Amount must be positive - REQUIRE_OP_VALIDATION_SUCCESS( op, amount, core.amount(1) ); - REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount(0) ); - REQUIRE_OP_VALIDATION_FAILURE( op, amount, core.amount(-1) ); - - transfer(committee_account(db), alice, core.amount(100000)); - - op.creator = alice_id; - op.owner = alice_id; - - account_id_type nobody = account_id_type(1234); - - trx.operations.push_back(op); - // Invalid account_id's - REQUIRE_THROW_WITH_VALUE( op, creator, nobody ); - REQUIRE_THROW_WITH_VALUE( op, owner, nobody ); - - // Insufficient funds - REQUIRE_THROW_WITH_VALUE( op, amount, core.amount(999999999) ); - // Alice can fund a bond to herself or to Bob - op.amount = core.amount( 1000 ); - - trx.operations.back() = op; - set_expiration( db, trx ); - sign(trx, alice_private_key); - - processed_transaction ptx = PUSH_TX( db, trx, ~0 ); - const vesting_balance_id_type& vbid = ptx.operation_results.back().get(); - - { - vesting_balance_withdraw_operation withdraw_op; - withdraw_op.vesting_balance = vbid; - withdraw_op.owner = alice_id; - withdraw_op.amount = op.amount; - - signed_transaction withdraw_tx; - withdraw_tx.operations.push_back( withdraw_op ); - set_expiration( db, withdraw_tx ); - sign(withdraw_tx, alice_private_key); - PUSH_TX( db, withdraw_tx ); - } -} FC_LOG_AND_RETHROW() } - BOOST_AUTO_TEST_SUITE_END() From 7a528039a753d883ebc3d35c2e5f733367e7a7b6 Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Tue, 27 Nov 2018 16:39:50 +0300 Subject: [PATCH 048/161] Remove function [get_mfs_vesting_balances] --- libraries/app/database_api.cpp | 27 ------------------- .../app/include/graphene/app/database_api.hpp | 3 --- .../wallet/include/graphene/wallet/wallet.hpp | 8 ------ libraries/wallet/wallet.cpp | 23 ---------------- 4 files changed, 61 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 59a46fac79..acb90c8af5 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -96,7 +96,6 @@ class database_api_impl : public std::enable_shared_from_this vector get_balance_objects( const vector
& addrs )const; vector get_vested_balances( const vector& objs )const; vector get_vesting_balances( const std::string account_id_or_name )const; - vector get_mfs_vesting_balances( const std::string account_id_or_name )const; // Assets vector> get_assets(const vector& asset_ids)const; @@ -1027,11 +1026,6 @@ vector database_api::get_vesting_balances( const std::st return my->get_vesting_balances( account_id_or_name ); } -vector database_api::get_mfs_vesting_balances( const std::string account_id_or_name )const -{ - return my->get_mfs_vesting_balances( account_id_or_name ); -} - vector database_api_impl::get_vesting_balances( const std::string account_id_or_name )const { try @@ -1048,27 +1042,6 @@ vector database_api_impl::get_vesting_balances( const st FC_CAPTURE_AND_RETHROW( (account_id_or_name) ); } -vector database_api_impl::get_mfs_vesting_balances( const std::string account_id_or_name )const -{ - try - { - const account_id_type account_id = get_account_from_string(account_id_or_name)->id; - vector result; - - auto& vesting_balances = _db.get_index_type().indices().get(); - auto key = boost::make_tuple(account_id, vesting_balance_type::market_fee_sharing); - auto mfs_vesting_range = vesting_balances.equal_range(key); - - std::for_each(mfs_vesting_range.first, mfs_vesting_range.second, - [&result](const vesting_balance_object& balance) { - result.emplace_back(balance); - }); - - return result; - } - FC_CAPTURE_AND_RETHROW( (account_id_or_name) ); -} - ////////////////////////////////////////////////////////////////////// // // // Assets // diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 601f6d67ed..506b427597 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -364,8 +364,6 @@ class database_api vector get_vesting_balances( const std::string account_id_or_name )const; - vector get_mfs_vesting_balances( const std::string account_id_or_name )const; - /** * @brief Get the total number of accounts registered with the blockchain */ @@ -786,7 +784,6 @@ FC_API(graphene::app::database_api, (get_balance_objects) (get_vested_balances) (get_vesting_balances) - (get_mfs_vesting_balances) // Assets (get_assets) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 4f059b386d..ca13357f0c 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1455,13 +1455,6 @@ class wallet_api */ vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ); - /** List account's market fee sharing vesting balances. - * Each account can have multiple market fee sharing vesting balances. - * @param account_name_or_id the name or id of the account whose balances you want - * @returns a list of the given account's market fee sharing vesting balances - */ - vector< vesting_balance_object_with_info > get_mfs_vesting_balances(string account_name_or_id); - /** * Withdraw a vesting balance. * @@ -1806,7 +1799,6 @@ FC_API( graphene::wallet::wallet_api, (create_worker) (update_worker_votes) (get_vesting_balances) - (get_mfs_vesting_balances) (withdraw_vesting) (vote_for_committee_member) (vote_for_witness) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 04027083ef..ac33c180e9 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1765,24 +1765,6 @@ class wallet_api_impl } FC_CAPTURE_AND_RETHROW( (account_name) ) } - vector get_mfs_vesting_balances(string account_name_or_id) const - { try { - fc::time_point_sec now = _remote_db->get_dynamic_global_properties().time; - - auto account = get_account(account_name_or_id); - auto always_id = account_id_to_string(account.id); - - vector vbos = _remote_db->get_mfs_vesting_balances(always_id); - - std::vector result; - for (const vesting_balance_object& vbo : vbos) - { - result.emplace_back(vbo, now); - } - return result; - } FC_CAPTURE_AND_RETHROW( (account_name_or_id) ) - } - signed_transaction withdraw_vesting( string witness_name, string amount, @@ -3712,11 +3694,6 @@ vector< vesting_balance_object_with_info > wallet_api::get_vesting_balances( str return my->get_vesting_balances( account_name ); } -vector wallet_api::get_mfs_vesting_balances(string account_name_or_id) -{ - return my->get_mfs_vesting_balances(account_name_or_id); -} - signed_transaction wallet_api::withdraw_vesting( string witness_name, string amount, From 59b41905530aa3ae2efd9fbb89b0be769b0c555b Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Tue, 27 Nov 2018 16:42:15 +0300 Subject: [PATCH 049/161] Check asset restrictions - added check asset restrictions while pay reward - added unit test --- libraries/chain/db_market.cpp | 7 +- tests/common/database_fixture.cpp | 8 +- tests/common/database_fixture.hpp | 6 +- tests/tests/market_fee_sharing_tests.cpp | 213 ++++++++++++++++++++--- 4 files changed, 196 insertions(+), 38 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 08ebad0ade..8feae4e0ef 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -1161,7 +1162,7 @@ asset database::pay_market_fees(const account_object& seller, const asset_object auto is_rewards_allowed = [&recv_asset, &seller]() { const auto &white_list = recv_asset.options.extensions.value.whitelist_market_fee_sharing; - return ( !white_list || (*white_list).empty() || ( (*white_list).find(seller.referrer) != (*white_list).end() ) ); + return ( !white_list || (*white_list).empty() || ( (*white_list).find(seller.registrar) != (*white_list).end() ) ); }; if ( is_rewards_allowed() ) @@ -1170,7 +1171,7 @@ asset database::pay_market_fees(const account_object& seller, const asset_object if ( reward_percent && *reward_percent ) { const auto reward_value = detail::calculate_percent(issuer_fees.amount, *reward_percent); - if ( reward_value > 0 ) + if ( reward_value > 0 && is_authorized_asset(*this, seller.registrar(*this), recv_asset) ) { reward = recv_asset.amount(reward_value); FC_ASSERT( reward < issuer_fees, "Market reward should be less than issuer fees"); @@ -1179,7 +1180,7 @@ asset database::pay_market_fees(const account_object& seller, const asset_object const auto referrer_rewards_value = detail::calculate_percent(reward.amount, referrer_rewards_percentage); auto registrar_reward = reward; - if ( referrer_rewards_value > 0 ) + if ( referrer_rewards_value > 0 && is_authorized_asset(*this, seller.referrer(*this), recv_asset)) { FC_ASSERT ( referrer_rewards_value <= reward.amount, "Referrer reward shouldn't be greater than total reward" ); const asset referrer_reward = recv_asset.amount(referrer_rewards_value); diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 6c633c4729..7413867d07 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -376,7 +376,7 @@ account_create_operation database_fixture::make_account( const std::string& name, const account_object& registrar, const account_object& referrer, - uint8_t referrer_percent /* = 100 */, + uint16_t referrer_percent /* = 100 */, public_key_type key /* = public_key_type() */ ) { @@ -600,7 +600,7 @@ const account_object& database_fixture::create_account( const string& name, const account_object& registrar, const account_object& referrer, - uint8_t referrer_percent /* = 100 */, + uint16_t referrer_percent /* = 100 (1%)*/, const public_key_type& key /*= public_key_type()*/ ) { @@ -622,7 +622,7 @@ const account_object& database_fixture::create_account( const private_key_type& key, const account_id_type& registrar_id /* = account_id_type() */, const account_id_type& referrer_id /* = account_id_type() */, - uint8_t referrer_percent /* = 100 */ + uint16_t referrer_percent /* = 100 (1%)*/ ) { try @@ -632,6 +632,8 @@ const account_object& database_fixture::create_account( account_create_operation account_create_op; account_create_op.registrar = registrar_id; + account_create_op.referrer = referrer_id; + account_create_op.referrer_percent = referrer_percent; account_create_op.name = name; account_create_op.owner = authority(1234, public_key_type(key.get_public_key()), 1234); account_create_op.active = authority(5678, public_key_type(key.get_public_key()), 5678); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 651d5f29cc..13e4ead075 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -221,7 +221,7 @@ struct database_fixture { const std::string& name, const account_object& registrar, const account_object& referrer, - uint8_t referrer_percent = 100, + uint16_t referrer_percent = 100, public_key_type key = public_key_type() ); @@ -300,7 +300,7 @@ struct database_fixture { const string& name, const account_object& registrar, const account_object& referrer, - uint8_t referrer_percent = 100, + uint16_t referrer_percent = 100, const public_key_type& key = public_key_type() ); @@ -309,7 +309,7 @@ struct database_fixture { const private_key_type& key, const account_id_type& registrar_id = account_id_type(), const account_id_type& referrer_id = account_id_type(), - uint8_t referrer_percent = 100 + uint16_t referrer_percent = 100 ); const committee_member_object& create_committee_member( const account_object& owner ); diff --git a/tests/tests/market_fee_sharing_tests.cpp b/tests/tests/market_fee_sharing_tests.cpp index d0544b64ae..143f2a78d6 100644 --- a/tests/tests/market_fee_sharing_tests.cpp +++ b/tests/tests/market_fee_sharing_tests.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "../common/database_fixture.hpp" @@ -31,10 +32,11 @@ struct reward_database_fixture : database_fixture } void update_asset( const account_id_type& issuer_id, - const fc::ecc::private_key& private_key, - const asset_id_type& asset_id, - uint16_t reward_percent, - const whitelist_market_fee_sharing_t &whitelist_market_fee_sharing = whitelist_market_fee_sharing_t{}) + const fc::ecc::private_key& private_key, + const asset_id_type& asset_id, + uint16_t reward_percent, + const whitelist_market_fee_sharing_t &whitelist_market_fee_sharing = whitelist_market_fee_sharing_t{}, + const flat_set &blacklist = flat_set()) { asset_update_operation op; op.issuer = issuer_id; @@ -42,6 +44,7 @@ struct reward_database_fixture : database_fixture op.new_options = asset_id(db).options; op.new_options.extensions.value.reward_percent = reward_percent; op.new_options.extensions.value.whitelist_market_fee_sharing = whitelist_market_fee_sharing; + op.new_options.blacklist_authorities = blacklist; signed_transaction tx; tx.operations.push_back( op ); @@ -51,6 +54,42 @@ struct reward_database_fixture : database_fixture PUSH_TX( db, tx ); } + void asset_update_blacklist_authority(const account_id_type& issuer_id, + const asset_id_type& asset_id, + const account_id_type& authority_account_id, + const fc::ecc::private_key& issuer_private_key) + { + asset_update_operation uop; + uop.issuer = issuer_id; + uop.asset_to_update = asset_id; + uop.new_options = asset_id(db).options; + uop.new_options.blacklist_authorities.insert(authority_account_id); + + signed_transaction tx; + tx.operations.push_back( uop ); + db.current_fee_schedule().set_fee( tx.operations.back() ); + set_expiration( db, tx ); + sign( tx, issuer_private_key ); + PUSH_TX( db, tx ); + } + + void add_account_to_blacklist(const account_id_type& authorizing_account_id, + const account_id_type& blacklisted_account_id, + const fc::ecc::private_key& authorizing_account_private_key) + { + account_whitelist_operation wop; + wop.authorizing_account = authorizing_account_id; + wop.account_to_list = blacklisted_account_id; + wop.new_listing = account_whitelist_operation::black_listed; + + signed_transaction tx; + tx.operations.push_back( wop ); + db.current_fee_schedule().set_fee( tx.operations.back() ); + set_expiration( db, tx ); + sign( tx, authorizing_account_private_key ); + PUSH_TX( db, tx); + } + void generate_blocks_past_hf1268() { database_fixture::generate_blocks( HARDFORK_1268_TIME ); @@ -170,7 +209,7 @@ BOOST_AUTO_TEST_CASE(asset_rewards_test) auto register_account = [&](const string& name, const account_object& referrer) -> const account_object& { - uint8_t referrer_percent = 100; + uint16_t referrer_percent = GRAPHENE_1_PERCENT; fc::ecc::private_key _private_key = generate_private_key(name); public_key_type _public_key = _private_key.get_public_key(); return create_account(name, registrar, referrer, referrer_percent, _public_key); @@ -247,7 +286,7 @@ BOOST_AUTO_TEST_CASE(asset_rewards_test) share_type alice_refereer_reward = get_market_fee_reward( alice.referrer, jillcoin_id ); // Bob's and Alice's registrars should get rewards - share_type bob_rgistrar_reward = get_market_fee_reward( bob.registrar, izzycoin_id ); + share_type bob_registrar_reward = get_market_fee_reward( bob.registrar, izzycoin_id ); share_type alice_registrar_reward = get_market_fee_reward( alice.registrar, jillcoin_id ); auto calculate_percent = [](const share_type& value, uint16_t percent) @@ -260,12 +299,12 @@ BOOST_AUTO_TEST_CASE(asset_rewards_test) BOOST_CHECK_GT( bob_refereer_reward, 0 ); BOOST_CHECK_GT( alice_refereer_reward, 0 ); - BOOST_CHECK_GT( bob_rgistrar_reward, 0 ); + BOOST_CHECK_GT( bob_registrar_reward, 0 ); BOOST_CHECK_GT( alice_registrar_reward, 0 ); const auto izzycoin_market_fee = calculate_percent(_izzy(1000).amount, izzycoin_market_percent); const auto izzycoin_reward = calculate_percent(izzycoin_market_fee, izzycoin_reward_percent); - BOOST_CHECK_EQUAL( izzycoin_reward, bob_refereer_reward + bob_rgistrar_reward ); + BOOST_CHECK_EQUAL( izzycoin_reward, bob_refereer_reward + bob_registrar_reward ); BOOST_CHECK_EQUAL( calculate_percent(izzycoin_reward, bob.referrer_rewards_percentage), bob_refereer_reward ); const auto jillcoin_market_fee = calculate_percent(_jill(1500).amount, jillcoin_market_percent); @@ -287,11 +326,10 @@ BOOST_AUTO_TEST_CASE(asset_claim_reward_test) price price(asset(1, asset_id_type(1)), asset(1)); uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT; - auto obj = jill_id(db); const asset_object jillcoin = create_user_issued_asset( "JCOIN", jill, charge_market_fee, price, 2, market_fee_percent ); - const account_object alice = create_account("alice", izzy, izzy, 50); - const account_object bob = create_account("bob", izzy, izzy, 50); + const account_object alice = create_account("alice", izzy, izzy, 50/*0.5%*/); + const account_object bob = create_account("bob", izzy, izzy, 50/*0.5%*/); // prepare users' balance issue_uia( alice, jillcoin.amount( 20000000 ) ); @@ -341,24 +379,26 @@ BOOST_AUTO_TEST_CASE(create_actors) { try { - ACTORS((jill)(izzy)); + ACTORS((jill)(izzyregistrar)(izzyreferrer)); - upgrade_to_lifetime_member(izzy); + upgrade_to_lifetime_member(izzyregistrar); + upgrade_to_lifetime_member(izzyreferrer); price price(asset(1, asset_id_type(1)), asset(1)); uint16_t market_fee_percent = 20 * GRAPHENE_1_PERCENT; auto obj = jill_id(db); const asset_object jillcoin = create_user_issued_asset( "JCOIN", jill, charge_market_fee, price, 2, market_fee_percent ); - const account_object alice = create_account("alice", izzy, izzy, 50); - const account_object bob = create_account("bob", izzy, izzy, 50); + const account_object alice = create_account("alice", izzyregistrar, izzyreferrer, 50/*0.5%*/); + const account_object bob = create_account("bob", izzyregistrar, izzyreferrer, 50/*0.5%*/); // prepare users' balance issue_uia( alice, jillcoin.amount( 20000000 ) ); transfer( committee_account, alice.get_id(), core_asset(1000) ); transfer( committee_account, bob.get_id(), core_asset(1000) ); - transfer( committee_account, izzy.get_id(), core_asset(1000) ); + transfer( committee_account, izzyregistrar.get_id(), core_asset(1000) ); + transfer( committee_account, izzyreferrer.get_id(), core_asset(1000) ); } FC_LOG_AND_RETHROW() } @@ -378,8 +418,10 @@ BOOST_AUTO_TEST_CASE(white_list_is_empty_test) flat_set whitelist; update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist); - GET_ACTOR(izzy); - BOOST_CHECK_THROW( get_market_fee_reward( izzy, jillcoin ), fc::exception ); + GET_ACTOR(izzyregistrar); + GET_ACTOR(izzyreferrer); + BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); + BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); GET_ACTOR(alice); GET_ACTOR(bob); @@ -387,8 +429,10 @@ BOOST_AUTO_TEST_CASE(white_list_is_empty_test) create_sell_order( alice, jillcoin.amount(200000), core_asset(1) ); create_sell_order( bob, core_asset(1), jillcoin.amount(100000) ); - const auto izzy_reward = get_market_fee_reward( izzy, jillcoin ); - BOOST_CHECK_GT(izzy_reward , 0); + const auto izzyregistrar_reward = get_market_fee_reward( izzyregistrar, jillcoin ); + const auto izzyreferrer_reward = get_market_fee_reward( izzyreferrer, jillcoin ); + BOOST_CHECK_GT(izzyregistrar_reward , 0); + BOOST_CHECK_GT(izzyreferrer_reward , 0); } FC_LOG_AND_RETHROW() } @@ -405,12 +449,49 @@ BOOST_AUTO_TEST_CASE(white_list_contains_registrar_test) constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT; const asset_object &jillcoin = get_asset("JCOIN"); - GET_ACTOR(izzy); - flat_set whitelist = {jill_id, izzy_id}; + GET_ACTOR(izzyregistrar); + GET_ACTOR(izzyreferrer); + flat_set whitelist = {jill_id, izzyregistrar_id}; + + update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist); + + BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); + BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); + + GET_ACTOR(alice); + GET_ACTOR(bob); + // Alice and Bob place orders which match + create_sell_order( alice, jillcoin.amount(200000), core_asset(1) ); + create_sell_order( bob, core_asset(1), jillcoin.amount(100000) ); + + const auto izzyregistrar_reward = get_market_fee_reward( izzyregistrar, jillcoin ); + const auto izzyreferrer_reward = get_market_fee_reward( izzyreferrer, jillcoin ); + BOOST_CHECK_GT(izzyregistrar_reward , 0); + BOOST_CHECK_GT(izzyreferrer_reward , 0); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(white_list_contains_referrer_test) +{ + try + { + INVOKE(create_actors); + + generate_blocks_past_hf1268(); + GET_ACTOR(jill); + + constexpr auto jillcoin_reward_percent = 2*GRAPHENE_1_PERCENT; + const asset_object &jillcoin = get_asset("JCOIN"); + + GET_ACTOR(izzyregistrar); + GET_ACTOR(izzyreferrer); + flat_set whitelist = {jill_id, izzyreferrer_id}; update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist); - BOOST_CHECK_THROW( get_market_fee_reward( izzy, jillcoin ), fc::exception ); + BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); + BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); GET_ACTOR(alice); GET_ACTOR(bob); @@ -418,8 +499,8 @@ BOOST_AUTO_TEST_CASE(white_list_contains_registrar_test) create_sell_order( alice, jillcoin.amount(200000), core_asset(1) ); create_sell_order( bob, core_asset(1), jillcoin.amount(100000) ); - const auto izzy_reward = get_market_fee_reward( izzy, jillcoin ); - BOOST_CHECK_GT(izzy_reward , 0); + BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); + BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); } FC_LOG_AND_RETHROW() } @@ -441,15 +522,18 @@ BOOST_AUTO_TEST_CASE(white_list_doesnt_contain_registrar_test) update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist); - GET_ACTOR(izzy); - BOOST_CHECK_THROW( get_market_fee_reward( izzy, jillcoin ), fc::exception ); + GET_ACTOR(izzyregistrar); + GET_ACTOR(izzyreferrer); + BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); + BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); GET_ACTOR(bob); // Alice and Bob place orders which match create_sell_order( alice, jillcoin.amount(200000), core_asset(1) ); create_sell_order( bob, core_asset(1), jillcoin.amount(100000) ); - BOOST_CHECK_THROW( get_market_fee_reward( izzy, jillcoin ), fc::exception ); + BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); + BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); } FC_LOG_AND_RETHROW() } @@ -733,7 +817,6 @@ BOOST_AUTO_TEST_CASE( create_vesting_balance_with_instant_vesting_policy_after_h GRAPHENE_REQUIRE_THROW(withdraw( core.amount(1) ), fc::exception); } FC_LOG_AND_RETHROW() } - BOOST_AUTO_TEST_CASE( create_vesting_balance_with_instant_vesting_policy_via_proposal_test ) { try { @@ -779,4 +862,76 @@ BOOST_AUTO_TEST_CASE( create_vesting_balance_with_instant_vesting_policy_via_pro } } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(white_list_asset_rewards_test) +{ + try + { + ACTORS((aliceregistrar)(bobregistrar)(alicereferrer)(bobreferrer)(izzy)(jill)); + + // Izzy issues white_list asset to Alice + // Jill issues white_list asset to Bob + // Bobreferrer added to blacklist for izzycoin asset + // Aliceregistrar added to blacklist for jillcoin asset + // Alice and Bob trade in the market and pay fees + // Check registrar/referrer rewards + upgrade_to_lifetime_member(aliceregistrar); + upgrade_to_lifetime_member(alicereferrer); + upgrade_to_lifetime_member(bobregistrar); + upgrade_to_lifetime_member(bobreferrer); + upgrade_to_lifetime_member(izzy); + upgrade_to_lifetime_member(jill); + + const account_object alice = create_account("alice", aliceregistrar, alicereferrer, 20*GRAPHENE_1_PERCENT); + const account_object bob = create_account("bob", bobregistrar, bobreferrer, 20*GRAPHENE_1_PERCENT); + + fund( alice, core_asset(1000000) ); + fund( bob, core_asset(1000000) ); + fund( izzy, core_asset(1000000) ); + fund( jill, core_asset(1000000) ); + + price price(asset(1, asset_id_type(1)), asset(1)); + constexpr auto izzycoin_market_percent = 10*GRAPHENE_1_PERCENT; + constexpr auto jillcoin_market_percent = 20*GRAPHENE_1_PERCENT; + const asset_id_type izzycoin_id = create_user_issued_asset( "IZZYCOIN", izzy, charge_market_fee|white_list, price, 0, izzycoin_market_percent ).id; + const asset_id_type jillcoin_id = create_user_issued_asset( "JILLCOIN", jill, charge_market_fee|white_list, price, 0, jillcoin_market_percent ).id; + + // Alice and Bob create some coins + issue_uia( alice, izzycoin_id(db).amount( 200000 ) ); + issue_uia( bob, jillcoin_id(db).amount( 200000 ) ); + + generate_blocks_past_hf1268(); + + constexpr auto izzycoin_reward_percent = 50*GRAPHENE_1_PERCENT; + constexpr auto jillcoin_reward_percent = 50*GRAPHENE_1_PERCENT; + + update_asset(izzy_id, izzy_private_key, izzycoin_id, izzycoin_reward_percent); + update_asset(jill_id, jill_private_key, jillcoin_id, jillcoin_reward_percent); + + BOOST_TEST_MESSAGE( "Attempting to blacklist bobreferrer for izzycoin asset" ); + asset_update_blacklist_authority(izzy_id, izzycoin_id, izzy_id, izzy_private_key); + add_account_to_blacklist(izzy_id, bobreferrer_id, izzy_private_key); + BOOST_CHECK( !(is_authorized_asset( db, bobreferrer_id(db), izzycoin_id(db) )) ); + + BOOST_TEST_MESSAGE( "Attempting to blacklist aliceregistrar for jillcoin asset" ); + asset_update_blacklist_authority(jill_id, jillcoin_id, jill_id, jill_private_key); + add_account_to_blacklist(jill_id, aliceregistrar_id, jill_private_key); + BOOST_CHECK( !(is_authorized_asset( db, aliceregistrar_id(db), jillcoin_id(db) )) ); + + // Alice and Bob place orders which match + create_sell_order( alice.id, izzycoin_id(db).amount(1000), jillcoin_id(db).amount(1500) ); // Alice is willing to sell her 1000 Izzy's for 1.5 Jill + create_sell_order( bob.id, jillcoin_id(db).amount(1500), izzycoin_id(db).amount(1000) ); // Bob is buying up to 1500 Izzy's for up to 0.6 Jill + + // 1000 Izzys and 1500 Jills are matched, so the fees should be + // 100 Izzy (10%) and 300 Jill (20%). + + // Only Bob's registrar should get rewards + share_type bob_registrar_reward = get_market_fee_reward( bob.registrar, izzycoin_id ); + BOOST_CHECK_GT( bob_registrar_reward, 0 ); + BOOST_CHECK_THROW( get_market_fee_reward( bob.referrer, izzycoin_id ), fc::exception ); + BOOST_CHECK_THROW( get_market_fee_reward( alice.registrar, jillcoin_id ), fc::exception ); + BOOST_CHECK_THROW( get_market_fee_reward( alice.referrer, jillcoin_id ), fc::exception ); + } + FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_SUITE_END() From 312300366af1a7e09b937a263bd919c4d8f90cde Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Tue, 27 Nov 2018 16:43:47 +0300 Subject: [PATCH 050/161] Optimize vesting_balance_index --- libraries/chain/db_balance.cpp | 53 ++++++++++++-- .../chain/include/graphene/chain/database.hpp | 9 ++- .../graphene/chain/vesting_balance_object.hpp | 69 +++++++++++++++++-- tests/common/database_fixture.cpp | 17 ++--- tests/tests/market_fee_sharing_tests.cpp | 68 ++++++++++++++---- 5 files changed, 174 insertions(+), 42 deletions(-) diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index 610ba8fae0..9aa8bbaf3b 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -81,6 +81,48 @@ void database::adjust_balance(account_id_type account, asset delta ) } FC_CAPTURE_AND_RETHROW( (account)(delta) ) } +namespace detail { + + /** + * Used as a key to search vesting_balance_object in the index + */ + struct vbo_mfs_key + { + account_id_type account_id; + asset_id_type asset_id; + + vbo_mfs_key(const account_id_type& account, const asset_id_type& asset): + account_id(account), + asset_id(asset) + {} + + bool operator()(const vbo_mfs_key& k, const vesting_balance_object& vbo)const + { + return ( vbo.balance_type == vesting_balance_type::market_fee_sharing ) && + ( k.asset_id == vbo.balance.asset_id ) && + ( k.account_id == vbo.owner ); + } + + uint64_t operator()(const vbo_mfs_key& k)const + { + return vbo_mfs_hash(k.account_id, k.asset_id); + } + }; +} //detail + +asset database::get_market_fee_vesting_balance(const account_id_type &account_id, const asset_id_type &asset_id) +{ + auto& vesting_balances = get_index_type().indices().get(); + const auto& key = detail::vbo_mfs_key{account_id, asset_id}; + auto vbo_it = vesting_balances.find(key, key, key); + + if( vbo_it == vesting_balances.end() ) + { + return asset(0, asset_id); + } + return vbo_it->balance; +} + void database::deposit_market_fee_vesting_balance(const account_id_type &account_id, const asset &delta) { try { FC_ASSERT( delta.amount >= 0, "Invalid negative value for balance"); @@ -89,14 +131,12 @@ void database::deposit_market_fee_vesting_balance(const account_id_type &account return; auto& vesting_balances = get_index_type().indices().get(); - auto market_vesting_balances = vesting_balances.equal_range(boost::make_tuple(account_id, vesting_balance_type::market_fee_sharing)); - auto market_balance = boost::range::find_if(market_vesting_balances, - [&delta](const vesting_balance_object& vbo) { return vbo.balance.asset_id == delta.asset_id;} - ); + const auto& key = detail::vbo_mfs_key{account_id, delta.asset_id}; + auto vbo_it = vesting_balances.find(key, key, key); auto block_time = head_block_time(); - if(market_balance == boost::end(market_vesting_balances) ) + if( vbo_it == vesting_balances.end() ) { create([&account_id, &delta, &block_time](vesting_balance_object &vbo) { vbo.owner = account_id; @@ -105,12 +145,11 @@ void database::deposit_market_fee_vesting_balance(const account_id_type &account vbo.policy = instant_vesting_policy{}; }); } else { - modify( *market_balance, [&block_time, &delta]( vesting_balance_object& vbo ) + modify( *vbo_it, [&block_time, &delta]( vesting_balance_object& vbo ) { vbo.deposit_vested(block_time, delta); }); } - } FC_CAPTURE_AND_RETHROW( (account_id)(delta) ) } optional< vesting_balance_id_type > database::deposit_lazy_vesting( diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 4e2915a14b..52b73075c1 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -304,7 +304,14 @@ namespace graphene { namespace chain { */ void adjust_balance(account_id_type account, asset delta); - void deposit_market_fee_vesting_balance(const account_id_type &account, const asset &delta); + void deposit_market_fee_vesting_balance(const account_id_type &account_id, const asset &delta); + /** + * @brief Retrieve a particular account's market fee vesting balance in a given asset + * @param owner Account whose balance should be retrieved + * @param asset_id ID of the asset to get balance in + * @return owner's balance in asset + */ + asset get_market_fee_vesting_balance(const account_id_type &account_id, const asset_id_type &asset_id); /** * @brief Helper to make lazy deposit to CDD VBO. diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index 1fba587dff..226dbf0c0a 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -199,6 +201,63 @@ namespace graphene { namespace chain { struct by_account; struct by_vesting_type; +namespace detail { + + /** + Calculate a hash for account_id_type and asset_id. + Use object_id.hash_value function to calculate sum of hashes from account_id_type and asset_id. + then subducts a result from std::numeric_limits::max(). + object_id.hash_value returns uint64_t but only 48 bits are significant + so std::numeric_limits::max() - (48 bits + 48 bits) always correct from point of natural numbers + */ + inline uint64_t vbo_mfs_hash(const account_id_type& account_id, const asset_id_type& asset_id) + { + return std::numeric_limits::max() - ( hash_value(account_id) + hash_value(asset_id) ); + } + + /** + * Used as CompatibleHash + Calculate a hash vesting_balance_object + if vesting_balance_object.balance_type is market_fee_sharing + calculate has as vbo_mfs_hash(vesting_balance_object.owner, hash(vbo.balance.asset_id) (see vbo_mfs_hash) + otherwise: hash_value(vesting_balance_object.id); + */ + struct vesting_balance_object_hash + { + uint64_t operator()(const vesting_balance_object& vbo) const + { + if ( vbo.balance_type == vesting_balance_type::market_fee_sharing ) + { + return vbo_mfs_hash(vbo.owner, vbo.balance.asset_id); + } + return hash_value(vbo.id); + } + }; + + /** + * Used as CompatiblePred + * Compares two vesting_balance_objects + * if vesting_balance_object.balance_type is a market_fee_sharing + * compare owners' ids and assets' ids + * otherwise: vesting_balance_object.id + */ + struct vesting_balance_object_equal + { + bool operator() (const vesting_balance_object& lhs, const vesting_balance_object& rhs) const + { + if ( ( lhs.balance_type == vesting_balance_type::market_fee_sharing ) && + ( lhs.balance_type == rhs.balance_type ) && + ( lhs.owner == rhs.owner ) && + ( lhs.balance.asset_id == rhs.balance.asset_id) + ) + { + return true; + } + return ( lhs.id == rhs.id ); + } + }; +} // detail + typedef multi_index_container< vesting_balance_object, indexed_by< @@ -207,12 +266,10 @@ namespace graphene { namespace chain { ordered_non_unique< tag, member >, - ordered_non_unique< tag, - composite_key< - vesting_balance_object, - member, - member - > + hashed_unique< tag, + identity, + detail::vesting_balance_object_hash, + detail::vesting_balance_object_equal > > > vesting_balance_multi_index_type; diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 7413867d07..806bc0144f 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -1130,23 +1130,14 @@ int64_t database_fixture::get_balance( const account_object& account, const asse return db.get_balance(account.get_id(), a.get_id()).amount.value; } -int64_t database_fixture::get_market_fee_reward( account_id_type account, asset_id_type asset_type)const +int64_t database_fixture::get_market_fee_reward( account_id_type account_id, asset_id_type asset_id)const { - auto& vesting_balances = db.get_index_type().indices().get(); - auto market_vesting_balances = vesting_balances.equal_range(boost::make_tuple(account, vesting_balance_type::market_fee_sharing)); - auto market_balance = boost::range::find_if(market_vesting_balances, - [&asset_type](const vesting_balance_object& vbo) { return vbo.balance.asset_id == asset_type;} - ); - - FC_ASSERT( market_balance != boost::end(market_vesting_balances) ); - - auto allowed_to_withdraw = market_balance->get_allowed_withdraw(db.head_block_time()); - return allowed_to_withdraw.amount.value; + return db.get_market_fee_vesting_balance(account_id, asset_id).amount.value; } -int64_t database_fixture::get_market_fee_reward( const account_object& account, const asset_object& a )const +int64_t database_fixture::get_market_fee_reward( const account_object& account, const asset_object& asset )const { - return get_market_fee_reward(account.get_id(), a.get_id()); + return get_market_fee_reward(account.get_id(), asset.get_id()); } vector< operation_history_object > database_fixture::get_operation_history( account_id_type account_id )const diff --git a/tests/tests/market_fee_sharing_tests.cpp b/tests/tests/market_fee_sharing_tests.cpp index 143f2a78d6..c8df1f56ac 100644 --- a/tests/tests/market_fee_sharing_tests.cpp +++ b/tests/tests/market_fee_sharing_tests.cpp @@ -102,6 +102,16 @@ struct reward_database_fixture : database_fixture }; const share_type core_precision = asset::scaled_precision( asset_id_type()(db).precision ); + + void create_vesting_balance_object(const account_id_type& account_id, vesting_balance_type balance_type ) + { + auto block_time = db.head_block_time(); + + db.create([&account_id, &block_time, balance_type] (vesting_balance_object &vbo) { + vbo.owner = account_id; + vbo.balance_type = balance_type; + }); + }; }; BOOST_FIXTURE_TEST_SUITE( fee_sharing_tests, reward_database_fixture ) @@ -420,8 +430,8 @@ BOOST_AUTO_TEST_CASE(white_list_is_empty_test) GET_ACTOR(izzyregistrar); GET_ACTOR(izzyreferrer); - BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); - BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0 ); GET_ACTOR(alice); GET_ACTOR(bob); @@ -455,8 +465,8 @@ BOOST_AUTO_TEST_CASE(white_list_contains_registrar_test) update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist); - BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); - BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0 ); GET_ACTOR(alice); GET_ACTOR(bob); @@ -490,8 +500,8 @@ BOOST_AUTO_TEST_CASE(white_list_contains_referrer_test) update_asset(jill_id, jill_private_key, jillcoin.get_id(), jillcoin_reward_percent, whitelist); - BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); - BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0 ); GET_ACTOR(alice); GET_ACTOR(bob); @@ -499,8 +509,8 @@ BOOST_AUTO_TEST_CASE(white_list_contains_referrer_test) create_sell_order( alice, jillcoin.amount(200000), core_asset(1) ); create_sell_order( bob, core_asset(1), jillcoin.amount(100000) ); - BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); - BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0 ); } FC_LOG_AND_RETHROW() } @@ -524,16 +534,16 @@ BOOST_AUTO_TEST_CASE(white_list_doesnt_contain_registrar_test) GET_ACTOR(izzyregistrar); GET_ACTOR(izzyreferrer); - BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); - BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0 ); GET_ACTOR(bob); // Alice and Bob place orders which match create_sell_order( alice, jillcoin.amount(200000), core_asset(1) ); create_sell_order( bob, core_asset(1), jillcoin.amount(100000) ); - BOOST_CHECK_THROW( get_market_fee_reward( izzyregistrar, jillcoin ), fc::exception ); - BOOST_CHECK_THROW( get_market_fee_reward( izzyreferrer, jillcoin ), fc::exception ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyregistrar, jillcoin ), 0 ); + BOOST_CHECK_EQUAL( get_market_fee_reward( izzyreferrer, jillcoin ), 0); } FC_LOG_AND_RETHROW() } @@ -927,11 +937,39 @@ BOOST_AUTO_TEST_CASE(white_list_asset_rewards_test) // Only Bob's registrar should get rewards share_type bob_registrar_reward = get_market_fee_reward( bob.registrar, izzycoin_id ); BOOST_CHECK_GT( bob_registrar_reward, 0 ); - BOOST_CHECK_THROW( get_market_fee_reward( bob.referrer, izzycoin_id ), fc::exception ); - BOOST_CHECK_THROW( get_market_fee_reward( alice.registrar, jillcoin_id ), fc::exception ); - BOOST_CHECK_THROW( get_market_fee_reward( alice.referrer, jillcoin_id ), fc::exception ); + BOOST_CHECK_EQUAL( get_market_fee_reward( bob.referrer, izzycoin_id ), 0 ); + BOOST_CHECK_EQUAL( get_market_fee_reward( alice.registrar, jillcoin_id ), 0 ); + BOOST_CHECK_EQUAL( get_market_fee_reward( alice.referrer, jillcoin_id ), 0 ); } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( create_vesting_balance_object_test ) +{ + /** + * Test checks that an account could have duplicates VBO (with the same asset_type) + * for any type of vesting_balance_type + * except vesting_balance_type::market_fee_sharing + */ + try { + + ACTOR(actor); + + create_vesting_balance_object(actor_id, vesting_balance_type::unspecified); + create_vesting_balance_object(actor_id, vesting_balance_type::unspecified); + + create_vesting_balance_object(actor_id, vesting_balance_type::cashback); + create_vesting_balance_object(actor_id, vesting_balance_type::cashback); + + create_vesting_balance_object(actor_id, vesting_balance_type::witness); + create_vesting_balance_object(actor_id, vesting_balance_type::witness); + + create_vesting_balance_object(actor_id, vesting_balance_type::worker); + create_vesting_balance_object(actor_id, vesting_balance_type::worker); + + create_vesting_balance_object(actor_id, vesting_balance_type::market_fee_sharing); + GRAPHENE_CHECK_THROW(create_vesting_balance_object(actor_id, vesting_balance_type::market_fee_sharing), fc::exception); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() From c5a6835826001c62f7971557a9b5e5521950660e Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Thu, 6 Dec 2018 12:27:33 +0300 Subject: [PATCH 051/161] Add perfomance benchmark --- .../performance/market_fee_sharing_tests.cpp | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 tests/performance/market_fee_sharing_tests.cpp diff --git a/tests/performance/market_fee_sharing_tests.cpp b/tests/performance/market_fee_sharing_tests.cpp new file mode 100644 index 0000000000..5c431595ea --- /dev/null +++ b/tests/performance/market_fee_sharing_tests.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018 Bitshares Foundation, and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include + +#include +#include +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; + +BOOST_FIXTURE_TEST_CASE(mfs_performance_test, database_fixture) +{ + try + { + ACTORS((issuer)); + + const unsigned int accounts = 3000; + const unsigned int iterations = 20; + + std::vector registrators; + for (unsigned int i = 0; i < accounts; ++i) + { + auto account = create_account("registrar" + std::to_string(i)); + transfer(committee_account, account.get_id(), asset(1000000)); + upgrade_to_lifetime_member(account); + + registrators.push_back(std::move(account)); + } + + generate_blocks(HARDFORK_1268_TIME); + generate_block(); + + additional_asset_options_t options; + options.value.reward_percent = 2 * GRAPHENE_1_PERCENT; + + const auto usd = create_user_issued_asset( + "USD", + issuer, + charge_market_fee, + price(asset(1, asset_id_type(1)), asset(1)), + 1, + 20 * GRAPHENE_1_PERCENT, + options); + + issue_uia(issuer, usd.amount(iterations * accounts * 2000)); + + std::vector traders; + for (unsigned int i = 0; i < accounts; ++i) + { + std::string name = "account" + std::to_string(i); + auto account = create_account(name, registrators[i], registrators[i], GRAPHENE_1_PERCENT); + transfer(committee_account, account.get_id(), asset(1000000)); + transfer(issuer, account, usd.amount(iterations * 2000)); + + traders.push_back(std::move(account)); + } + + using namespace std::chrono; + + const auto start = high_resolution_clock::now(); + + for (unsigned int i = 0; i < iterations; ++i) + { + for (unsigned int j = 0; j < accounts; ++j) + { + create_sell_order(traders[j], usd.amount(2000), asset(1)); + create_sell_order(traders[accounts - j - 1], asset(1), usd.amount(1000)); + } + } + + const auto end = high_resolution_clock::now(); + + const auto elapsed = duration_cast(end - start); + wlog("Elapsed: ${c} ms", ("c", elapsed.count())); + + for (unsigned int i = 0; i < accounts; ++i) + { + const auto reward = get_market_fee_reward(registrators[i], usd); + BOOST_CHECK_GT(reward, 0); + } + } + FC_LOG_AND_RETHROW() +} From 68982ca711bbfba96dc671470520b0d0787d5e92 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 11 Dec 2018 09:48:25 -0500 Subject: [PATCH 052/161] Added base16 to htlc hash for cli wallet --- .../wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 15 ++++++++++++-- tests/cli/main.cpp | 20 +++++++++++++------ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index d8cf519d71..2613e177cf 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1469,7 +1469,7 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t seconds_in_force, bool broadcast = false ); /**** diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 0b709add69..ea10dcc7c9 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -3104,10 +3105,20 @@ uint64_t wallet_api::get_asset_count()const } signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t seconds_in_force, bool broadcast) { - return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, preimage_hash, preimage_size, + // convert string back into binary + char s[3]; + s[2] = 0; + std::vector uns; + for(int i = 0; i < preimage_hash.length(); i+= 2) + { + s[0] = preimage_hash[i]; + s[1] = preimage_hash[i+1]; + uns.push_back( (int)strtol(&s[0], nullptr, 16)); + } + return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, uns, preimage_size, seconds_in_force, broadcast); } diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 6af62e98fe..3fdc689b82 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef _WIN32 #ifndef _WIN32_WINNT @@ -678,14 +679,21 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) 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::ripemd160 preimage_md = fc::ripemd160::hash(preimage_string); - std::vector hash(preimage_md.data_size()); - for(size_t i = 0; i < preimage_md.data_size(); ++i) - hash[i] = preimage_md.data()[i]; + fc::sha256 preimage_md = fc::sha256::hash(preimage_string); + std::stringstream ss; + int data_size = preimage_md.data_size(); + for(int 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_prepare("alice", "bob", - "BTS", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); + "1.3.0", "3", "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; @@ -707,7 +715,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_prepare("bob", "alice", - "BOBCOIN", "3", "RIPEMD160", hash, preimage_string.size(), timelock, true); + "BOBCOIN", "3", "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; From 44d4134501d2413f0e59355a8e531f588b52fb03 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 11 Dec 2018 11:03:24 -0500 Subject: [PATCH 053/161] fix for redeem --- .../wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 29 +++++++++++-------- tests/cli/main.cpp | 6 ++-- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 2613e177cf..7cc545c014 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1479,7 +1479,7 @@ class wallet_api * @param issuer Who is performing this operation (and paying the fee) * @param preimage the preimage that should evaluate to the preimage_hash */ - signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::string& preimage, bool broadcast = false ); /***** diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index ea10dcc7c9..9412207396 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3104,21 +3104,25 @@ uint64_t wallet_api::get_asset_count()const return my->_remote_db->get_asset_count(); } -signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, - string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, - const uint32_t seconds_in_force, bool broadcast) +std::vector string_to_vec(std::string incoming) { - // convert string back into binary char s[3]; s[2] = 0; - std::vector uns; - for(int i = 0; i < preimage_hash.length(); i+= 2) + std::vector vec; + for(int i = 0; i < incoming.length(); i+= 2) { - s[0] = preimage_hash[i]; - s[1] = preimage_hash[i+1]; - uns.push_back( (int)strtol(&s[0], nullptr, 16)); + s[0] = incoming[i]; + s[1] = incoming[i+1]; + vec.push_back( (int)strtol(&s[0], nullptr, 16)); } - return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, uns, preimage_size, + return vec; +} + +signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, + string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, + const uint32_t seconds_in_force, bool broadcast) +{ + return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, string_to_vec(preimage_hash), preimage_size, seconds_in_force, broadcast); } @@ -3127,10 +3131,11 @@ graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const return my->get_htlc(htlc_id); } -signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::vector& preimage, +signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::string& preimage, bool broadcast) { - return my->htlc_redeem(htlc_id, issuer, preimage, broadcast); + + return my->htlc_redeem(htlc_id, issuer, std::vector(preimage.begin(), preimage.end()), broadcast); } signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 3fdc689b82..f762511380 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -739,8 +739,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) { BOOST_TEST_MESSAGE("Alice uses her preimage to retrieve the BOBCOIN"); std::string secret = "My Secret"; - vector secret_vector(secret.begin(), secret.end()); - con.wallet_api_ptr->htlc_redeem(bob_htlc_id_as_string, "alice", secret_vector, true); + 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)); } @@ -750,8 +749,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) { BOOST_TEST_MESSAGE("Bob uses Alice's preimage to retrieve the BOBCOIN"); std::string secret = "My Secret"; - vector secret_vector(secret.begin(), secret.end()); - con.wallet_api_ptr->htlc_redeem(alice_htlc_id_as_string, "bob", secret_vector, true); + 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)); } From 13926f53f4ca84ce4fba8db51372c27163413be6 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 12 Dec 2018 11:14:00 -0500 Subject: [PATCH 054/161] Added details to history for HTLC create and redeem --- libraries/wallet/wallet.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 9412207396..34c4337a6c 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -135,6 +135,8 @@ struct operation_printer std::string operator()(const account_create_operation& op)const; std::string operator()(const account_update_operation& op)const; 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; }; template @@ -2974,6 +2976,36 @@ std::string operation_printer::operator()(const asset_create_operation& op) cons return fee(op.fee); } +std::string operation_printer::operator()(const htlc_redeem_operation& op) const +{ + auto from = wallet.get_account( op.source ); + + out << "Redeem HTLC from " << from.name + << " 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 +{ + auto a = wallet.get_asset( op.fee.asset_id ); + auto payer = wallet.get_account( op.fee_payer() ); + auto to = wallet.get_account( op.destination ); + operation_result_printer rprinter(wallet); + std::string database_id = result.visit(rprinter); + + out << "Create HTLC to " << to.name + << " with database id " << database_id + << " Fee paid by " << payer.name + << " (Fee: " << a.amount_to_pretty_string( op.fee ) << ")"; + return ""; +} + std::string operation_result_printer::operator()(const void_result& x) const { return ""; From 5103f7e98a39b93ec7dc60e96273bc55c50df4b3 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 12 Dec 2018 11:19:23 -0500 Subject: [PATCH 055/161] clearer text in account history --- libraries/wallet/wallet.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 34c4337a6c..0d31ca8834 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2978,10 +2978,8 @@ std::string operation_printer::operator()(const asset_create_operation& op) cons std::string operation_printer::operator()(const htlc_redeem_operation& op) const { - auto from = wallet.get_account( op.source ); - - out << "Redeem HTLC from " << from.name - << " with database id " << std::to_string(op.htlc_id.space_id) + 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 \""; From 2e5d74f722c693ffcc24a18747d8b46f162577ef Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 12 Dec 2018 20:40:36 -0500 Subject: [PATCH 056/161] Adjust history lines --- libraries/wallet/wallet.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 0d31ca8834..32585f3f82 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2999,8 +2999,12 @@ std::string operation_printer::operator()(const htlc_create_operation& op) const out << "Create HTLC to " << to.name << " with database id " << database_id - << " Fee paid by " << payer.name - << " (Fee: " << a.amount_to_pretty_string( op.fee ) << ")"; + << " preimage hash: ["; + for(unsigned char c : op.key_hash) + { + out << setfill('0') << std::setw(2) << std::hex << (int)c; + } + out << "] (Fee: " << a.amount_to_pretty_string( op.fee ) << ")"; return ""; } From d2dfa69f97432ac64068f50164eb8c1bbd073f9a Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 04:47:02 -0500 Subject: [PATCH 057/161] Modified wallet get_htlc to return pretty format --- .../include/graphene/chain/htlc_object.hpp | 2 +- .../include/graphene/chain/protocol/htlc.hpp | 15 ++++++ libraries/chain/protocol/htlc.cpp | 50 +++++++++++++++---- .../wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 32 +++++++++--- tests/cli/main.cpp | 5 +- 6 files changed, 82 insertions(+), 24 deletions(-) diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 324dbc706e..89366791e4 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -38,7 +38,7 @@ namespace graphene { namespace chain { sha256 = 0x02, sha1 = 0x03 }; - + /** * @brief database object to store HTLCs * diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 503398ba27..6a0f9ab88d 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -31,6 +31,21 @@ namespace graphene { namespace chain { + /** + * Convert the hash algorithm to a string + * @param algo the enum to convert + * @returns a string (lower case) + */ + std::string hash_algorithm_to_string(fc::enum_type algo); + + /** + * Convert a string to the enum that matches the hash algorithm + * @param incoing the string (case insensitive) + * @returns the matching enum + */ + fc::enum_type string_to_hash_algorithm(std::string incoming); + + struct htlc_create_operation : public base_operation { struct fee_parameters_type { diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 7d21d7ace4..0f3e61bd95 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -25,17 +25,45 @@ namespace graphene { namespace chain { - void htlc_create_operation::validate()const { - FC_ASSERT( source != destination ); - FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( amount.amount > 0 ); - } + void htlc_create_operation::validate()const { + FC_ASSERT( source != destination ); + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( amount.amount > 0 ); + } - void htlc_redeem_operation::validate()const { - FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( preimage.size() > 0 ); - } - void htlc_extend_operation::validate()const { - FC_ASSERT( fee.amount >= 0 ); + void htlc_redeem_operation::validate()const { + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( preimage.size() > 0 ); + } + void htlc_extend_operation::validate()const { + FC_ASSERT( fee.amount >= 0 ); + } + + std::string hash_algorithm_to_string(fc::enum_type algo) + { + switch(algo) + { + case (hash_algorithm::ripemd160): + return "ripemd160"; + case (hash_algorithm::sha256): + return "sha256"; + case (hash_algorithm::sha1): + return "sha1"; + default: + break; } + return "unknown"; + } + + fc::enum_type string_to_hash_algorithm(std::string incoming) + { + std::transform(incoming.begin(), incoming.end(), incoming.begin(), ::toupper); + if (incoming == "RIPEMD160") + return hash_algorithm::ripemd160; + if (incoming == "SHA256") + return hash_algorithm::sha256; + if (incoming == "SHA1") + return hash_algorithm::sha1; + return hash_algorithm::unknown; + } } } diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 7cc545c014..462245a6d7 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -495,7 +495,7 @@ class wallet_api * @param htlc_id the id of the HTLC object. * @returns the information about the HTLC object */ - htlc_object get_htlc(string htlc_id) const; + variant get_htlc(string htlc_id) const; /** Lookup the id of a named account. * @param account_name_or_id the name of the account to look up diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 32585f3f82..6879527bd9 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1762,12 +1762,7 @@ class wallet_api_impl create_op.seconds_in_force = seconds_in_force; create_op.key_hash = preimage_hash; create_op.key_size = preimage_size; - if ( "SHA256" == hash_algorithm ) - create_op.hash_type = graphene::chain::hash_algorithm::sha256; - if ( "RIPEMD160" == hash_algorithm ) - create_op.hash_type = graphene::chain::hash_algorithm::ripemd160; - if ( "SHA1" == hash_algorithm ) - create_op.hash_type = graphene::chain::hash_algorithm::sha1; + create_op.hash_type = graphene::chain::string_to_hash_algorithm(hash_algorithm); FC_ASSERT(create_op.hash_type != graphene::chain::hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); @@ -3160,9 +3155,30 @@ signed_transaction wallet_api::htlc_prepare( string source, string destination, seconds_in_force, broadcast); } -graphene::chain::htlc_object wallet_api::get_htlc(std::string htlc_id) const +std::string object_id_to_string(const graphene::db::object_id_type& obj_id) { - return my->get_htlc(htlc_id); + return std::to_string(obj_id.space()) + "." + std::to_string(obj_id.type()) + "." + std::to_string(obj_id.instance()); +} + +variant wallet_api::get_htlc(std::string htlc_id) const +{ + graphene::chain::htlc_object obj = my->get_htlc(htlc_id); + fc::mutable_variant_object ret_val; + ret_val["database_id"] = (std::string)obj.id; + ret_val["from"] = object_id_to_string(obj.from); + ret_val["to"] = object_id_to_string(obj.to); + ret_val["amount"] = obj.amount.amount.value; + ret_val["asset"] = object_id_to_string(obj.amount.asset_id); + ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); + ret_val["pending_fee_amount"] = obj.pending_fee.amount.value; + ret_val["pending_fee_asset"] = object_id_to_string(obj.pending_fee.asset_id); + std::stringstream hash_string; + for(unsigned char c : obj.preimage_hash) + hash_string << std::setfill('0') << std::setw(2) << std::hex << c; + ret_val["preimage_hash"] = hash_string.str(); + ret_val["preimage_algorithm"] = graphene::chain::hash_algorithm_to_string(obj.preimage_hash_algorithm); + ret_val["preimage_size"] = obj.preimage_size; + return ret_val; } signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::string& preimage, diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index f762511380..e6511aad70 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -681,7 +681,6 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) std::string preimage_string = "My Secret"; fc::sha256 preimage_md = fc::sha256::hash(preimage_string); std::stringstream ss; - int data_size = preimage_md.data_size(); for(int i = 0; i < preimage_md.data_size(); i++) { char d = preimage_md.data()[i]; @@ -710,7 +709,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // 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."); - graphene::chain::htlc_object alice_htlc = con.wallet_api_ptr->get_htlc(alice_htlc_id_as_string); + 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 @@ -732,7 +731,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // 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."); - graphene::chain::htlc_object bob_htlc = con.wallet_api_ptr->get_htlc(bob_htlc_id_as_string); + 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 From 4af8e6cbb1a9c54125121a5e122af7cb1059fcc2 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 05:17:24 -0500 Subject: [PATCH 058/161] hash format fix --- libraries/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 6879527bd9..fac63d3cd2 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3174,7 +3174,7 @@ variant wallet_api::get_htlc(std::string htlc_id) const ret_val["pending_fee_asset"] = object_id_to_string(obj.pending_fee.asset_id); std::stringstream hash_string; for(unsigned char c : obj.preimage_hash) - hash_string << std::setfill('0') << std::setw(2) << std::hex << c; + hash_string << std::setfill('0') << std::setw(2) << std::hex << (int)c; ret_val["preimage_hash"] = hash_string.str(); ret_val["preimage_algorithm"] = graphene::chain::hash_algorithm_to_string(obj.preimage_hash_algorithm); ret_val["preimage_size"] = obj.preimage_size; From 5492797da08964d9c227970f793f086dcc8e0d68 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 13:17:10 -0500 Subject: [PATCH 059/161] Add pending info to HTLC in account history --- libraries/wallet/wallet.cpp | 47 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index fac63d3cd2..fdd2382cc5 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -115,14 +115,16 @@ struct operation_printer ostream& out; const wallet_api_impl& wallet; operation_result result; + operation_history_object hist; std::string fee(const asset& a) const; public: - operation_printer( ostream& out, const wallet_api_impl& wallet, const operation_result& r = operation_result() ) + operation_printer( ostream& out, const wallet_api_impl& wallet, const operation_history_object& obj ) : out(out), wallet(wallet), - result(r) + result(obj.result), + hist(obj) {} typedef std::string result_type; @@ -2314,7 +2316,7 @@ class wallet_api_impl auto b = _remote_db->get_block_header(i.block_num); FC_ASSERT(b); ss << b->timestamp.to_iso_string() << " "; - i.op.visit(operation_printer(ss, *this, i.result)); + i.op.visit(operation_printer(ss, *this, i)); ss << " \n"; } @@ -2331,7 +2333,7 @@ class wallet_api_impl auto b = _remote_db->get_block_header(i.block_num); FC_ASSERT(b); ss << b->timestamp.to_iso_string() << " "; - i.op.visit(operation_printer(ss, *this, i.result)); + i.op.visit(operation_printer(ss, *this, i)); ss << " \n"; } @@ -2352,7 +2354,7 @@ class wallet_api_impl auto b = _remote_db->get_block_header(i.block_num); FC_ASSERT(b); ss << b->timestamp.to_iso_string() << " "; - i.op.visit(operation_printer(ss, *this, i.result)); + i.op.visit(operation_printer(ss, *this, i)); ss << " transaction_id : "; ss << d.transaction_id.str(); ss << " \n"; @@ -2394,7 +2396,7 @@ class wallet_api_impl { auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); std::stringstream ss; - r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); + r.trx.operations[0].visit( operation_printer( ss, *this, operation_history_object() ) ); ss << "\n"; for( const auto& out : r.outputs ) { @@ -2407,7 +2409,7 @@ class wallet_api_impl { auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); std::stringstream ss; - r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); + r.trx.operations[0].visit( operation_printer( ss, *this, operation_history_object() ) ); ss << "\n"; for( const auto& out : r.outputs ) { @@ -2986,20 +2988,24 @@ std::string operation_printer::operator()(const htlc_redeem_operation& op) const std::string operation_printer::operator()(const htlc_create_operation& op) const { - auto a = wallet.get_asset( op.fee.asset_id ); - auto payer = wallet.get_account( op.fee_payer() ); + auto fee_asset = wallet.get_asset( op.fee.asset_id ); auto to = wallet.get_account( op.destination ); operation_result_printer rprinter(wallet); std::string database_id = result.visit(rprinter); out << "Create HTLC to " << to.name - << " with database id " << database_id + << " with id " << database_id << " preimage hash: ["; for(unsigned char c : op.key_hash) { out << setfill('0') << std::setw(2) << std::hex << (int)c; } - out << "] (Fee: " << a.amount_to_pretty_string( op.fee ) << ")"; + out << "] (Fee: " << fee_asset.amount_to_pretty_string( op.fee ) << ")"; + // 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 ""; } @@ -3155,23 +3161,18 @@ signed_transaction wallet_api::htlc_prepare( string source, string destination, seconds_in_force, broadcast); } -std::string object_id_to_string(const graphene::db::object_id_type& obj_id) -{ - return std::to_string(obj_id.space()) + "." + std::to_string(obj_id.type()) + "." + std::to_string(obj_id.instance()); -} - variant wallet_api::get_htlc(std::string htlc_id) const { graphene::chain::htlc_object obj = my->get_htlc(htlc_id); fc::mutable_variant_object ret_val; ret_val["database_id"] = (std::string)obj.id; - ret_val["from"] = object_id_to_string(obj.from); - ret_val["to"] = object_id_to_string(obj.to); + ret_val["from"] = (std::string)((graphene::db::object_id_type)obj.from); + ret_val["to"] = (std::string)((graphene::db::object_id_type)obj.to); ret_val["amount"] = obj.amount.amount.value; - ret_val["asset"] = object_id_to_string(obj.amount.asset_id); + ret_val["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); ret_val["pending_fee_amount"] = obj.pending_fee.amount.value; - ret_val["pending_fee_asset"] = object_id_to_string(obj.pending_fee.asset_id); + ret_val["pending_fee_asset"] = (std::string)((graphene::db::object_id_type)obj.pending_fee.asset_id); std::stringstream hash_string; for(unsigned char c : obj.preimage_hash) hash_string << std::setfill('0') << std::setw(2) << std::hex << (int)c; @@ -3242,7 +3243,7 @@ vector wallet_api::get_account_history(string name, int limit) } } std::stringstream ss; - auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result)); + auto memo = o.op.visit(detail::operation_printer(ss, *my, o)); result.push_back( operation_detail{ memo, ss.str(), o } ); } @@ -3290,7 +3291,7 @@ vector wallet_api::get_relative_account_history( start); for (auto &o : current) { std::stringstream ss; - auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result)); + auto memo = o.op.visit(detail::operation_printer(ss, *my, o)); result.push_back(operation_detail{memo, ss.str(), o}); } if (current.size() < std::min(100, limit)) @@ -3334,7 +3335,7 @@ account_history_operation_detail wallet_api::get_account_history_by_operations( auto current = my->_remote_hist->get_account_history_by_operations(always_id, operation_types, start, min_limit); for (auto& obj : current.operation_history_objs) { std::stringstream ss; - auto memo = obj.op.visit(detail::operation_printer(ss, *my, obj.result)); + auto memo = obj.op.visit(detail::operation_printer(ss, *my, obj)); transaction_id_type transaction_id; auto block = get_block(obj.block_num); From 742f0257dc76609c9db3c3b8dbbe3fae3a438b62 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 13:30:01 -0500 Subject: [PATCH 060/161] Fix htlc_prepare method signature --- libraries/wallet/include/graphene/wallet/wallet.hpp | 4 ++-- libraries/wallet/wallet.cpp | 6 +++--- tests/cli/main.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 462245a6d7..7b087dab61 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1460,15 +1460,15 @@ class wallet_api * * @param source The account that will reserve the funds (and pay the fee) * @param destination The account that will receive the funds if the preimage is presented - * @param asset_symbol The asset that is to be traded * @param amount the amount of the asset that is to be traded + * @param asset_symbol The asset that is to be traded * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160 or SHA256. * @param preimage_hash the hash of the preimage * @param preimage_size the size of the preimage in bytes * @param seconds_in_force when the time lock expires * @param broadcast true if you wish to broadcast the transaction */ - signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, + signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t seconds_in_force, bool broadcast = false ); diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index fdd2382cc5..185b11f08e 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1747,7 +1747,7 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } - signed_transaction htlc_prepare( string source, string destination, string asset_symbol, string amount, + signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, const uint32_t seconds_in_force, bool broadcast = false ) { @@ -3153,11 +3153,11 @@ std::vector string_to_vec(std::string incoming) return vec; } -signed_transaction wallet_api::htlc_prepare( string source, string destination, string asset_symbol, string amount, +signed_transaction wallet_api::htlc_prepare( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t seconds_in_force, bool broadcast) { - return my->htlc_prepare(source, destination, asset_symbol, amount, hash_algorithm, string_to_vec(preimage_hash), preimage_size, + return my->htlc_prepare(source, destination, amount, asset_symbol, hash_algorithm, string_to_vec(preimage_hash), preimage_size, seconds_in_force, broadcast); } diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index e6511aad70..3ef22603cb 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -692,7 +692,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_prepare("alice", "bob", - "1.3.0", "3", "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; @@ -714,7 +714,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_prepare("bob", "alice", - "BOBCOIN", "3", "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; From b5b8dfc95dbb7adc5c010fb89c4f2d253f3f8a75 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 14:47:21 -0500 Subject: [PATCH 061/161] Add virtual op for refund --- libraries/chain/db_notify.cpp | 4 ++++ libraries/chain/db_update.cpp | 7 +++++++ .../include/graphene/chain/protocol/htlc.hpp | 19 +++++++++++++++++++ .../graphene/chain/protocol/operations.hpp | 3 ++- 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index ba8ff5f1ac..f379116c9e 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -268,6 +268,10 @@ struct get_impacted_account_visitor { _impacted.insert( op.fee_payer() ); } + void operator()( const htlc_refund_operation& op ) + { + _impacted.insert( op.fee_payer() ); // bidder + } }; void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 7552bc5528..df4d57b161 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -565,8 +565,15 @@ void database::clear_expired_htlcs() && htlc_idx.begin()->expiration <= head_block_time() ) { adjust_balance( htlc_idx.begin()->from, htlc_idx.begin()->amount ); + // virtual op + htlc_refund_operation vop; + vop.htlc_id = htlc_idx.begin()->id; + push_applied_operation(vop); + + // remove the db object remove( *htlc_idx.begin()); } + } } } diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 6a0f9ab88d..5a1c6ff165 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -190,14 +190,33 @@ namespace graphene { return fee_params.fee + (fee_params.fee_per_day * days); } }; + + struct htlc_refund_operation : public base_operation + { + struct fee_parameters_type {}; + + htlc_refund_operation(){} + htlc_refund_operation( const htlc_id_type& htlc_id ) : htlc_id(htlc_id) {} + + account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; } + void validate()const { FC_ASSERT( !"virtual operation" ); } + + /// This is a virtual operation; there is no fee + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + + htlc_id_type htlc_id; + asset fee; + }; } } FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) +FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(seconds_in_force)(extensions)(hash_type)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) +FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 2eded4a707..3f5c27d998 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -99,7 +99,8 @@ namespace graphene { namespace chain { asset_update_issuer_operation, htlc_create_operation, htlc_redeem_operation, - htlc_extend_operation + htlc_extend_operation, + htlc_refund_operation // VIRTUAL > operation; /// @} // operations group From fe24161fa4544358f89b102bb28394f894d6025a Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 13 Dec 2018 15:28:19 -0500 Subject: [PATCH 062/161] Make refund appear in proper account --- libraries/chain/db_update.cpp | 5 +++-- libraries/chain/include/graphene/chain/protocol/htlc.hpp | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index df4d57b161..1acfc2f9d8 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -564,9 +564,10 @@ void database::clear_expired_htlcs() while ( !htlc_idx.empty() && htlc_idx.begin() != htlc_idx.end() && htlc_idx.begin()->expiration <= head_block_time() ) { - adjust_balance( htlc_idx.begin()->from, htlc_idx.begin()->amount ); + const htlc_object& obj = *htlc_idx.begin(); + adjust_balance( obj.from, obj.amount ); // virtual op - htlc_refund_operation vop; + htlc_refund_operation vop(obj.id, obj.from); vop.htlc_id = htlc_idx.begin()->id; push_applied_operation(vop); diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 5a1c6ff165..d0f5258066 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -196,15 +196,17 @@ namespace graphene { struct fee_parameters_type {}; htlc_refund_operation(){} - htlc_refund_operation( const htlc_id_type& htlc_id ) : htlc_id(htlc_id) {} + htlc_refund_operation( const htlc_id_type& htlc_id, const account_id_type& to ) + : htlc_id(htlc_id), to(to) {} - account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; } + account_id_type fee_payer()const { return to; } void validate()const { FC_ASSERT( !"virtual operation" ); } /// This is a virtual operation; there is no fee share_type calculate_fee(const fee_parameters_type& k)const { return 0; } htlc_id_type htlc_id; + account_id_type to; asset fee; }; } @@ -219,4 +221,4 @@ FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(source)(destination)(amount)(key_hash)(key_size)(seconds_in_force)(extensions)(hash_type)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) -FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)) +FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)(to)) From 27e2e75e322200c0716d4794b7b04d4b4d5f6b37 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Dec 2018 13:58:14 -0500 Subject: [PATCH 063/161] Code cleanup pmconrad suggestions --- libraries/app/database_api.cpp | 45 -------- .../app/include/graphene/app/database_api.hpp | 10 -- .../chain/committee_member_evaluator.cpp | 2 +- libraries/chain/db_notify.cpp | 10 +- libraries/chain/db_update.cpp | 9 +- libraries/chain/hardfork.d/HTLC.hf | 4 - libraries/chain/hardfork.d/core-1468.hf | 4 + libraries/chain/htlc_evaluator.cpp | 102 +++++++----------- .../include/graphene/chain/htlc_object.hpp | 17 +-- .../chain/protocol/chain_parameters.hpp | 4 +- libraries/chain/proposal_evaluator.cpp | 2 +- libraries/wallet/wallet.cpp | 10 +- tests/cli/main.cpp | 4 +- tests/common/database_fixture.cpp | 1 - tests/tests/htlc_tests.cpp | 6 +- 15 files changed, 75 insertions(+), 155 deletions(-) delete mode 100644 libraries/chain/hardfork.d/HTLC.hf create mode 100644 libraries/chain/hardfork.d/core-1468.hf diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 6e3115b674..c89971a728 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -168,9 +168,6 @@ class database_api_impl : public std::enable_shared_from_this vector get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; vector get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; - // Escrow / HTLC - fc::optional get_htlc(const std::string htlc_id) const; - //private: static string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote ); @@ -420,10 +417,6 @@ void database_api_impl::cancel_all_subscriptions( bool reset_callback, bool rese // // ////////////////////////////////////////////////////////////////////// -optional database_api::get_htlc(const std::string& htlc_id) const -{ - return my->get_htlc( htlc_id ); -} optional database_api::get_block_header(uint32_t block_num)const { return my->get_block_header( block_num ); @@ -2296,44 +2289,6 @@ vector database_api_impl::get_withdraw_permissions_b return result; } -////////////////////////////////////////////////////////////////////// -// // -// Escrow / HTLC // -// // -////////////////////////////////////////////////////////////////////// -htlc_id_type htlc_string_to_id(std::string incoming) -{ - boost::replace_all(incoming, "\"", ""); - boost::replace_all(incoming, ".", ","); - std::stringstream ss(incoming); - htlc_id_type id; - int i; - int pos = 0; - while(ss >> i) - { - if (pos == 0 && i != id.space_id) - FC_ASSERT("Invalid HTLC ID"); - if (pos == 1 && i != id.type_id) - FC_ASSERT("Invalid HTLC ID"); - if (pos == 2) - id.instance = i; - pos++; - if (ss.peek() == ',') - ss.ignore(); - } - return id; -} - -fc::optional database_api_impl::get_htlc(const std::string htlc_id) const -{ - htlc_id_type id = htlc_string_to_id(htlc_id); - const auto& htlc_idx = _db.get_index_type().indices().get(); - auto itr = htlc_idx.find(id); - if (itr == htlc_idx.end()) - return {}; - return (*itr); -} - ////////////////////////////////////////////////////////////////////// // // // Private methods // diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 2bfd27c6d7..506b427597 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -267,13 +267,6 @@ class database_api */ account_id_type get_account_id_from_string(const std::string& name_or_id) const; - /** - * @brief Get HTLC object from an ID - * @param htlc_id the id of the HTLC - * @return the HTLC object - */ - fc::optional get_htlc( const std::string& htlc_id ) const; - /** * @brief Get a list of accounts by ID * @param account_names_or_ids IDs or names of the accounts to retrieve @@ -798,9 +791,6 @@ FC_API(graphene::app::database_api, (lookup_asset_symbols) (get_asset_count) - // HTLC / Escrow - (get_htlc) - // Markets / feeds (get_order_book) (get_limit_orders) diff --git a/libraries/chain/committee_member_evaluator.cpp b/libraries/chain/committee_member_evaluator.cpp index 62e658303d..9a258e6543 100644 --- a/libraries/chain/committee_member_evaluator.cpp +++ b/libraries/chain/committee_member_evaluator.cpp @@ -78,7 +78,7 @@ void_result committee_member_update_global_parameters_evaluator::do_evaluate(con { try { FC_ASSERT(trx_state->_is_proposed_trx); - FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME || !o.new_parameters.extensions.value.updatable_htlc_options.valid(), + FC_ASSERT( db().head_block_time() > HARDFORK_CORE_1468_TIME || !o.new_parameters.extensions.value.updatable_htlc_options.valid(), "Unable to set HTLC parameters until hardfork." ); return void_result(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index f379116c9e..3be760bba4 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -422,9 +422,13 @@ void get_relevant_accounts( const object* obj, flat_set& accoun FC_ASSERT( aobj != nullptr ); accounts.insert( aobj->bidder ); break; - } case impl_htlc_object_type: - break; - + } case impl_htlc_object_type: { + const auto& htlc_obj = dynamic_cast(obj); + FC_ASSERT( htlc_obj != nullptr); + accounts.insert( htlc_obj->from ); + accounts.insert( htlc_obj->to ); + break; + } } } } // end get_relevant_accounts( const object* obj, flat_set& accounts ) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 1acfc2f9d8..b5f55fbb66 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -561,20 +561,19 @@ void database::update_withdraw_permissions() void database::clear_expired_htlcs() { const auto& htlc_idx = get_index_type().indices().get(); - while ( !htlc_idx.empty() && htlc_idx.begin() != htlc_idx.end() + while ( htlc_idx.begin() != htlc_idx.end() && htlc_idx.begin()->expiration <= head_block_time() ) { const htlc_object& obj = *htlc_idx.begin(); adjust_balance( obj.from, obj.amount ); // virtual op - htlc_refund_operation vop(obj.id, obj.from); + htlc_refund_operation vop( obj.id, obj.from ); vop.htlc_id = htlc_idx.begin()->id; - push_applied_operation(vop); + push_applied_operation( vop ); // remove the db object - remove( *htlc_idx.begin()); + remove( *htlc_idx.begin() ); } - } } } diff --git a/libraries/chain/hardfork.d/HTLC.hf b/libraries/chain/hardfork.d/HTLC.hf deleted file mode 100644 index 2ab8857d78..0000000000 --- a/libraries/chain/hardfork.d/HTLC.hf +++ /dev/null @@ -1,4 +0,0 @@ -// HTLC implementation -#ifndef HARDFORK_HTLC_TIME -#define HARDFORK_HTLC_TIME (fc::time_point_sec( 1600000000 ) ) -#endif diff --git a/libraries/chain/hardfork.d/core-1468.hf b/libraries/chain/hardfork.d/core-1468.hf new file mode 100644 index 0000000000..a669d57c84 --- /dev/null +++ b/libraries/chain/hardfork.d/core-1468.hf @@ -0,0 +1,4 @@ +// HTLC implementation +#ifndef HARDFORK_CORE_1468_TIME +#define HARDFORK_CORE_1468_TIME (fc::time_point_sec( 1600000000 ) ) +#endif diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 15eb029bd4..62abffa339 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -37,41 +37,35 @@ namespace graphene { void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) { - //FC_ASSERT( db().head_block_time() > HARDFORK_HTLC_TIME, - // "Operation not allowed before HARDFORK_HTLC_TIME."); // remove after HARDFORK_ESCROW_TIME + //FC_ASSERT( db().head_block_time() > HARDFORK_CORE_1468_TIME, + // "Operation not allowed before HARDFORK_CORE_1468_TIME."); // remove after HARDFORK_ESCROW_TIME optional htlc_options = get_committee_htlc_options(db()); FC_ASSERT(htlc_options, "HTLC Committee options are not set."); // make sure the expiration is reasonable - FC_ASSERT( o.seconds_in_force < htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); + FC_ASSERT( o.seconds_in_force <= htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable - FC_ASSERT( o.key_size < htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); + FC_ASSERT( o.key_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure we have a hash algorithm set FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); - // make sure sender has the funds for the HTLC - FC_ASSERT( db().get_balance( o.source, o.amount.asset_id ) >= (o.amount ), "Insufficient funds" ); - // make sure sender has the funds for the fee - FC_ASSERT( db().get_balance( o.source, asset().asset_id) > o.fee, "Insufficient funds" ); return void_result(); } object_id_type htlc_create_evaluator::do_apply(const htlc_create_operation& o) { try { - //FC_ASSERT( db().head_block_time() > HARDFORK_ESCROW_TIME, - // "Operation not allowed before HARDFORK_ESCROW_TIME."); // remove after HARDFORK_ESCROW_TIME + graphene::chain::database& dbase = db(); + dbase.adjust_balance( o.source, -o.amount ); - db().adjust_balance( o.source, -o.amount ); - - const htlc_object& esc = db().create([&]( htlc_object& esc ) { + const htlc_object& esc = db().create([&dbase,o]( htlc_object& esc ) { esc.from = o.source; esc.to = o.destination; esc.amount = o.amount; esc.preimage_hash = o.key_hash; esc.preimage_size = o.key_size; - esc.expiration = db().head_block_time() + o.seconds_in_force; + esc.expiration = dbase.head_block_time() + o.seconds_in_force; esc.preimage_hash_algorithm = o.hash_type; }); return esc.id; @@ -82,86 +76,62 @@ namespace graphene { template bool htlc_redeem_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { - // convert incoming_preimage to an array - unsigned char incoming_array[incoming_preimage.size()]; - for(unsigned i = 0; i < incoming_preimage.size(); ++i) - incoming_array[i] = incoming_preimage[i]; - T attempted_hash = T::hash( (char*)incoming_array, incoming_preimage.size()); + T attempted_hash = T::hash( (const char*)incoming_preimage.data(), incoming_preimage.size()); if (attempted_hash.data_size() != valid_hash.size()) return false; - char* data = attempted_hash.data(); - for(size_t i = 0; i < attempted_hash.data_size(); ++i) - { - if ( ((unsigned char)data[i]) != valid_hash[i]) - return false; - } - return true; + return memcmp(attempted_hash.data(), valid_hash.data(), attempted_hash.data_size()) == 0; } 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->preimage_size, "Preimage size mismatch."); - FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "Preimage provided after escrow expiration."); + FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); + FC_ASSERT(db().head_block_time() < htlc_obj->expiration, "Preimage provided after escrow expiration."); // see if the preimages match bool match = false; - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha256) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::ripemd160) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - if (htlc_obj->preimage_hash_algorithm == graphene::chain::hash_algorithm::sha1) - match = test_hash(o.preimage, htlc_obj->preimage_hash); - - FC_ASSERT(match, "Provided preimage does not generate correct hash."); - return void_result(); + switch(htlc_obj->preimage_hash_algorithm) + { + case (graphene::chain::hash_algorithm::sha256): + match = test_hash(o.preimage, htlc_obj->preimage_hash); + break; + case (graphene::chain::hash_algorithm::ripemd160): + match = test_hash(o.preimage, htlc_obj->preimage_hash); + break; + case (graphene::chain::hash_algorithm::sha1): + match = test_hash(o.preimage, htlc_obj->preimage_hash); + break; + default: + break; + } + + FC_ASSERT(match, "Provided preimage does not generate correct hash."); + return void_result(); } void_result htlc_redeem_evaluator::do_apply(const htlc_redeem_operation& o) { - db().adjust_balance(htlc_obj->to, htlc_obj->amount); - db().remove(*htlc_obj); - return void_result(); + db().adjust_balance(htlc_obj->to, htlc_obj->amount); + db().remove(*htlc_obj); + return void_result(); } void_result htlc_extend_evaluator::do_evaluate(const htlc_extend_operation& o) { - htlc_obj = &db().get(o.htlc_id); - - FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "HTLC has already expired."); - - return void_result(); + htlc_obj = &db().get(o.htlc_id); + FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "HTLC has already expired."); + return void_result(); } void_result htlc_extend_evaluator::do_apply(const htlc_extend_operation& o) { db().modify(*htlc_obj, [&o](htlc_object& db_obj) { - db_obj.expiration = fc::time_point_sec(db_obj.expiration.sec_since_epoch() + o.seconds_to_add); + db_obj.expiration += o.seconds_to_add; }); return void_result(); } - - - - - - - - - - - - - - - - - - - - } // namespace chain } // namespace graphene diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 89366791e4..6b8b0389c3 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -55,7 +55,6 @@ namespace graphene { namespace chain { account_id_type to; asset amount; fc::time_point_sec expiration; - asset pending_fee; vector preimage_hash; fc::enum_type preimage_hash_algorithm; uint16_t preimage_size; @@ -68,13 +67,15 @@ namespace graphene { namespace chain { indexed_by< ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag< by_expiration >, member< htlc_object, - fc::time_point_sec, &htlc_object::expiration > >, + ordered_unique< tag< by_expiration >, + composite_key< htlc_object, + member< htlc_object, fc::time_point_sec, &htlc_object::expiration >, + member< object, object_id_type, &object::id > > >, - ordered_non_unique< tag< by_from_id >, - composite_key< htlc_object, member< htlc_object, account_id_type, &htlc_object::from > - > - > + ordered_unique< tag< by_from_id >, + composite_key< htlc_object, + member< htlc_object, account_id_type, &htlc_object::from >, + member< object, object_id_type, &object::id > > > > > htlc_object_index_type; @@ -98,5 +99,5 @@ namespace fc FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), - (from)(to)(amount)(expiration)(pending_fee) + (from)(to)(amount)(expiration) (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 4d0863f8b5..dba8281305 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -88,7 +88,9 @@ FC_REFLECT( graphene::chain::htlc_options, (max_preimage_size) ) -FC_REFLECT( graphene::chain::chain_parameters::ext, (updatable_htlc_options)) +FC_REFLECT( graphene::chain::chain_parameters::ext, + (updatable_htlc_options) +) FC_REFLECT( graphene::chain::chain_parameters, (current_fees) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 23c83199e2..f60eb0e740 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -146,7 +146,7 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati operation_get_required_authorities(op.op, auths, auths, other); // If we are attempting to set HTLC parameters, make sure the HTLC hardfork date has passed. FC_ASSERT( op.op.which() != operation::tag::value - || d.head_block_time() >= HARDFORK_HTLC_TIME + || d.head_block_time() >= HARDFORK_CORE_1468_TIME || !op.op.get().new_parameters.extensions .value.updatable_htlc_options.valid(), "Unable to set HTLC parameters before hardfork."); } diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 185b11f08e..9d9ecb65b9 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -652,9 +652,11 @@ class wallet_api_impl htlc_object get_htlc(string htlc_id) const { - auto obj = _remote_db->get_htlc(htlc_id); - FC_ASSERT(obj, "HTLC not found"); - return *obj; + htlc_id_type id; + fc::from_variant(htlc_id, id); + auto obj = _remote_db->get_objects( { id }).front(); + htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); + return htlc; } asset_id_type get_asset_id(string asset_symbol_or_id) const @@ -3171,8 +3173,6 @@ variant wallet_api::get_htlc(std::string htlc_id) const ret_val["amount"] = obj.amount.amount.value; ret_val["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); - ret_val["pending_fee_amount"] = obj.pending_fee.amount.value; - ret_val["pending_fee_asset"] = (std::string)((graphene::db::object_id_type)obj.pending_fee.asset_id); std::stringstream hash_string; for(unsigned char c : obj.preimage_hash) hash_string << std::setfill('0') << std::setw(2) << std::hex << (int)c; diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 3ef22603cb..a858c5827a 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -703,7 +703,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // 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 = fc::json::to_pretty_string(htlc_id); + 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); } @@ -725,7 +725,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) // 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 = fc::json::to_pretty_string(htlc_id); + 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); } diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 9bbb22ab51..1214d8be9a 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -290,7 +290,6 @@ void database_fixture::verify_asset_supplies( const database& db ) for( auto itr = htlc_idx.begin(); itr != htlc_idx.end(); ++itr ) { total_balances[itr->amount.asset_id] += itr->amount.amount; - total_balances[itr->pending_fee.asset_id] += itr->pending_fee.amount; } for( const asset_object& asset_obj : db.get_index_type().indices() ) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 9414ef1ce3..1b0ff56f59 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -95,7 +95,7 @@ void set_committee_parameters(database_fixture* db_fixture) void advance_past_hardfork(database_fixture* db_fixture) { - db_fixture->generate_blocks(HARDFORK_HTLC_TIME); + db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME); set_committee_parameters(db_fixture); set_expiration(db_fixture->db, db_fixture->trx); } @@ -191,8 +191,8 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) // make sure Bob (or anyone) can see the details of the transaction graphene::app::database_api db_api(db); - optional htlc = db_api.get_htlc(fc::json::to_pretty_string(alice_htlc_id)); - BOOST_CHECK(htlc); + auto obj = db_api.get_objects( {alice_htlc_id }).front(); + graphene::chain::htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); // let it expire (wait for timeout) generate_blocks(fc::time_point_sec(120) ); From a56562a3c10c04602a27914cd96ee8adfb37b464 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sat, 15 Dec 2018 17:47:44 -0300 Subject: [PATCH 064/161] update median feeds when crossing hf1270 --- libraries/chain/db_maint.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index dfb3bec2b4..04396b3ecc 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -964,6 +964,22 @@ void database::process_bitassets() } } +void update_median_feeds(database& db) +{ + time_point_sec head_time = db.head_block_time(); + time_point_sec next_maint_time = db.get_dynamic_global_properties().next_maintenance_time; + + const auto update_bitasset = [head_time, next_maint_time]( asset_bitasset_data_object &o ) + { + o.update_median_feeds( head_time, next_maint_time ); + }; + + for( const auto& d : db.get_index_type().indices() ) + { + db.modify( d, update_bitasset ); + } +} + /****** * @brief one-time data process for hard fork core-868-890 * @@ -1292,6 +1308,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g if( to_update_and_match_call_orders_for_hf_1270 ) { update_call_orders_hf_1270(*this); + update_median_feeds(*this); match_call_orders(*this); } From d7306ab244634bf0bd302d7b821b53580cf63890 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sat, 15 Dec 2018 17:48:36 -0300 Subject: [PATCH 065/161] add testcases for mcr bug(hf1270) --- tests/tests/market_tests.cpp | 310 +++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) diff --git a/tests/tests/market_tests.cpp b/tests/tests/market_tests.cpp index 1f29f0c843..9447a06b79 100644 --- a/tests/tests/market_tests.cpp +++ b/tests/tests/market_tests.cpp @@ -1507,4 +1507,314 @@ BOOST_AUTO_TEST_CASE(target_cr_test_call_limit) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(mcr_bug_increase_before1270) +{ try { + + generate_blocks(HARDFORK_CORE_453_TIME); + + generate_block(); + + set_expiration( db, trx ); + + ACTORS((seller)(borrower)(borrower2)(feedproducer)); + + const auto& bitusd = create_bitasset("USDBIT", feedproducer_id); + const auto& core = asset_id_type()(db); + + int64_t init_balance(1000000); + + transfer(committee_account, borrower_id, asset(init_balance)); + transfer(committee_account, borrower2_id, asset(init_balance)); + update_feed_producers( bitusd, {feedproducer.id} ); + + price_feed current_feed; + current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100); + current_feed.maintenance_collateral_ratio = 1750; + current_feed.maximum_short_squeeze_ratio = 1100; + publish_feed( bitusd, feedproducer, current_feed ); + + const call_order_object& b1 = *borrow( borrower, bitusd.amount(1000), asset(1800)); + auto b1_id = b1.id; + const call_order_object& b2 = *borrow( borrower2, bitusd.amount(1000), asset(2000) ); + auto b2_id = b2.id; + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), init_balance - 1800 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), init_balance - 2000 ); + + // move order to margin call territory with mcr only + current_feed.maintenance_collateral_ratio = 2000; + publish_feed( bitusd, feedproducer, current_feed ); + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000 ); + + BOOST_CHECK( db.find( b1_id ) ); + BOOST_CHECK( db.find( b2_id ) ); + + // attempt to trade the margin call + create_sell_order( borrower2, bitusd.amount(1000), core.amount(1100) ); + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 0 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000 ); + + print_market(bitusd.symbol, core.symbol); + + // both calls are still there, no margin call, mcr bug + BOOST_CHECK( db.find( b1_id ) ); + BOOST_CHECK( db.find( b2_id ) ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(mcr_bug_increase_after1270) +{ try { + + generate_blocks(HARDFORK_CORE_1270_TIME); + + generate_block(); + + set_expiration( db, trx ); + + ACTORS((seller)(borrower)(borrower2)(feedproducer)); + + const auto& bitusd = create_bitasset("USDBIT", feedproducer_id); + const auto& core = asset_id_type()(db); + + int64_t init_balance(1000000); + + transfer(committee_account, borrower_id, asset(init_balance)); + transfer(committee_account, borrower2_id, asset(init_balance)); + update_feed_producers( bitusd, {feedproducer.id} ); + + price_feed current_feed; + current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100); + current_feed.maintenance_collateral_ratio = 1750; + current_feed.maximum_short_squeeze_ratio = 1100; + publish_feed( bitusd, feedproducer, current_feed ); + + const call_order_object& b1 = *borrow( borrower, bitusd.amount(1000), asset(1800)); + auto b1_id = b1.id; + const call_order_object& b2 = *borrow( borrower2, bitusd.amount(1000), asset(2000) ); + auto b2_id = b2.id; + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), init_balance - 1800 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), init_balance - 2000 ); + + // move order to margin call territory with mcr only + current_feed.maintenance_collateral_ratio = 2000; + publish_feed( bitusd, feedproducer, current_feed ); + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000 ); + + BOOST_CHECK( db.find( b1_id ) ); + BOOST_CHECK( db.find( b2_id ) ); + + // attempt to trade the margin call + create_sell_order( borrower2, bitusd.amount(1000), core.amount(1100) ); + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 0 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998900 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 999100 ); + + print_market(bitusd.symbol, core.symbol); + + // b1 is margin called + BOOST_CHECK( ! db.find( b1_id ) ); + BOOST_CHECK( db.find( b2_id ) ); + + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(mcr_bug_decrease_before1270) +{ try { + + generate_blocks(HARDFORK_CORE_453_TIME); + + generate_block(); + + set_expiration( db, trx ); + + ACTORS((seller)(borrower)(borrower2)(feedproducer)); + + const auto& bitusd = create_bitasset("USDBIT", feedproducer_id); + const auto& core = asset_id_type()(db); + + int64_t init_balance(1000000); + + transfer(committee_account, borrower_id, asset(init_balance)); + transfer(committee_account, borrower2_id, asset(init_balance)); + update_feed_producers( bitusd, {feedproducer.id} ); + + price_feed current_feed; + current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100); + current_feed.maintenance_collateral_ratio = 1750; + current_feed.maximum_short_squeeze_ratio = 1100; + publish_feed( bitusd, feedproducer, current_feed ); + + const call_order_object& b1 = *borrow( borrower, bitusd.amount(1000), asset(1800)); + auto b1_id = b1.id; + const call_order_object& b2 = *borrow( borrower2, bitusd.amount(1000), asset(2000) ); + auto b2_id = b2.id; + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), init_balance - 1800 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), init_balance - 2000 ); + + // move order to margin call territory with the feed + current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(150); + publish_feed( bitusd, feedproducer, current_feed ); + + // getting out of margin call territory with mcr change + current_feed.maintenance_collateral_ratio = 1100; + publish_feed( bitusd, feedproducer, current_feed ); + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000 ); + + BOOST_CHECK( db.find( b1_id ) ); + BOOST_CHECK( db.find( b2_id ) ); + + // attempt to trade the margin call + create_sell_order( borrower2, bitusd.amount(1000), core.amount(1100) ); + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 0 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998350 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 999650 ); + + print_market(bitusd.symbol, core.symbol); + + // margin call at b1, mcr bug + BOOST_CHECK( !db.find( b1_id ) ); + BOOST_CHECK( db.find( b2_id ) ); + + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(mcr_bug_decrease_after1270) +{ try { + + generate_blocks(HARDFORK_CORE_1270_TIME); + + generate_block(); + + set_expiration( db, trx ); + + ACTORS((seller)(borrower)(borrower2)(feedproducer)); + + const auto& bitusd = create_bitasset("USDBIT", feedproducer_id); + const auto& core = asset_id_type()(db); + + int64_t init_balance(1000000); + + transfer(committee_account, borrower_id, asset(init_balance)); + transfer(committee_account, borrower2_id, asset(init_balance)); + update_feed_producers( bitusd, {feedproducer.id} ); + + price_feed current_feed; + current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100); + current_feed.maintenance_collateral_ratio = 1750; + current_feed.maximum_short_squeeze_ratio = 1100; + publish_feed( bitusd, feedproducer, current_feed ); + + const call_order_object& b1 = *borrow( borrower, bitusd.amount(1000), asset(1800)); + auto b1_id = b1.id; + const call_order_object& b2 = *borrow( borrower2, bitusd.amount(1000), asset(2000) ); + auto b2_id = b2.id; + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), init_balance - 1800 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), init_balance - 2000 ); + + // move order to margin call territory with the feed + current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(150); + publish_feed( bitusd, feedproducer, current_feed ); + + // getting out of margin call territory with mcr decrease + current_feed.maintenance_collateral_ratio = 1100; + publish_feed( bitusd, feedproducer, current_feed ); + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000 ); + + BOOST_CHECK( db.find( b1_id ) ); + BOOST_CHECK( db.find( b2_id ) ); + + // attempt to trade the margin call + create_sell_order( borrower2, bitusd.amount(1000), core.amount(1100) ); + + BOOST_CHECK_EQUAL( get_balance( borrower, bitusd ), 1000 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, bitusd ), 0 ); + BOOST_CHECK_EQUAL( get_balance( borrower , core ), 998200 ); + BOOST_CHECK_EQUAL( get_balance( borrower2, core ), 998000 ); + + print_market(bitusd.symbol, core.symbol); + + // both calls are there, no margin call, good + BOOST_CHECK( db.find( b1_id ) ); + BOOST_CHECK( db.find( b2_id ) ); + + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(mcr_bug_cross1270) +{ try { + + INVOKE(mcr_bug_increase_before1270); + + auto mi = db.get_global_properties().parameters.maintenance_interval; + generate_blocks(HARDFORK_CORE_1270_TIME - mi); + + const asset_object& core = get_asset("BTS"); + const asset_object& bitusd = get_asset("USDBIT"); + const asset_id_type bitusd_id = bitusd.id; + const account_object& feedproducer = get_account("feedproducer"); + + // feed is expired + BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio, 1750); + BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).feed_is_expired(db.head_block_time()), false); // should be true? + + // make new feed + price_feed current_feed; + current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100); + current_feed.maintenance_collateral_ratio = 2000; + current_feed.maximum_short_squeeze_ratio = 1100; + publish_feed( bitusd, feedproducer, current_feed ); + + BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio, 2000); + BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).feed_is_expired(db.head_block_time()), false); + + // pass hardfork + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + // feed is still valid + BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio, 2000); + + // margin call is traded + print_market(asset_id_type(1)(db).symbol, asset_id_type()(db).symbol); + + // call b1 not there anymore + BOOST_CHECK( !db.find( call_order_id_type() ) ); + BOOST_CHECK( db.find( call_order_id_type(1) ) ); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() From e56389949a7247f86e869097f5774e78ca978be8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Dec 2018 16:17:09 -0500 Subject: [PATCH 066/161] cleanup, consistent variable naming --- libraries/chain/CMakeLists.txt | 1 + libraries/chain/htlc_evaluator.cpp | 14 ++-- libraries/chain/htlc_object.cpp | 45 ++++++++++ .../include/graphene/chain/htlc_evaluator.hpp | 2 +- .../include/graphene/chain/htlc_object.hpp | 15 +++- .../graphene/chain/protocol/fee_schedule.hpp | 2 +- .../include/graphene/chain/protocol/htlc.hpp | 65 +++------------ libraries/chain/protocol/htlc.cpp | 27 ------ .../wallet/include/graphene/wallet/wallet.hpp | 4 +- libraries/wallet/wallet.cpp | 32 +++---- tests/tests/htlc_tests.cpp | 83 +++++++++---------- 11 files changed, 137 insertions(+), 153 deletions(-) create mode 100644 libraries/chain/htlc_object.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 71d2fd2d2e..e6551c1189 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -78,6 +78,7 @@ add_library( graphene_chain withdraw_permission_evaluator.cpp worker_evaluator.cpp htlc_evaluator.cpp + htlc_object.cpp confidential_evaluator.cpp special_authority.cpp buyback.cpp diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 62abffa339..ef94c644d0 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -45,11 +45,13 @@ namespace graphene { FC_ASSERT(htlc_options, "HTLC Committee options are not set."); // make sure the expiration is reasonable - FC_ASSERT( o.seconds_in_force <= htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); + FC_ASSERT( o.claim_period_seconds <= htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable - FC_ASSERT( o.key_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); + FC_ASSERT( o.preimage_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure we have a hash algorithm set FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); + // make sure the sender has the funds for the HTLC + FC_ASSERT( db().get_balance( o.source, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; return void_result(); } @@ -63,9 +65,9 @@ namespace graphene { esc.from = o.source; esc.to = o.destination; esc.amount = o.amount; - esc.preimage_hash = o.key_hash; - esc.preimage_size = o.key_size; - esc.expiration = dbase.head_block_time() + o.seconds_in_force; + esc.preimage_hash = o.preimage_hash; + esc.preimage_size = o.preimage_size; + esc.expiration = dbase.head_block_time() + o.claim_period_seconds; esc.preimage_hash_algorithm = o.hash_type; }); return esc.id; @@ -74,7 +76,7 @@ namespace graphene { } template - bool htlc_redeem_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) + bool htlc_redeem_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) { T attempted_hash = T::hash( (const char*)incoming_preimage.data(), incoming_preimage.size()); if (attempted_hash.data_size() != valid_hash.size()) diff --git a/libraries/chain/htlc_object.cpp b/libraries/chain/htlc_object.cpp new file mode 100644 index 0000000000..f7f4c93b21 --- /dev/null +++ b/libraries/chain/htlc_object.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Bitshares Foundation, and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include + +namespace graphene { namespace chain { + + /**** + * Convert string to hash algorithm enum + * @param incoming the string to convert + * @returns one of the valid algorithms or the enum value "unknown" + */ + fc::enum_type string_to_hash_algorithm(std::string incoming) + { + std::transform(incoming.begin(), incoming.end(), incoming.begin(), ::toupper); + if (incoming == "RIPEMD160") + return hash_algorithm::ripemd160; + if (incoming == "SHA256") + return hash_algorithm::sha256; + if (incoming == "SHA1") + return hash_algorithm::sha1; + return hash_algorithm::unknown; + } + +} } diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp index 7c6b641db5..c93b579742 100644 --- a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -46,7 +46,7 @@ namespace graphene { const htlc_object* htlc_obj = nullptr; private: template - bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash); + bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash); }; class htlc_extend_evaluator : public evaluator diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 6b8b0389c3..01a5510467 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -39,6 +39,13 @@ namespace graphene { namespace chain { sha1 = 0x03 }; + /**** + * Convert string to hash algorithm enum + * @param incoming the string to convert + * @returns one of the valid algorithms or the enum value "unknown" + */ + fc::enum_type string_to_hash_algorithm(std::string incoming); + /** * @brief database object to store HTLCs * @@ -55,7 +62,7 @@ namespace graphene { namespace chain { account_id_type to; asset amount; fc::time_point_sec expiration; - vector preimage_hash; + vector preimage_hash; fc::enum_type preimage_hash_algorithm; uint16_t preimage_size; }; @@ -87,11 +94,11 @@ namespace graphene { namespace chain { namespace fc { template<> - struct get_typename> + struct get_typename> { static const char* name() { - return "fc::enum_type"; + return "fc::enum_type"; } }; } @@ -100,4 +107,4 @@ FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)(s FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), (from)(to)(amount)(expiration) - (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); \ No newline at end of file + (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index 3a054cf2ec..0b7e8ab457 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -43,7 +43,7 @@ namespace graphene { namespace chain { FC_ASSERT( itr != parameters.end() ); return itr->template get(); } - typename Operation::fee_parameters_type& get(flat_set& parameters) + typename Operation::fee_parameters_type& get(flat_set& parameters)const { auto itr = parameters.find( typename Operation::fee_parameters_type() ); FC_ASSERT( itr != parameters.end() ); diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index d0f5258066..f8ca2c393d 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -31,21 +31,6 @@ namespace graphene { namespace chain { - /** - * Convert the hash algorithm to a string - * @param algo the enum to convert - * @returns a string (lower case) - */ - std::string hash_algorithm_to_string(fc::enum_type algo); - - /** - * Convert a string to the enum that matches the hash algorithm - * @param incoing the string (case insensitive) - * @returns the matching enum - */ - fc::enum_type string_to_hash_algorithm(std::string incoming); - - struct htlc_create_operation : public base_operation { struct fee_parameters_type { @@ -60,15 +45,15 @@ namespace graphene { account_id_type destination; // the amount to hold asset amount; - // hash algorithm used to create key_hash + // hash algorithm used to create preimage_hash fc::enum_type hash_type = graphene::chain::hash_algorithm::unknown; // the hash of the preimage - std::vector key_hash; + std::vector preimage_hash; // the size of the preimage - uint16_t key_size; + uint16_t preimage_size; // The time the funds will be returned to the source if not claimed - uint32_t seconds_in_force; + uint32_t claim_period_seconds; // for future expansion extensions_type extensions; @@ -77,14 +62,6 @@ namespace graphene { */ void validate()const; - /** - * @brief Determines who is required to sign - */ - void get_required_active_authorities( boost::container::flat_set& a )const - { - a.insert(source); - } - /** * @brief who will pay the fee */ @@ -95,7 +72,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = seconds_in_force / (60 * 60 * 24); + uint32_t days = claim_period_seconds / (60 * 60 * 24); return fee_params.fee + (fee_params.fee_per_day * days); } @@ -104,7 +81,7 @@ namespace graphene { struct htlc_redeem_operation : public base_operation { struct fee_parameters_type { - uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t fee_per_kb = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; // paid to network @@ -112,9 +89,9 @@ namespace graphene { // the object we are attempting to update htlc_id_type htlc_id; // who is attempting to update the transaction - account_id_type update_issuer; + account_id_type redeemer; // the preimage (not used if after epoch timeout) - std::vector preimage; + std::vector preimage; // for future expansion extensions_type extensions; @@ -123,25 +100,17 @@ namespace graphene { */ void validate()const; - /*** - * @determines who should have signed this object - */ - void get_required_active_authorities( boost::container::flat_set& a )const - { - a.insert(update_issuer); - } - /** * @brief Who is to pay the fee */ - account_id_type fee_payer()const { return update_issuer; } + account_id_type fee_payer()const { return redeemer; } /**** * @brief calculates the fee to be paid for this operation */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - return fee_params.fee; + return preimage.size() / 1024 * fee_params.fee_per_kb; } }; @@ -168,14 +137,6 @@ namespace graphene { */ void validate()const; - /*** - * @determines who should have signed this object - */ - void get_required_active_authorities( boost::container::flat_set& a )const - { - a.insert(update_issuer); - } - /** * @brief Who is to pay the fee */ @@ -213,12 +174,12 @@ namespace graphene { } FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) -FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee_per_kb) ) FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_create_operation, - (fee)(source)(destination)(amount)(key_hash)(key_size)(seconds_in_force)(extensions)(hash_type)) -FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(update_issuer)(preimage)(extensions)) + (fee)(source)(destination)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)(hash_type)) +FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(redeemer)(preimage)(extensions)) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)(to)) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 0f3e61bd95..a83f42302f 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -39,31 +39,4 @@ namespace graphene { namespace chain { FC_ASSERT( fee.amount >= 0 ); } - std::string hash_algorithm_to_string(fc::enum_type algo) - { - switch(algo) - { - case (hash_algorithm::ripemd160): - return "ripemd160"; - case (hash_algorithm::sha256): - return "sha256"; - case (hash_algorithm::sha1): - return "sha1"; - default: - break; - } - return "unknown"; - } - - fc::enum_type string_to_hash_algorithm(std::string incoming) - { - std::transform(incoming.begin(), incoming.end(), incoming.begin(), ::toupper); - if (incoming == "RIPEMD160") - return hash_algorithm::ripemd160; - if (incoming == "SHA256") - return hash_algorithm::sha256; - if (incoming == "SHA1") - return hash_algorithm::sha1; - return hash_algorithm::unknown; - } } } diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 7b087dab61..a934d19644 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1465,12 +1465,12 @@ class wallet_api * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160 or SHA256. * @param preimage_hash the hash of the preimage * @param preimage_size the size of the preimage in bytes - * @param seconds_in_force when the time lock expires + * @param claim_period_seconds when the time lock expires * @param broadcast true if you wish to broadcast the transaction */ signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, - const uint32_t seconds_in_force, bool broadcast = false ); + const uint32_t claim_period_seconds, bool broadcast = false ); /**** * Update a hashed time lock contract diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 9d9ecb65b9..4477bc044f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1750,8 +1750,8 @@ class wallet_api_impl } signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, - string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, - const uint32_t seconds_in_force, bool broadcast = false ) + string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + const uint32_t claim_period_seconds, bool broadcast = false ) { try { @@ -1763,9 +1763,9 @@ class wallet_api_impl create_op.source = get_account(source).id; create_op.destination = get_account(destination).id; create_op.amount = asset_obj->amount_from_string(amount); - create_op.seconds_in_force = seconds_in_force; - create_op.key_hash = preimage_hash; - create_op.key_size = preimage_size; + create_op.claim_period_seconds = claim_period_seconds; + create_op.preimage_hash = preimage_hash; + create_op.preimage_size = preimage_size; create_op.hash_type = graphene::chain::string_to_hash_algorithm(hash_algorithm); FC_ASSERT(create_op.hash_type != graphene::chain::hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); @@ -1777,10 +1777,10 @@ class wallet_api_impl return sign_transaction(tx, broadcast); } FC_CAPTURE_AND_RETHROW( (source)(destination)(amount)(asset_symbol)(hash_algorithm) - (preimage_hash)(preimage_size)(seconds_in_force)(broadcast) ) + (preimage_hash)(preimage_size)(claim_period_seconds)(broadcast) ) } - signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) { try { @@ -1792,7 +1792,7 @@ class wallet_api_impl htlc_redeem_operation update_op; update_op.htlc_id = htlc_obj->id; - update_op.update_issuer = issuer_obj.id; + update_op.redeemer = issuer_obj.id; update_op.preimage = preimage; signed_transaction tx; @@ -2998,7 +2998,7 @@ std::string operation_printer::operator()(const htlc_create_operation& op) const out << "Create HTLC to " << to.name << " with id " << database_id << " preimage hash: ["; - for(unsigned char c : op.key_hash) + for(uint8_t c : op.preimage_hash) { out << setfill('0') << std::setw(2) << std::hex << (int)c; } @@ -3141,11 +3141,11 @@ uint64_t wallet_api::get_asset_count()const return my->_remote_db->get_asset_count(); } -std::vector string_to_vec(std::string incoming) +std::vector string_to_vec(std::string incoming) { char s[3]; s[2] = 0; - std::vector vec; + std::vector vec; for(int i = 0; i < incoming.length(); i+= 2) { s[0] = incoming[i]; @@ -3157,10 +3157,10 @@ std::vector string_to_vec(std::string incoming) signed_transaction wallet_api::htlc_prepare( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, - const uint32_t seconds_in_force, bool broadcast) + const uint32_t claim_period_seconds, bool broadcast) { return my->htlc_prepare(source, destination, amount, asset_symbol, hash_algorithm, string_to_vec(preimage_hash), preimage_size, - seconds_in_force, broadcast); + claim_period_seconds, broadcast); } variant wallet_api::get_htlc(std::string htlc_id) const @@ -3174,10 +3174,10 @@ variant wallet_api::get_htlc(std::string htlc_id) const ret_val["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); std::stringstream hash_string; - for(unsigned char c : obj.preimage_hash) + for(uint8_t c : obj.preimage_hash) hash_string << std::setfill('0') << std::setw(2) << std::hex << (int)c; ret_val["preimage_hash"] = hash_string.str(); - ret_val["preimage_algorithm"] = graphene::chain::hash_algorithm_to_string(obj.preimage_hash_algorithm); + ret_val["preimage_algorithm"] = (std::string)obj.preimage_hash_algorithm; ret_val["preimage_size"] = obj.preimage_size; return ret_val; } @@ -3186,7 +3186,7 @@ signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string iss bool broadcast) { - return my->htlc_redeem(htlc_id, issuer, std::vector(preimage.begin(), preimage.end()), broadcast); + return my->htlc_redeem(htlc_id, issuer, std::vector(preimage.begin(), preimage.end()), broadcast); } signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 1b0ff56f59..c5b09e6a92 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -55,27 +55,22 @@ using namespace graphene::chain::test; BOOST_FIXTURE_TEST_SUITE( htlc_tests, database_fixture ) -void generate_random_preimage(uint16_t key_size, std::vector& vec) +void generate_random_preimage(uint16_t key_size, std::vector& vec) { - std::independent_bits_engine rbe; + std::independent_bits_engine rbe; std::generate(begin(vec), end(vec), std::ref(rbe)); return; } -std::vector hash_it(std::vector preimage) +/**** + * Hash the preimage and put it in a vector + * @param preimage the preimage + * @returns a vector that cointains the sha256 hash of the preimage + */ +std::vector hash_it(std::vector preimage) { - // convert the preimage to a char array - unsigned char char_array[preimage.size()]; - for(unsigned int i = 0; i < preimage.size(); ++i) - char_array[i] = preimage[i]; - fc::sha256 hash = fc::sha256::hash((char*)char_array, preimage.size()); - std::vector ret_val(hash.data_size()); - char* data = hash.data(); - for(size_t i = 0; i < hash.data_size(); i++) - { - ret_val[i] = data[i]; - } - return ret_val; + fc::sha256 hash = fc::sha256::hash((char*)preimage.data(), preimage.size()); + return std::vector(hash.data(), hash.data() + hash.data_size()); } void set_committee_parameters(database_fixture* db_fixture) @@ -108,10 +103,10 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - uint16_t key_size = 256; + uint16_t preimage_size = 256; std::vector pre_image(256); - generate_random_preimage(key_size, pre_image); - std::vector key_hash = hash_it(pre_image); + generate_random_preimage(preimage_size, pre_image); + std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -123,10 +118,10 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.seconds_in_force = 60; - create_operation.key_hash = key_hash; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; - create_operation.key_size = key_size; + create_operation.preimage_size = preimage_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); @@ -155,10 +150,10 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - uint16_t key_size = 256; + uint16_t preimage_size = 256; std::vector pre_image(256); - generate_random_preimage(key_size, pre_image); - std::vector key_hash = hash_it(pre_image); + generate_random_preimage(preimage_size, pre_image); + std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -170,10 +165,10 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.seconds_in_force = 60; - create_operation.key_hash = key_hash; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; - create_operation.key_size = key_size; + create_operation.preimage_size = preimage_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); @@ -212,10 +207,10 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); - uint16_t key_size = 256; - std::vector pre_image(key_size); - generate_random_preimage(key_size, pre_image); - std::vector key_hash = hash_it(pre_image); + uint16_t preimage_size = 256; + std::vector pre_image(preimage_size); + generate_random_preimage(preimage_size, pre_image); + std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -228,10 +223,10 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.amount = graphene::chain::asset( 100000 ); create_operation.destination = bob_id; - create_operation.seconds_in_force = 86400; - create_operation.key_hash = key_hash; + create_operation.claim_period_seconds = 86400; + create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; - create_operation.key_size = key_size; + create_operation.preimage_size = preimage_size; create_operation.source = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); trx.operations.push_back(create_operation); @@ -252,7 +247,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) // send an update operation to claim the funds { graphene::chain::htlc_redeem_operation update_operation; - update_operation.update_issuer = bob_id; + update_operation.redeemer = bob_id; update_operation.htlc_id = alice_htlc_id; update_operation.preimage = pre_image; update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation ); @@ -284,10 +279,10 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - uint16_t key_size = 256; + uint16_t preimage_size = 256; std::vector pre_image(256); - generate_random_preimage(key_size, pre_image); - std::vector key_hash = hash_it(pre_image); + generate_random_preimage(preimage_size, pre_image); + std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -298,9 +293,9 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.seconds_in_force = 3; - create_operation.key_hash = key_hash; - create_operation.key_size = key_size; + create_operation.claim_period_seconds = 3; + create_operation.preimage_hash = preimage_hash; + create_operation.preimage_size = preimage_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, bob_private_key); @@ -312,10 +307,10 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); create_operation.destination = bob_id; - create_operation.seconds_in_force = 3; - create_operation.key_hash = key_hash; + create_operation.claim_period_seconds = 3; + create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; - create_operation.key_size = key_size; + create_operation.preimage_size = preimage_size; create_operation.source = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); From 62395643e0d7b446dba5ec9da68e56c91345596e Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Dec 2018 17:27:18 -0500 Subject: [PATCH 067/161] More consistent naming --- libraries/chain/htlc_evaluator.cpp | 43 +++++++++---------- .../include/graphene/chain/protocol/htlc.hpp | 8 ++-- libraries/chain/protocol/htlc.cpp | 1 - .../wallet/include/graphene/wallet/wallet.hpp | 12 +++--- libraries/wallet/wallet.cpp | 27 +++++------- tests/tests/htlc_tests.cpp | 24 +++++------ 6 files changed, 53 insertions(+), 62 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index ef94c644d0..6eb4a33a0a 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -51,28 +51,28 @@ namespace graphene { // make sure we have a hash algorithm set FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); // make sure the sender has the funds for the HTLC - FC_ASSERT( db().get_balance( o.source, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; + FC_ASSERT( db().get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; return void_result(); } object_id_type htlc_create_evaluator::do_apply(const htlc_create_operation& o) { - try { - graphene::chain::database& dbase = db(); - dbase.adjust_balance( o.source, -o.amount ); - - const htlc_object& esc = db().create([&dbase,o]( htlc_object& esc ) { - esc.from = o.source; - esc.to = o.destination; - esc.amount = o.amount; - esc.preimage_hash = o.preimage_hash; - esc.preimage_size = o.preimage_size; - esc.expiration = dbase.head_block_time() + o.claim_period_seconds; - esc.preimage_hash_algorithm = o.hash_type; - }); - return esc.id; - - } FC_CAPTURE_AND_RETHROW( (o) ) + try { + graphene::chain::database& dbase = db(); + dbase.adjust_balance( o.from, -o.amount ); + + const htlc_object& esc = db().create([&dbase,o]( htlc_object& esc ) { + esc.from = o.from; + esc.to = o.to; + esc.amount = o.amount; + esc.preimage_hash = o.preimage_hash; + esc.preimage_size = o.preimage_size; + esc.expiration = dbase.head_block_time() + o.claim_period_seconds; + esc.preimage_hash_algorithm = o.hash_type; + }); + return esc.id; + + } FC_CAPTURE_AND_RETHROW( (o) ) } template @@ -86,12 +86,12 @@ namespace graphene { void_result htlc_redeem_evaluator::do_evaluate(const htlc_redeem_operation& o) { - htlc_obj = &db().get(o.htlc_id); + htlc_obj = &db().get(o.htlc_id); FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); - FC_ASSERT(db().head_block_time() < htlc_obj->expiration, "Preimage provided after escrow expiration."); + FC_ASSERT(db().head_block_time() < htlc_obj->expiration, "Preimage provided after escrow expiration."); - // see if the preimages match + // see if the preimages match bool match = false; switch(htlc_obj->preimage_hash_algorithm) { @@ -122,7 +122,6 @@ namespace graphene { void_result htlc_extend_evaluator::do_evaluate(const htlc_extend_operation& o) { htlc_obj = &db().get(o.htlc_id); - FC_ASSERT(db().head_block_time().sec_since_epoch() < htlc_obj->expiration.sec_since_epoch(), "HTLC has already expired."); return void_result(); } @@ -132,7 +131,7 @@ namespace graphene { db_obj.expiration += o.seconds_to_add; }); - return void_result(); + return void_result(); } } // namespace chain diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index f8ca2c393d..7a7469eb2e 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -40,9 +40,9 @@ namespace graphene { // paid to network asset fee; // where the held monies are to come from - account_id_type source; + account_id_type from; // where the held monies will go if the preimage is provided - account_id_type destination; + account_id_type to; // the amount to hold asset amount; // hash algorithm used to create preimage_hash @@ -65,7 +65,7 @@ namespace graphene { /** * @brief who will pay the fee */ - account_id_type fee_payer()const { return source; } + account_id_type fee_payer()const { return from; } /**** * @brief calculates the fee to be paid for this operation @@ -179,7 +179,7 @@ FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) ( FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_create_operation, - (fee)(source)(destination)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)(hash_type)) + (fee)(from)(to)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)(hash_type)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(redeemer)(preimage)(extensions)) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)(to)) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index a83f42302f..56e4cb8d06 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -26,7 +26,6 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { - FC_ASSERT( source != destination ); FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount.amount > 0 ); } diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index a934d19644..d0b80cceca 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1462,10 +1462,10 @@ class wallet_api * @param destination The account that will receive the funds if the preimage is presented * @param amount the amount of the asset that is to be traded * @param asset_symbol The asset that is to be traded - * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160 or SHA256. + * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160, SHA1 or SHA256. * @param preimage_hash the hash of the preimage * @param preimage_size the size of the preimage in bytes - * @param claim_period_seconds when the time lock expires + * @param claim_period_seconds how long after creation until the lock expires * @param broadcast true if you wish to broadcast the transaction */ signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, @@ -1483,14 +1483,14 @@ class wallet_api bool broadcast = false ); /***** - * Set a new timelock on an existing HTLC + * Increase the timelock on an existing HTLC * * @param htlc_id The object identifier of the HTLC on the blockchain * @param issuer Who is performing this operation (and paying the fee) - * @param timelock the new time of expiry + * @param seconds_to_add how many seconds to add to the existing timelock * @param broadcast true to broadcast to the network */ - signed_transaction htlc_extend_expiry(string htlc_id, string issuer, const uint32_t seconds_to_add, + signed_transaction htlc_extend(string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast = false); /** @@ -1846,7 +1846,7 @@ FC_API( graphene::wallet::wallet_api, (update_worker_votes) (htlc_prepare) (htlc_redeem) - (htlc_extend_expiry) + (htlc_extend) (get_vesting_balances) (withdraw_vesting) (vote_for_committee_member) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 4477bc044f..0e9d1d1997 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1760,8 +1760,8 @@ class wallet_api_impl FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); htlc_create_operation create_op; - create_op.source = get_account(source).id; - create_op.destination = get_account(destination).id; + create_op.from = get_account(source).id; + create_op.to = get_account(destination).id; create_op.amount = asset_obj->amount_from_string(amount); create_op.claim_period_seconds = claim_period_seconds; create_op.preimage_hash = preimage_hash; @@ -1804,7 +1804,7 @@ class wallet_api_impl } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(preimage)(broadcast) ) } - signed_transaction htlc_extend_expiry ( string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast) + signed_transaction htlc_extend ( string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast) { try { @@ -2991,7 +2991,7 @@ std::string operation_printer::operator()(const htlc_redeem_operation& op) const 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.destination ); + auto to = wallet.get_account( op.to ); operation_result_printer rprinter(wallet); std::string database_id = result.visit(rprinter); @@ -3143,16 +3143,11 @@ uint64_t wallet_api::get_asset_count()const std::vector string_to_vec(std::string incoming) { - char s[3]; - s[2] = 0; - std::vector vec; - for(int i = 0; i < incoming.length(); i+= 2) - { - s[0] = incoming[i]; - s[1] = incoming[i+1]; - vec.push_back( (int)strtol(&s[0], nullptr, 16)); - } - return vec; + FC_ASSERT(incoming.size() % 2 == 0, "Invalid incoming hash."); + std::size_t outgoing_size = incoming.size() / 2; + std::vector outgoing(outgoing_size); + fc::from_hex(incoming, (char*)outgoing.data(), outgoing_size); + return outgoing; } signed_transaction wallet_api::htlc_prepare( string source, string destination, string amount, string asset_symbol, @@ -3189,10 +3184,10 @@ signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string iss return my->htlc_redeem(htlc_id, issuer, std::vector(preimage.begin(), preimage.end()), broadcast); } -signed_transaction wallet_api::htlc_extend_expiry ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, +signed_transaction wallet_api::htlc_extend ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, bool broadcast) { - return my->htlc_extend_expiry(htlc_id, issuer, seconds_to_add, broadcast); + return my->htlc_extend(htlc_id, issuer, seconds_to_add, broadcast); } vector wallet_api::get_account_history(string name, int limit)const diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index c5b09e6a92..da4ec86224 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -117,12 +117,12 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.destination = bob_id; + create_operation.to = bob_id; create_operation.claim_period_seconds = 60; create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.preimage_size = preimage_size; - create_operation.source = alice_id; + create_operation.from = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); @@ -164,18 +164,17 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.destination = bob_id; + create_operation.to = bob_id; create_operation.claim_period_seconds = 60; create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.preimage_size = preimage_size; - create_operation.source = alice_id; + create_operation.from = alice_id; 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(); - // can we assume that alice's transaction will be the only one in this block? processed_transaction alice_trx = blk.transactions[0]; alice_htlc_id = alice_trx.operation_results[0].get(); generate_block(); @@ -222,19 +221,18 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 100000 ); - create_operation.destination = bob_id; + create_operation.to = bob_id; create_operation.claim_period_seconds = 86400; create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.preimage_size = preimage_size; - create_operation.source = alice_id; + create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); 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(); - // can we assume that alice's transaction will be the only one in this block? processed_transaction alice_trx = blk.transactions[0]; alice_htlc_id = alice_trx.operation_results[0].get(); } @@ -265,7 +263,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) trx.clear(); } // verify funds end up in Bob's account - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 1000000 ); + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 1100000 ); BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); } @@ -292,11 +290,11 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) { graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.destination = bob_id; + create_operation.to = bob_id; create_operation.claim_period_seconds = 3; create_operation.preimage_hash = preimage_hash; create_operation.preimage_size = preimage_size; - create_operation.source = alice_id; + create_operation.from = alice_id; trx.operations.push_back(create_operation); sign(trx, bob_private_key); GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, database::skip_nothing), fc::exception); @@ -306,12 +304,12 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) { graphene::chain::htlc_create_operation create_operation; create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.destination = bob_id; + create_operation.to = bob_id; create_operation.claim_period_seconds = 3; create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.preimage_size = preimage_size; - create_operation.source = alice_id; + create_operation.from = alice_id; trx.operations.push_back(create_operation); sign(trx, alice_private_key); PUSH_TX(db, trx, database::skip_nothing); From e914a372ebda10c55328c8c631f24ac33f7d60b4 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Dec 2018 17:52:38 -0500 Subject: [PATCH 068/161] Switch htlc object from implementation to protocol --- libraries/chain/db_notify.cpp | 14 +++++++------- .../chain/include/graphene/chain/htlc_object.hpp | 4 ++-- .../include/graphene/chain/protocol/types.hpp | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 3be760bba4..354505b485 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -360,6 +360,12 @@ void get_relevant_accounts( const object* obj, flat_set& accoun } case balance_object_type:{ /** these are free from any accounts */ break; + } case htlc_object_type: { + const auto& htlc_obj = dynamic_cast(obj); + FC_ASSERT( htlc_obj != nullptr); + accounts.insert( htlc_obj->from ); + accounts.insert( htlc_obj->to ); + break; } } } @@ -422,13 +428,7 @@ void get_relevant_accounts( const object* obj, flat_set& accoun FC_ASSERT( aobj != nullptr ); accounts.insert( aobj->bidder ); break; - } case impl_htlc_object_type: { - const auto& htlc_obj = dynamic_cast(obj); - FC_ASSERT( htlc_obj != nullptr); - accounts.insert( htlc_obj->from ); - accounts.insert( htlc_obj->to ); - break; - } + } } } } // end get_relevant_accounts( const object* obj, flat_set& accounts ) diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 01a5510467..7c45d4d636 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -55,8 +55,8 @@ namespace graphene { namespace chain { class htlc_object : public graphene::db::abstract_object { public: // uniquely identify this object in the database - static const uint8_t space_id = implementation_ids; - static const uint8_t type_id = impl_htlc_object_type; + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = htlc_object_type; account_id_type from; account_id_type to; diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 4d04426527..53b0378c65 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -140,6 +140,7 @@ namespace graphene { namespace chain { vesting_balance_object_type, worker_object_type, balance_object_type, + htlc_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -162,8 +163,7 @@ namespace graphene { namespace chain { impl_special_authority_object_type, impl_buyback_object_type, impl_fba_accumulator_object_type, - impl_collateral_bid_object_type, - impl_htlc_object_type, + impl_collateral_bid_object_type }; //typedef fc::unsigned_int object_id_type; @@ -183,6 +183,7 @@ namespace graphene { namespace chain { class worker_object; class balance_object; class blinded_balance_object; + class htlc_object; typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type; @@ -198,6 +199,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type; typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type; typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type; + typedef object_id< protocol_ids, htlc_object_type, htlc_object> htlc_id_type; // implementation types class global_property_object; @@ -215,7 +217,6 @@ namespace graphene { namespace chain { class buyback_object; class fba_accumulator_object; class collateral_bid_object; - class htlc_object; typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type; typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type; @@ -236,7 +237,6 @@ namespace graphene { namespace chain { typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type; typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type; typedef object_id< implementation_ids, impl_collateral_bid_object_type, collateral_bid_object > collateral_bid_id_type; - typedef object_id< implementation_ids, impl_htlc_object_type, htlc_object> htlc_id_type; typedef fc::ripemd160 block_id_type; typedef fc::ripemd160 checksum_type; @@ -346,6 +346,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (vesting_balance_object_type) (worker_object_type) (balance_object_type) + (htlc_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, @@ -367,7 +368,6 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type, (impl_buyback_object_type) (impl_fba_accumulator_object_type) (impl_collateral_bid_object_type) - (impl_htlc_object_type) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) From c808e61170340fcba6b0b917db14affd0ddfeb4d Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 15 Dec 2018 18:37:08 -0500 Subject: [PATCH 069/161] Move committee parameter hardfork validation to visitor --- libraries/chain/proposal_evaluator.cpp | 8 +++----- libraries/chain/protocol/htlc.cpp | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index f60eb0e740..efab4be33a 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -94,6 +94,9 @@ struct proposal_operation_hardfork_visitor FC_ASSERT(!"Virtual operation"); } } + void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const { + FC_ASSERT(block_time > HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468"); + } // loop and self visit in proposals void operator()(const graphene::chain::proposal_create_operation &v) const { for (const op_wrapper &op : v.proposed_ops) @@ -144,11 +147,6 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati for( auto& op : o.proposed_ops ) { operation_get_required_authorities(op.op, auths, auths, other); - // If we are attempting to set HTLC parameters, make sure the HTLC hardfork date has passed. - FC_ASSERT( op.op.which() != operation::tag::value - || d.head_block_time() >= HARDFORK_CORE_1468_TIME - || !op.op.get().new_parameters.extensions - .value.updatable_htlc_options.valid(), "Unable to set HTLC parameters before hardfork."); } FC_ASSERT( other.size() == 0 ); // TODO: what about other??? diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 56e4cb8d06..be444735f6 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -28,11 +28,13 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount.amount > 0 ); + FC_ASSERT( hash_type == hash_algorithm::ripemd160 + || hash_type == graphene::chain::hash_algorithm::sha1 + || hash_type == hash_algorithm::sha256, "Unknown Hash Algorithm"); } void htlc_redeem_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( preimage.size() > 0 ); } void htlc_extend_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); From fea41d77006512637da6fe7e5796942de3f0b9ed Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 17 Dec 2018 17:13:11 -0500 Subject: [PATCH 070/161] htlc_prepare is now htlc_create, plus start of fee testing (wip) --- libraries/chain/htlc_evaluator.cpp | 3 - .../graphene/chain/protocol/fee_schedule.hpp | 22 +++++++ .../include/graphene/chain/protocol/htlc.hpp | 3 +- libraries/chain/proposal_evaluator.cpp | 5 +- .../wallet/include/graphene/wallet/wallet.hpp | 4 +- libraries/wallet/wallet.cpp | 6 +- tests/cli/main.cpp | 4 +- tests/tests/htlc_tests.cpp | 58 ++++++++++++++++++- 8 files changed, 92 insertions(+), 13 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 6eb4a33a0a..6087442cb2 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -37,9 +37,6 @@ namespace graphene { void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) { - //FC_ASSERT( db().head_block_time() > HARDFORK_CORE_1468_TIME, - // "Operation not allowed before HARDFORK_CORE_1468_TIME."); // remove after HARDFORK_ESCROW_TIME - optional htlc_options = get_committee_htlc_options(db()); FC_ASSERT(htlc_options, "HTLC Committee options are not set."); diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index 0b7e8ab457..39fa0a390f 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -113,6 +113,28 @@ namespace graphene { namespace chain { } }; + /* + template<> + class fee_helper { + public: + const htlc_create_operation::fee_parameters_type& cget(const flat_set& 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; + } + typename htlc_create_operation::fee_parameters_type& get(flat_set& parameters)const + { + auto itr = parameters.find( htlc_create_operation::fee_parameters_type() ); + FC_ASSERT( itr != parameters.end() ); + return itr->get(); + } + }; + */ + /** * @brief contains all of the parameters necessary to calculate the fee for any operation */ diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 7a7469eb2e..a9428f492c 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -27,6 +27,7 @@ #include #include #include +#include // std::min namespace graphene { namespace chain { @@ -72,7 +73,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = claim_period_seconds / (60 * 60 * 24); + uint32_t days = std::min(1, claim_period_seconds / (60 * 60 * 24)); return fee_params.fee + (fee_params.fee_per_day * days); } diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index efab4be33a..ed335dcff5 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -95,7 +95,10 @@ struct proposal_operation_hardfork_visitor } } void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const { - FC_ASSERT(block_time > HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468"); + if (block_time < HARDFORK_CORE_1468_TIME) { + FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(), "Unable to set HTLC options before hardfork 1468"); + // TODO: Do not allow changing of fees before hardfork + } } // loop and self visit in proposals void operator()(const graphene::chain::proposal_create_operation &v) const { diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index d0b80cceca..e069fbcdc3 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1468,7 +1468,7 @@ class wallet_api * @param claim_period_seconds how long after creation until the lock expires * @param broadcast true if you wish to broadcast the transaction */ - signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, + signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t claim_period_seconds, bool broadcast = false ); @@ -1844,7 +1844,7 @@ FC_API( graphene::wallet::wallet_api, (update_witness) (create_worker) (update_worker_votes) - (htlc_prepare) + (htlc_create) (htlc_redeem) (htlc_extend) (get_vesting_balances) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 0e9d1d1997..99445c13e7 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1749,7 +1749,7 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } - signed_transaction htlc_prepare( string source, string destination, string amount, string asset_symbol, + signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, const uint32_t claim_period_seconds, bool broadcast = false ) { @@ -3150,11 +3150,11 @@ std::vector string_to_vec(std::string incoming) return outgoing; } -signed_transaction wallet_api::htlc_prepare( string source, string destination, string amount, string asset_symbol, +signed_transaction wallet_api::htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t claim_period_seconds, bool broadcast) { - return my->htlc_prepare(source, destination, amount, asset_symbol, hash_algorithm, string_to_vec(preimage_hash), preimage_size, + return my->htlc_create(source, destination, amount, asset_symbol, hash_algorithm, string_to_vec(preimage_hash), preimage_size, claim_period_seconds, broadcast); } diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index a858c5827a..7968f75349 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -691,7 +691,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) 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_prepare("alice", "bob", + = con.wallet_api_ptr->htlc_create("alice", "bob", "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: @@ -713,7 +713,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) 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_prepare("bob", "alice", + con.wallet_api_ptr->htlc_create("bob", "alice", "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: diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index da4ec86224..81567283b7 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -90,7 +90,7 @@ void set_committee_parameters(database_fixture* db_fixture) void advance_past_hardfork(database_fixture* db_fixture) { - db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME); + db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME+1); set_committee_parameters(db_fixture); set_expiration(db_fixture->db, db_fixture->trx); } @@ -319,6 +319,7 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) BOOST_AUTO_TEST_CASE( set_htlc_params ) { +try { { // try to set committee parameters before hardfork proposal_create_operation cop = proposal_create_operation::committee_proposal( @@ -348,7 +349,54 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) trx.clear(); } + /* + { + BOOST_TEST_MESSAGE("Attempting to set HTLC fees before hard fork."); + // get existing fees + const chain_parameters& existing_params = db.get_global_properties().parameters; + const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); + // build a map of existing fees + flat_map< int, fee_parameters > fee_map; + fee_map.reserve( existing_fee_schedule.parameters.size() ); + for( const fee_parameters& op_fee : existing_fee_schedule.parameters ) + fee_map[ op_fee.which() ] = op_fee; + // add the ability to look up the fee shedule + fee_schedule_type new_fee_schedule; + new_fee_schedule.scale = existing_fee_schedule.scale; + // replace the htlc_create_operation fee structure with the new one + for(auto param : existing_fee_schedule.parameters) + { + if (param.which() != ((operation)htlc_create_operation()).which()) // we want to keep it as is... + new_fee_schedule.parameters.insert(param); + else + { + // we want to change this one + htlc_create_operation::fee_parameters_type htlc_param; + htlc_param.fee_per_day = 2; + htlc_param.fee = 2; + new_fee_schedule.parameters.insert(htlc_param); + } + } + // send a fee change proposal + chain_parameters new_parameters = existing_params; + new_parameters.current_fees = new_fee_schedule; + committee_member_update_global_parameters_operation cmuop; + cmuop.new_parameters = new_parameters; + proposal_create_operation cop = proposal_create_operation::committee_proposal( + existing_params, db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + cop.proposed_ops.emplace_back( cmuop ); + cop.expiration_time = db.head_block_time() + fc::days(1); + cop.fee = asset( 100000 ); + trx.operations.push_back( cop ); + BOOST_TEST_MESSAGE("About to push fees before their time."); + db.push_transaction( trx ); + BOOST_TEST_MESSAGE("Attempted to push fees before their time."); + } + */ // now things should start working... + BOOST_TEST_MESSAGE("Advancing to HTLC hardfork time."); advance_past_hardfork(this); proposal_id_type good_proposal_id; @@ -367,6 +415,7 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) graphene::chain::processed_transaction proc_trx =db.push_transaction(trx); good_proposal_id = proc_trx.operation_results[0].get(); } + BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); { proposal_update_operation uop; @@ -396,6 +445,13 @@ BOOST_AUTO_TEST_CASE( set_htlc_params ) BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); +} catch (fc::exception &fcx) { + BOOST_FAIL("FC Exception: " << fcx.to_detail_string()); +} catch (std::exception &ex) { + BOOST_FAIL("Exception: " << ex.what()); +} catch (...) { + BOOST_FAIL("Uncaught exception."); +} } BOOST_AUTO_TEST_SUITE_END() From e2e571ae018c2aeff4c72168211b9e239902c244 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 17 Dec 2018 18:21:25 -0500 Subject: [PATCH 071/161] 1 day and 1kb mins on fees --- libraries/chain/include/graphene/chain/protocol/htlc.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index a9428f492c..526aa31037 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -73,7 +73,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = std::min(1, claim_period_seconds / (60 * 60 * 24)); + uint32_t days = std::min(1, claim_period_seconds / (60 * 60 * 24)); return fee_params.fee + (fee_params.fee_per_day * days); } @@ -111,7 +111,9 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - return preimage.size() / 1024 * fee_params.fee_per_kb; + if (fee_params.fee_per_kb > 0) + return std::min(1, preimage.size() / (1024 * fee_params.fee_per_kb)); + return 0; } }; @@ -148,7 +150,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = seconds_to_add / (60 * 60 * 24); + uint32_t days = std::min(1, seconds_to_add / (60 * 60 * 24)); return fee_params.fee + (fee_params.fee_per_day * days); } }; From 5c7a9e89d8e5efad6b0fa1c2851906bb9ee833d5 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 18 Dec 2018 00:24:11 -0500 Subject: [PATCH 072/161] still trying to get fees working --- .../chain/include/graphene/chain/protocol/htlc.hpp | 8 ++++---- tests/tests/htlc_tests.cpp | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 526aa31037..f7b583c232 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -27,7 +27,7 @@ #include #include #include -#include // std::min +#include // std::max namespace graphene { namespace chain { @@ -73,7 +73,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = std::min(1, claim_period_seconds / (60 * 60 * 24)); + uint64_t days = std::max(1, claim_period_seconds / (60 * 60 * 24)); return fee_params.fee + (fee_params.fee_per_day * days); } @@ -112,7 +112,7 @@ namespace graphene { share_type calculate_fee(const fee_parameters_type& fee_params)const { if (fee_params.fee_per_kb > 0) - return std::min(1, preimage.size() / (1024 * fee_params.fee_per_kb)); + return std::max(1, preimage.size() / (1024 * fee_params.fee_per_kb)); return 0; } }; @@ -150,7 +150,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = std::min(1, seconds_to_add / (60 * 60 * 24)); + uint32_t days = std::max(1, seconds_to_add / (60 * 60 * 24)); return fee_params.fee + (fee_params.fee_per_day * days); } }; diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 81567283b7..beef8276e4 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -146,7 +146,7 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) ACTORS((alice)(bob)); - int64_t init_balance(100000); + int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); @@ -162,8 +162,8 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; - - create_operation.amount = graphene::chain::asset( 10000 ); + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 2 coins to Bob"); + create_operation.amount = graphene::chain::asset( 2 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; create_operation.preimage_hash = preimage_hash; @@ -180,8 +180,8 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) generate_block(); } - // verify funds on hold (TODO: make sure this can cover fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + // verify funds on hold... 100 - 2 = 98, minus the 1 coin fee = 9 + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 97 * GRAPHENE_BLOCKCHAIN_PRECISION ); // make sure Bob (or anyone) can see the details of the transaction graphene::app::database_api db_api(db); @@ -189,9 +189,9 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) graphene::chain::htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); // let it expire (wait for timeout) - generate_blocks(fc::time_point_sec(120) ); + generate_blocks( fc::time_point::now() + fc::seconds(120) ); // verify funds return (minus the fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 90000 ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 99 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify Bob cannot execute the contract after the fact } From a2192ec21e9d95372dc5e3d790bbc698bfb32a0c Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 18 Dec 2018 20:23:17 -0500 Subject: [PATCH 073/161] fix htlc fee tests --- .../graphene/chain/protocol/fee_schedule.hpp | 36 +- libraries/chain/proposal_evaluator.cpp | 4 +- tests/tests/htlc_tests.cpp | 366 +++++++++++------- 3 files changed, 255 insertions(+), 151 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index 39fa0a390f..eddc7e7094 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -113,7 +113,6 @@ namespace graphene { namespace chain { } }; - /* template<> class fee_helper { public: @@ -126,15 +125,34 @@ namespace graphene { namespace chain { static htlc_create_operation::fee_parameters_type htlc_create_operation_fee_dummy; return htlc_create_operation_fee_dummy; } - typename htlc_create_operation::fee_parameters_type& get(flat_set& parameters)const + }; + + template<> + class fee_helper { + public: + const htlc_redeem_operation::fee_parameters_type& cget(const flat_set& parameters)const { - auto itr = parameters.find( htlc_create_operation::fee_parameters_type() ); - FC_ASSERT( itr != parameters.end() ); - return itr->get(); + 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 flat_set& 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 */ @@ -168,6 +186,12 @@ namespace graphene { namespace chain { { return fee_helper().get(parameters); } + template + const bool exists()const + { + auto itr = parameters.find(typename Operation::fee_parameters_type()); + return itr != parameters.end(); + } /** * @note must be sorted by fee_parameters.which() and have no duplicates diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index ed335dcff5..ff295c00f8 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -97,7 +97,9 @@ struct proposal_operation_hardfork_visitor void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const { if (block_time < HARDFORK_CORE_1468_TIME) { FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(), "Unable to set HTLC options before hardfork 1468"); - // TODO: Do not allow changing of fees before hardfork + FC_ASSERT(!op.new_parameters.current_fees->exists()); + FC_ASSERT(!op.new_parameters.current_fees->exists()); + FC_ASSERT(!op.new_parameters.current_fees->exists()); } } // loop and self visit in proposals diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index beef8276e4..2918ff6c52 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -73,24 +73,92 @@ std::vector hash_it(std::vector preimage) return std::vector(hash.data(), hash.data() + hash.data_size()); } +flat_map< uint64_t, graphene::chain::fee_parameters > get_htlc_fee_parameters() +{ + flat_map ret_val; + + htlc_create_operation::fee_parameters_type create_param; + create_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + create_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + ret_val[((operation)htlc_create_operation()).which()] = create_param; + + htlc_redeem_operation::fee_parameters_type redeem_param; + redeem_param.fee_per_kb = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + ret_val[((operation)htlc_redeem_operation()).which()] = redeem_param; + + htlc_extend_operation::fee_parameters_type extend_param; + extend_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + extend_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + ret_val[((operation)htlc_extend_operation()).which()] = extend_param; + + return ret_val; +} + +/**** + * @brief push through a proposal that sets htlc parameters and fees + * @param db_fixture the database connection + */ void set_committee_parameters(database_fixture* db_fixture) { - // set the committee parameters - db_fixture->db.modify(db_fixture->db.get_global_properties(), [](global_property_object& p) { - // htlc options - 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; - // htlc operation fees - p.parameters.current_fees->get().fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; - p.parameters.current_fees->get().fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; - }); + // htlc fees + // get existing fee_schedule + const chain_parameters& existing_params = db_fixture->db.get_global_properties().parameters; + const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); + // create a new fee_shedule + fee_schedule_type new_fee_schedule; + new_fee_schedule.scale = existing_fee_schedule.scale; + // replace the old with the new + flat_map params_map = get_htlc_fee_parameters(); + 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); + } + } + // htlc parameters + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db_fixture->db.get_global_properties().parameters, db_fixture->db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db_fixture->db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation uop; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 19200; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + uop.new_parameters.extensions.value.updatable_htlc_options = new_params; + uop.new_parameters.current_fees = new_fee_schedule; + cop.proposed_ops.emplace_back(uop); + + db_fixture->trx.operations.push_back(cop); + graphene::chain::processed_transaction proc_trx =db_fixture->db.push_transaction(db_fixture->trx); + proposal_id_type good_proposal_id = proc_trx.operation_results[0].get(); + + proposal_update_operation puo; + puo.proposal = good_proposal_id; + puo.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + puo.active_approvals_to_add = { + db_fixture->get_account("init0").get_id(), db_fixture->get_account("init1").get_id(), + db_fixture->get_account("init2").get_id(), db_fixture->get_account("init3").get_id(), + db_fixture->get_account("init4").get_id(), db_fixture->get_account("init5").get_id(), + db_fixture->get_account("init6").get_id(), db_fixture->get_account("init7").get_id()}; + db_fixture->trx.operations.push_back(puo); + db_fixture->sign( db_fixture->trx, db_fixture->init_account_priv_key ); + db_fixture->db.push_transaction(db_fixture->trx); + db_fixture->trx.clear(); + + db_fixture->generate_blocks( good_proposal_id( db_fixture->db ).expiration_time + 5 ); + db_fixture->generate_blocks( db_fixture->db.get_dynamic_global_properties().next_maintenance_time ); + db_fixture->generate_block(); // get the maintenance skip slots out of the way + } void advance_past_hardfork(database_fixture* db_fixture) { db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME+1); + set_expiration(db_fixture->db, db_fixture->trx); set_committee_parameters(db_fixture); set_expiration(db_fixture->db, db_fixture->trx); } @@ -201,7 +269,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) ACTORS((alice)(bob)); - int64_t init_balance(1000000); + int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); @@ -220,7 +288,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) { graphene::chain::htlc_create_operation create_operation; - create_operation.amount = graphene::chain::asset( 100000 ); + create_operation.amount = graphene::chain::asset( 20 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 86400; create_operation.preimage_hash = preimage_hash; @@ -237,8 +305,8 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) alice_htlc_id = alice_trx.operation_results[0].get(); } - // verify funds on hold (make sure this can cover fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); + // verify funds on hold (make sure this can cover fees) (100 - 20 - fee) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 7800000 ); // TODO: make sure Bob (or anyone) can see the details of the transaction @@ -262,9 +330,10 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) generate_block(); trx.clear(); } - // verify funds end up in Bob's account - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 1100000 ); - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 700000 ); + // verify funds end up in Bob's account (100 + 20 - 0.00002(fee)) + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 11999998 ); + // 100 - 20 - 0.00002 + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 7999998 ); } BOOST_AUTO_TEST_CASE( other_peoples_money ) @@ -317,141 +386,150 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) } } -BOOST_AUTO_TEST_CASE( set_htlc_params ) +BOOST_AUTO_TEST_CASE( htlc_hardfork_tests ) { -try { - { - // try to set committee parameters before hardfork - proposal_create_operation cop = proposal_create_operation::committee_proposal( - db.get_global_properties().parameters, db.head_block_time()); - cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; - cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; - committee_member_update_global_parameters_operation cmuop; - graphene::chain::htlc_options new_params; - new_params.max_preimage_size = 2048; - new_params.max_timeout_secs = 60 * 60 * 24 * 28; - cmuop.new_parameters.extensions.value.updatable_htlc_options = new_params; - cop.proposed_ops.emplace_back(cmuop); - trx.operations.push_back(cop); - // update with signatures - proposal_update_operation uop; - uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; - uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), - get_account("init2").get_id(), get_account("init3").get_id(), - get_account("init4").get_id(), get_account("init5").get_id(), - get_account("init6").get_id(), get_account("init7").get_id()}; - trx.operations.push_back(uop); - sign( trx, init_account_priv_key ); - BOOST_TEST_MESSAGE("Sending proposal."); - GRAPHENE_CHECK_THROW(db.push_transaction(trx), fc::exception); - BOOST_TEST_MESSAGE("Verifying that proposal did not succeeed."); - BOOST_CHECK(!db.get_global_properties().parameters.extensions.value.updatable_htlc_options.valid()); - trx.clear(); - } + try { + { + // try to set committee parameters before hardfork + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db.get_global_properties().parameters, db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation cmuop; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 2048; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + cmuop.new_parameters.extensions.value.updatable_htlc_options = new_params; + cop.proposed_ops.emplace_back(cmuop); + trx.operations.push_back(cop); + // update with signatures + proposal_update_operation uop; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; + trx.operations.push_back(uop); + sign( trx, init_account_priv_key ); + BOOST_TEST_MESSAGE("Sending proposal."); + GRAPHENE_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_TEST_MESSAGE("Verifying that proposal did not succeeed."); + BOOST_CHECK(!db.get_global_properties().parameters.extensions.value.updatable_htlc_options.valid()); + trx.clear(); + } - /* - { - BOOST_TEST_MESSAGE("Attempting to set HTLC fees before hard fork."); - // get existing fees - const chain_parameters& existing_params = db.get_global_properties().parameters; - const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); - // build a map of existing fees - flat_map< int, fee_parameters > fee_map; - fee_map.reserve( existing_fee_schedule.parameters.size() ); - for( const fee_parameters& op_fee : existing_fee_schedule.parameters ) - fee_map[ op_fee.which() ] = op_fee; - // add the ability to look up the fee shedule - fee_schedule_type new_fee_schedule; - new_fee_schedule.scale = existing_fee_schedule.scale; - // replace the htlc_create_operation fee structure with the new one - for(auto param : existing_fee_schedule.parameters) { - if (param.which() != ((operation)htlc_create_operation()).which()) // we want to keep it as is... - new_fee_schedule.parameters.insert(param); - else + BOOST_TEST_MESSAGE("Attempting to set HTLC 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 + fee_schedule_type new_fee_schedule; + new_fee_schedule.scale = existing_fee_schedule.scale; + // replace the old with the new + flat_map params_map = get_htlc_fee_parameters(); + for(auto param : existing_fee_schedule.parameters) { - // we want to change this one - htlc_create_operation::fee_parameters_type htlc_param; - htlc_param.fee_per_day = 2; - htlc_param.fee = 2; - new_fee_schedule.parameters.insert(htlc_param); + 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(); } - // send a fee change proposal - chain_parameters new_parameters = existing_params; - new_parameters.current_fees = new_fee_schedule; - committee_member_update_global_parameters_operation cmuop; - cmuop.new_parameters = new_parameters; - proposal_create_operation cop = proposal_create_operation::committee_proposal( - existing_params, db.head_block_time()); - cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; - cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; - cop.proposed_ops.emplace_back( cmuop ); - cop.expiration_time = db.head_block_time() + fc::days(1); - cop.fee = asset( 100000 ); - trx.operations.push_back( cop ); - BOOST_TEST_MESSAGE("About to push fees before their time."); - db.push_transaction( trx ); - BOOST_TEST_MESSAGE("Attempted to push fees before their time."); - } - */ - // now things should start working... - BOOST_TEST_MESSAGE("Advancing to HTLC hardfork time."); - advance_past_hardfork(this); - proposal_id_type good_proposal_id; - BOOST_TEST_MESSAGE( "Creating a proposal to change the max_preimage_size to 2048" ); - { - 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; - graphene::chain::htlc_options new_params; - new_params.max_preimage_size = 2048; - new_params.max_timeout_secs = 60 * 60 * 24 * 28; - uop.new_parameters.extensions.value.updatable_htlc_options = new_params; - cop.proposed_ops.emplace_back(uop); - trx.operations.push_back(cop); - graphene::chain::processed_transaction proc_trx =db.push_transaction(trx); - good_proposal_id = proc_trx.operation_results[0].get(); - } + // now things should start working... + BOOST_TEST_MESSAGE("Advancing to HTLC hardfork time."); + advance_past_hardfork(this); - BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); - { - proposal_update_operation uop; - uop.proposal = good_proposal_id; - uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; - uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), - get_account("init2").get_id(), get_account("init3").get_id(), - get_account("init4").get_id(), get_account("init5").get_id(), - get_account("init6").get_id(), get_account("init7").get_id()}; - trx.operations.push_back(uop); - sign( trx, init_account_priv_key ); - db.push_transaction(trx); - BOOST_CHECK(good_proposal_id(db).is_authorized_to_execute(db)); + proposal_id_type good_proposal_id; + BOOST_TEST_MESSAGE( "Creating a proposal to change the max_preimage_size to 2048 and set higher fees" ); + { + // 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 + fee_schedule_type new_fee_schedule; + new_fee_schedule.scale = existing_fee_schedule.scale; + // replace the old with the new + flat_map params_map = get_htlc_fee_parameters(); + 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; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 2048; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + uop.new_parameters.extensions.value.updatable_htlc_options = new_params; + uop.new_parameters.current_fees = new_fee_schedule; + cop.proposed_ops.emplace_back(uop); + trx.operations.push_back(cop); + graphene::chain::processed_transaction proc_trx =db.push_transaction(trx); + good_proposal_id = proc_trx.operation_results[0].get(); + } + + BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); + { + proposal_update_operation uop; + uop.proposal = good_proposal_id; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; + trx.operations.push_back(uop); + sign( trx, init_account_priv_key ); + db.push_transaction(trx); + BOOST_CHECK(good_proposal_id(db).is_authorized_to_execute(db)); + } + BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); + + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u); + + BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); + generate_blocks(good_proposal_id(db).expiration_time + 5); + BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u); + + BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" ); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); // get the maintenance skip slots out of the way + + BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get().fee, 2 * GRAPHENE_BLOCKCHAIN_PRECISION); + + } catch (fc::exception &fcx) { + BOOST_FAIL("FC Exception: " << fcx.to_detail_string()); + } catch (std::exception &ex) { + BOOST_FAIL("Exception: " << ex.what()); + } catch (...) { + BOOST_FAIL("Uncaught exception."); } - BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); - - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); - - BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); - generate_blocks(good_proposal_id(db).expiration_time + 5); - BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 1024u); - - BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" ); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); // get the maintenance skip slots out of the way - - BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); -} catch (fc::exception &fcx) { - BOOST_FAIL("FC Exception: " << fcx.to_detail_string()); -} catch (std::exception &ex) { - BOOST_FAIL("Exception: " << ex.what()); -} catch (...) { - BOOST_FAIL("Uncaught exception."); -} } BOOST_AUTO_TEST_SUITE_END() From f7d7f5ce41910edf2646a2b51f09846136e2f348 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 18 Dec 2018 22:49:52 -0500 Subject: [PATCH 074/161] Making fees work with htlc tests --- .../include/graphene/chain/protocol/htlc.hpp | 2 +- tests/tests/htlc_tests.cpp | 182 +++++++++++------- 2 files changed, 112 insertions(+), 72 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index f7b583c232..d2e4a15aa5 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -112,7 +112,7 @@ namespace graphene { share_type calculate_fee(const fee_parameters_type& fee_params)const { if (fee_params.fee_per_kb > 0) - return std::max(1, preimage.size() / (1024 * fee_params.fee_per_kb)); + return std::max(fee_params.fee_per_kb, preimage.size() / 1024 * fee_params.fee_per_kb); return 0; } }; diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 2918ff6c52..149a479aed 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -106,7 +106,7 @@ void set_committee_parameters(database_fixture* db_fixture) const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); // create a new fee_shedule fee_schedule_type new_fee_schedule; - new_fee_schedule.scale = existing_fee_schedule.scale; + new_fee_schedule.scale = GRAPHENE_100_PERCENT; // replace the old with the new flat_map params_map = get_htlc_fee_parameters(); for(auto param : existing_fee_schedule.parameters) @@ -157,67 +157,23 @@ void set_committee_parameters(database_fixture* db_fixture) void advance_past_hardfork(database_fixture* db_fixture) { - db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME+1); + db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME); set_expiration(db_fixture->db, db_fixture->trx); set_committee_parameters(db_fixture); set_expiration(db_fixture->db, db_fixture->trx); } -BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) -{ - ACTORS((alice)(bob)); - - int64_t init_balance(100000); - - transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - - uint16_t preimage_size = 256; - std::vector pre_image(256); - generate_random_preimage(preimage_size, pre_image); - std::vector preimage_hash = hash_it(pre_image); - - graphene::chain::htlc_id_type alice_htlc_id; - // cler everything out - generate_block(); - trx.clear(); - // Alice tries to put a contract on the blockchain - { - graphene::chain::htlc_create_operation create_operation; - - create_operation.amount = graphene::chain::asset( 10000 ); - create_operation.to = bob_id; - create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = graphene::chain::hash_algorithm::sha256; - create_operation.preimage_size = preimage_size; - create_operation.from = alice_id; - trx.operations.push_back(create_operation); - sign(trx, alice_private_key); - GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); - } - // attempt to create a proposal that contains htlc stuff - { - - graphene::chain::committee_member_update_global_parameters_operation param_op; - - graphene::chain::proposal_create_operation create_operation; - - create_operation.fee_paying_account = committee_account; - create_operation.review_period_seconds = 60 * 60 * 48; - create_operation.proposed_ops.emplace_back(param_op); - } -} - BOOST_AUTO_TEST_CASE( htlc_expires ) { - advance_past_hardfork(this); - +try { ACTORS((alice)(bob)); int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + advance_past_hardfork(this); + uint16_t preimage_size = 256; std::vector pre_image(256); generate_random_preimage(preimage_size, pre_image); @@ -231,13 +187,14 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) { graphene::chain::htlc_create_operation create_operation; BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 2 coins to Bob"); - create_operation.amount = graphene::chain::asset( 2 * GRAPHENE_BLOCKCHAIN_PRECISION ); + 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 = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; 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); PUSH_TX(db, trx, ~0); @@ -248,8 +205,8 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) generate_block(); } - // verify funds on hold... 100 - 2 = 98, minus the 1 coin fee = 9 - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 97 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // 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); @@ -257,16 +214,15 @@ BOOST_AUTO_TEST_CASE( htlc_expires ) graphene::chain::htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); // let it expire (wait for timeout) - generate_blocks( fc::time_point::now() + fc::seconds(120) ); + generate_blocks( db.head_block_time() + fc::seconds(120) ); // verify funds return (minus the fees) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 99 * GRAPHENE_BLOCKCHAIN_PRECISION ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 96 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify Bob cannot execute the contract after the fact +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( htlc_fulfilled ) { - advance_past_hardfork(this); - ACTORS((alice)(bob)); int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); @@ -274,16 +230,17 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); + advance_past_hardfork(this); + uint16_t preimage_size = 256; std::vector pre_image(preimage_size); generate_random_preimage(preimage_size, pre_image); std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; - // cler everything out + // clear everything out generate_block(); trx.clear(); - enable_fees(); // Alice puts a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; @@ -296,7 +253,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); - trx.operations.push_back(create_operation); + trx.operations.push_back( create_operation ); sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); trx.clear(); @@ -305,8 +262,8 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) alice_htlc_id = alice_trx.operation_results[0].get(); } - // verify funds on hold (make sure this can cover fees) (100 - 20 - fee) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 7800000 ); + // make sure Alice's money gets put on hold (100 - 20 - 4(fee) ) + BOOST_CHECK_EQUAL( get_balance( alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION ); // TODO: make sure Bob (or anyone) can see the details of the transaction @@ -330,19 +287,20 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) generate_block(); trx.clear(); } - // verify funds end up in Bob's account (100 + 20 - 0.00002(fee)) - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 11999998 ); - // 100 - 20 - 0.00002 - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 7999998 ); + // verify funds end up in Bob's account (100 + 20 - 2(fee) ) + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 118 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify funds remain out of Alice's acount ( 100 - 20 - 4 ) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION ); } BOOST_AUTO_TEST_CASE( other_peoples_money ) { +try { advance_past_hardfork(this); ACTORS((alice)(bob)); - int64_t init_balance(100000); + int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION ); transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); @@ -358,35 +316,38 @@ BOOST_AUTO_TEST_CASE( other_peoples_money ) // Bob attempts to put a contract on the blockchain using Alice's funds { graphene::chain::htlc_create_operation create_operation; - create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 3; create_operation.preimage_hash = preimage_hash; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; + create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); trx.operations.push_back(create_operation); sign(trx, bob_private_key); - GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, database::skip_nothing), fc::exception); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx ), fc::exception); trx.clear(); } // now try the same but with Alice's signature (should work) { graphene::chain::htlc_create_operation create_operation; - create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 3; create_operation.preimage_hash = preimage_hash; create_operation.hash_type = graphene::chain::hash_algorithm::sha256; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; + create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); trx.operations.push_back(create_operation); sign(trx, alice_private_key); - PUSH_TX(db, trx, database::skip_nothing); + PUSH_TX( db, trx ); trx.clear(); } +} FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE( htlc_hardfork_tests ) +BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) { try { { @@ -532,4 +493,83 @@ BOOST_AUTO_TEST_CASE( htlc_hardfork_tests ) } } +BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) +{ + ACTORS((alice)(bob)); + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + uint16_t preimage_size = 256; + std::vector pre_image(256); + generate_random_preimage(preimage_size, pre_image); + std::vector preimage_hash = hash_it(pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // cler everything out + generate_block(); + trx.clear(); + // Alice tries to put a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = preimage_hash; + create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.preimage_size = preimage_size; + create_operation.from = alice_id; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + } +} + +BOOST_AUTO_TEST_CASE( fee_calculations ) +{ + // create + { + htlc_create_operation::fee_parameters_type create_fee; + create_fee.fee = 2; + create_fee.fee_per_day = 2; + htlc_create_operation create; + create.claim_period_seconds = 0; + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); + // almost 2 days + create.claim_period_seconds = 2 * 60 * 60 * 24 - 1; + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); + // 2 days + create.claim_period_seconds++; + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 6 ); + } + // redeem + { + htlc_redeem_operation::fee_parameters_type redeem_fee; + redeem_fee.fee_per_kb = 2; + htlc_redeem_operation redeem; + redeem.preimage = std::vector(); + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 2 ) ; + std::string test(2048, 'a'); + redeem.preimage = std::vector( test.begin(), test.end() ); + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 4 ) ; + } + // extend + { + htlc_extend_operation::fee_parameters_type extend_fee; + extend_fee.fee = 2; + extend_fee.fee_per_day = 2; + htlc_extend_operation extend; + extend.seconds_to_add = 0; + BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); + // almost 2 days + extend.seconds_to_add = 2 * 60 * 60 * 24 - 1; + BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); + // 2 days + extend.seconds_to_add++; + BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 6 ); + } +} + BOOST_AUTO_TEST_SUITE_END() From 96ec5f36df7c85b8df7892c88eb9f9dfcb9eb484 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 19 Dec 2018 00:22:50 -0500 Subject: [PATCH 075/161] move hash_algoritm enum plus misc --- libraries/app/database_api.cpp | 1 - libraries/chain/db_notify.cpp | 6 +-- libraries/chain/htlc_evaluator.cpp | 10 ++--- libraries/chain/htlc_object.cpp | 10 ++--- .../include/graphene/chain/htlc_object.hpp | 16 ++------ .../graphene/chain/protocol/fee_schedule.hpp | 6 --- .../include/graphene/chain/protocol/htlc.hpp | 27 +++++++++---- libraries/chain/protocol/htlc.cpp | 6 +-- libraries/wallet/wallet.cpp | 3 +- programs/js_operation_serializer/main.cpp | 1 + tests/common/database_fixture.cpp | 1 + tests/tests/htlc_tests.cpp | 39 ++++++++++++------- 12 files changed, 68 insertions(+), 58 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index c89971a728..acb90c8af5 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 354505b485..0862dd28ac 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -270,7 +270,7 @@ struct get_impacted_account_visitor } void operator()( const htlc_refund_operation& op ) { - _impacted.insert( op.fee_payer() ); // bidder + _impacted.insert( op.fee_payer() ); } }; @@ -360,7 +360,7 @@ void get_relevant_accounts( const object* obj, flat_set& accoun } case balance_object_type:{ /** these are free from any accounts */ break; - } case htlc_object_type: { + } case htlc_object_type:{ const auto& htlc_obj = dynamic_cast(obj); FC_ASSERT( htlc_obj != nullptr); accounts.insert( htlc_obj->from ); @@ -428,7 +428,7 @@ void get_relevant_accounts( const object* obj, flat_set& accoun FC_ASSERT( aobj != nullptr ); accounts.insert( aobj->bidder ); break; - } + } } } } // end get_relevant_accounts( const object* obj, flat_set& accounts ) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 6087442cb2..fba098025d 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -46,7 +46,7 @@ namespace graphene { // make sure the preimage length is reasonable FC_ASSERT( o.preimage_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); // make sure we have a hash algorithm set - FC_ASSERT( o.hash_type != graphene::chain::hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); + FC_ASSERT( o.hash_type != htlc_hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); // make sure the sender has the funds for the HTLC FC_ASSERT( db().get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; return void_result(); @@ -58,7 +58,7 @@ namespace graphene { graphene::chain::database& dbase = db(); dbase.adjust_balance( o.from, -o.amount ); - const htlc_object& esc = db().create([&dbase,o]( htlc_object& esc ) { + const htlc_object& esc = db().create([&dbase,&o]( htlc_object& esc ) { esc.from = o.from; esc.to = o.to; esc.amount = o.amount; @@ -92,13 +92,13 @@ namespace graphene { bool match = false; switch(htlc_obj->preimage_hash_algorithm) { - case (graphene::chain::hash_algorithm::sha256): + case (htlc_hash_algorithm::sha256): match = test_hash(o.preimage, htlc_obj->preimage_hash); break; - case (graphene::chain::hash_algorithm::ripemd160): + case (htlc_hash_algorithm::ripemd160): match = test_hash(o.preimage, htlc_obj->preimage_hash); break; - case (graphene::chain::hash_algorithm::sha1): + case (htlc_hash_algorithm::sha1): match = test_hash(o.preimage, htlc_obj->preimage_hash); break; default: diff --git a/libraries/chain/htlc_object.cpp b/libraries/chain/htlc_object.cpp index f7f4c93b21..e460619212 100644 --- a/libraries/chain/htlc_object.cpp +++ b/libraries/chain/htlc_object.cpp @@ -30,16 +30,16 @@ namespace graphene { namespace chain { * @param incoming the string to convert * @returns one of the valid algorithms or the enum value "unknown" */ - fc::enum_type string_to_hash_algorithm(std::string incoming) + fc::enum_type string_to_hash_algorithm(std::string incoming) { std::transform(incoming.begin(), incoming.end(), incoming.begin(), ::toupper); if (incoming == "RIPEMD160") - return hash_algorithm::ripemd160; + return htlc_hash_algorithm::ripemd160; if (incoming == "SHA256") - return hash_algorithm::sha256; + return htlc_hash_algorithm::sha256; if (incoming == "SHA1") - return hash_algorithm::sha1; - return hash_algorithm::unknown; + return htlc_hash_algorithm::sha1; + return htlc_hash_algorithm::unknown; } } } diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 7c45d4d636..57dab81416 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -26,25 +26,19 @@ #include #include #include +#include #include #include #include namespace graphene { namespace chain { - enum hash_algorithm { - unknown = 0x00, - ripemd160 = 0x01, - sha256 = 0x02, - sha1 = 0x03 - }; - /**** * Convert string to hash algorithm enum * @param incoming the string to convert * @returns one of the valid algorithms or the enum value "unknown" */ - fc::enum_type string_to_hash_algorithm(std::string incoming); + fc::enum_type string_to_hash_algorithm(std::string incoming); /** * @brief database object to store HTLCs @@ -63,7 +57,7 @@ namespace graphene { namespace chain { asset amount; fc::time_point_sec expiration; vector preimage_hash; - fc::enum_type preimage_hash_algorithm; + fc::enum_type preimage_hash_algorithm; uint16_t preimage_size; }; @@ -94,7 +88,7 @@ namespace graphene { namespace chain { namespace fc { template<> - struct get_typename> + struct get_typename> { static const char* name() { @@ -103,8 +97,6 @@ namespace fc }; } -FC_REFLECT_ENUM( graphene::chain::hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); - FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), (from)(to)(amount)(expiration) (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index eddc7e7094..e578f1d9f4 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -43,12 +43,6 @@ namespace graphene { namespace chain { FC_ASSERT( itr != parameters.end() ); return itr->template get(); } - typename Operation::fee_parameters_type& get(flat_set& parameters)const - { - auto itr = parameters.find( typename Operation::fee_parameters_type() ); - FC_ASSERT( itr != parameters.end() ); - return itr->template get(); - } }; template<> diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index d2e4a15aa5..2c6ba84f8e 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -24,7 +24,6 @@ #pragma once #include #include -#include #include #include #include // std::max @@ -32,6 +31,13 @@ namespace graphene { namespace chain { + enum htlc_hash_algorithm { + unknown = 0x00, + ripemd160 = 0x01, + sha256 = 0x02, + sha1 = 0x03 + }; + struct htlc_create_operation : public base_operation { struct fee_parameters_type { @@ -47,8 +53,8 @@ namespace graphene { // the amount to hold asset amount; // hash algorithm used to create preimage_hash - fc::enum_type hash_type - = graphene::chain::hash_algorithm::unknown; + fc::enum_type hash_type + = htlc_hash_algorithm::unknown; // the hash of the preimage std::vector preimage_hash; // the size of the preimage @@ -73,7 +79,8 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint64_t days = std::max(1, claim_period_seconds / (60 * 60 * 24)); + + uint64_t days = std::max( 1, std::ceil( claim_period_seconds / (double)(60 * 60 * 24) ) ); return fee_params.fee + (fee_params.fee_per_day * days); } @@ -82,6 +89,7 @@ namespace graphene { struct htlc_redeem_operation : public base_operation { struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; uint64_t fee_per_kb = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; @@ -112,8 +120,9 @@ namespace graphene { share_type calculate_fee(const fee_parameters_type& fee_params)const { if (fee_params.fee_per_kb > 0) - return std::max(fee_params.fee_per_kb, preimage.size() / 1024 * fee_params.fee_per_kb); - return 0; + return fee_params.fee + std::max(fee_params.fee_per_kb, + std::ceil( preimage.size() / (double)1024 ) * fee_params.fee_per_kb ); + return fee_params.fee; } }; @@ -150,7 +159,7 @@ namespace graphene { */ share_type calculate_fee(const fee_parameters_type& fee_params)const { - uint32_t days = std::max(1, seconds_to_add / (60 * 60 * 24)); + uint32_t days = std::max(1, std::ceil( seconds_to_add / (double)(60 * 60 * 24) ) ); return fee_params.fee + (fee_params.fee_per_day * days); } }; @@ -176,8 +185,10 @@ namespace graphene { } } +FC_REFLECT_ENUM( graphene::chain::htlc_hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); + FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) -FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee_per_kb) ) +FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) (fee_per_kb) ) FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index be444735f6..04ef3f0bd6 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -28,9 +28,9 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount.amount > 0 ); - FC_ASSERT( hash_type == hash_algorithm::ripemd160 - || hash_type == graphene::chain::hash_algorithm::sha1 - || hash_type == hash_algorithm::sha256, "Unknown Hash Algorithm"); + FC_ASSERT( hash_type == htlc_hash_algorithm::ripemd160 + || hash_type == htlc_hash_algorithm::sha1 + || hash_type == htlc_hash_algorithm::sha256, "Unknown Hash Algorithm"); } void htlc_redeem_operation::validate()const { diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 99445c13e7..cc5d8a4062 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -1767,7 +1768,7 @@ class wallet_api_impl create_op.preimage_hash = preimage_hash; create_op.preimage_size = preimage_size; create_op.hash_type = graphene::chain::string_to_hash_algorithm(hash_algorithm); - FC_ASSERT(create_op.hash_type != graphene::chain::hash_algorithm::unknown, + FC_ASSERT(create_op.hash_type != htlc_hash_algorithm::unknown, "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); signed_transaction tx; diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index e955f8d7fa..24a4065e64 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 1214d8be9a..d1e62ea197 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 149a479aed..6faff73d40 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -45,6 +45,7 @@ #include #include +#include #include @@ -83,6 +84,7 @@ flat_map< uint64_t, graphene::chain::fee_parameters > get_htlc_fee_parameters() ret_val[((operation)htlc_create_operation()).which()] = create_param; htlc_redeem_operation::fee_parameters_type redeem_param; + redeem_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; redeem_param.fee_per_kb = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; ret_val[((operation)htlc_redeem_operation()).which()] = redeem_param; @@ -191,7 +193,7 @@ try { create_operation.to = bob_id; create_operation.claim_period_seconds = 60; create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.hash_type = htlc_hash_algorithm::sha256; 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); @@ -249,7 +251,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.to = bob_id; create_operation.claim_period_seconds = 86400; create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.hash_type = htlc_hash_algorithm::sha256; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); @@ -287,8 +289,8 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) generate_block(); trx.clear(); } - // verify funds end up in Bob's account (100 + 20 - 2(fee) ) - BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 118 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify funds end up in Bob's account (100 + 20 - 4(fee) ) + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 116 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify funds remain out of Alice's acount ( 100 - 20 - 4 ) BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION ); } @@ -335,7 +337,7 @@ try { create_operation.to = bob_id; create_operation.claim_period_seconds = 3; create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.hash_type = htlc_hash_algorithm::sha256; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); @@ -518,7 +520,7 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) create_operation.to = bob_id; create_operation.claim_period_seconds = 60; create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = graphene::chain::hash_algorithm::sha256; + create_operation.hash_type = htlc_hash_algorithm::sha256; create_operation.preimage_size = preimage_size; create_operation.from = alice_id; trx.operations.push_back(create_operation); @@ -535,12 +537,13 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) create_fee.fee = 2; create_fee.fee_per_day = 2; htlc_create_operation create; + // no days create.claim_period_seconds = 0; BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); - // almost 2 days - create.claim_period_seconds = 2 * 60 * 60 * 24 - 1; + // exactly 1 day + create.claim_period_seconds = 60 * 60 * 24; BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); - // 2 days + // tad over a day create.claim_period_seconds++; BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 6 ); } @@ -548,12 +551,19 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) { htlc_redeem_operation::fee_parameters_type redeem_fee; redeem_fee.fee_per_kb = 2; + redeem_fee.fee = 2; htlc_redeem_operation redeem; + // no preimage redeem.preimage = std::vector(); - BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 2 ) ; - std::string test(2048, 'a'); + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 4 ) ; + // exactly 1KB + std::string test(1024, 'a'); redeem.preimage = std::vector( test.begin(), test.end() ); BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 4 ) ; + // just 1 byte over 1KB + std::string larger(1025, 'a'); + redeem.preimage = std::vector( larger.begin(), larger.end() ); + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 6 ) ; } // extend { @@ -561,12 +571,13 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) extend_fee.fee = 2; extend_fee.fee_per_day = 2; htlc_extend_operation extend; + // no days extend.seconds_to_add = 0; BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); - // almost 2 days - extend.seconds_to_add = 2 * 60 * 60 * 24 - 1; + // exactly 1 day + extend.seconds_to_add = 60 * 60 * 24; BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); - // 2 days + // 1 day and 1 second extend.seconds_to_add++; BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 6 ); } From 0244ff5e51867c89dc8497728f163c6ea3546c8f Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 19 Dec 2018 18:02:23 +0100 Subject: [PATCH 076/161] Added missing (?) includes --- libraries/chain/db_notify.cpp | 1 + libraries/chain/db_update.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 0862dd28ac..e044bd843d 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index b5f55fbb66..05aae9d058 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include From 5ffa2a0b2332daa66ab8a68cfe0fb2756df215f4 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 19 Dec 2018 17:03:41 +0100 Subject: [PATCH 077/161] Get rid of floating point calculations --- .../include/graphene/chain/protocol/htlc.hpp | 26 ++++--------------- libraries/chain/protocol/htlc.cpp | 20 ++++++++++++++ tests/tests/htlc_tests.cpp | 6 ++--- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 2c6ba84f8e..f7d3b88d68 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -77,13 +77,7 @@ namespace graphene { /**** * @brief calculates the fee to be paid for this operation */ - share_type calculate_fee(const fee_parameters_type& fee_params)const - { - - uint64_t days = std::max( 1, std::ceil( claim_period_seconds / (double)(60 * 60 * 24) ) ); - return fee_params.fee + (fee_params.fee_per_day * days); - } - + share_type calculate_fee(const fee_parameters_type& fee_params)const; }; struct htlc_redeem_operation : public base_operation @@ -117,13 +111,7 @@ namespace graphene { /**** * @brief calculates the fee to be paid for this operation */ - share_type calculate_fee(const fee_parameters_type& fee_params)const - { - if (fee_params.fee_per_kb > 0) - return fee_params.fee + std::max(fee_params.fee_per_kb, - std::ceil( preimage.size() / (double)1024 ) * fee_params.fee_per_kb ); - return fee_params.fee; - } + share_type calculate_fee(const fee_parameters_type& fee_params)const; }; struct htlc_extend_operation : public base_operation @@ -138,7 +126,7 @@ namespace graphene { // the object we are attempting to update htlc_id_type htlc_id; // who is attempting to update the transaction - account_id_type update_issuer; + account_id_type update_issuer; // how much to add uint32_t seconds_to_add; // for future expansion @@ -147,7 +135,7 @@ namespace graphene { /*** * @brief Perform obvious checks to validate this object */ - void validate()const; + void validate()const; /** * @brief Who is to pay the fee @@ -157,11 +145,7 @@ namespace graphene { /**** * @brief calculates the fee to be paid for this operation */ - share_type calculate_fee(const fee_parameters_type& fee_params)const - { - uint32_t days = std::max(1, std::ceil( seconds_to_add / (double)(60 * 60 * 24) ) ); - return fee_params.fee + (fee_params.fee_per_day * days); - } + share_type calculate_fee(const fee_parameters_type& fee_params)const; }; struct htlc_refund_operation : public base_operation diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 04ef3f0bd6..dd8f12e40f 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -23,6 +23,8 @@ */ #include +#define SECONDS_PER_DAY (60 * 60 * 24) + namespace graphene { namespace chain { void htlc_create_operation::validate()const { @@ -33,11 +35,29 @@ namespace graphene { namespace chain { || hash_type == htlc_hash_algorithm::sha256, "Unknown Hash Algorithm"); } + share_type htlc_create_operation::calculate_fee(const fee_parameters_type& fee_params)const + { + uint64_t days = (claim_period_seconds + SECONDS_PER_DAY - 1) / SECONDS_PER_DAY; + return fee_params.fee + (fee_params.fee_per_day * days); + } + void htlc_redeem_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); } + + share_type htlc_redeem_operation::calculate_fee(const fee_parameters_type& fee_params)const + { + return fee_params.fee + + ( preimage.size() + 1023 ) / 1024 * fee_params.fee_per_kb; + } + void htlc_extend_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); } + share_type htlc_extend_operation::calculate_fee(const fee_parameters_type& fee_params)const + { + uint32_t days = (seconds_to_add + SECONDS_PER_DAY - 1) / SECONDS_PER_DAY; + return fee_params.fee + (fee_params.fee_per_day * days); + } } } diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 6faff73d40..466b72b9c3 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -539,7 +539,7 @@ 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, 4 ); + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 2 ); // exactly 1 day create.claim_period_seconds = 60 * 60 * 24; BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); @@ -555,7 +555,7 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) htlc_redeem_operation redeem; // no preimage redeem.preimage = std::vector(); - BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 4 ) ; + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 2 ) ; // exactly 1KB std::string test(1024, 'a'); redeem.preimage = std::vector( test.begin(), test.end() ); @@ -573,7 +573,7 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) htlc_extend_operation extend; // no days extend.seconds_to_add = 0; - BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); + BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 2 ); // exactly 1 day extend.seconds_to_add = 60 * 60 * 24; BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); From 145d79066b4d23087abd6429269ead9e0881cdd8 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 19 Dec 2018 18:36:54 +0100 Subject: [PATCH 078/161] Added hf protection for htlc_operations in proposals --- libraries/chain/proposal_evaluator.cpp | 9 ++++ tests/tests/htlc_tests.cpp | 73 ++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index ff295c00f8..f943cc5ad9 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -102,6 +102,15 @@ struct proposal_operation_hardfork_visitor FC_ASSERT(!op.new_parameters.current_fees->exists()); } } + void operator()(const graphene::chain::htlc_create_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); + } + void operator()(const graphene::chain::htlc_redeem_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); + } + void operator()(const graphene::chain::htlc_extend_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); + } // loop and self visit in proposals void operator()(const graphene::chain::proposal_create_operation &v) const { for (const op_wrapper &op : v.proposed_ops) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 466b72b9c3..2b26721553 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -486,17 +486,10 @@ BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get().fee, 2 * GRAPHENE_BLOCKCHAIN_PRECISION); - } catch (fc::exception &fcx) { - BOOST_FAIL("FC Exception: " << fcx.to_detail_string()); - } catch (std::exception &ex) { - BOOST_FAIL("Exception: " << ex.what()); - } catch (...) { - BOOST_FAIL("Uncaught exception."); - } -} +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) -{ +{ try { ACTORS((alice)(bob)); int64_t init_balance(100000); @@ -509,9 +502,10 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; - // cler everything out + // clear everything out generate_block(); trx.clear(); + // Alice tries to put a contract on the blockchain { graphene::chain::htlc_create_operation create_operation; @@ -526,8 +520,65 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) trx.operations.push_back(create_operation); sign(trx, alice_private_key); GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + trx.clear(); } -} + + // Propose htlc_create + { + proposal_create_operation pco; + pco.expiration_time = db.head_block_time() + fc::minutes(1); + pco.fee_paying_account = alice_id; + + graphene::chain::htlc_create_operation create_operation; + create_operation.amount = graphene::chain::asset( 10000 ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 60; + create_operation.preimage_hash = preimage_hash; + create_operation.hash_type = htlc_hash_algorithm::sha256; + create_operation.preimage_size = preimage_size; + create_operation.from = alice_id; + + pco.proposed_ops.emplace_back( create_operation ); + trx.operations.push_back( pco ); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception ); + trx.clear(); + } + + // Propose htlc_redeem + { + proposal_create_operation pco; + pco.expiration_time = db.head_block_time() + fc::minutes(1); + pco.fee_paying_account = alice_id; + + graphene::chain::htlc_redeem_operation rop; + rop.redeemer = bob_id; + rop.htlc_id = alice_htlc_id; + string preimage_str = "Arglebargle"; + rop.preimage.insert( rop.preimage.begin(), preimage_str.begin(), preimage_str.end() ); + + pco.proposed_ops.emplace_back( rop ); + trx.operations.push_back( pco ); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception ); + trx.clear(); + } + + // Propose htlc_extend + { + proposal_create_operation pco; + pco.expiration_time = db.head_block_time() + fc::minutes(1); + pco.fee_paying_account = alice_id; + + graphene::chain::htlc_extend_operation xop; + xop.htlc_id = alice_htlc_id; + xop.seconds_to_add = 100; + xop.update_issuer = alice_id; + + pco.proposed_ops.emplace_back( xop ); + trx.operations.push_back( pco ); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception ); + trx.clear(); + } +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( fee_calculations ) { From e255f39bed52c867f8c047300ba8d1ac0343eca5 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 19 Dec 2018 18:55:34 +0100 Subject: [PATCH 079/161] Validate that preimage hash length matches algorithm hash length --- libraries/chain/protocol/htlc.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index dd8f12e40f..a1e1d14f91 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -23,16 +23,30 @@ */ #include +#include +#include +#include + #define SECONDS_PER_DAY (60 * 60 * 24) namespace graphene { namespace chain { void htlc_create_operation::validate()const { + static const fc::ripemd160 ripemd160_instance; + static const fc::sha1 sha1_instance; + static const fc::sha256 sha256_instance; + FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount.amount > 0 ); FC_ASSERT( hash_type == htlc_hash_algorithm::ripemd160 || hash_type == htlc_hash_algorithm::sha1 || hash_type == htlc_hash_algorithm::sha256, "Unknown Hash Algorithm"); + FC_ASSERT( ( hash_type == htlc_hash_algorithm::ripemd160 + && preimage_hash.size() == ripemd160_instance.data_size() ) + || ( hash_type == htlc_hash_algorithm::sha1 + && preimage_hash.size() == sha1_instance.data_size() ) + || ( hash_type == htlc_hash_algorithm::sha256 + && preimage_hash.size() == sha256_instance.data_size() ), "Hash length mismatch"); } share_type htlc_create_operation::calculate_fee(const fee_parameters_type& fee_params)const From da6fbef6cfeb5a38df7c0d09133ff76a4943a527 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 19 Dec 2018 23:25:34 +0100 Subject: [PATCH 080/161] Refactoring: use a static_variant instead of separate enum + hash vector --- libraries/chain/CMakeLists.txt | 1 - libraries/chain/htlc_evaluator.cpp | 45 +++++------- libraries/chain/htlc_object.cpp | 45 ------------ .../include/graphene/chain/htlc_evaluator.hpp | 3 - .../include/graphene/chain/htlc_object.hpp | 24 +------ .../include/graphene/chain/protocol/htlc.hpp | 48 +++++++------ .../include/graphene/chain/protocol/types.hpp | 17 +++++ libraries/chain/protocol/htlc.cpp | 17 ----- libraries/wallet/wallet.cpp | 71 ++++++++++++------- tests/tests/htlc_tests.cpp | 27 +++---- 10 files changed, 116 insertions(+), 182 deletions(-) delete mode 100644 libraries/chain/htlc_object.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index e6551c1189..71d2fd2d2e 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -78,7 +78,6 @@ add_library( graphene_chain withdraw_permission_evaluator.cpp worker_evaluator.cpp htlc_evaluator.cpp - htlc_object.cpp confidential_evaluator.cpp special_authority.cpp buyback.cpp diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index fba098025d..37512a0d1f 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include #include #include #include @@ -45,8 +44,6 @@ namespace graphene { FC_ASSERT( o.claim_period_seconds <= htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); // make sure the preimage length is reasonable FC_ASSERT( o.preimage_size <= htlc_options->max_preimage_size, "HTLC preimage length exceeds allowed length" ); - // make sure we have a hash algorithm set - FC_ASSERT( o.hash_type != htlc_hash_algorithm::unknown, "HTLC Hash Algorithm must be set" ); // make sure the sender has the funds for the HTLC FC_ASSERT( db().get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; return void_result(); @@ -65,21 +62,28 @@ namespace graphene { esc.preimage_hash = o.preimage_hash; esc.preimage_size = o.preimage_size; esc.expiration = dbase.head_block_time() + o.claim_period_seconds; - esc.preimage_hash_algorithm = o.hash_type; }); return esc.id; } FC_CAPTURE_AND_RETHROW( (o) ) } - template - bool htlc_redeem_evaluator::test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash) + class htlc_redeem_visitor { - T attempted_hash = T::hash( (const char*)incoming_preimage.data(), incoming_preimage.size()); - if (attempted_hash.data_size() != valid_hash.size()) - return false; - return memcmp(attempted_hash.data(), valid_hash.data(), attempted_hash.data_size()) == 0; - } + //private: + const std::vector& data; + public: + typedef bool result_type; + + htlc_redeem_visitor( const std::vector& preimage ) + : data( preimage ) {} + + template + bool operator()( const T& preimage_hash )const + { + return T::hash( (const char*)data.data(), (uint32_t) data.size() ) == preimage_hash; + } + }; void_result htlc_redeem_evaluator::do_evaluate(const htlc_redeem_operation& o) { @@ -88,24 +92,9 @@ namespace graphene { FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); FC_ASSERT(db().head_block_time() < htlc_obj->expiration, "Preimage provided after escrow expiration."); - // see if the preimages match - bool match = false; - switch(htlc_obj->preimage_hash_algorithm) - { - case (htlc_hash_algorithm::sha256): - match = test_hash(o.preimage, htlc_obj->preimage_hash); - break; - case (htlc_hash_algorithm::ripemd160): - match = test_hash(o.preimage, htlc_obj->preimage_hash); - break; - case (htlc_hash_algorithm::sha1): - match = test_hash(o.preimage, htlc_obj->preimage_hash); - break; - default: - break; - } + const htlc_redeem_visitor vtor( o.preimage ); + FC_ASSERT( htlc_obj->preimage_hash.visit( vtor ), "Provided preimage does not generate correct hash."); - FC_ASSERT(match, "Provided preimage does not generate correct hash."); return void_result(); } diff --git a/libraries/chain/htlc_object.cpp b/libraries/chain/htlc_object.cpp deleted file mode 100644 index e460619212..0000000000 --- a/libraries/chain/htlc_object.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018 Bitshares Foundation, and contributors. - * - * The MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include - -namespace graphene { namespace chain { - - /**** - * Convert string to hash algorithm enum - * @param incoming the string to convert - * @returns one of the valid algorithms or the enum value "unknown" - */ - fc::enum_type string_to_hash_algorithm(std::string incoming) - { - std::transform(incoming.begin(), incoming.end(), incoming.begin(), ::toupper); - if (incoming == "RIPEMD160") - return htlc_hash_algorithm::ripemd160; - if (incoming == "SHA256") - return htlc_hash_algorithm::sha256; - if (incoming == "SHA1") - return htlc_hash_algorithm::sha1; - return htlc_hash_algorithm::unknown; - } - -} } diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp index c93b579742..08a24b2bdc 100644 --- a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -44,9 +44,6 @@ namespace graphene { void_result do_evaluate( const htlc_redeem_operation& o); void_result do_apply( const htlc_redeem_operation& o); const htlc_object* htlc_obj = nullptr; - private: - template - bool test_hash(const std::vector& incoming_preimage, const std::vector& valid_hash); }; class htlc_extend_evaluator : public evaluator diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index 57dab81416..e9c4515042 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -33,13 +33,6 @@ namespace graphene { namespace chain { - /**** - * Convert string to hash algorithm enum - * @param incoming the string to convert - * @returns one of the valid algorithms or the enum value "unknown" - */ - fc::enum_type string_to_hash_algorithm(std::string incoming); - /** * @brief database object to store HTLCs * @@ -56,8 +49,7 @@ namespace graphene { namespace chain { account_id_type to; asset amount; fc::time_point_sec expiration; - vector preimage_hash; - fc::enum_type preimage_hash_algorithm; + htlc_hash preimage_hash; uint16_t preimage_size; }; @@ -85,18 +77,6 @@ namespace graphene { namespace chain { } } // namespace graphene::chain -namespace fc -{ - template<> - struct get_typename> - { - static const char* name() - { - return "fc::enum_type"; - } - }; -} - FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), (from)(to)(amount)(expiration) - (preimage_hash)(preimage_hash_algorithm)(preimage_size) ); + (preimage_hash)(preimage_size) ); diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index f7d3b88d68..310282da12 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -22,6 +22,10 @@ * THE SOFTWARE. */ #pragma once + +#include +#include +#include #include #include #include @@ -31,12 +35,15 @@ namespace graphene { namespace chain { - enum htlc_hash_algorithm { - unknown = 0x00, - ripemd160 = 0x01, - sha256 = 0x02, - sha1 = 0x03 - }; + typedef fc::ripemd160 htlc_algo_ripemd160; + typedef fc::sha1 htlc_algo_sha1; + typedef fc::sha256 htlc_algo_sha256; + + typedef fc::static_variant< + htlc_algo_ripemd160, + htlc_algo_sha1, + htlc_algo_sha256 + > htlc_hash; struct htlc_create_operation : public base_operation { @@ -45,29 +52,26 @@ namespace graphene { uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; }; // paid to network - asset fee; + asset fee; // where the held monies are to come from account_id_type from; // where the held monies will go if the preimage is provided - account_id_type to; + account_id_type to; // the amount to hold - asset amount; - // hash algorithm used to create preimage_hash - fc::enum_type hash_type - = htlc_hash_algorithm::unknown; - // the hash of the preimage - std::vector preimage_hash; + asset amount; + // the (typed) hash of the preimage + htlc_hash preimage_hash; // the size of the preimage - uint16_t preimage_size; + uint16_t preimage_size; // The time the funds will be returned to the source if not claimed - uint32_t claim_period_seconds; + uint32_t claim_period_seconds; // for future expansion - extensions_type extensions; + extensions_type extensions; /*** * @brief Does simple validation of this object */ - void validate()const; + void validate()const; /** * @brief who will pay the fee @@ -92,9 +96,9 @@ namespace graphene { // the object we are attempting to update htlc_id_type htlc_id; // who is attempting to update the transaction - account_id_type redeemer; + account_id_type redeemer; // the preimage (not used if after epoch timeout) - std::vector preimage; + std::vector preimage; // for future expansion extensions_type extensions; @@ -169,7 +173,7 @@ namespace graphene { } } -FC_REFLECT_ENUM( graphene::chain::htlc_hash_algorithm, (unknown)(ripemd160)(sha256)(sha1)); +FC_REFLECT_TYPENAME( graphene::chain::htlc_hash ) FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) (fee_per_kb) ) @@ -177,7 +181,7 @@ FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) ( FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_create_operation, - (fee)(from)(to)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)(hash_type)) + (fee)(from)(to)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(redeemer)(preimage)(extensions)) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)(to)) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 53b0378c65..a9202c32a8 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -36,6 +36,23 @@ #include +// TODO: move this to fc +#include +FC_REFLECT_TYPENAME( fc::sha1 ) +namespace fc { namespace raw { + template + inline void pack( T& ds, const fc::sha1& ep, uint32_t _max_depth = 1 ) { + ds << ep; + } + + template + inline void unpack( T& ds, sha1& ep, uint32_t _max_depth = 1 ) { + ds >> ep; + } + +} } +// /TODO: move to fc + #include #include #include diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index a1e1d14f91..b176c6e85d 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -23,30 +23,13 @@ */ #include -#include -#include -#include - #define SECONDS_PER_DAY (60 * 60 * 24) namespace graphene { namespace chain { void htlc_create_operation::validate()const { - static const fc::ripemd160 ripemd160_instance; - static const fc::sha1 sha1_instance; - static const fc::sha256 sha256_instance; - FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( amount.amount > 0 ); - FC_ASSERT( hash_type == htlc_hash_algorithm::ripemd160 - || hash_type == htlc_hash_algorithm::sha1 - || hash_type == htlc_hash_algorithm::sha256, "Unknown Hash Algorithm"); - FC_ASSERT( ( hash_type == htlc_hash_algorithm::ripemd160 - && preimage_hash.size() == ripemd160_instance.data_size() ) - || ( hash_type == htlc_hash_algorithm::sha1 - && preimage_hash.size() == sha1_instance.data_size() ) - || ( hash_type == htlc_hash_algorithm::sha256 - && preimage_hash.size() == sha256_instance.data_size() ), "Hash length mismatch"); } share_type htlc_create_operation::calculate_fee(const fee_parameters_type& fee_params)const diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index cc5d8a4062..0eabbe35d1 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -265,6 +265,27 @@ struct op_prototype_visitor } }; +class htlc_hash_to_string_visitor +{ +public: + typedef string result_type; + + result_type operator()( const fc::ripemd160& hash )const + { + return "RIPEMD160 " + hash.str(); + } + + result_type operator()( const fc::sha1& hash )const + { + return "SHA1 " + hash.str(); + } + + result_type operator()( const fc::sha256& hash )const + { + return "SHA256 " + hash.str(); + } +}; + class wallet_api_impl { public: @@ -1750,8 +1771,21 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } + static htlc_hash do_hash( const string& algorithm, const std::string& hash ) + { + string name_upper; + std::transform( algorithm.begin(), algorithm.end(), std::back_inserter(name_upper), ::toupper); + if( name_upper == "RIPEMD160" ) + return fc::ripemd160( hash ); + if( name_upper == "SHA256" ) + return fc::sha256( hash ); + if( name_upper == "SHA1" ) + return fc::sha1( hash ); + FC_THROW_EXCEPTION( fc::invalid_arg_exception, "Unknown algorithm '${a}'", ("a",algorithm) ); + } + signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, - string hash_algorithm, const std::vector& preimage_hash, size_t preimage_size, + string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t claim_period_seconds, bool broadcast = false ) { try @@ -1765,11 +1799,8 @@ class wallet_api_impl create_op.to = get_account(destination).id; create_op.amount = asset_obj->amount_from_string(amount); create_op.claim_period_seconds = claim_period_seconds; - create_op.preimage_hash = preimage_hash; + create_op.preimage_hash = do_hash( hash_algorithm, preimage_hash ); create_op.preimage_size = preimage_size; - create_op.hash_type = graphene::chain::string_to_hash_algorithm(hash_algorithm); - FC_ASSERT(create_op.hash_type != htlc_hash_algorithm::unknown, - "Unknown hash algorithm: ${algo}", ("algo", hash_algorithm)); signed_transaction tx; tx.operations.push_back(create_op); @@ -2991,6 +3022,8 @@ std::string operation_printer::operator()(const htlc_redeem_operation& op) const std::string operation_printer::operator()(const htlc_create_operation& op) const { + static htlc_hash_to_string_visitor vtor; + auto fee_asset = wallet.get_asset( op.fee.asset_id ); auto to = wallet.get_account( op.to ); operation_result_printer rprinter(wallet); @@ -2998,12 +3031,9 @@ std::string operation_printer::operator()(const htlc_create_operation& op) const out << "Create HTLC to " << to.name << " with id " << database_id - << " preimage hash: ["; - for(uint8_t c : op.preimage_hash) - { - out << setfill('0') << std::setw(2) << std::hex << (int)c; - } - out << "] (Fee: " << fee_asset.amount_to_pretty_string( op.fee ) << ")"; + << " preimage hash: [" + << op.preimage_hash.visit( vtor ) + << "] (Fee: " << fee_asset.amount_to_pretty_string( op.fee ) << ")"; // 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) @@ -3142,25 +3172,18 @@ uint64_t wallet_api::get_asset_count()const return my->_remote_db->get_asset_count(); } -std::vector string_to_vec(std::string incoming) -{ - FC_ASSERT(incoming.size() % 2 == 0, "Invalid incoming hash."); - std::size_t outgoing_size = incoming.size() / 2; - std::vector outgoing(outgoing_size); - fc::from_hex(incoming, (char*)outgoing.data(), outgoing_size); - return outgoing; -} - signed_transaction wallet_api::htlc_create( string source, string destination, string amount, string asset_symbol, string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, const uint32_t claim_period_seconds, bool broadcast) { - return my->htlc_create(source, destination, amount, asset_symbol, hash_algorithm, string_to_vec(preimage_hash), preimage_size, + return my->htlc_create(source, destination, amount, asset_symbol, hash_algorithm, preimage_hash, preimage_size, claim_period_seconds, broadcast); } variant wallet_api::get_htlc(std::string htlc_id) const { + static detail::htlc_hash_to_string_visitor vtor; + graphene::chain::htlc_object obj = my->get_htlc(htlc_id); fc::mutable_variant_object ret_val; ret_val["database_id"] = (std::string)obj.id; @@ -3169,11 +3192,7 @@ variant wallet_api::get_htlc(std::string htlc_id) const ret_val["amount"] = obj.amount.amount.value; ret_val["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); - std::stringstream hash_string; - for(uint8_t c : obj.preimage_hash) - hash_string << std::setfill('0') << std::setw(2) << std::hex << (int)c; - ret_val["preimage_hash"] = hash_string.str(); - ret_val["preimage_algorithm"] = (std::string)obj.preimage_hash_algorithm; + ret_val["preimage_hash"] = obj.preimage_hash.visit( vtor ); ret_val["preimage_size"] = obj.preimage_size; return ret_val; } diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 2b26721553..1f659ea89e 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -68,10 +68,10 @@ void generate_random_preimage(uint16_t key_size, std::vector& vec) * @param preimage the preimage * @returns a vector that cointains the sha256 hash of the preimage */ -std::vector hash_it(std::vector preimage) +template +H hash_it(std::vector preimage) { - fc::sha256 hash = fc::sha256::hash((char*)preimage.data(), preimage.size()); - return std::vector(hash.data(), hash.data() + hash.data_size()); + return H::hash( (char*)preimage.data(), preimage.size() ); } flat_map< uint64_t, graphene::chain::fee_parameters > get_htlc_fee_parameters() @@ -179,7 +179,6 @@ try { uint16_t preimage_size = 256; std::vector pre_image(256); generate_random_preimage(preimage_size, pre_image); - std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -192,8 +191,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 = preimage_hash; - create_operation.hash_type = htlc_hash_algorithm::sha256; + 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); @@ -237,7 +235,6 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) uint16_t preimage_size = 256; std::vector pre_image(preimage_size); generate_random_preimage(preimage_size, pre_image); - std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // clear everything out @@ -250,8 +247,7 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) create_operation.amount = graphene::chain::asset( 20 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 86400; - create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = htlc_hash_algorithm::sha256; + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); @@ -309,7 +305,6 @@ try { uint16_t preimage_size = 256; std::vector pre_image(256); generate_random_preimage(preimage_size, pre_image); - std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // cler everything out @@ -321,7 +316,7 @@ try { create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 3; - create_operation.preimage_hash = preimage_hash; + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); @@ -336,8 +331,7 @@ try { create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION ); create_operation.to = bob_id; create_operation.claim_period_seconds = 3; - create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = htlc_hash_algorithm::sha256; + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); @@ -499,7 +493,6 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) uint16_t preimage_size = 256; std::vector pre_image(256); generate_random_preimage(preimage_size, pre_image); - std::vector preimage_hash = hash_it(pre_image); graphene::chain::htlc_id_type alice_htlc_id; // clear everything out @@ -513,8 +506,7 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) create_operation.amount = graphene::chain::asset( 10000 ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = htlc_hash_algorithm::sha256; + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; trx.operations.push_back(create_operation); @@ -533,8 +525,7 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) create_operation.amount = graphene::chain::asset( 10000 ); create_operation.to = bob_id; create_operation.claim_period_seconds = 60; - create_operation.preimage_hash = preimage_hash; - create_operation.hash_type = htlc_hash_algorithm::sha256; + create_operation.preimage_hash = hash_it( pre_image ); create_operation.preimage_size = preimage_size; create_operation.from = alice_id; From 793220eed2ff3bbd64a489845307d25e451fb965 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 20 Dec 2018 15:02:28 -0500 Subject: [PATCH 081/161] added extend test + smaller misc --- libraries/chain/db_notify.cpp | 2 +- libraries/chain/htlc_evaluator.cpp | 1 - tests/tests/htlc_tests.cpp | 32 +++++++++++++++++++++++------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index e044bd843d..3def684732 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -363,7 +363,7 @@ void get_relevant_accounts( const object* obj, flat_set& accoun break; } case htlc_object_type:{ const auto& htlc_obj = dynamic_cast(obj); - FC_ASSERT( htlc_obj != nullptr); + FC_ASSERT( htlc_obj != nullptr ); accounts.insert( htlc_obj->from ); accounts.insert( htlc_obj->to ); break; diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 37512a0d1f..9306e2b2eb 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -90,7 +90,6 @@ namespace graphene { htlc_obj = &db().get(o.htlc_id); FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); - FC_ASSERT(db().head_block_time() < htlc_obj->expiration, "Preimage provided after escrow expiration."); const htlc_redeem_visitor vtor( o.preimage ); FC_ASSERT( htlc_obj->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 1f659ea89e..0a25a6ee3f 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -223,6 +223,7 @@ try { BOOST_AUTO_TEST_CASE( htlc_fulfilled ) { +try { ACTORS((alice)(bob)); int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); @@ -263,24 +264,40 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) // make sure Alice's money gets put on hold (100 - 20 - 4(fee) ) BOOST_CHECK_EQUAL( get_balance( alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION ); - // TODO: make sure Bob (or anyone) can see the details of the transaction + // extend the timeout so that Bob has more time + { + graphene::chain::htlc_extend_operation extend_operation; + extend_operation.htlc_id = alice_htlc_id; + extend_operation.seconds_to_add = 86400; + extend_operation.update_issuer = alice_id; + extend_operation.fee = db.current_fee_schedule().calculate_fee( extend_operation ); + trx.operations.push_back( extend_operation ); + sign( trx, alice_private_key ); + PUSH_TX( db, trx, ~0 ); + trx.clear(); + generate_blocks( db.head_block_time() + fc::seconds(87000) ); + set_expiration( db, trx ); + } + + // make sure Alice's money is still on hold, and account for extra fee + BOOST_CHECK_EQUAL( get_balance( alice_id, graphene::chain::asset_id_type()), 72 * GRAPHENE_BLOCKCHAIN_PRECISION ); - // send an update operation to claim the funds + // send a redeem operation to claim the funds { graphene::chain::htlc_redeem_operation update_operation; update_operation.redeemer = bob_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); + trx.operations.push_back( update_operation ); sign(trx, bob_private_key); try { - PUSH_TX(db, trx, ~0); + PUSH_TX( db, trx, ~0 ); } - catch (fc::exception& ex) + catch ( fc::exception& ex ) { - BOOST_FAIL(ex.what()); + BOOST_FAIL( ex.to_detail_string() ); } generate_block(); trx.clear(); @@ -288,7 +305,8 @@ BOOST_AUTO_TEST_CASE( htlc_fulfilled ) // verify funds end up in Bob's account (100 + 20 - 4(fee) ) BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 116 * GRAPHENE_BLOCKCHAIN_PRECISION ); // verify funds remain out of Alice's acount ( 100 - 20 - 4 ) - BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION ); + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 72 * GRAPHENE_BLOCKCHAIN_PRECISION ); +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE( other_peoples_money ) From 57248daa1b3cca8965135df2bddc93f23a88975b Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 20 Dec 2018 15:14:42 -0500 Subject: [PATCH 082/161] handle fee multiplication overflow --- libraries/chain/protocol/htlc.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index b176c6e85d..11f046e2cf 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -32,17 +32,20 @@ namespace graphene { namespace chain { FC_ASSERT( amount.amount > 0 ); } - 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 )const { - uint64_t days = (claim_period_seconds + SECONDS_PER_DAY - 1) / SECONDS_PER_DAY; - return fee_params.fee + (fee_params.fee_per_day * days); + 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; + FC_ASSERT( days == 0 || per_day_fee / fee_params.fee_per_day == days ); + return fee_params.fee + per_day_fee; } void htlc_redeem_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); } - share_type htlc_redeem_operation::calculate_fee(const fee_parameters_type& fee_params)const + share_type htlc_redeem_operation::calculate_fee( const fee_parameters_type& fee_params )const { return fee_params.fee + ( preimage.size() + 1023 ) / 1024 * fee_params.fee_per_kb; @@ -52,9 +55,11 @@ namespace graphene { namespace chain { FC_ASSERT( fee.amount >= 0 ); } - share_type htlc_extend_operation::calculate_fee(const fee_parameters_type& fee_params)const + share_type htlc_extend_operation::calculate_fee( const fee_parameters_type& fee_params )const { - uint32_t days = (seconds_to_add + SECONDS_PER_DAY - 1) / SECONDS_PER_DAY; - return fee_params.fee + (fee_params.fee_per_day * days); + uint32_t days = ( seconds_to_add + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; + uint64_t per_day_fee = fee_params.fee_per_day * days; + FC_ASSERT( days == 0 || per_day_fee / fee_params.fee_per_day == days ); + return fee_params.fee + per_day_fee; } } } From e8172e92ee0d83d661a99988a904dbcfb3aa6f9d Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 20 Dec 2018 15:20:43 -0500 Subject: [PATCH 083/161] added messages to asserts --- libraries/chain/protocol/htlc.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 11f046e2cf..e9be01b5e5 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -28,8 +28,8 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { - FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( amount.amount > 0 ); + FC_ASSERT( fee.amount >= 0, "Fee amount should not be zero" ); + FC_ASSERT( amount.amount > 0, "HTLC amount should not be zero" ); } share_type htlc_create_operation::calculate_fee( const fee_parameters_type& fee_params )const @@ -37,12 +37,12 @@ namespace graphene { namespace chain { 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; - FC_ASSERT( days == 0 || per_day_fee / fee_params.fee_per_day == days ); + FC_ASSERT( days == 0 || per_day_fee / days == fee_params.fee_per_day, "Fee calculation overflow" ); return fee_params.fee + per_day_fee; } void htlc_redeem_operation::validate()const { - FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( fee.amount >= 0, "Fee amount should not be zero" ); } share_type htlc_redeem_operation::calculate_fee( const fee_parameters_type& fee_params )const @@ -52,14 +52,14 @@ namespace graphene { namespace chain { } void htlc_extend_operation::validate()const { - FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( fee.amount >= 0 , "Fee amount should not be zero"); } share_type htlc_extend_operation::calculate_fee( const fee_parameters_type& fee_params )const { uint32_t days = ( seconds_to_add + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; uint64_t per_day_fee = fee_params.fee_per_day * days; - FC_ASSERT( days == 0 || per_day_fee / fee_params.fee_per_day == days ); + FC_ASSERT( days == 0 || per_day_fee / days == fee_params.fee_per_day, "Fee calculation overflow" ); return fee_params.fee + per_day_fee; } } } From 2f08968c5d4044220663cd4cd0ccaa49d41d68a5 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 20 Dec 2018 19:59:46 -0500 Subject: [PATCH 084/161] virtual op for notifications --- libraries/chain/db_notify.cpp | 7 +++++- libraries/chain/htlc_evaluator.cpp | 3 +++ .../include/graphene/chain/protocol/htlc.hpp | 24 +++++++++++++++++++ .../graphene/chain/protocol/operations.hpp | 1 + 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 3def684732..17e71e044f 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -259,12 +259,17 @@ struct get_impacted_account_visitor } void operator()( const htlc_create_operation& op ) { - _impacted.insert( op.fee_payer() ); + _impacted.insert( op.fee_payer() ); + _impacted.insert( op.to ); } void operator()( const htlc_redeem_operation& op ) { _impacted.insert( op.fee_payer() ); } + void operator()( const htlc_redeemed_operation& op ) + { + _impacted.insert( op.from ); + } void operator()( const htlc_extend_operation& op ) { _impacted.insert( op.fee_payer() ); diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 9306e2b2eb..218558a98d 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -100,6 +100,9 @@ namespace graphene { void_result htlc_redeem_evaluator::do_apply(const htlc_redeem_operation& o) { db().adjust_balance(htlc_obj->to, htlc_obj->amount); + // notify related parties + htlc_redeemed_operation virt_op( htlc_obj->id, htlc_obj->from, htlc_obj->to, htlc_obj->amount ); + db().push_applied_operation( virt_op ); db().remove(*htlc_obj); return void_result(); } diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index 310282da12..ef5f2b1469 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -118,6 +118,28 @@ namespace graphene { share_type calculate_fee(const fee_parameters_type& fee_params)const; }; + /** + * virtual op to assist with notifying related parties + */ + struct htlc_redeemed_operation : public base_operation + { + struct fee_parameters_type {}; + + htlc_redeemed_operation() {} + htlc_redeemed_operation( htlc_id_type htlc_id, account_id_type from, account_id_type to, asset amount) : + htlc_id(htlc_id), from(from), to(to), amount(amount) {} + + account_id_type fee_payer()const { return to; } + void validate()const { FC_ASSERT( !"virtual operation" ); } + + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + + htlc_id_type htlc_id; + account_id_type from, to; + asset amount; + asset fee; + }; + struct htlc_extend_operation : public base_operation { struct fee_parameters_type { @@ -177,11 +199,13 @@ FC_REFLECT_TYPENAME( graphene::chain::htlc_hash ) FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) (fee_per_kb) ) +FC_REFLECT( graphene::chain::htlc_redeemed_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL FC_REFLECT( graphene::chain::htlc_create_operation, (fee)(from)(to)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)) FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(redeemer)(preimage)(extensions)) +FC_REFLECT( graphene::chain::htlc_redeemed_operation, (fee)(htlc_id)(from)(to)(amount) ) FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)(to)) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 3f5c27d998..9b5ffabb37 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -99,6 +99,7 @@ namespace graphene { namespace chain { asset_update_issuer_operation, htlc_create_operation, htlc_redeem_operation, + htlc_redeemed_operation, // VIRTUAL htlc_extend_operation, htlc_refund_operation // VIRTUAL > operation; From 22ea563f2856327290dd57dd00ddbd215415ee3b Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 21 Dec 2018 08:40:47 -0500 Subject: [PATCH 085/161] More overflow protection + wording changes --- libraries/chain/protocol/htlc.cpp | 13 +++++++++---- tests/tests/htlc_tests.cpp | 9 +-------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index e9be01b5e5..9f60e7dec8 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -28,8 +28,8 @@ namespace graphene { namespace chain { void htlc_create_operation::validate()const { - FC_ASSERT( fee.amount >= 0, "Fee amount should not be zero" ); - FC_ASSERT( amount.amount > 0, "HTLC amount should not be zero" ); + FC_ASSERT( fee.amount >= 0, "Fee amount should not be negative" ); + 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 @@ -42,17 +42,22 @@ namespace graphene { namespace chain { } void htlc_redeem_operation::validate()const { - FC_ASSERT( fee.amount >= 0, "Fee amount should not be zero" ); + FC_ASSERT( fee.amount >= 0, "Fee amount should not be negative" ); } share_type htlc_redeem_operation::calculate_fee( const fee_parameters_type& fee_params )const { + if (fee_params.fee_per_kb == 0) + return fee_params.fee; + uint64_t product = 1024 * fee_params.fee_per_kb; + FC_ASSERT( product / 1024 == fee_params.fee_per_kb, "Fee calculation overflow"); return fee_params.fee + ( preimage.size() + 1023 ) / 1024 * fee_params.fee_per_kb; + } void htlc_extend_operation::validate()const { - FC_ASSERT( fee.amount >= 0 , "Fee amount should not be zero"); + FC_ASSERT( fee.amount >= 0 , "Fee amount should not be negative"); } share_type htlc_extend_operation::calculate_fee( const fee_parameters_type& fee_params )const diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 0a25a6ee3f..98518f6484 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -291,14 +291,7 @@ try { update_operation.fee = db.current_fee_schedule().calculate_fee( update_operation ); trx.operations.push_back( update_operation ); sign(trx, bob_private_key); - try - { - PUSH_TX( db, trx, ~0 ); - } - catch ( fc::exception& ex ) - { - BOOST_FAIL( ex.to_detail_string() ); - } + PUSH_TX( db, trx, ~0 ); generate_block(); trx.clear(); } From 8e77fff86cb847dc4b6b5b5b43c020312d4a0a0c Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 21 Dec 2018 09:37:45 -0500 Subject: [PATCH 086/161] fix bug in fee calculation --- libraries/chain/protocol/htlc.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp index 9f60e7dec8..645feb6dbf 100644 --- a/libraries/chain/protocol/htlc.cpp +++ b/libraries/chain/protocol/htlc.cpp @@ -47,13 +47,10 @@ namespace graphene { namespace chain { share_type htlc_redeem_operation::calculate_fee( const fee_parameters_type& fee_params )const { - if (fee_params.fee_per_kb == 0) - return fee_params.fee; - uint64_t product = 1024 * fee_params.fee_per_kb; - FC_ASSERT( product / 1024 == fee_params.fee_per_kb, "Fee calculation overflow"); - return fee_params.fee - + ( preimage.size() + 1023 ) / 1024 * fee_params.fee_per_kb; - + uint64_t kb = ( preimage.size() + 1023 ) / 1024; + uint64_t product = kb * fee_params.fee_per_kb; + FC_ASSERT( kb == 0 || product / kb == fee_params.fee_per_kb, "Fee calculation overflow"); + return fee_params.fee + product; } void htlc_extend_operation::validate()const { From 19375f7b6aa1c8c95d6213c2b53ff285f707a2f7 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 21 Dec 2018 17:12:44 +0100 Subject: [PATCH 087/161] Fixed build error --- libraries/chain/include/graphene/chain/protocol/types.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 170baa6b19..87536f42ee 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -38,7 +38,6 @@ // TODO: move this to fc #include -FC_REFLECT_TYPENAME( fc::sha1 ) namespace fc { namespace raw { template inline void pack( T& ds, const fc::sha1& ep, uint32_t _max_depth = 1 ) { From bf9537fec6bb9d5191b71b1079799480e7c74310 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Mon, 24 Dec 2018 10:47:13 -0300 Subject: [PATCH 088/161] add support for existing tests after hf1270 --- tests/common/database_fixture.hpp | 1 + tests/tests/market_tests.cpp | 88 ++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 78f7b65d19..4a71f1d05f 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -186,6 +186,7 @@ struct database_fixture { optional data_dir; bool skip_key_index_test = false; uint32_t anon_acct_count; + bool hf1270 = false; database_fixture(); ~database_fixture(); diff --git a/tests/tests/market_tests.cpp b/tests/tests/market_tests.cpp index 9447a06b79..fefcb895eb 100644 --- a/tests/tests/market_tests.cpp +++ b/tests/tests/market_tests.cpp @@ -231,9 +231,11 @@ BOOST_AUTO_TEST_CASE(issue_338_etc) */ BOOST_AUTO_TEST_CASE(hardfork_core_338_test) { try { - auto mi = db.get_global_properties().parameters.maintenance_interval; - generate_blocks(HARDFORK_CORE_343_TIME - mi); // assume all hard forks occur at same time - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + if(hf1270) + generate_blocks(HARDFORK_CORE_1270_TIME); + else + generate_blocks(HARDFORK_CORE_343_TIME); set_expiration( db, trx ); @@ -325,7 +327,8 @@ BOOST_AUTO_TEST_CASE(hardfork_core_338_test) // call's call_price will be updated after the match, to 741/31/1.75 CORE/USD = 2964/217 // it's above settlement price (10/1) so won't be margin called again - BOOST_CHECK( price(asset(2964),asset(217,usd_id)) == call.call_price ); + if(!hf1270) // can use call price only if we are before hf1270 + BOOST_CHECK( price(asset(2964),asset(217,usd_id)) == call.call_price ); // This would match with call before, but would match with call2 after #343 fixed BOOST_CHECK( !create_sell_order(seller, bitusd.amount(700), core.amount(6000) ) ); @@ -342,7 +345,8 @@ BOOST_AUTO_TEST_CASE(hardfork_core_338_test) BOOST_CHECK_EQUAL( 1000, call3.debt.value ); BOOST_CHECK_EQUAL( 16000, call3.collateral.value ); // call2's call_price will be updated after the match, to 78/3/1.75 CORE/USD = 312/21 - BOOST_CHECK( price(asset(312),asset(21,usd_id)) == call2.call_price ); + if(!hf1270) // can use call price only if we are before hf1270 + BOOST_CHECK( price(asset(312),asset(21,usd_id)) == call2.call_price ); // it's above settlement price (10/1) so won't be margin called // at this moment, collateralization of call is 7410 / 310 = 23.9 @@ -406,9 +410,11 @@ BOOST_AUTO_TEST_CASE(hardfork_core_338_test) */ BOOST_AUTO_TEST_CASE(hardfork_core_453_test) { try { - auto mi = db.get_global_properties().parameters.maintenance_interval; - generate_blocks(HARDFORK_CORE_453_TIME - mi); // assume all hard forks occur at same time - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + if(hf1270) + generate_blocks(HARDFORK_CORE_1270_TIME); + else + generate_blocks(HARDFORK_CORE_343_TIME); set_expiration( db, trx ); @@ -478,7 +484,6 @@ BOOST_AUTO_TEST_CASE(hardfork_core_453_test) // generate a block generate_block(); - } FC_LOG_AND_RETHROW() } /*** @@ -486,9 +491,11 @@ BOOST_AUTO_TEST_CASE(hardfork_core_453_test) */ BOOST_AUTO_TEST_CASE(hardfork_core_625_big_limit_order_test) { try { - auto mi = db.get_global_properties().parameters.maintenance_interval; - generate_blocks(HARDFORK_CORE_625_TIME - mi); // assume all hard forks occur at same time - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + if(hf1270) + generate_blocks(HARDFORK_CORE_1270_TIME); + else + generate_blocks(HARDFORK_CORE_625_TIME); set_expiration( db, trx ); @@ -1195,9 +1202,11 @@ BOOST_AUTO_TEST_CASE(hard_fork_343_cross_test) */ BOOST_AUTO_TEST_CASE(target_cr_test_limit_call) { try { - auto mi = db.get_global_properties().parameters.maintenance_interval; - generate_blocks(HARDFORK_CORE_834_TIME - mi); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + if(hf1270) + generate_blocks(HARDFORK_CORE_1270_TIME); + else + generate_blocks(HARDFORK_CORE_834_TIME); set_expiration( db, trx ); @@ -1284,6 +1293,8 @@ BOOST_AUTO_TEST_CASE(target_cr_test_limit_call) // even though call2 has a higher CR, since call's TCR is less than call2's TCR, so we expect call will cover less when called BOOST_CHECK_LT( call_to_cover.value, call2_to_cover.value ); + print_market(asset_id_type(1)(db).symbol, asset_id_type()(db).symbol); + // Create a big sell order slightly below the call price, will be matched with several orders BOOST_CHECK( !create_sell_order(seller, bitusd.amount(700*4), core.amount(5900*4) ) ); @@ -1293,6 +1304,9 @@ BOOST_AUTO_TEST_CASE(target_cr_test_limit_call) BOOST_CHECK_EQUAL( 10, get_balance(buyer3, bitusd) ); BOOST_CHECK_EQUAL( init_balance - 111, get_balance(buyer3, core) ); + print_market(asset_id_type(1)(db).symbol, asset_id_type()(db).symbol); + return; + // then it will match with call, at mssp: 1/11 = 1000/11000 const call_order_object* tmp_call = db.find( call_id ); BOOST_CHECK( tmp_call != nullptr ); @@ -1373,9 +1387,11 @@ BOOST_AUTO_TEST_CASE(target_cr_test_limit_call) */ BOOST_AUTO_TEST_CASE(target_cr_test_call_limit) { try { - auto mi = db.get_global_properties().parameters.maintenance_interval; - generate_blocks(HARDFORK_CORE_834_TIME - mi); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + if(hf1270) + generate_blocks(HARDFORK_CORE_1270_TIME); + else + generate_blocks(HARDFORK_CORE_834_TIME); set_expiration( db, trx ); @@ -1817,4 +1833,40 @@ BOOST_AUTO_TEST_CASE(mcr_bug_cross1270) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(hardfork_core_338_test_after_hf1270) +{ try { + hf1270 = true; + INVOKE(hardfork_core_338_test); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(hardfork_core_453_test_after_hf1270) +{ try { + hf1270 = true; + INVOKE(hardfork_core_453_test); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(hardfork_core_625_big_limit_order_test_after_hf1270) +{ try { + hf1270 = true; + INVOKE(hardfork_core_625_big_limit_order_test); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(target_cr_test_limit_call_after_hf1270) +{ try { + hf1270 = true; + INVOKE(target_cr_test_limit_call); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(target_cr_test_call_limit_after_hf1270) +{ try { + hf1270 = true; + INVOKE(target_cr_test_call_limit); + +} FC_LOG_AND_RETHROW() } + + BOOST_AUTO_TEST_SUITE_END() From 9f063640d18473a29898410b8c300ff836557bfc Mon Sep 17 00:00:00 2001 From: Alfredo Date: Tue, 25 Dec 2018 15:11:22 -0300 Subject: [PATCH 089/161] print call orders instead of market in target_cr_test_limit_call --- tests/tests/market_tests.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/tests/market_tests.cpp b/tests/tests/market_tests.cpp index fefcb895eb..4c5e2e0c06 100644 --- a/tests/tests/market_tests.cpp +++ b/tests/tests/market_tests.cpp @@ -1293,7 +1293,8 @@ BOOST_AUTO_TEST_CASE(target_cr_test_limit_call) // even though call2 has a higher CR, since call's TCR is less than call2's TCR, so we expect call will cover less when called BOOST_CHECK_LT( call_to_cover.value, call2_to_cover.value ); - print_market(asset_id_type(1)(db).symbol, asset_id_type()(db).symbol); + //print_market(asset_id_type(1)(db).symbol, asset_id_type()(db).symbol); + print_call_orders(); // Create a big sell order slightly below the call price, will be matched with several orders BOOST_CHECK( !create_sell_order(seller, bitusd.amount(700*4), core.amount(5900*4) ) ); @@ -1304,7 +1305,8 @@ BOOST_AUTO_TEST_CASE(target_cr_test_limit_call) BOOST_CHECK_EQUAL( 10, get_balance(buyer3, bitusd) ); BOOST_CHECK_EQUAL( init_balance - 111, get_balance(buyer3, core) ); - print_market(asset_id_type(1)(db).symbol, asset_id_type()(db).symbol); + //print_market(asset_id_type(1)(db).symbol, asset_id_type()(db).symbol); + print_call_orders(); return; // then it will match with call, at mssp: 1/11 = 1000/11000 From 8c0152c4be1b5d1fac957447d0515aee44870aa5 Mon Sep 17 00:00:00 2001 From: abitmore Date: Tue, 25 Dec 2018 13:34:31 -0500 Subject: [PATCH 090/161] Fix target CR calculation --- libraries/chain/market_object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/market_object.cpp b/libraries/chain/market_object.cpp index ab4726bcbd..0edfcc363c 100644 --- a/libraries/chain/market_object.cpp +++ b/libraries/chain/market_object.cpp @@ -91,7 +91,7 @@ share_type call_order_object::get_max_debt_to_cover( price match_price, uint16_t tcr = std::max( *target_collateral_ratio, maintenance_collateral_ratio ); // use mcr if target cr is too small price target_collateralization = ( after_core_hardfork_1270 ? - ~feed_price * ratio_type( tcr, GRAPHENE_COLLATERAL_RATIO_DENOM ) : + feed_price * ratio_type( tcr, GRAPHENE_COLLATERAL_RATIO_DENOM ) : price() ); // be defensive here, make sure match_price is in collateral / debt format From 3aec53d9266c33e265e8aabad7348d59d70e0308 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Tue, 25 Dec 2018 20:55:11 -0300 Subject: [PATCH 091/161] remove prints after fix --- tests/tests/market_tests.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/tests/market_tests.cpp b/tests/tests/market_tests.cpp index 4c5e2e0c06..b08257fe24 100644 --- a/tests/tests/market_tests.cpp +++ b/tests/tests/market_tests.cpp @@ -1292,10 +1292,7 @@ BOOST_AUTO_TEST_CASE(target_cr_test_limit_call) BOOST_CHECK_LT( call2_to_cover.value, call2_id(db).debt.value ); // even though call2 has a higher CR, since call's TCR is less than call2's TCR, so we expect call will cover less when called BOOST_CHECK_LT( call_to_cover.value, call2_to_cover.value ); - - //print_market(asset_id_type(1)(db).symbol, asset_id_type()(db).symbol); - print_call_orders(); - + // Create a big sell order slightly below the call price, will be matched with several orders BOOST_CHECK( !create_sell_order(seller, bitusd.amount(700*4), core.amount(5900*4) ) ); @@ -1305,10 +1302,6 @@ BOOST_AUTO_TEST_CASE(target_cr_test_limit_call) BOOST_CHECK_EQUAL( 10, get_balance(buyer3, bitusd) ); BOOST_CHECK_EQUAL( init_balance - 111, get_balance(buyer3, core) ); - //print_market(asset_id_type(1)(db).symbol, asset_id_type()(db).symbol); - print_call_orders(); - return; - // then it will match with call, at mssp: 1/11 = 1000/11000 const call_order_object* tmp_call = db.find( call_id ); BOOST_CHECK( tmp_call != nullptr ); From 46e2943698effedb346f5729519119f3eb04ff1a Mon Sep 17 00:00:00 2001 From: Alfredo Date: Thu, 27 Dec 2018 13:19:23 -0300 Subject: [PATCH 092/161] add hf1270 swan tests --- tests/tests/swan_tests.cpp | 94 +++++++++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 7 deletions(-) diff --git a/tests/tests/swan_tests.cpp b/tests/tests/swan_tests.cpp index f0d7ce9aab..040060c915 100644 --- a/tests/tests/swan_tests.cpp +++ b/tests/tests/swan_tests.cpp @@ -114,6 +114,10 @@ struct swan_fixture : database_fixture { generate_blocks( HARDFORK_CORE_216_TIME ); generate_block(); } + void wait_for_hf_core_1270() { + generate_blocks( HARDFORK_CORE_1270_TIME ); + generate_block(); + } void wait_for_maintenance() { generate_blocks( db.get_dynamic_global_properties().next_maintenance_time ); @@ -141,6 +145,9 @@ BOOST_FIXTURE_TEST_SUITE( swan_tests, swan_fixture ) */ BOOST_AUTO_TEST_CASE( black_swan ) { try { + if(hf1270) + wait_for_hf_core_1270(); + init_standard_swan(); force_settle( borrower(), swan().amount(100) ); @@ -167,6 +174,11 @@ BOOST_AUTO_TEST_CASE( black_swan ) */ BOOST_AUTO_TEST_CASE( black_swan_issue_346 ) { try { + if(hf1270) { + wait_for_hf_core_1270(); + set_expiration(db, trx); + } + ACTORS((buyer)(seller)(borrower)(borrower2)(settler)(feeder)); const asset_object& core = asset_id_type()(db); @@ -246,11 +258,11 @@ BOOST_AUTO_TEST_CASE( black_swan_issue_346 ) force_settle( settler, bitusd.amount(100) ); // wait for forced settlement to execute - // this would throw on Sep.18 testnet, see #346 + // this would throw on Sep.18 testnet, see #346 (https://github.com/cryptonomex/graphene/issues/346) wait_for_settlement(); } - // issue 350 + // issue 350 (https://github.com/cryptonomex/graphene/issues/350) { // ok, new asset const asset_object& bitusd = setup_asset(); @@ -265,6 +277,8 @@ BOOST_AUTO_TEST_CASE( black_swan_issue_346 ) // We attempt to match against $0.019 order and black swan, // and this is intended behavior. See discussion in ticket. // + print_market(bitusd.symbol, core.symbol); + BOOST_CHECK( bitusd.bitasset_data(db).has_settlement() ); BOOST_CHECK( db.find_object( oid_019 ) != nullptr ); BOOST_CHECK( db.find_object( oid_020 ) == nullptr ); @@ -282,7 +296,10 @@ BOOST_AUTO_TEST_CASE( revive_recovered ) { try { init_standard_swan( 700 ); - wait_for_hf_core_216(); + if(hf1270) + wait_for_hf_core_1270(); + else + wait_for_hf_core_216(); // revive after price recovers set_feed( 700, 800 ); @@ -304,7 +321,10 @@ BOOST_AUTO_TEST_CASE( recollateralize ) // no hardfork yet GRAPHENE_REQUIRE_THROW( bid_collateral( borrower2(), back().amount(1000), swan().amount(100) ), fc::exception ); - wait_for_hf_core_216(); + if(hf1270) + wait_for_hf_core_1270(); + else + wait_for_hf_core_216(); int64_t b2_balance = get_balance( borrower2(), back() ); bid_collateral( borrower2(), back().amount(1000), swan().amount(100) ); @@ -396,7 +416,10 @@ BOOST_AUTO_TEST_CASE( revive_empty_recovered ) { try { limit_order_id_type oid = init_standard_swan( 1000 ); - wait_for_hf_core_216(); + if(hf1270) + wait_for_hf_core_1270(); + else + wait_for_hf_core_216(); set_expiration( db, trx ); cancel_limit_order( oid(db) ); @@ -423,7 +446,10 @@ BOOST_AUTO_TEST_CASE( revive_empty_recovered ) */ BOOST_AUTO_TEST_CASE( revive_empty ) { try { - wait_for_hf_core_216(); + if(hf1270) + wait_for_hf_core_1270(); + else + wait_for_hf_core_216(); limit_order_id_type oid = init_standard_swan( 1000 ); @@ -447,7 +473,10 @@ BOOST_AUTO_TEST_CASE( revive_empty ) */ BOOST_AUTO_TEST_CASE( revive_empty_with_bid ) { try { - wait_for_hf_core_216(); + if(hf1270) + wait_for_hf_core_1270(); + else + wait_for_hf_core_216(); standard_users(); standard_asset(); @@ -491,4 +520,55 @@ BOOST_AUTO_TEST_CASE( revive_empty_with_bid ) } } +BOOST_AUTO_TEST_CASE(black_swan_after_hf1270) +{ try { + hf1270 = true; + INVOKE(black_swan); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(black_swan_issue_346_hf1270) +{ try { + hf1270 = true; + INVOKE(black_swan_issue_346); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(revive_recovered_hf1270) +{ try { + hf1270 = true; + INVOKE(revive_recovered); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(recollateralize_hf1270) +{ try { + hf1270 = true; + INVOKE(recollateralize); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(revive_empty_recovered_hf1270) +{ try { + hf1270 = true; + INVOKE(revive_empty_recovered); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(revive_empty_hf1270) +{ try { + hf1270 = true; + INVOKE(revive_empty); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE(revive_empty_with_bid_hf1270) +{ try { + hf1270 = true; + INVOKE(revive_empty_with_bid); + +} FC_LOG_AND_RETHROW() } + + + BOOST_AUTO_TEST_SUITE_END() From 0cb12741c61321005ea740f3e3e7140edb7f62f7 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 27 Dec 2018 15:55:16 -0500 Subject: [PATCH 093/161] check max_supply before borrowing MPAs --- libraries/chain/market_evaluator.cpp | 4 ++ tests/common/database_fixture.cpp | 2 +- tests/tests/operation_tests.cpp | 68 +++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index 62dfc7c7b4..544aef320b 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -167,6 +167,10 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.", ("sym", _debt_asset->symbol) ); + const auto& dyn_data = _debt_asset->dynamic_data(d); + FC_ASSERT( dyn_data.current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply, + "Borrowing this quantity would exceed MAX_SUPPLY" ); + _bitasset_data = &_debt_asset->bitasset_data(d); /// if there is a settlement for this asset, then no further margin positions may be taken and diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 874e2f8241..b976e1c2af 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -451,7 +451,7 @@ const asset_object& database_fixture::create_bitasset( creator.issuer = issuer; creator.fee = asset(); creator.symbol = name; - creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY / 2; creator.precision = precision; creator.common_options.market_fee_percent = market_fee_percent; if( issuer == GRAPHENE_WITNESS_ACCOUNT ) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 398eb42b11..fd95424b17 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -592,7 +592,6 @@ BOOST_AUTO_TEST_CASE( call_order_update_validation_test ) op.extensions.value.target_collateral_ratio = 65535; op.validate(); // still valid - } // Tests that target_cr option can't be set before hard fork core-834 @@ -2004,6 +2003,73 @@ BOOST_AUTO_TEST_CASE( reserve_asset_test ) } } +BOOST_AUTO_TEST_CASE( call_order_update_evaluator_test ) +{ + try + { + ACTORS( (alice) (bob) ); + transfer(committee_account, alice_id, asset(1000000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + + const auto& bitusd = create_bitasset( "USDBIT", alice_id ); + const auto& core = asset_id_type()(db); + + BOOST_TEST_MESSAGE( "Setting price feed to $100000 / 1" ); + update_feed_producers( bitusd, {alice_id} ); + price_feed current_feed; + current_feed.settlement_price = bitusd.amount( 100000 ) / core.amount(1); + publish_feed( bitusd, alice, current_feed ); + + { + BOOST_TEST_MESSAGE( "Attempting a call_order_update that exceeds max_supply" ); + call_order_update_operation op; + op.funding_account = alice_id; + op.delta_collateral = asset( 1000000 * GRAPHENE_BLOCKCHAIN_PRECISION ); + op.delta_debt = asset( bitusd.options.max_supply + 1, bitusd.id ); + transaction tx; + tx.operations.push_back( op ); + set_expiration( db, tx ); + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ), fc::exception ); + } + + { + BOOST_TEST_MESSAGE( "Creating 2 bitusd and transferring to bob (increases current supply)" ); + call_order_update_operation op; + op.funding_account = alice_id; + op.delta_collateral = asset( 100 * GRAPHENE_BLOCKCHAIN_PRECISION ); + op.delta_debt = asset( 2, bitusd.id ); + transaction tx; + tx.operations.push_back( op ); + set_expiration( db, tx ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); + transfer( alice, bob, asset( 2, bitusd.id ) ); + } + + { + BOOST_TEST_MESSAGE( "Again attempting a call_order_update_operation that is max_supply - 1 (should throw)" ); + call_order_update_operation op; + op.funding_account = alice_id; + op.delta_collateral = asset( 100000 * GRAPHENE_BLOCKCHAIN_PRECISION ); + op.delta_debt = asset( bitusd.options.max_supply - 1, bitusd.id ); + transaction tx; + tx.operations.push_back( op ); + set_expiration( db, tx ); + GRAPHENE_REQUIRE_THROW(PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ), fc::exception); + } + + { + BOOST_TEST_MESSAGE( "Again attempting a call_order_update_operation that equals max_supply (should work)" ); + call_order_update_operation op; + op.funding_account = alice_id; + op.delta_collateral = asset( 100000 * GRAPHENE_BLOCKCHAIN_PRECISION ); + op.delta_debt = asset( bitusd.options.max_supply - 2, bitusd.id ); + transaction tx; + tx.operations.push_back( op ); + set_expiration( db, tx ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); + } + } FC_LOG_AND_RETHROW() +} + /** * This test demonstrates how using the call_order_update_operation to * trigger a margin call is legal if there is a matching order. From e00383305331a33bb986014295907cbce1a83860 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 27 Dec 2018 16:01:15 -0500 Subject: [PATCH 094/161] Remove unnecessary variable --- libraries/chain/market_evaluator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index 544aef320b..d8999455f5 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -167,8 +167,7 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.", ("sym", _debt_asset->symbol) ); - const auto& dyn_data = _debt_asset->dynamic_data(d); - FC_ASSERT( dyn_data.current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply, + FC_ASSERT( _debt_asset->dynamic_data(d).current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply, "Borrowing this quantity would exceed MAX_SUPPLY" ); _bitasset_data = &_debt_asset->bitasset_data(d); From d58e523b4e73ea502d9fb93a280ed8cf13ce9e8d Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 31 Dec 2018 12:40:53 -0500 Subject: [PATCH 095/161] Add hardfork protection --- libraries/chain/hardfork.d/CORE_1465.hf | 4 ++++ libraries/chain/market_evaluator.cpp | 9 ++++++--- tests/tests/operation_tests.cpp | 4 ++++ 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 libraries/chain/hardfork.d/CORE_1465.hf diff --git a/libraries/chain/hardfork.d/CORE_1465.hf b/libraries/chain/hardfork.d/CORE_1465.hf new file mode 100644 index 0000000000..bdedbf8052 --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_1465.hf @@ -0,0 +1,4 @@ +// bitshares-core issue #1465 check max_supply before processing call_order_update +#ifndef HARDFORK_CORE_1465_TIME +#define HARDFORK_CORE_1465_TIME (fc::time_point_sec( 1600000000 )) // 2020-09-13 12:26:40 +#endif diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index d8999455f5..d96a1d1668 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -157,8 +157,10 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope { try { database& d = db(); + auto next_maintenance_time = d.get_dynamic_global_properties().next_maintenance_time; + // TODO: remove this check and the assertion after hf_834 - if( d.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_834_TIME ) + if( next_maintenance_time <= HARDFORK_CORE_834_TIME ) FC_ASSERT( !o.extensions.value.target_collateral_ratio.valid(), "Can not set target_collateral_ratio in call_order_update_operation before hardfork 834." ); @@ -167,8 +169,9 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.", ("sym", _debt_asset->symbol) ); - FC_ASSERT( _debt_asset->dynamic_data(d).current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply, - "Borrowing this quantity would exceed MAX_SUPPLY" ); + FC_ASSERT( next_maintenance_time <= HARDFORK_CORE_1465_TIME + || _debt_asset->dynamic_data(d).current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply, + "Borrowing this quantity would exceed MAX_SUPPLY" ); _bitasset_data = &_debt_asset->bitasset_data(d); diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index fd95424b17..e8206df1b6 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -2010,6 +2010,10 @@ BOOST_AUTO_TEST_CASE( call_order_update_evaluator_test ) ACTORS( (alice) (bob) ); transfer(committee_account, alice_id, asset(1000000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + // advance past hardfork + generate_blocks( HARDFORK_CORE_1465_TIME ); + set_expiration( db, trx ); + const auto& bitusd = create_bitasset( "USDBIT", alice_id ); const auto& core = asset_id_type()(db); From 52b6c7ea8385be36e625a075dc1e2db857151bf5 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 3 Jan 2019 14:11:24 -0500 Subject: [PATCH 096/161] Add hard-fork maintenance logic. Test needed. --- libraries/chain/db_maint.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 4ee7cb2825..5090b50b14 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -919,6 +919,30 @@ void database::process_bitassets() } } +/**** + * @brief a one-time data process to correct max_supply + */ +void process_hf_1465( database& db ) +{ + const auto head_time = db.head_block_time(); + const auto head_num = db.head_block_num(); + wlog( "Processing hard fork core-1465 at block ${n}", ("n",head_num) ); + // for each market issued asset + const auto& asset_idx = db.get_index_type().indices().get(); + for( auto asset_itr = asset_idx.lower_bound(true); asset_itr != asset_idx.end(); ++asset_itr ) + { + const auto& current_asset = *asset_itr; + graphene::chain::share_type current_supply = current_asset.dynamic_data(db).current_supply; + graphene::chain::share_type max_supply = current_asset.options.max_supply; + if (current_supply > max_supply && max_supply != GRAPHENE_MAX_SHARE_SUPPLY) + { + db.modify( current_asset, [current_supply](asset_object& obj) { + obj.options.max_supply = graphene::chain::share_type(std::min(current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY)); + }); + } + } +} + /****** * @brief one-time data process for hard fork core-868-890 * @@ -1225,6 +1249,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g && !to_update_and_match_call_orders ) process_hf_935( *this ); + // make sure current_supply is less than or equal to max_supply + if ( dgpo.next_maintenance_time <= HARDFORK_CORE_1465_TIME && next_maintenance_time > HARDFORK_CORE_1465_TIME ) + process_hf_1465(*this); + modify(dgpo, [next_maintenance_time](dynamic_global_property_object& d) { d.next_maintenance_time = next_maintenance_time; d.accounts_registered_this_interval = 0; From bc45e8b5d8ddd6e29f7999365c95c6f578eb4fe8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 3 Jan 2019 14:32:25 -0500 Subject: [PATCH 097/161] Included pre-hardfork test --- tests/tests/operation_tests.cpp | 47 ++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index e8206df1b6..0d00dcbc35 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -2008,20 +2008,53 @@ BOOST_AUTO_TEST_CASE( call_order_update_evaluator_test ) try { ACTORS( (alice) (bob) ); - transfer(committee_account, alice_id, asset(1000000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + transfer(committee_account, alice_id, asset(10000000 * GRAPHENE_BLOCKCHAIN_PRECISION)); + + const auto& core = asset_id_type()(db); + + // attempt to increase current supply beyond max_supply + const auto& bitjmj = create_bitasset( "JMJBIT", alice_id ); + share_type original_max_supply = bitjmj.options.max_supply; + + { + BOOST_TEST_MESSAGE( "Setting price feed to $100000 / 1" ); + update_feed_producers( bitjmj, {alice_id} ); + price_feed current_feed; + current_feed.settlement_price = bitjmj.amount( 100000 ) / core.amount(1); + publish_feed( bitjmj, alice, current_feed ); + } + + { + BOOST_TEST_MESSAGE( "Attempting a call_order_update that exceeds max_supply" ); + call_order_update_operation op; + op.funding_account = alice_id; + op.delta_collateral = asset( 1000000 * GRAPHENE_BLOCKCHAIN_PRECISION ); + op.delta_debt = asset( bitjmj.options.max_supply + 1, bitjmj.id ); + transaction tx; + tx.operations.push_back( op ); + set_expiration( db, tx ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); + generate_block(); + } // advance past hardfork generate_blocks( HARDFORK_CORE_1465_TIME ); set_expiration( db, trx ); + // bitjmj should have its problem corrected + auto newbitjmj = bitjmj.get_id()(db); + BOOST_REQUIRE_GT(newbitjmj.options.max_supply.value, original_max_supply.value); + + // now try with an asset after the hardfork const auto& bitusd = create_bitasset( "USDBIT", alice_id ); - const auto& core = asset_id_type()(db); - BOOST_TEST_MESSAGE( "Setting price feed to $100000 / 1" ); - update_feed_producers( bitusd, {alice_id} ); - price_feed current_feed; - current_feed.settlement_price = bitusd.amount( 100000 ) / core.amount(1); - publish_feed( bitusd, alice, current_feed ); + { + BOOST_TEST_MESSAGE( "Setting price feed to $100000 / 1" ); + update_feed_producers( bitusd, {alice_id} ); + price_feed current_feed; + current_feed.settlement_price = bitusd.amount( 100000 ) / core.amount(1); + publish_feed( bitusd, alice, current_feed ); + } { BOOST_TEST_MESSAGE( "Attempting a call_order_update that exceeds max_supply" ); From 5928010ff503e98cc6356334fce00b84b99fb4a8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 7 Jan 2019 15:45:52 -0500 Subject: [PATCH 098/161] Added log message at hardfork, fixed test --- libraries/chain/db_maint.cpp | 4 ++++ tests/tests/operation_tests.cpp | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 5090b50b14..d0b6dc6426 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -936,6 +936,10 @@ void process_hf_1465( database& db ) graphene::chain::share_type max_supply = current_asset.options.max_supply; if (current_supply > max_supply && max_supply != GRAPHENE_MAX_SHARE_SUPPLY) { + wlog( "Adjusting max_supply of ${asset} because current_supply (${current_supply}) is greater than ${old}.", + ("asset", current_asset.symbol) + ("current_supply", current_supply.value) + ("old", max_supply)); db.modify( current_asset, [current_supply](asset_object& obj) { obj.options.max_supply = graphene::chain::share_type(std::min(current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY)); }); diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 0d00dcbc35..fe5481cf15 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -2014,6 +2014,7 @@ BOOST_AUTO_TEST_CASE( call_order_update_evaluator_test ) // attempt to increase current supply beyond max_supply const auto& bitjmj = create_bitasset( "JMJBIT", alice_id ); + auto bitjmj_id = bitjmj.get_id(); share_type original_max_supply = bitjmj.options.max_supply; { @@ -2042,7 +2043,7 @@ BOOST_AUTO_TEST_CASE( call_order_update_evaluator_test ) set_expiration( db, trx ); // bitjmj should have its problem corrected - auto newbitjmj = bitjmj.get_id()(db); + auto newbitjmj = bitjmj_id(db); BOOST_REQUIRE_GT(newbitjmj.options.max_supply.value, original_max_supply.value); // now try with an asset after the hardfork From 6c5e48f94044c00b0a99159da49aaae5bfd783f9 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 8 Jan 2019 11:09:42 -0500 Subject: [PATCH 099/161] fix old reference in test --- tests/tests/operation_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index fe5481cf15..771074267a 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -2054,7 +2054,7 @@ BOOST_AUTO_TEST_CASE( call_order_update_evaluator_test ) update_feed_producers( bitusd, {alice_id} ); price_feed current_feed; current_feed.settlement_price = bitusd.amount( 100000 ) / core.amount(1); - publish_feed( bitusd, alice, current_feed ); + publish_feed( bitusd, alice_id(db), current_feed ); } { @@ -2079,7 +2079,7 @@ BOOST_AUTO_TEST_CASE( call_order_update_evaluator_test ) tx.operations.push_back( op ); set_expiration( db, tx ); PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); - transfer( alice, bob, asset( 2, bitusd.id ) ); + transfer( alice_id(db), bob_id(db), asset( 2, bitusd.id ) ); } { From 4e2514c40305c501271d36cc322b46c0324c0469 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Wed, 16 Jan 2019 17:31:16 -0300 Subject: [PATCH 100/161] skip black_swan_issue_346 test after hf1270 --- tests/tests/swan_tests.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/tests/tests/swan_tests.cpp b/tests/tests/swan_tests.cpp index 040060c915..eb36bb67d7 100644 --- a/tests/tests/swan_tests.cpp +++ b/tests/tests/swan_tests.cpp @@ -174,10 +174,6 @@ BOOST_AUTO_TEST_CASE( black_swan ) */ BOOST_AUTO_TEST_CASE( black_swan_issue_346 ) { try { - if(hf1270) { - wait_for_hf_core_1270(); - set_expiration(db, trx); - } ACTORS((buyer)(seller)(borrower)(borrower2)(settler)(feeder)); @@ -277,8 +273,6 @@ BOOST_AUTO_TEST_CASE( black_swan_issue_346 ) // We attempt to match against $0.019 order and black swan, // and this is intended behavior. See discussion in ticket. // - print_market(bitusd.symbol, core.symbol); - BOOST_CHECK( bitusd.bitasset_data(db).has_settlement() ); BOOST_CHECK( db.find_object( oid_019 ) != nullptr ); BOOST_CHECK( db.find_object( oid_020 ) == nullptr ); @@ -527,12 +521,7 @@ BOOST_AUTO_TEST_CASE(black_swan_after_hf1270) } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE(black_swan_issue_346_hf1270) -{ try { - hf1270 = true; - INVOKE(black_swan_issue_346); - -} FC_LOG_AND_RETHROW() } +// black_swan_issue_346_hf1270 is skipped as it is already failing with HARDFORK_CORE_834_TIME BOOST_AUTO_TEST_CASE(revive_recovered_hf1270) { try { @@ -569,6 +558,4 @@ BOOST_AUTO_TEST_CASE(revive_empty_with_bid_hf1270) } FC_LOG_AND_RETHROW() } - - BOOST_AUTO_TEST_SUITE_END() From 7abf7c574ca3dc60ecec872a3d4907e216847221 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Thu, 17 Jan 2019 00:08:13 -0300 Subject: [PATCH 101/161] update bitasset tests for hf1270 --- tests/tests/bitasset_tests.cpp | 54 ++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/tests/tests/bitasset_tests.cpp b/tests/tests/bitasset_tests.cpp index 759ea8744e..017e20a94a 100644 --- a/tests/tests/bitasset_tests.cpp +++ b/tests/tests/bitasset_tests.cpp @@ -453,6 +453,10 @@ BOOST_AUTO_TEST_CASE( hf_890_test ) generate_blocks(HARDFORK_615_TIME, true, skip); // get around Graphene issue #615 feed expiration bug generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip); + auto hf_time = HARDFORK_CORE_868_890_TIME; + if(hf1270) + hf_time = HARDFORK_CORE_1270_TIME; + for( int i=0; i<2; ++i ) { int blocks = 0; @@ -460,7 +464,7 @@ BOOST_AUTO_TEST_CASE( hf_890_test ) if( i == 1 ) // go beyond hard fork { - blocks += generate_blocks(HARDFORK_CORE_868_890_TIME - mi, true, skip); + blocks += generate_blocks(hf_time - mi, true, skip); blocks += generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip); } set_expiration( db, trx ); @@ -526,7 +530,7 @@ BOOST_AUTO_TEST_CASE( hf_890_test ) ba_op.asset_to_update = usd_id; ba_op.issuer = asset_to_update.issuer; ba_op.new_options = asset_to_update.bitasset_data(db).options; - ba_op.new_options.feed_lifetime_sec = HARDFORK_CORE_868_890_TIME.sec_since_epoch() + ba_op.new_options.feed_lifetime_sec = hf_time.sec_since_epoch() - db.head_block_time().sec_since_epoch() + mi + 1800; @@ -542,7 +546,7 @@ BOOST_AUTO_TEST_CASE( hf_890_test ) BOOST_CHECK( db.find( sell_id ) ); // go beyond hard fork - blocks += generate_blocks(HARDFORK_CORE_868_890_TIME - mi, true, skip); + blocks += generate_blocks(hf_time - mi, true, skip); blocks += generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip); } @@ -924,7 +928,7 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); generate_block( skip ); - for( int i = 0; i < 6; ++i ) + for( int i = 0; i < 8; ++i ) { idump( (i) ); int blocks = 0; @@ -940,6 +944,10 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) generate_blocks( HARDFORK_CORE_935_TIME - mi, true, skip ); generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); } + else if( i == 6 ) // go beyond hard fork 1270 + { + generate_blocks( HARDFORK_CORE_1270_TIME, true, skip ); + } set_expiration( db, trx ); ACTORS( (seller)(borrower)(feedproducer)(feedproducer2)(feedproducer3) ); @@ -1050,7 +1058,7 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) ba_op.asset_to_update = usd_id; ba_op.issuer = asset_to_update.issuer; ba_op.new_options = asset_to_update.bitasset_data(db).options; - ba_op.new_options.feed_lifetime_sec = HARDFORK_CORE_935_TIME.sec_since_epoch() + ba_op.new_options.feed_lifetime_sec = HARDFORK_CORE_1270_TIME.sec_since_epoch() + mi * 3 + 86400 * 2 - db.head_block_time().sec_since_epoch(); trx.operations.push_back(ba_op); @@ -1103,22 +1111,30 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) blocks += generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip); } - // after hard fork 935, the limit order should be filled + // after hard fork 935, the limit order is filled only for the MSSR test + if( db.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_935_TIME && + db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_1270_TIME ) { // check median BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); - if( i % 2 == 0 ) // MCR test, median MCR should be 350% - BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500 ); - else // MSSR test, MSSR should be 125% - BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1250 ); - // the limit order should have been filled - // TODO FIXME this test case is failing for MCR test, - // because call_order's call_price didn't get updated after MCR changed - // BOOST_CHECK( !db.find( sell_id ) ); - if( i % 2 == 1 ) // MSSR test - BOOST_CHECK( !db.find( sell_id ) ); + if( i % 2 != 0 ) { // MSSR test, MSSR should be 125%, order filled + BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1250); + BOOST_CHECK(!db.find(sell_id)); + } + // go beyond hard fork 1270 + blocks += generate_blocks(HARDFORK_CORE_1270_TIME, true, skip); } + // after hard fork 1270 + if( db.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_1270_TIME) + { + // check median + BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); + if( i % 2 == 0 ) { // MCR test, median MCR should be 350%, order filled + BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500); + BOOST_CHECK(!db.find(sell_id)); // MCR bug fixed + } + } // undo above tx's and reset generate_block( skip ); @@ -1376,5 +1392,11 @@ BOOST_AUTO_TEST_CASE( reset_backing_asset_switching_to_witness_fed ) } } */ +BOOST_AUTO_TEST_CASE(hf_890_test_hf1270) +{ try { + hf1270 = true; + INVOKE(hf_890_test); + +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() From 60b25a04263beda7202e7ae8409bccb4608d56c3 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 18 Jan 2019 12:17:19 -0500 Subject: [PATCH 102/161] Switch uin8_t for char for clearer serialization --- libraries/chain/htlc_evaluator.cpp | 4 ++-- .../include/graphene/chain/protocol/htlc.hpp | 2 +- libraries/wallet/wallet.cpp | 4 ++-- tests/tests/htlc_tests.cpp | 20 +++++++++---------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 218558a98d..7ecc8ac98e 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -71,11 +71,11 @@ namespace graphene { class htlc_redeem_visitor { //private: - const std::vector& data; + const std::vector& data; public: typedef bool result_type; - htlc_redeem_visitor( const std::vector& preimage ) + htlc_redeem_visitor( const std::vector& preimage ) : data( preimage ) {} template diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp index ef5f2b1469..5e9ab847a8 100644 --- a/libraries/chain/include/graphene/chain/protocol/htlc.hpp +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -98,7 +98,7 @@ namespace graphene { // who is attempting to update the transaction account_id_type redeemer; // the preimage (not used if after epoch timeout) - std::vector preimage; + std::vector preimage; // for future expansion extensions_type extensions; diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 0eabbe35d1..5d12c05ed5 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1812,7 +1812,7 @@ class wallet_api_impl (preimage_hash)(preimage_size)(claim_period_seconds)(broadcast) ) } - signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) { try { @@ -3201,7 +3201,7 @@ signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string iss bool broadcast) { - return my->htlc_redeem(htlc_id, issuer, std::vector(preimage.begin(), preimage.end()), broadcast); + return my->htlc_redeem(htlc_id, issuer, std::vector(preimage.begin(), preimage.end()), broadcast); } signed_transaction wallet_api::htlc_extend ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index 98518f6484..fa5181da1f 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -56,9 +56,9 @@ using namespace graphene::chain::test; BOOST_FIXTURE_TEST_SUITE( htlc_tests, database_fixture ) -void generate_random_preimage(uint16_t key_size, std::vector& vec) +void generate_random_preimage(uint16_t key_size, std::vector& vec) { - std::independent_bits_engine rbe; + std::independent_bits_engine rbe; std::generate(begin(vec), end(vec), std::ref(rbe)); return; } @@ -69,7 +69,7 @@ void generate_random_preimage(uint16_t key_size, std::vector& vec) * @returns a vector that cointains the sha256 hash of the preimage */ template -H hash_it(std::vector preimage) +H hash_it(std::vector preimage) { return H::hash( (char*)preimage.data(), preimage.size() ); } @@ -177,7 +177,7 @@ try { advance_past_hardfork(this); uint16_t preimage_size = 256; - std::vector pre_image(256); + std::vector pre_image(256); generate_random_preimage(preimage_size, pre_image); graphene::chain::htlc_id_type alice_htlc_id; @@ -234,7 +234,7 @@ try { advance_past_hardfork(this); uint16_t preimage_size = 256; - std::vector pre_image(preimage_size); + std::vector pre_image(preimage_size); generate_random_preimage(preimage_size, pre_image); graphene::chain::htlc_id_type alice_htlc_id; @@ -314,7 +314,7 @@ try { transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); uint16_t preimage_size = 256; - std::vector pre_image(256); + std::vector pre_image(256); generate_random_preimage(preimage_size, pre_image); graphene::chain::htlc_id_type alice_htlc_id; @@ -502,7 +502,7 @@ BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); uint16_t preimage_size = 256; - std::vector pre_image(256); + std::vector pre_image(256); generate_random_preimage(preimage_size, pre_image); graphene::chain::htlc_id_type alice_htlc_id; @@ -607,15 +607,15 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) redeem_fee.fee = 2; htlc_redeem_operation redeem; // no preimage - redeem.preimage = std::vector(); + redeem.preimage = std::vector(); BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 2 ) ; // exactly 1KB std::string test(1024, 'a'); - redeem.preimage = std::vector( test.begin(), test.end() ); + redeem.preimage = std::vector( test.begin(), test.end() ); BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 4 ) ; // just 1 byte over 1KB std::string larger(1025, 'a'); - redeem.preimage = std::vector( larger.begin(), larger.end() ); + redeem.preimage = std::vector( larger.begin(), larger.end() ); BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 6 ) ; } // extend From 097799bf7d3adddbcc33b873a0473351f7e06fb3 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 18 Jan 2019 16:41:35 -0500 Subject: [PATCH 103/161] Save a ptr to asset_dynamic_data_object --- libraries/chain/db_maint.cpp | 1 - .../chain/include/graphene/chain/market_evaluator.hpp | 1 + libraries/chain/market_evaluator.cpp | 7 +++++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index d0b6dc6426..84e0b990bb 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -924,7 +924,6 @@ void database::process_bitassets() */ void process_hf_1465( database& db ) { - const auto head_time = db.head_block_time(); const auto head_num = db.head_block_num(); wlog( "Processing hard fork core-1465 at block ${n}", ("n",head_num) ); // for each market issued asset diff --git a/libraries/chain/include/graphene/chain/market_evaluator.hpp b/libraries/chain/include/graphene/chain/market_evaluator.hpp index 96a4ac07ed..af66323578 100644 --- a/libraries/chain/include/graphene/chain/market_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/market_evaluator.hpp @@ -87,6 +87,7 @@ namespace graphene { namespace chain { const account_object* _paying_account = nullptr; const call_order_object* _order = nullptr; const asset_bitasset_data_object* _bitasset_data = nullptr; + const asset_dynamic_data_object* _dynamic_data_obj = nullptr; }; class bid_collateral_evaluator : public evaluator diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index d96a1d1668..d63d1be06e 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -188,6 +188,10 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope else if( _bitasset_data->current_feed.settlement_price.is_null() ) FC_THROW_EXCEPTION(insufficient_feeds, "Cannot borrow asset with no price feed."); + _dynamic_data_obj = &_debt_asset->dynamic_asset_data_id(d); + FC_ASSERT( _dynamic_data_obj->current_supply + o.delta_debt.amount >= 0, + "This transaction would bring current supply below zero."); + // Note: there was code here checking whether the account has enough balance to increase delta collateral, // which is now removed since the check is implicitly done later by `adjust_balance()` in `do_apply()`. @@ -204,9 +208,8 @@ object_id_type call_order_update_evaluator::do_apply(const call_order_update_ope d.adjust_balance( o.funding_account, o.delta_debt ); // Deduct the debt paid from the total supply of the debt asset. - d.modify(_debt_asset->dynamic_asset_data_id(d), [&](asset_dynamic_data_object& dynamic_asset) { + d.modify(*_dynamic_data_obj, [&](asset_dynamic_data_object& dynamic_asset) { dynamic_asset.current_supply += o.delta_debt.amount; - FC_ASSERT(dynamic_asset.current_supply >= 0); }); } From 3e1e5b679a5a1fba2cbf5a9d57a2d22c9992ce08 Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Wed, 23 Jan 2019 16:41:47 +0300 Subject: [PATCH 104/161] Move hardfork timestamp into the far future Fix notes --- libraries/chain/asset_evaluator.cpp | 1 + libraries/chain/hardfork.d/CORE_1268.hf | 2 +- libraries/chain/vesting_balance_evaluator.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index aa6688f6eb..f6d0f23062 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -36,6 +36,7 @@ namespace graphene { namespace chain { namespace detail { + // TODO review and remove code below and links to it after hf_1268 void check_asset_options_hf_1268(const fc::time_point_sec& block_time, const asset_options& options) { if( block_time < HARDFORK_1268_TIME ) diff --git a/libraries/chain/hardfork.d/CORE_1268.hf b/libraries/chain/hardfork.d/CORE_1268.hf index 6872db2fb2..352463c043 100644 --- a/libraries/chain/hardfork.d/CORE_1268.hf +++ b/libraries/chain/hardfork.d/CORE_1268.hf @@ -1,4 +1,4 @@ // #1268 Distribute Asset Market Fees to Referral Program #ifndef HARDFORK_1268_TIME -#define HARDFORK_1268_TIME (fc::time_point_sec( 1530705600 )) // Wednesday, July 4, 2018 12:00:00 PM +#define HARDFORK_1268_TIME (fc::time_point_sec( 1577880000 )) // Wednesday, January 1, 2020 12:00:00 PM #endif diff --git a/libraries/chain/vesting_balance_evaluator.cpp b/libraries/chain/vesting_balance_evaluator.cpp index 6f05343c24..586045e4e6 100644 --- a/libraries/chain/vesting_balance_evaluator.cpp +++ b/libraries/chain/vesting_balance_evaluator.cpp @@ -30,6 +30,7 @@ namespace graphene { namespace chain { namespace detail { + // TODO review and remove code below and links to it after hf_1268 void check_vesting_balance_policy_hf_1268(const fc::time_point_sec& block_time, const vesting_policy_initializer& policy) { if( block_time < HARDFORK_1268_TIME ) From 1b9f6c9e14090c2bf02e49bbc1427589a384a680 Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Wed, 23 Jan 2019 16:48:07 +0300 Subject: [PATCH 105/161] Merge with latest hardfork branch --- .travis.yml | 5 +- README.md | 2 + libraries/app/api.cpp | 13 +- libraries/app/application.cpp | 55 +- libraries/app/application_impl.hxx | 4 + libraries/app/database_api.cpp | 42 +- libraries/app/include/graphene/app/api.hpp | 6 +- libraries/chain/CMakeLists.txt | 3 +- libraries/chain/account_evaluator.cpp | 12 +- libraries/chain/account_object.cpp | 56 +- .../chain/committee_member_evaluator.cpp | 4 + libraries/chain/db_balance.cpp | 19 +- libraries/chain/db_block.cpp | 108 ++- libraries/chain/db_init.cpp | 31 +- libraries/chain/db_maint.cpp | 64 +- libraries/chain/db_management.cpp | 116 ++-- libraries/chain/db_notify.cpp | 30 +- libraries/chain/db_update.cpp | 19 + libraries/chain/fork_database.cpp | 74 +- libraries/chain/hardfork.d/CORE_1268.hf | 2 +- libraries/chain/hardfork.d/CORE_1479.hf | 4 + libraries/chain/hardfork.d/core-1468.hf | 4 + libraries/chain/htlc_evaluator.cpp | 126 ++++ .../include/graphene/chain/account_object.hpp | 61 +- .../graphene/chain/budget_record_object.hpp | 90 +++ .../chain/include/graphene/chain/config.hpp | 2 +- .../chain/include/graphene/chain/database.hpp | 36 +- .../chain/include/graphene/chain/db_with.hpp | 14 +- .../include/graphene/chain/fork_database.hpp | 3 - .../include/graphene/chain/htlc_evaluator.hpp | 59 ++ .../include/graphene/chain/htlc_object.hpp | 82 +++ .../graphene/chain/proposal_evaluator.hpp | 21 + .../graphene/chain/protocol/account.hpp | 2 + .../include/graphene/chain/protocol/base.hpp | 3 + .../include/graphene/chain/protocol/block.hpp | 20 +- .../chain/protocol/chain_parameters.hpp | 25 +- .../graphene/chain/protocol/fee_schedule.hpp | 46 ++ .../include/graphene/chain/protocol/htlc.hpp | 211 ++++++ .../graphene/chain/protocol/operations.hpp | 8 +- .../graphene/chain/protocol/transaction.hpp | 57 +- .../include/graphene/chain/protocol/types.hpp | 36 +- libraries/chain/proposal_evaluator.cpp | 53 ++ libraries/chain/protocol/block.cpp | 64 +- libraries/chain/protocol/htlc.cpp | 67 ++ libraries/chain/protocol/operations.cpp | 6 + libraries/chain/protocol/transaction.cpp | 50 +- libraries/db/include/graphene/db/index.hpp | 141 +++- libraries/db/object_database.cpp | 17 +- .../include/graphene/net/core_messages.hpp | 4 +- libraries/net/node.cpp | 2 +- .../graphene/debug_witness/debug_witness.hpp | 2 +- .../delayed_node/delayed_node_plugin.cpp | 4 +- .../elasticsearch/elasticsearch_plugin.cpp | 23 +- libraries/plugins/es_objects/es_objects.cpp | 195 +----- .../graphene/es_objects/es_objects.hpp | 155 ++--- .../include/graphene/witness/witness.hpp | 2 +- .../wallet/include/graphene/wallet/wallet.hpp | 49 ++ libraries/wallet/wallet.cpp | 218 +++++- programs/build_helpers/member_enumerator.cpp | 1 + programs/js_operation_serializer/main.cpp | 1 + tests/app/main.cpp | 2 +- tests/cli/main.cpp | 199 +++++- tests/common/database_fixture.cpp | 72 +- tests/common/database_fixture.hpp | 10 + tests/tests/authority_tests.cpp | 114 +++- tests/tests/basic_tests.cpp | 11 +- tests/tests/bitasset_tests.cpp | 3 +- tests/tests/block_tests.cpp | 54 +- tests/tests/confidential_tests.cpp | 8 +- tests/tests/database_tests.cpp | 85 +++ tests/tests/fee_tests.cpp | 5 - tests/tests/htlc_tests.cpp | 639 ++++++++++++++++++ tests/tests/operation_tests.cpp | 12 +- tests/tests/operation_tests2.cpp | 51 +- tests/tests/swan_tests.cpp | 24 + tests/tests/uia_tests.cpp | 4 +- tests/tests/voting_tests.cpp | 125 ++++ 77 files changed, 3253 insertions(+), 794 deletions(-) create mode 100644 libraries/chain/hardfork.d/CORE_1479.hf create mode 100644 libraries/chain/hardfork.d/core-1468.hf create mode 100644 libraries/chain/htlc_evaluator.cpp create mode 100644 libraries/chain/include/graphene/chain/budget_record_object.hpp create mode 100644 libraries/chain/include/graphene/chain/htlc_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/htlc_object.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/htlc.hpp create mode 100644 libraries/chain/protocol/htlc.cpp create mode 100644 tests/tests/htlc_tests.cpp diff --git a/.travis.yml b/.travis.yml index 9e21993904..236fa6ef78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,8 +34,9 @@ script: - cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DBoost_USE_STATIC_LIBS=OFF -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON . - 'which build-wrapper-linux-x86-64 && build-wrapper-linux-x86-64 --out-dir bw-output make -j 2 cli_wallet witness_node chain_test cli_test || make -j 2 cli_wallet witness_node chain_test cli_test' - set -o pipefail + - '[ $((`date +%s` - `cat _start_time`)) -gt $((42 * 60)) ] && touch _empty_cache || true' - '[ -r _empty_cache ] || tests/chain_test 2>&1 | cat' - '[ -r _empty_cache ] || tests/cli_test 2>&1 | cat' - 'find libraries/[acdenptuw]*/CMakeFiles/*.dir programs/[cdgjsw]*/CMakeFiles/*.dir -type d | while read d; do gcov -o "$d" "${d/CMakeFiles*.dir//}"/*.cpp; done >/dev/null' - - '( [ -r _empty_cache -o $((`date +%s` - `cat _start_time`)) -gt $((42 * 60)) ] && echo "WARNING! Skipping sonar scanner due to time constraints!" ) || ( which sonar-scanner && sonar-scanner || true )' - - '[ ! -r _empty_cache ] || ( echo "Please restart with populated cache" && false )' + - '[ -r _empty_cache ] || ( which sonar-scanner && sonar-scanner || true )' + - '[ ! -r _empty_cache ] || ( echo "WARNING! Skipped some tests due to compile time! Please restart with populated cache." && false )' diff --git a/README.md b/README.md index a516790596..e5147f0112 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ Visit [BitShares.org](https://bitshares.org/) to learn about BitShares and join Information for developers can be found in the [Bitshares Developer Portal](https://dev.bitshares.works/). Users interested in how bitshares works can go to the [BitShares Documentation](https://how.bitshares.works/) site. +For security issues and bug bounty program please visit [Hack the DEX](https://hackthedex.io). + Getting Started --------------- Build instructions and additional documentation are available in the diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index dbe6043e50..f3cffa70ff 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -159,18 +159,18 @@ namespace graphene { namespace app { } } - void network_broadcast_api::broadcast_transaction(const signed_transaction& trx) + void network_broadcast_api::broadcast_transaction(const precomputable_transaction& trx) { - trx.validate(); + _app.chain_database()->precompute_parallel( trx ).wait(); _app.chain_database()->push_transaction(trx); if( _app.p2p_node() != nullptr ) _app.p2p_node()->broadcast_transaction(trx); } - fc::variant network_broadcast_api::broadcast_transaction_synchronous(const signed_transaction& trx) + fc::variant network_broadcast_api::broadcast_transaction_synchronous(const precomputable_transaction& trx) { fc::promise::ptr prom( new fc::promise() ); - broadcast_transaction_with_callback( [=]( const fc::variant& v ){ + broadcast_transaction_with_callback( [prom]( const fc::variant& v ){ prom->set_value(v); }, trx ); @@ -179,14 +179,15 @@ namespace graphene { namespace app { void network_broadcast_api::broadcast_block( const signed_block& b ) { + _app.chain_database()->precompute_parallel( b ).wait(); _app.chain_database()->push_block(b); if( _app.p2p_node() != nullptr ) _app.p2p_node()->broadcast( net::block_message( b )); } - void network_broadcast_api::broadcast_transaction_with_callback(confirmation_callback cb, const signed_transaction& trx) + void network_broadcast_api::broadcast_transaction_with_callback(confirmation_callback cb, const precomputable_transaction& trx) { - trx.validate(); + _app.chain_database()->precompute_parallel( trx ).wait(); _callbacks[trx.id()] = cb; _app.chain_database()->push_transaction(trx); if( _app.p2p_node() != nullptr ) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index f3b9c2f5db..cc7828034f 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -169,8 +170,8 @@ void application_impl::reset_p2p_node(const fc::path& data_dir) "seed01.liondani.com:1776", // liondani (GERMANY) "104.236.144.84:1777", // puppies (USA) "128.199.143.47:2015", // Harvey (Singapore) - "23.92.53.182:1776", // sahkan (USA) - "192.121.166.162:1776", // sahkan (UK) + "209.105.239.13:1776", // sahkan (USA) + "45.35.12.22:1776", // sahkan (USA) "51.15.61.160:1776", // lafona (France) "bts-seed1.abit-more.com:62015", // abit (China) "node.blckchnd.com:4243", // blckchnd (Germany) @@ -394,12 +395,34 @@ void application_impl::startup() _chain_db->enable_standby_votes_tracking( _options->at("enable-standby-votes-tracking").as() ); } - if( _options->count("replay-blockchain") ) + if( _options->count("replay-blockchain") || _options->count("revalidate-blockchain") ) _chain_db->wipe( _data_dir / "blockchain", false ); try { - _chain_db->open( _data_dir / "blockchain", initial_state, GRAPHENE_CURRENT_DB_VERSION ); + // these flags are used in open() only, i. e. during replay + uint32_t skip; + if( _options->count("revalidate-blockchain") ) // see also handle_block() + { + if( !loaded_checkpoints.empty() ) + wlog( "Warning - revalidate will not validate before last checkpoint" ); + if( _options->count("force-validate") ) + skip = graphene::chain::database::skip_nothing; + else + skip = graphene::chain::database::skip_transaction_signatures; + } + else // no revalidate, skip most checks + skip = graphene::chain::database::skip_witness_signature | + graphene::chain::database::skip_block_size_check | + graphene::chain::database::skip_merkle_check | + graphene::chain::database::skip_transaction_signatures | + graphene::chain::database::skip_transaction_dupe_check | + graphene::chain::database::skip_tapos_check | + graphene::chain::database::skip_witness_schedule_check; + + graphene::chain::detail::with_skip_flags( *_chain_db, skip, [this,&initial_state] () { + _chain_db->open( _data_dir / "blockchain", initial_state, GRAPHENE_CURRENT_DB_VERSION ); + }); } catch( const fc::exception& e ) { @@ -517,13 +540,17 @@ bool application_impl::handle_block(const graphene::net::block_message& blk_msg, FC_ASSERT( (latency.count()/1000) > -5000, "Rejecting block with timestamp in the future" ); try { - // TODO: in the case where this block is valid but on a fork that's too old for us to switch to, - // you can help the network code out by throwing a block_older_than_undo_history exception. - // when the net code sees that, it will stop trying to push blocks from that chain, but - // leave that peer connected so that they can get sync blocks from us - bool result = _chain_db->push_block( blk_msg.block, - (_is_block_producer | _force_validate) ? - database::skip_nothing : database::skip_transaction_signatures ); + const uint32_t skip = (_is_block_producer | _force_validate) ? + database::skip_nothing : database::skip_transaction_signatures; + bool result = valve.do_serial( [this,&blk_msg,skip] () { + _chain_db->precompute_parallel( blk_msg.block, skip ).wait(); + }, [this,&blk_msg,skip] () { + // TODO: in the case where this block is valid but on a fork that's too old for us to switch to, + // you can help the network code out by throwing a block_older_than_undo_history exception. + // when the net code sees that, it will stop trying to push blocks from that chain, but + // leave that peer connected so that they can get sync blocks from us + return _chain_db->push_block( blk_msg.block, skip ); + }); // the block was accepted, so we now know all of the transactions contained in the block if (!sync_mode) @@ -573,6 +600,7 @@ void application_impl::handle_transaction(const graphene::net::trx_message& tran trx_count = 0; } + _chain_db->precompute_parallel( transaction_message.trx ).wait(); _chain_db->push_transaction( transaction_message.trx ); } FC_CAPTURE_AND_RETHROW( (transaction_message) ) } @@ -961,9 +989,10 @@ void application::set_program_options(boost::program_options::options_descriptio "Path to create a Genesis State at. If a well-formed JSON file exists at the path, it will be parsed and any " "missing fields in a Genesis State will be added, and any unknown fields will be removed. If no file or an " "invalid file is found, it will be replaced with an example Genesis State.") - ("replay-blockchain", "Rebuild object graph by replaying all blocks") + ("replay-blockchain", "Rebuild object graph by replaying all blocks without validation") + ("revalidate-blockchain", "Rebuild object graph by replaying all blocks with full validation") ("resync-blockchain", "Delete all blocks and re-sync with network from scratch") - ("force-validate", "Force validation of all transactions") + ("force-validate", "Force validation of all transactions during normal operation") ("genesis-timestamp", bpo::value(), "Replace timestamp from genesis.json with current time plus this many seconds (experts only!)") ; diff --git a/libraries/app/application_impl.hxx b/libraries/app/application_impl.hxx index 5b0c543728..9f601bce79 100644 --- a/libraries/app/application_impl.hxx +++ b/libraries/app/application_impl.hxx @@ -1,6 +1,8 @@ #pragma once #include +#include + #include #include #include @@ -194,6 +196,8 @@ class application_impl : public net::node_delegate std::map> _available_plugins; bool _is_finished_syncing = false; + private: + fc::serial_valve valve; }; }}} // namespace graphene namespace app namespace detail diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index acb90c8af5..f067a101e8 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -547,6 +547,10 @@ vector> database_api::get_key_references( vector> database_api_impl::get_key_references( vector keys )const { + const auto& idx = _db.get_index_type(); + const auto& aidx = dynamic_cast(idx); + const auto& refs = aidx.get_secondary_index(); + vector< vector > final_result; final_result.reserve(keys.size()); @@ -566,10 +570,6 @@ vector> database_api_impl::get_key_references( vector(); - const auto& aidx = dynamic_cast&>(idx); - const auto& refs = aidx.get_secondary_index(); - auto itr = refs.account_to_key_memberships.find(key); vector result; for( auto& a : {a1,a2,a3,a4,a5} ) @@ -577,7 +577,7 @@ vector> database_api_impl::get_key_references( vectorsecond.size() ); + result.reserve( result.size() + itr->second.size() ); for( auto item : itr->second ) { result.push_back(item); @@ -585,9 +585,10 @@ vector> database_api_impl::get_key_references( vectorsecond.size() ); + result.reserve( result.size() + itr->second.size() ); for( auto item : itr->second ) result.push_back(item); } final_result.emplace_back( std::move(result) ); @@ -620,7 +621,7 @@ bool database_api_impl::is_public_key_registered(string public_key) const return false; } const auto& idx = _db.get_index_type(); - const auto& aidx = dynamic_cast&>(idx); + const auto& aidx = dynamic_cast(idx); const auto& refs = aidx.get_secondary_index(); auto itr = refs.account_to_key_memberships.find(key); bool is_known = itr != refs.account_to_key_memberships.end(); @@ -755,6 +756,10 @@ std::map database_api::get_full_accounts( const vector database_api_impl::get_full_accounts( const vector& names_or_ids, bool subscribe) { + const auto& proposal_idx = _db.get_index_type(); + const auto& pidx = dynamic_cast(proposal_idx); + const auto& proposals_by_account = pidx.get_secondary_index(); + std::map results; for (const std::string& account_name_or_id : names_or_ids) @@ -784,9 +789,6 @@ std::map database_api_impl::get_full_accounts( const acnt.cashback_balance = account->cashback_balance(_db); } // Add the account's proposals - const auto& proposal_idx = _db.get_index_type(); - const auto& pidx = dynamic_cast&>(proposal_idx); - const auto& proposals_by_account = pidx.get_secondary_index(); auto required_approvals_itr = proposals_by_account._account_to_proposals.find( account->id ); if( required_approvals_itr != proposals_by_account._account_to_proposals.end() ) { @@ -797,11 +799,9 @@ std::map database_api_impl::get_full_accounts( const // Add the account's balances - auto balance_range = _db.get_index_type().indices().get().equal_range(boost::make_tuple(account->id)); - std::for_each(balance_range.first, balance_range.second, - [&acnt](const account_balance_object& balance) { - acnt.balances.emplace_back(balance); - }); + const auto& balances = _db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >().get_account_balances( account->id ); + for( const auto balance : balances ) + acnt.balances.emplace_back( *balance.second ); // Add the account's vesting balances auto vesting_range = _db.get_index_type().indices().get().equal_range(account->id); @@ -869,7 +869,7 @@ vector database_api::get_account_references( const std::string vector database_api_impl::get_account_references( const std::string account_id_or_name )const { const auto& idx = _db.get_index_type(); - const auto& aidx = dynamic_cast&>(idx); + const auto& aidx = dynamic_cast(idx); const auto& refs = aidx.get_secondary_index(); const account_id_type account_id = get_account_from_string(account_id_or_name)->id; auto itr = refs.account_to_account_memberships.find(account_id); @@ -953,10 +953,10 @@ vector database_api_impl::get_account_balances(const std::string& account if (assets.empty()) { // if the caller passes in an empty list of assets, return balances for all assets the account owns - const account_balance_index& balance_index = _db.get_index_type(); - auto range = balance_index.indices().get().equal_range(boost::make_tuple(acnt)); - for (const account_balance_object& balance : boost::make_iterator_range(range.first, range.second)) - result.push_back(asset(balance.get_balance())); + const auto& balance_index = _db.get_index_type< primary_index< account_balance_index > >(); + const auto& balances = balance_index.get_secondary_index< balances_by_account_index >().get_account_balances( acnt ); + for( const auto balance : balances ) + result.push_back( balance.second->get_balance() ); } else { @@ -2448,7 +2448,7 @@ void database_api_impl::on_applied_block() } if( market.valid() && _market_subscriptions.count(*market) ) // FIXME this may cause fill_order_operation be pushed before order creation - subscribed_markets_ops[*market].emplace_back( std::move( std::make_pair( op.op, op.result ) ) ); + subscribed_markets_ops[*market].emplace_back(std::make_pair(op.op, op.result)); } /// we need to ensure the database_api is not deleted for the life of the async operation auto capture_this = shared_from_this(); diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index aea35be08b..af34bbaf73 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -266,19 +266,19 @@ namespace graphene { namespace app { * The transaction will be checked for validity in the local database prior to broadcasting. If it fails to * apply locally, an error will be thrown and the transaction will not be broadcast. */ - void broadcast_transaction(const signed_transaction& trx); + void broadcast_transaction(const precomputable_transaction& trx); /** this version of broadcast transaction registers a callback method that will be called when the transaction is * included into a block. The callback method includes the transaction id, block number, and transaction number in the * block. */ - void broadcast_transaction_with_callback( confirmation_callback cb, const signed_transaction& trx); + void broadcast_transaction_with_callback( confirmation_callback cb, const precomputable_transaction& trx); /** this version of broadcast transaction registers a callback method that will be called when the transaction is * included into a block. The callback method includes the transaction id, block number, and transaction number in the * block. */ - fc::variant broadcast_transaction_synchronous(const signed_transaction& trx); + fc::variant broadcast_transaction_synchronous(const precomputable_transaction& trx); /** * @brief Broadcast a signed block to the network diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 6fe3f47883..71d2fd2d2e 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -58,7 +58,7 @@ add_library( graphene_chain protocol/fee_schedule.cpp protocol/confidential.cpp protocol/vote.cpp - + protocol/htlc.cpp genesis_state.cpp get_config.cpp @@ -77,6 +77,7 @@ add_library( graphene_chain vesting_balance_evaluator.cpp withdraw_permission_evaluator.cpp worker_evaluator.cpp + htlc_evaluator.cpp confidential_evaluator.cpp special_authority.cpp buyback.cpp diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index d550007c76..086953dcfb 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -119,7 +119,6 @@ void verify_account_votes( const database& db, const account_options& options ) } } - void_result account_create_evaluator::do_evaluate( const account_create_operation& op ) { try { database& d = db(); @@ -318,11 +317,16 @@ void_result account_update_evaluator::do_apply( const account_update_operation& bool sa_before = acnt->has_special_authority(); // update account statistics - if( o.new_options.valid() && o.new_options->is_voting() != acnt->options.is_voting() ) + if( o.new_options.valid() ) { - d.modify( acnt->statistics( d ), []( account_statistics_object& aso ) + d.modify( acnt->statistics( d ), [&]( account_statistics_object& aso ) { - aso.is_voting = !aso.is_voting; + if(o.new_options->is_voting() != acnt->options.is_voting()) + aso.is_voting = !aso.is_voting; + + if((o.new_options->votes != acnt->options.votes || + o.new_options->voting_account != acnt->options.voting_account)) + aso.last_vote_time = d.head_block_time(); } ); } diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 952a4c7c2c..7acaf10b21 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -121,9 +121,9 @@ set account_member_index::get_account_members(const account_obj result.insert(auth.first); return result; } -set account_member_index::get_key_members(const account_object& a)const +set account_member_index::get_key_members(const account_object& a)const { - set result; + set result; for( auto auth : a.owner.key_auths ) result.insert(auth.first); for( auto auth : a.active.key_auths ) @@ -215,7 +215,7 @@ void account_member_index::object_modified(const object& after) { - set after_key_members = get_key_members(a); + set after_key_members = get_key_members(a); vector removed; removed.reserve(before_key_members.size()); std::set_difference(before_key_members.begin(), before_key_members.end(), @@ -269,4 +269,54 @@ void account_referrer_index::object_modified( const object& after ) { } +const uint8_t balances_by_account_index::bits = 20; +const uint64_t balances_by_account_index::mask = (1ULL << balances_by_account_index::bits) - 1; + +void balances_by_account_index::object_inserted( const object& obj ) +{ + const auto& abo = dynamic_cast< const account_balance_object& >( obj ); + while( balances.size() < (abo.owner.instance.value >> bits) + 1 ) + { + balances.reserve( (abo.owner.instance.value >> bits) + 1 ); + balances.resize( balances.size() + 1 ); + balances.back().resize( 1ULL << bits ); + } + balances[abo.owner.instance.value >> bits][abo.owner.instance.value & mask][abo.asset_type] = &abo; +} + +void balances_by_account_index::object_removed( const object& obj ) +{ + const auto& abo = dynamic_cast< const account_balance_object& >( obj ); + if( balances.size() < (abo.owner.instance.value >> bits) + 1 ) return; + balances[abo.owner.instance.value >> bits][abo.owner.instance.value & mask].erase( abo.asset_type ); +} + +void balances_by_account_index::about_to_modify( const object& before ) +{ + ids_being_modified.emplace( before.id ); +} + +void balances_by_account_index::object_modified( const object& after ) +{ + FC_ASSERT( ids_being_modified.top() == after.id, "Modification of ID is not supported!"); + ids_being_modified.pop(); +} + +const map< asset_id_type, const account_balance_object* >& balances_by_account_index::get_account_balances( const account_id_type& acct )const +{ + static const map< asset_id_type, const account_balance_object* > _empty; + + if( balances.size() < (acct.instance.value >> bits) + 1 ) return _empty; + return balances[acct.instance.value >> bits][acct.instance.value & mask]; +} + +const account_balance_object* balances_by_account_index::get_account_balance( const account_id_type& acct, const asset_id_type& asset )const +{ + if( balances.size() < (acct.instance.value >> bits) + 1 ) return nullptr; + const auto& mine = balances[acct.instance.value >> bits][acct.instance.value & mask]; + const auto itr = mine.find( asset ); + if( mine.end() == itr ) return nullptr; + return itr->second; +} + } } // graphene::chain diff --git a/libraries/chain/committee_member_evaluator.cpp b/libraries/chain/committee_member_evaluator.cpp index 4e7eb827e5..9a258e6543 100644 --- a/libraries/chain/committee_member_evaluator.cpp +++ b/libraries/chain/committee_member_evaluator.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,9 @@ void_result committee_member_update_global_parameters_evaluator::do_evaluate(con { try { FC_ASSERT(trx_state->_is_proposed_trx); + FC_ASSERT( db().head_block_time() > HARDFORK_CORE_1468_TIME || !o.new_parameters.extensions.value.updatable_htlc_options.valid(), + "Unable to set HTLC parameters until hardfork." ); + return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index 9aa8bbaf3b..2a48679e3e 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -34,11 +34,11 @@ namespace graphene { namespace chain { asset database::get_balance(account_id_type owner, asset_id_type asset_id) const { - auto& index = get_index_type().indices().get(); - auto itr = index.find(boost::make_tuple(owner, asset_id)); - if( itr == index.end() ) + auto& index = get_index_type< primary_index< account_balance_index > >().get_secondary_index(); + auto abo = index.get_account_balance( owner, asset_id ); + if( !abo ) return asset(0, asset_id); - return itr->get_balance(); + return abo->get_balance(); } asset database::get_balance(const account_object& owner, const asset_object& asset_obj) const @@ -56,9 +56,9 @@ void database::adjust_balance(account_id_type account, asset delta ) if( delta.amount == 0 ) return; - auto& index = get_index_type().indices().get(); - auto itr = index.find(boost::make_tuple(account, delta.asset_id)); - if(itr == index.end()) + auto& index = get_index_type< primary_index< account_balance_index > >().get_secondary_index(); + auto abo = index.get_account_balance( account, delta.asset_id ); + if( !abo ) { FC_ASSERT( delta.amount > 0, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", ("a",account(*this).name) @@ -73,8 +73,9 @@ void database::adjust_balance(account_id_type account, asset delta ) }); } else { if( delta.amount < 0 ) - FC_ASSERT( itr->get_balance() >= -delta, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", ("a",account(*this).name)("b",to_pretty_string(itr->get_balance()))("r",to_pretty_string(-delta))); - modify(*itr, [delta](account_balance_object& b) { + FC_ASSERT( abo->get_balance() >= -delta, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", + ("a",account(*this).name)("b",to_pretty_string(abo->get_balance()))("r",to_pretty_string(-delta))); + modify(*abo, [delta](account_balance_object& b) { b.adjust_balance(delta); }); } diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index efc5562a89..24618dbd34 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -37,6 +37,7 @@ #include #include +#include #include namespace graphene { namespace chain { @@ -227,7 +228,7 @@ bool database::_push_block(const signed_block& new_block) * queues full as well, it will be kept in the queue to be propagated later when a new block flushes out the pending * queues. */ -processed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip ) +processed_transaction database::push_transaction( const precomputable_transaction& trx, uint32_t skip ) { try { processed_transaction result; detail::with_skip_flags( *this, skip, [&]() @@ -237,7 +238,7 @@ processed_transaction database::push_transaction( const signed_transaction& trx, return result; } FC_CAPTURE_AND_RETHROW( (trx) ) } -processed_transaction database::_push_transaction( const signed_transaction& trx ) +processed_transaction database::_push_transaction( const precomputable_transaction& trx ) { // If this is the first transaction pushed after applying a block, start a new undo session. // This allows us to quickly rewind to the clean state of the head block, in case a new block arrives. @@ -465,15 +466,17 @@ signed_block database::_generate_block( void database::pop_block() { try { _pending_tx_session.reset(); - auto head_id = head_block_id(); - optional head_block = fetch_block_by_id( head_id ); - GRAPHENE_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" ); - - _fork_db.pop_block(); + auto fork_db_head = _fork_db.head(); + FC_ASSERT( fork_db_head, "Trying to pop() from empty fork database!?" ); + if( fork_db_head->id == head_block_id() ) + _fork_db.pop_block(); + else + { + fork_db_head = _fork_db.fetch_block( head_block_id() ); + FC_ASSERT( fork_db_head, "Trying to pop() block that's not in fork database!?" ); + } pop_undo(); - - _popped_tx.insert( _popped_tx.begin(), head_block->transactions.begin(), head_block->transactions.end() ); - + _popped_tx.insert( _popped_tx.begin(), fork_db_head->data.transactions.begin(), fork_db_head->data.transactions.end() ); } FC_CAPTURE_AND_RETHROW() } void database::clear_pending() @@ -584,6 +587,7 @@ void database::_apply_block( const signed_block& next_block ) clear_expired_transactions(); clear_expired_proposals(); clear_expired_orders(); + clear_expired_htlcs(); update_expired_feeds(); // this will update expired feeds and some core exchange rates update_core_exchange_rates(); // this will update remaining core exchange rates update_withdraw_permissions(); @@ -621,22 +625,17 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx { try { uint32_t skip = get_node_properties().skip_flags; - if( true || !(skip&skip_validate) ) /* issue #505 explains why this skip_flag is disabled */ - trx.validate(); + trx.validate(); auto& trx_idx = get_mutable_index_type(); const chain_id_type& chain_id = get_chain_id(); - transaction_id_type trx_id; if( !(skip & skip_transaction_dupe_check) ) - { - trx_id = trx.id(); - FC_ASSERT( trx_idx.indices().get().find(trx_id) == trx_idx.indices().get().end() ); - } + FC_ASSERT( trx_idx.indices().get().find(trx.id()) == trx_idx.indices().get().end() ); transaction_evaluation_state eval_state(this); const chain_parameters& chain_parameters = get_global_properties().parameters; eval_state._trx = &trx; - if( !(skip & (skip_transaction_signatures | skip_authority_check) ) ) + if( !(skip & skip_transaction_signatures) ) { auto get_active = [&]( account_id_type id ) { return &id(*this).active; }; auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; }; @@ -665,8 +664,8 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx //Insert transaction into unique transactions database. if( !(skip & skip_transaction_dupe_check) ) { - create([&trx_id,&trx](transaction_object& transaction) { - transaction.trx_id = trx_id; + create([&trx](transaction_object& transaction) { + transaction.trx_id = trx.id(); transaction.trx = trx; }); } @@ -683,14 +682,6 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx } ptrx.operation_results = std::move(eval_state.operation_results); - if( head_block_time() < HARDFORK_CORE_1040_TIME ) // TODO totally remove this code block after hard fork - { - //Make sure the temp account has no non-zero balances - const auto& index = get_index_type().indices().get(); - auto range = index.equal_range( boost::make_tuple( GRAPHENE_TEMP_ACCOUNT ) ); - std::for_each(range.first, range.second, [](const account_balance_object& b) { FC_ASSERT(b.balance == 0); }); - } - return ptrx; } FC_CAPTURE_AND_RETHROW( (trx) ) } @@ -750,4 +741,65 @@ bool database::before_last_checkpoint()const return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num()); } + +static const uint32_t skip_expensive = database::skip_transaction_signatures | database::skip_witness_signature + | database::skip_merkle_check | database::skip_transaction_dupe_check; + +template +void database::_precompute_parallel( const Trx* trx, const size_t count, const uint32_t skip )const +{ + for( size_t i = 0; i < count; ++i, ++trx ) + { + trx->validate(); // TODO - parallelize wrt confidential operations + if( !(skip&skip_transaction_dupe_check) ) + trx->id(); + if( !(skip&skip_transaction_signatures) ) + trx->get_signature_keys( get_chain_id() ); + } +} + +fc::future database::precompute_parallel( const signed_block& block, const uint32_t skip )const +{ try { + std::vector> workers; + if( !block.transactions.empty() ) + { + if( (skip & skip_expensive) == skip_expensive ) + _precompute_parallel( &block.transactions[0], block.transactions.size(), skip ); + else + { + uint32_t chunks = fc::asio::default_io_service_scope::get_num_threads(); + uint32_t chunk_size = ( block.transactions.size() + chunks - 1 ) / chunks; + workers.reserve( chunks + 1 ); + for( size_t base = 0; base < block.transactions.size(); base += chunk_size ) + workers.push_back( fc::do_parallel( [this,&block,base,chunk_size,skip] () { + _precompute_parallel( &block.transactions[base], + base + chunk_size < block.transactions.size() ? chunk_size : block.transactions.size() - base, + skip ); + }) ); + } + } + + if( !(skip&skip_witness_signature) ) + workers.push_back( fc::do_parallel( [&block] () { block.signee(); } ) ); + if( !(skip&skip_merkle_check) ) + block.calculate_merkle_root(); + block.id(); + + if( workers.empty() ) + return fc::future< void >( fc::promise< void >::ptr( new fc::promise< void >( true ) ) ); + + auto first = workers.begin(); + auto worker = first; + while( ++worker != workers.end() ) + worker->wait(); + return *first; +} FC_LOG_AND_RETHROW() } + +fc::future database::precompute_parallel( const precomputable_transaction& trx )const +{ + return fc::do_parallel([this,&trx] () { + _precompute_parallel( &trx, 1, skip_nothing ); + }); +} + } } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 263c5df3e5..eb0c52f42e 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #include #include #include +#include #include #include @@ -60,6 +62,7 @@ #include #include #include +#include #include @@ -125,6 +128,9 @@ const uint8_t witness_object::type_id; const uint8_t worker_object::space_id; const uint8_t worker_object::type_id; +const uint8_t htlc_object::space_id; +const uint8_t htlc_object::type_id; + void database::initialize_evaluators() { @@ -173,6 +179,9 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -181,15 +190,15 @@ void database::initialize_indexes() _undo_db.set_max_size( GRAPHENE_MIN_UNDO_HISTORY ); //Protocol object indexes - add_index< primary_index >(); + add_index< primary_index >(); // 8192 assets per chunk add_index< primary_index >(); - auto acnt_index = add_index< primary_index >(); + auto acnt_index = add_index< primary_index >(); // ~1 million accounts per chunk acnt_index->add_secondary_index(); acnt_index->add_secondary_index(); - add_index< primary_index >(); - add_index< primary_index >(); + add_index< primary_index >(); // 256 members per chunk + add_index< primary_index >(); // 1024 witnesses per chunk add_index< primary_index >(); add_index< primary_index >(); @@ -201,22 +210,26 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index< htlc_index> >(); //Implementation object indexes add_index< primary_index >(); - add_index< primary_index >(); - add_index< primary_index >(); + + auto bal_idx = add_index< primary_index >(); + bal_idx->add_secondary_index(); + + add_index< primary_index >(); // 8192 add_index< primary_index> >(); add_index< primary_index> >(); - add_index< primary_index >(); + add_index< primary_index >(); // 1 Mi add_index< primary_index> >(); add_index< primary_index> >(); add_index< primary_index > >(); add_index< primary_index > >(); + add_index< primary_index > >(); add_index< primary_index< special_authority_index > >(); add_index< primary_index< buyback_index > >(); add_index< primary_index >(); - add_index< primary_index< simple_index< fba_accumulator_object > > >(); } @@ -233,7 +246,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) _undo_db.disable(); struct auth_inhibitor { auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags) - { db.node_properties().skip_flags |= skip_authority_check; } + { db.node_properties().skip_flags |= skip_transaction_signatures; } ~auth_inhibitor() { db.node_properties().skip_flags = old_flags; } private: diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 7f1a8a4c6c..4ee7cb2825 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -131,33 +132,6 @@ struct worker_pay_visitor } }; -/// @brief A budget record struct to be used in initialize_budget_record and process_budget -struct budget_record -{ - uint64_t time_since_last_budget = 0; - - // sources of budget - share_type from_initial_reserve = 0; - share_type from_accumulated_fees = 0; - share_type from_unused_witness_budget = 0; - - // witness budget requested by the committee - share_type requested_witness_budget = 0; - - // funds that can be released from reserve at maximum rate - share_type total_budget = 0; - - // sinks of budget, should sum up to total_budget - share_type witness_budget = 0; - share_type worker_budget = 0; - - // unused budget - share_type leftover_worker_funds = 0; - - // change in supply due to budget operations - share_type supply_delta = 0; -}; - void database::update_worker_votes() { const auto& idx = get_index_type().indices().get(); @@ -560,6 +534,12 @@ void database::process_budget() _dpo.last_budget_time = now; }); + create< budget_record_object >( [&]( budget_record_object& _rec ) + { + _rec.time = head_block_time(); + _rec.record = rec; + }); + // available_funds is money we could spend, but don't want to. // we simply let it evaporate back into the reserve. } @@ -713,7 +693,7 @@ void distribute_fba_balances( database& db ) void create_buyback_orders( database& db ) { const auto& bbo_idx = db.get_index_type< buyback_index >().indices().get(); - const auto& bal_idx = db.get_index_type< account_balance_index >().indices().get< by_account_asset >(); + const auto& bal_idx = db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >(); for( const buyback_object& bbo : bbo_idx ) { @@ -721,7 +701,6 @@ void create_buyback_orders( database& db ) assert( asset_to_buy.buyback_account.valid() ); const account_object& buyback_account = (*(asset_to_buy.buyback_account))(db); - asset_id_type next_asset = asset_id_type(); if( !buyback_account.allowed_assets.valid() ) { @@ -729,16 +708,11 @@ void create_buyback_orders( database& db ) continue; } - while( true ) + for( const auto& entry : bal_idx.get_account_balances( buyback_account.id ) ) { - auto it = bal_idx.lower_bound( boost::make_tuple( buyback_account.id, next_asset ) ); - if( it == bal_idx.end() ) - break; - if( it->owner != buyback_account.id ) - break; + const auto* it = entry.second; asset_id_type asset_to_sell = it->asset_type; share_type amount_to_sell = it->balance; - next_asset = asset_to_sell + 1; if( asset_to_sell == asset_to_buy.id ) continue; if( amount_to_sell == 0 ) @@ -834,11 +808,14 @@ void database::process_bids( const asset_bitasset_data_object& bad ) while( covered < bdd.current_supply && itr != bid_idx.end() && itr->inv_swan_price.quote.asset_id == to_revive_id ) { const collateral_bid_object& bid = *itr; - asset total_collateral = bid.inv_swan_price.quote * bad.settlement_price; + asset debt_in_bid = bid.inv_swan_price.quote; + if( debt_in_bid.amount > bdd.current_supply ) + debt_in_bid.amount = bdd.current_supply; + asset total_collateral = debt_in_bid * bad.settlement_price; total_collateral += bid.inv_swan_price.base; - price call_price = price::call_price( bid.inv_swan_price.quote, total_collateral, bad.current_feed.maintenance_collateral_ratio ); + price call_price = price::call_price( debt_in_bid, total_collateral, bad.current_feed.maintenance_collateral_ratio ); if( ~call_price >= bad.current_feed.settlement_price ) break; - covered += bid.inv_swan_price.quote.amount; + covered += debt_in_bid.amount; ++itr; } if( covered < bdd.current_supply ) return; @@ -850,9 +827,12 @@ void database::process_bids( const asset_bitasset_data_object& bad ) { const collateral_bid_object& bid = *itr; ++itr; - share_type debt = bid.inv_swan_price.quote.amount; - share_type collateral = (bid.inv_swan_price.quote * bad.settlement_price).amount; - if( bid.inv_swan_price.quote.amount >= to_cover ) + asset debt_in_bid = bid.inv_swan_price.quote; + if( debt_in_bid.amount > bdd.current_supply ) + debt_in_bid.amount = bdd.current_supply; + share_type debt = debt_in_bid.amount; + share_type collateral = (debt_in_bid * bad.settlement_price).amount; + if( debt >= to_cover ) { debt = to_cover; collateral = remaining_fund; diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 6f627adb55..9167768f48 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -35,6 +35,8 @@ #include #include #include +#include +#include namespace graphene { namespace chain { @@ -74,68 +76,78 @@ void database::reindex( fc::path data_dir ) else _undo_db.disable(); - uint32_t skip = skip_witness_signature | - skip_block_size_check | - skip_merkle_check | - skip_transaction_signatures | - skip_transaction_dupe_check | - skip_tapos_check | - skip_witness_schedule_check | - skip_authority_check; + uint32_t skip = node_properties().skip_flags; - size_t total_processed_block_size; size_t total_block_size = _block_id_to_block.total_block_size(); const auto& gpo = get_global_properties(); - for( uint32_t i = head_block_num() + 1; i <= last_block_num; ++i ) + std::queue< std::tuple< size_t, signed_block, fc::future< void > > > blocks; + uint32_t next_block_num = head_block_num() + 1; + uint32_t i = next_block_num; + while( next_block_num <= last_block_num || !blocks.empty() ) { - if( i % 10000 == 0 ) + if( next_block_num <= last_block_num && blocks.size() < 20 ) { - total_processed_block_size = _block_id_to_block.blocks_current_position(); - - ilog( - " [by size: ${size}% ${processed} of ${total}] [by num: ${num}% ${i} of ${last}]", - ("size", double(total_processed_block_size) / total_block_size * 100) - ("processed", total_processed_block_size) - ("total", total_block_size) - ("num", double(i*100)/last_block_num) - ("i", i) - ("last", last_block_num) - ); - } - if( i == flush_point ) - { - ilog( "Writing database to disk at block ${i}", ("i",i) ); - flush(); - ilog( "Done" ); - } - if( head_block_time() >= last_block->timestamp - gpo.parameters.maximum_time_until_expiration ) - skip &= ~skip_transaction_dupe_check; - fc::optional< signed_block > block = _block_id_to_block.fetch_by_number(i); - if( !block.valid() ) - { - wlog( "Reindexing terminated due to gap: Block ${i} does not exist!", ("i", i) ); - uint32_t dropped_count = 0; - while( true ) + const size_t processed_block_size = _block_id_to_block.blocks_current_position(); + fc::optional< signed_block > block = _block_id_to_block.fetch_by_number( next_block_num++ ); + if( block.valid() ) { - fc::optional< block_id_type > last_id = _block_id_to_block.last_id(); - // this can trigger if we attempt to e.g. read a file that has block #2 but no block #1 - if( !last_id.valid() ) - break; - // we've caught up to the gap - if( block_header::num_from_id( *last_id ) <= i ) - break; - _block_id_to_block.remove( *last_id ); - dropped_count++; + if( block->timestamp >= last_block->timestamp - gpo.parameters.maximum_time_until_expiration ) + skip &= ~skip_transaction_dupe_check; + blocks.emplace( processed_block_size, std::move(*block), fc::future() ); + std::get<2>(blocks.back()) = precompute_parallel( std::get<1>(blocks.back()), skip ); + } + else + { + wlog( "Reindexing terminated due to gap: Block ${i} does not exist!", ("i", i) ); + uint32_t dropped_count = 0; + while( true ) + { + fc::optional< block_id_type > last_id = _block_id_to_block.last_id(); + // this can trigger if we attempt to e.g. read a file that has block #2 but no block #1 + if( !last_id.valid() ) + break; + // we've caught up to the gap + if( block_header::num_from_id( *last_id ) <= i ) + break; + _block_id_to_block.remove( *last_id ); + dropped_count++; + } + wlog( "Dropped ${n} blocks from after the gap", ("n", dropped_count) ); + next_block_num = last_block_num + 1; // don't load more blocks } - wlog( "Dropped ${n} blocks from after the gap", ("n", dropped_count) ); - break; } - if( i < undo_point ) - apply_block( *block, skip ); else { - _undo_db.enable(); - push_block( *block, skip ); + std::get<2>(blocks.front()).wait(); + const signed_block& block = std::get<1>(blocks.front()); + + if( i % 10000 == 0 ) + { + ilog( + " [by size: ${size}% ${processed} of ${total}] [by num: ${num}% ${i} of ${last}]", + ("size", double(std::get<0>(blocks.front())) / total_block_size * 100) + ("processed", std::get<0>(blocks.front())) + ("total", total_block_size) + ("num", double(i*100)/last_block_num) + ("i", i) + ("last", last_block_num) + ); + } + if( i == flush_point ) + { + ilog( "Writing database to disk at block ${i}", ("i",i) ); + flush(); + ilog( "Done" ); + } + if( i < undo_point ) + apply_block( block, skip ); + else + { + _undo_db.enable(); + push_block( block, skip ); + } + blocks.pop(); + i++; } } _undo_db.enable(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index ff44a177df..fafaa3bf00 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -256,6 +257,27 @@ struct get_impacted_account_visitor { _impacted.insert( op.fee_payer() ); // account_id } + void operator()( const htlc_create_operation& op ) + { + _impacted.insert( op.fee_payer() ); + _impacted.insert( op.to ); + } + void operator()( const htlc_redeem_operation& op ) + { + _impacted.insert( op.fee_payer() ); + } + void operator()( const htlc_redeemed_operation& op ) + { + _impacted.insert( op.from ); + } + void operator()( const htlc_extend_operation& op ) + { + _impacted.insert( op.fee_payer() ); + } + void operator()( const htlc_refund_operation& op ) + { + _impacted.insert( op.fee_payer() ); + } }; void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set& result ) @@ -344,6 +366,12 @@ void get_relevant_accounts( const object* obj, flat_set& accoun } case balance_object_type:{ /** these are free from any accounts */ break; + } case htlc_object_type:{ + const auto& htlc_obj = dynamic_cast(obj); + FC_ASSERT( htlc_obj != nullptr ); + accounts.insert( htlc_obj->from ); + accounts.insert( htlc_obj->to ); + break; } } } @@ -393,7 +421,7 @@ void get_relevant_accounts( const object* obj, flat_set& accoun break; case impl_witness_schedule_object_type: break; - case impl_reserved1_object_type: + case impl_budget_record_object_type: break; case impl_special_authority_object_type: break; diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 9a5bcad1eb..05aae9d058 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -558,4 +559,22 @@ void database::update_withdraw_permissions() remove(*permit_index.begin()); } +void database::clear_expired_htlcs() +{ + const auto& htlc_idx = get_index_type().indices().get(); + while ( htlc_idx.begin() != htlc_idx.end() + && htlc_idx.begin()->expiration <= head_block_time() ) + { + const htlc_object& obj = *htlc_idx.begin(); + adjust_balance( obj.from, obj.amount ); + // virtual op + htlc_refund_operation vop( obj.id, obj.from ); + vop.htlc_id = htlc_idx.begin()->id; + push_applied_operation( vop ); + + // remove the db object + remove( *htlc_idx.begin() ); + } +} + } } diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 39e9d3b83f..71da27629b 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -24,6 +24,8 @@ #include #include +#include + namespace graphene { namespace chain { fork_database::fork_database() { @@ -39,7 +41,7 @@ void fork_database::pop_block() FC_ASSERT( _head, "no block to pop" ); auto prev = _head->prev.lock(); FC_ASSERT( prev, "popping block would leave head block null" ); - _head = prev; + _head = prev; } void fork_database::start_block(signed_block b) @@ -50,7 +52,7 @@ void fork_database::start_block(signed_block b) } /** - * Pushes the block into the fork database and caches it if it doesn't link + * Pushes the block into the fork database * */ shared_ptr fork_database::push_block(const signed_block& b) @@ -64,7 +66,6 @@ shared_ptr fork_database::push_block(const signed_block& b) wlog( "Pushing block to fork database that failed to link: ${id}, ${num}", ("id",b.id())("num",b.block_num()) ); wlog( "Head: ${num}, ${id}", ("num",_head->data.block_num())("id",_head->data.id()) ); throw; - _unlinked_index.insert( item ); } return _head; } @@ -92,35 +93,10 @@ void fork_database::_push_block(const item_ptr& item) { _head = item; uint32_t min_num = _head->num - std::min( _max_size, _head->num ); -// ilog( "min block in fork DB ${n}, max_size: ${m}", ("n",min_num)("m",_max_size) ); auto& num_idx = _index.get(); while( num_idx.size() && (*num_idx.begin())->num < min_num ) num_idx.erase( num_idx.begin() ); - - _unlinked_index.get().erase(_head->num - _max_size); } - //_push_next( item ); -} - -/** - * Iterate through the unlinked cache and insert anything that - * links to the newly inserted item. This will start a recursive - * set of calls performing a depth-first insertion of pending blocks as - * _push_next(..) calls _push_block(...) which will in turn call _push_next - */ -void fork_database::_push_next( const item_ptr& new_item ) -{ - auto& prev_idx = _unlinked_index.get(); - - auto itr = prev_idx.find( new_item->id ); - while( itr != prev_idx.end() ) - { - auto tmp = *itr; - prev_idx.erase( itr ); - _push_block( tmp ); - - itr = prev_idx.find( new_item->id ); - } } void fork_database::set_max_size( uint32_t s ) @@ -128,29 +104,15 @@ void fork_database::set_max_size( uint32_t s ) _max_size = s; if( !_head ) return; - { /// index - auto& by_num_idx = _index.get(); - auto itr = by_num_idx.begin(); - while( itr != by_num_idx.end() ) - { - if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) ) - by_num_idx.erase(itr); - else - break; - itr = by_num_idx.begin(); - } - } - { /// unlinked_index - auto& by_num_idx = _unlinked_index.get(); - auto itr = by_num_idx.begin(); - while( itr != by_num_idx.end() ) - { - if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) ) - by_num_idx.erase(itr); - else - break; - itr = by_num_idx.begin(); - } + auto& by_num_idx = _index.get(); + auto itr = by_num_idx.begin(); + while( itr != by_num_idx.end() ) + { + if( (*itr)->num < std::max(int64_t(0),int64_t(_head->num) - _max_size) ) + by_num_idx.erase(itr); + else + break; + itr = by_num_idx.begin(); } } @@ -158,11 +120,7 @@ bool fork_database::is_known_block(const block_id_type& id)const { auto& index = _index.get(); auto itr = index.find(id); - if( itr != index.end() ) - return true; - auto& unlinked_index = _unlinked_index.get(); - auto unlinked_itr = unlinked_index.find(id); - return unlinked_itr != unlinked_index.end(); + return itr != index.end(); } item_ptr fork_database::fetch_block(const block_id_type& id)const @@ -171,10 +129,6 @@ item_ptr fork_database::fetch_block(const block_id_type& id)const auto itr = index.find(id); if( itr != index.end() ) return *itr; - auto& unlinked_index = _unlinked_index.get(); - auto unlinked_itr = unlinked_index.find(id); - if( unlinked_itr != unlinked_index.end() ) - return *unlinked_itr; return item_ptr(); } diff --git a/libraries/chain/hardfork.d/CORE_1268.hf b/libraries/chain/hardfork.d/CORE_1268.hf index 352463c043..6872db2fb2 100644 --- a/libraries/chain/hardfork.d/CORE_1268.hf +++ b/libraries/chain/hardfork.d/CORE_1268.hf @@ -1,4 +1,4 @@ // #1268 Distribute Asset Market Fees to Referral Program #ifndef HARDFORK_1268_TIME -#define HARDFORK_1268_TIME (fc::time_point_sec( 1577880000 )) // Wednesday, January 1, 2020 12:00:00 PM +#define HARDFORK_1268_TIME (fc::time_point_sec( 1530705600 )) // Wednesday, July 4, 2018 12:00:00 PM #endif diff --git a/libraries/chain/hardfork.d/CORE_1479.hf b/libraries/chain/hardfork.d/CORE_1479.hf new file mode 100644 index 0000000000..2f8ee807e3 --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_1479.hf @@ -0,0 +1,4 @@ +// bitshares-core issue #1479 nodes crashing on self-approving proposal +#ifndef HARDFORK_CORE_1479_TIME +#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1545436800 )) // 2018-12-22T00:00:00Z +#endif diff --git a/libraries/chain/hardfork.d/core-1468.hf b/libraries/chain/hardfork.d/core-1468.hf new file mode 100644 index 0000000000..a669d57c84 --- /dev/null +++ b/libraries/chain/hardfork.d/core-1468.hf @@ -0,0 +1,4 @@ +// HTLC implementation +#ifndef HARDFORK_CORE_1468_TIME +#define HARDFORK_CORE_1468_TIME (fc::time_point_sec( 1600000000 ) ) +#endif diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp new file mode 100644 index 0000000000..218558a98d --- /dev/null +++ b/libraries/chain/htlc_evaluator.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include + +namespace graphene { + namespace chain { + + optional get_committee_htlc_options(graphene::chain::database& db) + { + return db.get_global_properties().parameters.extensions.value.updatable_htlc_options; + } + + void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) + { + optional htlc_options = get_committee_htlc_options(db()); + + FC_ASSERT(htlc_options, "HTLC Committee options are not set."); + + // make sure the expiration is reasonable + FC_ASSERT( o.claim_period_seconds <= htlc_options->max_timeout_secs, "HTLC Timeout exceeds allowed length" ); + // make sure the preimage length is reasonable + 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( db().get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; + return void_result(); + } + + object_id_type htlc_create_evaluator::do_apply(const htlc_create_operation& o) + { + try { + graphene::chain::database& dbase = db(); + dbase.adjust_balance( o.from, -o.amount ); + + const htlc_object& esc = db().create([&dbase,&o]( htlc_object& esc ) { + esc.from = o.from; + esc.to = o.to; + esc.amount = o.amount; + esc.preimage_hash = o.preimage_hash; + esc.preimage_size = o.preimage_size; + esc.expiration = dbase.head_block_time() + o.claim_period_seconds; + }); + return esc.id; + + } FC_CAPTURE_AND_RETHROW( (o) ) + } + + class htlc_redeem_visitor + { + //private: + const std::vector& data; + public: + typedef bool result_type; + + htlc_redeem_visitor( const std::vector& preimage ) + : data( preimage ) {} + + template + bool operator()( const T& preimage_hash )const + { + return T::hash( (const char*)data.data(), (uint32_t) data.size() ) == preimage_hash; + } + }; + + 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->preimage_size, "Preimage size mismatch."); + + const htlc_redeem_visitor vtor( o.preimage ); + FC_ASSERT( htlc_obj->preimage_hash.visit( vtor ), "Provided preimage does not generate correct hash."); + + return void_result(); + } + + void_result htlc_redeem_evaluator::do_apply(const htlc_redeem_operation& o) + { + db().adjust_balance(htlc_obj->to, htlc_obj->amount); + // notify related parties + htlc_redeemed_operation virt_op( htlc_obj->id, htlc_obj->from, htlc_obj->to, htlc_obj->amount ); + db().push_applied_operation( virt_op ); + db().remove(*htlc_obj); + return void_result(); + } + + void_result htlc_extend_evaluator::do_evaluate(const htlc_extend_operation& o) + { + htlc_obj = &db().get(o.htlc_id); + return void_result(); + } + + void_result htlc_extend_evaluator::do_apply(const htlc_extend_operation& o) + { + db().modify(*htlc_obj, [&o](htlc_object& db_obj) { + db_obj.expiration += o.seconds_to_add; + }); + + return void_result(); + } + + } // namespace chain +} // namespace graphene diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index cae9d35984..cb52552eae 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -70,6 +70,8 @@ namespace graphene { namespace chain { bool is_voting = false; ///< redundately store whether this account is voting for better maintenance performance + time_point_sec last_vote_time; // add last time voted + /// Whether this account owns some CORE asset and is voting inline bool has_some_core_voting() const { @@ -292,14 +294,6 @@ namespace graphene { namespace chain { */ class account_member_index : public secondary_index { - class key_compare { - public: - inline bool operator()( const public_key_type& a, const public_key_type& b )const - { - return a.key_data < b.key_data; - } - }; - public: virtual void object_inserted( const object& obj ) override; virtual void object_removed( const object& obj ) override; @@ -308,20 +302,20 @@ namespace graphene { namespace chain { /** given an account or key, map it to the set of accounts that reference it in an active or owner authority */ - map< account_id_type, set > account_to_account_memberships; - map< public_key_type, set, key_compare > account_to_key_memberships; + map< account_id_type, set > account_to_account_memberships; + map< public_key_type, set, pubkey_comparator > account_to_key_memberships; /** some accounts use address authorities in the genesis block */ - map< address, set > account_to_address_memberships; + map< address, set > account_to_address_memberships; protected: - set get_account_members( const account_object& a )const; - set get_key_members( const account_object& a )const; - set
get_address_members( const account_object& a )const; + set get_account_members( const account_object& a )const; + set get_key_members( const account_object& a )const; + set
get_address_members( const account_object& a )const; - set before_account_members; - set before_key_members; - set
before_address_members; + set before_account_members; + set before_key_members; + set
before_address_members; }; @@ -341,7 +335,30 @@ namespace graphene { namespace chain { map< account_id_type, set > referred_by; }; - struct by_account_asset; + /** + * @brief This secondary index will allow fast access to the balance objects + * that belonging to an account. + */ + class balances_by_account_index : public secondary_index + { + public: + virtual void object_inserted( const object& obj ) override; + virtual void object_removed( const object& obj ) override; + virtual void about_to_modify( const object& before ) override; + virtual void object_modified( const object& after ) override; + + const map< asset_id_type, const account_balance_object* >& get_account_balances( const account_id_type& acct )const; + const account_balance_object* get_account_balance( const account_id_type& acct, const asset_id_type& asset )const; + + private: + static const uint8_t bits; + static const uint64_t mask; + + /** Maps each account to its balance objects */ + vector< vector< map< asset_id_type, const account_balance_object* > > > balances; + std::stack< object_id_type > ids_being_modified; + }; + struct by_asset_balance; struct by_maintenance_flag; /** @@ -353,13 +370,6 @@ namespace graphene { namespace chain { ordered_unique< tag, member< object, object_id_type, &object::id > >, ordered_non_unique< tag, member< account_balance_object, bool, &account_balance_object::maintenance_flag > >, - ordered_unique< tag, - composite_key< - account_balance_object, - member, - member - > - >, ordered_unique< tag, composite_key< account_balance_object, @@ -453,6 +463,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (core_in_balance) (has_cashback_vb) (is_voting) + (last_vote_time) (lifetime_fees_paid) (pending_fees)(pending_vested_fees) ) diff --git a/libraries/chain/include/graphene/chain/budget_record_object.hpp b/libraries/chain/include/graphene/chain/budget_record_object.hpp new file mode 100644 index 0000000000..49544793a6 --- /dev/null +++ b/libraries/chain/include/graphene/chain/budget_record_object.hpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015 Cryptonomex, Inc., and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once +#include +#include +#include + +namespace graphene { namespace chain { + +struct budget_record +{ + uint64_t time_since_last_budget = 0; + + // sources of budget + share_type from_initial_reserve = 0; + share_type from_accumulated_fees = 0; + share_type from_unused_witness_budget = 0; + + // witness budget requested by the committee + share_type requested_witness_budget = 0; + + // funds that can be released from reserve at maximum rate + share_type total_budget = 0; + + // sinks of budget, should sum up to total_budget + share_type witness_budget = 0; + share_type worker_budget = 0; + + // unused budget + share_type leftover_worker_funds = 0; + + // change in supply due to budget operations + share_type supply_delta = 0; +}; + +class budget_record_object; + +class budget_record_object : public graphene::db::abstract_object +{ + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_budget_record_object_type; + + fc::time_point_sec time; + budget_record record; +}; + +} } + +FC_REFLECT( + graphene::chain::budget_record, + (time_since_last_budget) + (from_initial_reserve) + (from_accumulated_fees) + (from_unused_witness_budget) + (requested_witness_budget) + (total_budget) + (witness_budget) + (worker_budget) + (leftover_worker_funds) + (supply_delta) +) + +FC_REFLECT_DERIVED( + graphene::chain::budget_record_object, + (graphene::db::object), + (time) + (record) +) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 3ab5f47f19..e97bacc032 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -121,7 +121,7 @@ #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 -#define GRAPHENE_CURRENT_DB_VERSION "BTS2.19" +#define GRAPHENE_CURRENT_DB_VERSION "BTS2.181221" #define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT) diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 52b73075c1..9bc3c67d3a 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -70,12 +70,11 @@ namespace graphene { namespace chain { skip_fork_db = 1 << 3, ///< used while reindexing skip_block_size_check = 1 << 4, ///< used when applying locally generated transactions skip_tapos_check = 1 << 5, ///< used while reindexing -- note this skips expiration check as well - skip_authority_check = 1 << 6, ///< used while reindexing -- disables any checking of authority on transactions + // skip_authority_check = 1 << 6, ///< removed because effectively identical to skip_transaction_signatures skip_merkle_check = 1 << 7, ///< used while reindexing skip_assert_evaluation = 1 << 8, ///< used while reindexing skip_undo_history_check = 1 << 9, ///< used while reindexing - skip_witness_schedule_check = 1 << 10, ///< used while reindexing - skip_validate = 1 << 11 ///< used prior to checkpoint, skips validate() call on transaction + skip_witness_schedule_check = 1 << 10 ///< used while reindexing }; /** @@ -137,9 +136,9 @@ namespace graphene { namespace chain { bool before_last_checkpoint()const; bool push_block( const signed_block& b, uint32_t skip = skip_nothing ); - processed_transaction push_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); + processed_transaction push_transaction( const precomputable_transaction& trx, uint32_t skip = skip_nothing ); bool _push_block( const signed_block& b ); - processed_transaction _push_transaction( const signed_transaction& trx ); + processed_transaction _push_transaction( const precomputable_transaction& trx ); ///@throws fc::exception if the proposed transaction fails to apply. processed_transaction push_proposal( const proposal_object& proposal ); @@ -419,7 +418,7 @@ namespace graphene { namespace chain { /** when popping a block, the transactions that were removed get cached here so they * can be reapplied at the proper time */ - std::deque< signed_transaction > _popped_tx; + std::deque< precomputable_transaction > _popped_tx; /** * @} @@ -428,6 +427,29 @@ namespace graphene { namespace chain { /// Enable or disable tracking of votes of standby witnesses and committee members inline void enable_standby_votes_tracking(bool enable) { _track_standby_votes = enable; } + /** Precomputes digests, signatures and operation validations depending + * on skip flags. "Expensive" computations may be done in a parallel + * thread. + * + * @param block the block to preprocess + * @param skip indicates which computations can be skipped + * @return a future that will resolve to the input block with + * precomputations applied + */ + fc::future precompute_parallel( const signed_block& block, const uint32_t skip = skip_nothing )const; + + /** Precomputes digests, signatures and operation validations. + * "Expensive" computations may be done in a parallel thread. + * + * @param trx the transaction to preprocess + * @return a future that will resolve to the input transaction with + * precomputations applied + */ + fc::future precompute_parallel( const precomputable_transaction& trx )const; + private: + template + void _precompute_parallel( const Trx* trx, const size_t count, const uint32_t skip )const; + protected: //Mark pop_undo() as protected -- we do not want outside calling pop_undo(); it should call pop_block() instead void pop_undo() { object_database::pop_undo(); } @@ -449,6 +471,7 @@ namespace graphene { namespace chain { void apply_block( const signed_block& next_block, uint32_t skip = skip_nothing ); processed_transaction apply_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); operation_result apply_operation( transaction_evaluation_state& eval_state, const operation& op ); + private: void _apply_block( const signed_block& next_block ); processed_transaction _apply_transaction( const signed_transaction& trx ); @@ -478,6 +501,7 @@ namespace graphene { namespace chain { void update_withdraw_permissions(); bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true, const asset_bitasset_data_object* bitasset_ptr = nullptr ); + void clear_expired_htlcs(); ///Steps performed only at maintenance intervals ///@{ diff --git a/libraries/chain/include/graphene/chain/db_with.hpp b/libraries/chain/include/graphene/chain/db_with.hpp index de93bb15f5..7ae189216f 100644 --- a/libraries/chain/include/graphene/chain/db_with.hpp +++ b/libraries/chain/include/graphene/chain/db_with.hpp @@ -80,11 +80,9 @@ struct pending_transactions_restorer { try { if( !_db.is_known_transaction( tx.id() ) ) { - // since push_transaction() takes a signed_transaction, - // the operation_results field will be ignored. _db._push_transaction( tx ); } - } catch ( const fc::exception& ) { + } catch ( const fc::exception& ) { // ignore invalid transactions } } _db._popped_tx.clear(); @@ -93,17 +91,11 @@ struct pending_transactions_restorer try { if( !_db.is_known_transaction( tx.id() ) ) { - // since push_transaction() takes a signed_transaction, - // the operation_results field will be ignored. _db._push_transaction( tx ); } } - catch( const fc::exception& e ) - { - /* - wlog( "Pending transaction became invalid after switching to block ${b} ${t}", ("b", _db.head_block_id())("t",_db.head_block_time()) ); - wlog( "The invalid pending transaction caused exception ${e}", ("e", e.to_detail_string() ) ); - */ + catch( const fc::exception& ) + { // ignore invalid transactions } } } diff --git a/libraries/chain/include/graphene/chain/fork_database.hpp b/libraries/chain/include/graphene/chain/fork_database.hpp index be3991ed80..363a21f2ce 100644 --- a/libraries/chain/include/graphene/chain/fork_database.hpp +++ b/libraries/chain/include/graphene/chain/fork_database.hpp @@ -93,12 +93,10 @@ namespace graphene { namespace chain { struct block_id; struct block_num; - struct by_previous; typedef multi_index_container< item_ptr, indexed_by< hashed_unique, member, std::hash>, - hashed_non_unique, const_mem_fun, std::hash>, ordered_non_unique, member> > > fork_multi_index_type; @@ -112,7 +110,6 @@ namespace graphene { namespace chain { uint32_t _max_size = 1024; - fork_multi_index_type _unlinked_index; fork_multi_index_type _index; shared_ptr _head; }; diff --git a/libraries/chain/include/graphene/chain/htlc_evaluator.hpp b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp new file mode 100644 index 0000000000..08a24b2bdc --- /dev/null +++ b/libraries/chain/include/graphene/chain/htlc_evaluator.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once +#include + +namespace graphene { + namespace chain { + + class htlc_create_evaluator : public evaluator + { + public: + typedef htlc_create_operation operation_type; + + void_result do_evaluate( const htlc_create_operation& o); + object_id_type do_apply( const htlc_create_operation& o); + }; + + class htlc_redeem_evaluator : public evaluator + { + public: + typedef htlc_redeem_operation operation_type; + + void_result do_evaluate( const htlc_redeem_operation& o); + void_result do_apply( const htlc_redeem_operation& o); + const htlc_object* htlc_obj = nullptr; + }; + + class htlc_extend_evaluator : public evaluator + { + public: + typedef htlc_extend_operation operation_type; + + void_result do_evaluate( const htlc_extend_operation& o); + void_result do_apply( const htlc_extend_operation& o); + const htlc_object* htlc_obj = nullptr; + }; + } // namespace graphene +} // namespace graphene diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp new file mode 100644 index 0000000000..e9c4515042 --- /dev/null +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace graphene { namespace chain { + + /** + * @brief database object to store HTLCs + * + * This object is stored in the database while an HTLC is active. The HTLC will + * become inactive at expiration or when unlocked via the preimage. + */ + class htlc_object : public graphene::db::abstract_object { + public: + // uniquely identify this object in the database + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = htlc_object_type; + + account_id_type from; + account_id_type to; + asset amount; + fc::time_point_sec expiration; + htlc_hash preimage_hash; + uint16_t preimage_size; + }; + + struct by_from_id; + struct by_expiration; + typedef multi_index_container< + htlc_object, + indexed_by< + ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, + + ordered_unique< tag< by_expiration >, + composite_key< htlc_object, + member< htlc_object, fc::time_point_sec, &htlc_object::expiration >, + member< object, object_id_type, &object::id > > >, + + ordered_unique< tag< by_from_id >, + composite_key< htlc_object, + member< htlc_object, account_id_type, &htlc_object::from >, + member< object, object_id_type, &object::id > > > + > + + > htlc_object_index_type; + + typedef generic_index< htlc_object, htlc_object_index_type > htlc_index; + +} } // namespace graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), + (from)(to)(amount)(expiration) + (preimage_hash)(preimage_size) ); diff --git a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp index 36f99ccfbe..04b5c62d22 100644 --- a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp @@ -28,6 +28,25 @@ namespace graphene { namespace chain { + class hardfork_visitor_1479 + { + public: + typedef void result_type; + + uint64_t max_update_instance = 0; + uint64_t nested_update_count = 0; + + template + void operator()(const T &v) const {} + + void operator()(const proposal_update_operation &v); + + void operator()(const proposal_delete_operation &v); + + // loop and self visit in proposals + void operator()(const graphene::chain::proposal_create_operation &v); + }; + class proposal_create_evaluator : public evaluator { public: @@ -37,6 +56,8 @@ namespace graphene { namespace chain { object_id_type do_apply( const proposal_create_operation& o ); transaction _proposed_trx; + + hardfork_visitor_1479 vtor_1479; }; class proposal_update_evaluator : public evaluator diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index f6178d3e24..f2be53837b 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -274,6 +274,7 @@ FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing, (no_listing)(white_listed)(black_listed)(white_and_black_listed)) FC_REFLECT(graphene::chain::account_create_operation::ext, (null_ext)(owner_special_authority)(active_special_authority)(buyback_options) ) +FC_REFLECT_TYPENAME(graphene::chain::extension) FC_REFLECT( graphene::chain::account_create_operation, (fee)(registrar) (referrer)(referrer_percent) @@ -281,6 +282,7 @@ FC_REFLECT( graphene::chain::account_create_operation, ) FC_REFLECT(graphene::chain::account_update_operation::ext, (null_ext)(owner_special_authority)(active_special_authority) ) +FC_REFLECT_TYPENAME(graphene::chain::extension) FC_REFLECT( graphene::chain::account_update_operation, (fee)(account)(owner)(active)(new_options)(extensions) ) diff --git a/libraries/chain/include/graphene/chain/protocol/base.hpp b/libraries/chain/include/graphene/chain/protocol/base.hpp index 52240b934a..73209a1861 100644 --- a/libraries/chain/include/graphene/chain/protocol/base.hpp +++ b/libraries/chain/include/graphene/chain/protocol/base.hpp @@ -27,6 +27,8 @@ #include #include +#include + namespace graphene { namespace chain { /** @@ -94,6 +96,7 @@ namespace graphene { namespace chain { void get_required_active_authorities( flat_set& )const{} void get_required_owner_authorities( flat_set& )const{} void validate()const{} + fc::optional< fc::future > validate_parallel( uint32_t skip )const; static uint64_t calculate_data_fee( uint64_t bytes, uint64_t price_per_kbyte ); }; diff --git a/libraries/chain/include/graphene/chain/protocol/block.hpp b/libraries/chain/include/graphene/chain/protocol/block.hpp index 98e627c928..aa8c46052f 100644 --- a/libraries/chain/include/graphene/chain/protocol/block.hpp +++ b/libraries/chain/include/graphene/chain/protocol/block.hpp @@ -26,8 +26,9 @@ namespace graphene { namespace chain { - struct block_header + class block_header { + public: digest_type digest()const; block_id_type previous; uint32_t block_num()const { return num_from_id(previous) + 1; } @@ -41,20 +42,27 @@ namespace graphene { namespace chain { static uint32_t num_from_id(const block_id_type& id); }; - struct signed_block_header : public block_header + class signed_block_header : public block_header { - block_id_type id()const; - fc::ecc::public_key signee()const; + public: + const block_id_type& id()const; + const fc::ecc::public_key& signee()const; void sign( const fc::ecc::private_key& signer ); bool validate_signee( const fc::ecc::public_key& expected_signee )const; signature_type witness_signature; + protected: + mutable fc::ecc::public_key _signee; + mutable block_id_type _block_id; }; - struct signed_block : public signed_block_header + class signed_block : public signed_block_header { - checksum_type calculate_merkle_root()const; + public: + const checksum_type& calculate_merkle_root()const; vector transactions; + protected: + mutable checksum_type _calculated_merkle_root; }; } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 695d9541ee..dba8281305 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -30,7 +30,12 @@ namespace graphene { namespace chain { struct fee_schedule; } } namespace graphene { namespace chain { - typedef static_variant<> parameter_extension; + struct htlc_options + { + uint32_t max_timeout_secs; + uint32_t max_preimage_size; + }; + struct chain_parameters { /** using a smart ref breaks the circular dependency created between operations and the fee schedule */ @@ -63,14 +68,30 @@ namespace graphene { namespace chain { uint16_t accounts_per_fee_scale = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE; ///< number of accounts between fee scalings uint8_t account_fee_scale_bitshifts = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS; ///< number of times to left bitshift account registration fee at each scaling uint8_t max_authority_depth = GRAPHENE_MAX_SIG_CHECK_DEPTH; - extensions_type extensions; + + struct ext + { + optional< htlc_options > updatable_htlc_options; + }; + + extension extensions; /** defined in fee_schedule.cpp */ void validate()const; + }; } } // graphene::chain +FC_REFLECT( graphene::chain::htlc_options, + (max_timeout_secs) + (max_preimage_size) +) + +FC_REFLECT( graphene::chain::chain_parameters::ext, + (updatable_htlc_options) +) + FC_REFLECT( graphene::chain::chain_parameters, (current_fees) (block_interval) diff --git a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp index a08ee98a2c..e578f1d9f4 100644 --- a/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp +++ b/libraries/chain/include/graphene/chain/protocol/fee_schedule.hpp @@ -107,6 +107,46 @@ namespace graphene { namespace chain { } }; + template<> + class fee_helper { + public: + const htlc_create_operation::fee_parameters_type& cget(const flat_set& 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 flat_set& 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 flat_set& 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,6 +180,12 @@ namespace graphene { namespace chain { { return fee_helper().get(parameters); } + template + const bool exists()const + { + auto itr = parameters.find(typename Operation::fee_parameters_type()); + return itr != parameters.end(); + } /** * @note must be sorted by fee_parameters.which() and have no duplicates diff --git a/libraries/chain/include/graphene/chain/protocol/htlc.hpp b/libraries/chain/include/graphene/chain/protocol/htlc.hpp new file mode 100644 index 0000000000..ef5f2b1469 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/htlc.hpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include // std::max + +namespace graphene { + namespace chain { + + typedef fc::ripemd160 htlc_algo_ripemd160; + typedef fc::sha1 htlc_algo_sha1; + typedef fc::sha256 htlc_algo_sha256; + + typedef fc::static_variant< + htlc_algo_ripemd160, + htlc_algo_sha1, + htlc_algo_sha256 + > htlc_hash; + + struct htlc_create_operation : public base_operation + { + struct fee_parameters_type { + 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 + account_id_type from; + // where the held monies will go if the preimage is provided + account_id_type to; + // the amount to hold + asset amount; + // the (typed) hash of the preimage + htlc_hash preimage_hash; + // the size of the preimage + 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; + + /*** + * @brief Does simple validation of this object + */ + void validate()const; + + /** + * @brief who will pay the fee + */ + account_id_type fee_payer()const { return from; } + + /**** + * @brief calculates the fee to be paid for this operation + */ + share_type calculate_fee(const fee_parameters_type& fee_params)const; + }; + + struct htlc_redeem_operation : public base_operation + { + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t fee_per_kb = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + + // paid to network + asset fee; + // the object we are attempting to update + htlc_id_type htlc_id; + // who is attempting to update the transaction + account_id_type redeemer; + // the preimage (not used if after epoch timeout) + std::vector preimage; + // for future expansion + extensions_type extensions; + + /*** + * @brief Perform obvious checks to validate this object + */ + void validate()const; + + /** + * @brief Who is to pay the fee + */ + account_id_type fee_payer()const { return redeemer; } + + /**** + * @brief calculates the fee to be paid for this operation + */ + share_type calculate_fee(const fee_parameters_type& fee_params)const; + }; + + /** + * virtual op to assist with notifying related parties + */ + struct htlc_redeemed_operation : public base_operation + { + struct fee_parameters_type {}; + + htlc_redeemed_operation() {} + htlc_redeemed_operation( htlc_id_type htlc_id, account_id_type from, account_id_type to, asset amount) : + htlc_id(htlc_id), from(from), to(to), amount(amount) {} + + account_id_type fee_payer()const { return to; } + void validate()const { FC_ASSERT( !"virtual operation" ); } + + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + + htlc_id_type htlc_id; + account_id_type from, to; + asset amount; + asset fee; + }; + + struct htlc_extend_operation : public base_operation + { + struct fee_parameters_type { + uint64_t fee = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint64_t fee_per_day = 1 * GRAPHENE_BLOCKCHAIN_PRECISION; + }; + + // paid to network + asset fee; + // the object we are attempting to update + htlc_id_type htlc_id; + // who is attempting to update the transaction + account_id_type update_issuer; + // how much to add + uint32_t seconds_to_add; + // for future expansion + extensions_type extensions; + + /*** + * @brief Perform obvious checks to validate this object + */ + void validate()const; + + /** + * @brief Who is to pay the fee + */ + account_id_type fee_payer()const { return update_issuer; } + + /**** + * @brief calculates the fee to be paid for this operation + */ + share_type calculate_fee(const fee_parameters_type& fee_params)const; + }; + + struct htlc_refund_operation : public base_operation + { + struct fee_parameters_type {}; + + htlc_refund_operation(){} + htlc_refund_operation( const htlc_id_type& htlc_id, const account_id_type& to ) + : htlc_id(htlc_id), to(to) {} + + account_id_type fee_payer()const { return to; } + void validate()const { FC_ASSERT( !"virtual operation" ); } + + /// This is a virtual operation; there is no fee + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + + htlc_id_type htlc_id; + account_id_type to; + asset fee; + }; + } +} + +FC_REFLECT_TYPENAME( graphene::chain::htlc_hash ) + +FC_REFLECT( graphene::chain::htlc_create_operation::fee_parameters_type, (fee) (fee_per_day) ) +FC_REFLECT( graphene::chain::htlc_redeem_operation::fee_parameters_type, (fee) (fee_per_kb) ) +FC_REFLECT( graphene::chain::htlc_redeemed_operation::fee_parameters_type, ) // VIRTUAL +FC_REFLECT( graphene::chain::htlc_extend_operation::fee_parameters_type, (fee) (fee_per_day)) +FC_REFLECT( graphene::chain::htlc_refund_operation::fee_parameters_type, ) // VIRTUAL + +FC_REFLECT( graphene::chain::htlc_create_operation, + (fee)(from)(to)(amount)(preimage_hash)(preimage_size)(claim_period_seconds)(extensions)) +FC_REFLECT( graphene::chain::htlc_redeem_operation, (fee)(htlc_id)(redeemer)(preimage)(extensions)) +FC_REFLECT( graphene::chain::htlc_redeemed_operation, (fee)(htlc_id)(from)(to)(amount) ) +FC_REFLECT( graphene::chain::htlc_extend_operation, (fee)(htlc_id)(update_issuer)(seconds_to_add)(extensions)) +FC_REFLECT( graphene::chain::htlc_refund_operation, (fee)(htlc_id)(to)) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index de2cfa7fd9..9b5ffabb37 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -38,6 +38,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -95,7 +96,12 @@ namespace graphene { namespace chain { bid_collateral_operation, execute_bid_operation, // VIRTUAL asset_claim_pool_operation, - asset_update_issuer_operation + asset_update_issuer_operation, + htlc_create_operation, + htlc_redeem_operation, + htlc_redeemed_operation, // VIRTUAL + htlc_extend_operation, + htlc_refund_operation // VIRTUAL > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 95c3996135..84234afb9e 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -62,8 +62,9 @@ namespace graphene { namespace chain { /** * @brief groups operations that should be applied atomically */ - struct transaction + class transaction { + public: /** * Least significant 16 bits from the reference block number. If @ref relative_expiration is zero, this field * must be zero as well. @@ -85,11 +86,9 @@ namespace graphene { namespace chain { extensions_type extensions; /// Calculate the digest for a transaction - digest_type digest()const; - transaction_id_type id()const; - void validate() const; - /// Calculate the digest used for signature validation - digest_type sig_digest( const chain_id_type& chain_id )const; + digest_type digest()const; + virtual const transaction_id_type& id()const; + virtual void validate() const; void set_expiration( fc::time_point_sec expiration_time ); void set_reference_block( const block_id_type& reference_block ); @@ -113,13 +112,19 @@ namespace graphene { namespace chain { } void get_required_authorities( flat_set& active, flat_set& owner, vector& other )const; + + protected: + // Calculate the digest used for signature validation + digest_type sig_digest( const chain_id_type& chain_id )const; + mutable transaction_id_type _tx_id_buffer; }; /** * @brief adds a signature to a transaction */ - struct signed_transaction : public transaction + class signed_transaction : public transaction { + public: signed_transaction( const transaction& trx = transaction() ) : transaction(trx){} @@ -176,19 +181,36 @@ namespace graphene { namespace chain { * otherwise, the @ref chain_id parameter will be ignored, and * @ref signees will be returned directly. */ - const flat_set& get_signature_keys( const chain_id_type& chain_id )const; + virtual const flat_set& get_signature_keys( const chain_id_type& chain_id )const; /** Signatures */ vector signatures; - /** Public keys extracted from signatures */ - mutable flat_set signees; + /** Removes all operations and signatures */ + void clear() { operations.clear(); signatures.clear(); } - /// Removes all operations, signatures and signees - void clear() { operations.clear(); signatures.clear(); signees.clear(); } + /** Removes all signatures */ + void clear_signatures() { signatures.clear(); } + protected: + /** Public keys extracted from signatures */ + mutable flat_set _signees; + }; - /// Removes all signatures and signees - void clear_signatures() { signatures.clear(); signees.clear(); } + /** This represents a signed transaction that will never have its operations, + * signatures etc. modified again, after initial creation. It is therefore + * safe to cache results from various calls. + */ + class precomputable_transaction : public signed_transaction { + public: + precomputable_transaction() {} + precomputable_transaction( const signed_transaction& tx ) : signed_transaction(tx) {}; + precomputable_transaction( signed_transaction&& tx ) : signed_transaction( std::move(tx) ) {}; + + virtual const transaction_id_type& id()const override; + virtual void validate()const override; + virtual const flat_set& get_signature_keys( const chain_id_type& chain_id )const override; + protected: + mutable bool _validated = false; }; void verify_authority( const vector& ops, const flat_set& sigs, @@ -212,10 +234,10 @@ namespace graphene { namespace chain { * If an operation did not create any new object IDs then 0 * should be returned. */ - struct processed_transaction : public signed_transaction + struct processed_transaction : public precomputable_transaction { processed_transaction( const signed_transaction& trx = signed_transaction() ) - : signed_transaction(trx){} + : precomputable_transaction(trx){} vector operation_results; @@ -229,4 +251,5 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::transaction, (ref_block_num)(ref_block_prefix)(expiration)(operations)(extensions) ) // Note: not reflecting signees field for backward compatibility; in addition, it should not be in p2p messages FC_REFLECT_DERIVED( graphene::chain::signed_transaction, (graphene::chain::transaction), (signatures) ) -FC_REFLECT_DERIVED( graphene::chain::processed_transaction, (graphene::chain::signed_transaction), (operation_results) ) +FC_REFLECT_DERIVED( graphene::chain::precomputable_transaction, (graphene::chain::signed_transaction), ) +FC_REFLECT_DERIVED( graphene::chain::processed_transaction, (graphene::chain::precomputable_transaction), (operation_results) ) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 06ab7ff291..87536f42ee 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -36,6 +36,22 @@ #include +// TODO: move this to fc +#include +namespace fc { namespace raw { + template + inline void pack( T& ds, const fc::sha1& ep, uint32_t _max_depth = 1 ) { + ds << ep; + } + + template + inline void unpack( T& ds, sha1& ep, uint32_t _max_depth = 1 ) { + ds >> ep; + } + +} } +// /TODO: move to fc + #include #include #include @@ -140,6 +156,7 @@ namespace graphene { namespace chain { vesting_balance_object_type, worker_object_type, balance_object_type, + htlc_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -158,7 +175,7 @@ namespace graphene { namespace chain { impl_blinded_balance_object_type, impl_chain_property_object_type, impl_witness_schedule_object_type, - impl_reserved1_object_type, // formerly impl_budget_record_object_type, TODO: delete me + impl_budget_record_object_type, impl_special_authority_object_type, impl_buyback_object_type, impl_fba_accumulator_object_type, @@ -182,6 +199,7 @@ namespace graphene { namespace chain { class worker_object; class balance_object; class blinded_balance_object; + class htlc_object; typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type; @@ -197,6 +215,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type; typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type; typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type; + typedef object_id< protocol_ids, htlc_object_type, htlc_object> htlc_id_type; // implementation types class global_property_object; @@ -210,6 +229,7 @@ namespace graphene { namespace chain { class account_transaction_history_object; class chain_property_object; class witness_schedule_object; + class budget_record_object; class special_authority_object; class buyback_object; class fba_accumulator_object; @@ -229,6 +249,7 @@ namespace graphene { namespace chain { account_transaction_history_object> account_transaction_history_id_type; typedef object_id< implementation_ids, impl_chain_property_object_type, chain_property_object> chain_property_id_type; typedef object_id< implementation_ids, impl_witness_schedule_object_type, witness_schedule_object> witness_schedule_id_type; + typedef object_id< implementation_ids, impl_budget_record_object_type, budget_record_object > budget_record_id_type; typedef object_id< implementation_ids, impl_blinded_balance_object_type, blinded_balance_object > blinded_balance_id_type; typedef object_id< implementation_ids, impl_special_authority_object_type, special_authority_object > special_authority_id_type; typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type; @@ -264,6 +285,14 @@ namespace graphene { namespace chain { friend bool operator != ( const public_key_type& p1, const public_key_type& p2); }; + class pubkey_comparator { + public: + inline bool operator()( const public_key_type& a, const public_key_type& b )const + { + return a.key_data < b.key_data; + } + }; + struct extended_public_key_type { struct binary_key @@ -343,6 +372,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (vesting_balance_object_type) (worker_object_type) (balance_object_type) + (htlc_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, @@ -359,7 +389,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type, (impl_blinded_balance_object_type) (impl_chain_property_object_type) (impl_witness_schedule_object_type) - (impl_reserved1_object_type) + (impl_budget_record_object_type) (impl_special_authority_object_type) (impl_buyback_object_type) (impl_fba_accumulator_object_type) @@ -391,10 +421,12 @@ FC_REFLECT_TYPENAME( graphene::chain::account_statistics_id_type ) FC_REFLECT_TYPENAME( graphene::chain::transaction_obj_id_type ) FC_REFLECT_TYPENAME( graphene::chain::block_summary_id_type ) FC_REFLECT_TYPENAME( graphene::chain::account_transaction_history_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::budget_record_id_type ) FC_REFLECT_TYPENAME( graphene::chain::special_authority_id_type ) FC_REFLECT_TYPENAME( graphene::chain::buyback_id_type ) FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type ) FC_REFLECT_TYPENAME( graphene::chain::collateral_bid_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::htlc_id_type ) FC_REFLECT( graphene::chain::void_t, ) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 975d14cb54..348688d605 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -108,6 +108,23 @@ struct proposal_operation_hardfork_visitor FC_ASSERT(!"Virtual operation"); } } + void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const { + if (block_time < HARDFORK_CORE_1468_TIME) { + FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(), "Unable to set HTLC options before hardfork 1468"); + FC_ASSERT(!op.new_parameters.current_fees->exists()); + FC_ASSERT(!op.new_parameters.current_fees->exists()); + FC_ASSERT(!op.new_parameters.current_fees->exists()); + } + } + void operator()(const graphene::chain::htlc_create_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); + } + void operator()(const graphene::chain::htlc_redeem_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); + } + void operator()(const graphene::chain::htlc_extend_operation &op) const { + FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" ); + } // loop and self visit in proposals void operator()(const graphene::chain::proposal_create_operation &v) const { for (const op_wrapper &op : v.proposed_ops) @@ -127,6 +144,27 @@ struct hardfork_visitor_214 // non-recursive proposal visitor } }; +void hardfork_visitor_1479::operator()(const proposal_update_operation &v) +{ + if( nested_update_count == 0 || v.proposal.instance.value > max_update_instance ) + max_update_instance = v.proposal.instance.value; + nested_update_count++; +} + +void hardfork_visitor_1479::operator()(const proposal_delete_operation &v) +{ + if( nested_update_count == 0 || v.proposal.instance.value > max_update_instance ) + max_update_instance = v.proposal.instance.value; + nested_update_count++; +} + +// loop and self visit in proposals +void hardfork_visitor_1479::operator()(const graphene::chain::proposal_create_operation &v) +{ + for (const op_wrapper &op : v.proposed_ops) + op.op.visit(*this); +} + void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o) { try { const database& d = db(); @@ -142,6 +180,7 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati for (const op_wrapper &op : o.proposed_ops) op.op.visit( hf214 ); } + vtor_1479( o ); const auto& global_parameters = d.get_global_properties().parameters; @@ -213,6 +252,20 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati std::set_difference(required_active.begin(), required_active.end(), proposal.required_owner_approvals.begin(), proposal.required_owner_approvals.end(), std::inserter(proposal.required_active_approvals, proposal.required_active_approvals.begin())); + + if( d.head_block_time() > HARDFORK_CORE_1479_TIME ) + FC_ASSERT( vtor_1479.nested_update_count == 0 || proposal.id.instance() > vtor_1479.max_update_instance, + "Cannot update/delete a proposal with a future id!" ); + else if( vtor_1479.nested_update_count > 0 && proposal.id.instance() <= vtor_1479.max_update_instance ) + { + // prevent approval + transfer_operation top; + top.from = GRAPHENE_NULL_ACCOUNT; + top.to = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT; + top.amount = asset( GRAPHENE_MAX_SHARE_SUPPLY ); + proposal.proposed_transaction.operations.emplace_back( top ); + wlog( "Issue 1479: ${p}", ("p",proposal) ); + } }); return proposal.id; diff --git a/libraries/chain/protocol/block.cpp b/libraries/chain/protocol/block.cpp index d32365dd08..9fdf4707eb 100644 --- a/libraries/chain/protocol/block.cpp +++ b/libraries/chain/protocol/block.cpp @@ -37,19 +37,23 @@ namespace graphene { namespace chain { return fc::endian_reverse_u32(id._hash[0]); } - block_id_type signed_block_header::id()const + const block_id_type& signed_block_header::id()const { - auto tmp = fc::sha224::hash( *this ); - tmp._hash[0] = fc::endian_reverse_u32(block_num()); // store the block num in the ID, 160 bits is plenty for the hash - static_assert( sizeof(tmp._hash[0]) == 4, "should be 4 bytes" ); - block_id_type result; - memcpy(result._hash, tmp._hash, std::min(sizeof(result), sizeof(tmp))); - return result; + if( !_block_id._hash[0] ) + { + auto tmp = fc::sha224::hash( *this ); + tmp._hash[0] = fc::endian_reverse_u32(block_num()); // store the block num in the ID, 160 bits is plenty for the hash + static_assert( sizeof(tmp._hash[0]) == 4, "should be 4 bytes" ); + memcpy(_block_id._hash, tmp._hash, std::min(sizeof(_block_id), sizeof(tmp))); + } + return _block_id; } - fc::ecc::public_key signed_block_header::signee()const + const fc::ecc::public_key& signed_block_header::signee()const { - return fc::ecc::public_key( witness_signature, digest(), true/*enforce canonical*/ ); + if( !_signee.valid() ) + _signee = fc::ecc::public_key( witness_signature, digest(), true/*enforce canonical*/ ); + return _signee; } void signed_block_header::sign( const fc::ecc::private_key& signer ) @@ -62,31 +66,35 @@ namespace graphene { namespace chain { return signee() == expected_signee; } - checksum_type signed_block::calculate_merkle_root()const + const checksum_type& signed_block::calculate_merkle_root()const { + static const checksum_type empty_checksum; if( transactions.size() == 0 ) - return checksum_type(); - - vector ids; - ids.resize( transactions.size() ); - for( uint32_t i = 0; i < transactions.size(); ++i ) - ids[i] = transactions[i].merkle_digest(); + return empty_checksum; - vector::size_type current_number_of_hashes = ids.size(); - while( current_number_of_hashes > 1 ) + if( !_calculated_merkle_root._hash[0] ) { - // hash ID's in pairs - uint32_t i_max = current_number_of_hashes - (current_number_of_hashes&1); - uint32_t k = 0; + vector ids; + ids.resize( transactions.size() ); + for( uint32_t i = 0; i < transactions.size(); ++i ) + ids[i] = transactions[i].merkle_digest(); - for( uint32_t i = 0; i < i_max; i += 2 ) - ids[k++] = digest_type::hash( std::make_pair( ids[i], ids[i+1] ) ); + vector::size_type current_number_of_hashes = ids.size(); + while( current_number_of_hashes > 1 ) + { + // hash ID's in pairs + uint32_t i_max = current_number_of_hashes - (current_number_of_hashes&1); + uint32_t k = 0; - if( current_number_of_hashes&1 ) - ids[k++] = ids[i_max]; - current_number_of_hashes = k; + for( uint32_t i = 0; i < i_max; i += 2 ) + ids[k++] = digest_type::hash( std::make_pair( ids[i], ids[i+1] ) ); + + if( current_number_of_hashes&1 ) + ids[k++] = ids[i_max]; + current_number_of_hashes = k; + } + _calculated_merkle_root = checksum_type::hash( ids[0] ); } - return checksum_type::hash( ids[0] ); + return _calculated_merkle_root; } - } } diff --git a/libraries/chain/protocol/htlc.cpp b/libraries/chain/protocol/htlc.cpp new file mode 100644 index 0000000000..645feb6dbf --- /dev/null +++ b/libraries/chain/protocol/htlc.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include + +#define SECONDS_PER_DAY (60 * 60 * 24) + +namespace graphene { namespace chain { + + void htlc_create_operation::validate()const { + FC_ASSERT( fee.amount >= 0, "Fee amount should not be negative" ); + 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 + { + 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; + FC_ASSERT( days == 0 || per_day_fee / days == fee_params.fee_per_day, "Fee calculation overflow" ); + return fee_params.fee + per_day_fee; + } + + void htlc_redeem_operation::validate()const { + FC_ASSERT( fee.amount >= 0, "Fee amount should not be negative" ); + } + + share_type htlc_redeem_operation::calculate_fee( const fee_parameters_type& fee_params )const + { + uint64_t kb = ( preimage.size() + 1023 ) / 1024; + uint64_t product = kb * fee_params.fee_per_kb; + FC_ASSERT( kb == 0 || product / kb == fee_params.fee_per_kb, "Fee calculation overflow"); + return fee_params.fee + product; + } + + void htlc_extend_operation::validate()const { + FC_ASSERT( fee.amount >= 0 , "Fee amount should not be negative"); + } + + share_type htlc_extend_operation::calculate_fee( const fee_parameters_type& fee_params )const + { + uint32_t days = ( seconds_to_add + SECONDS_PER_DAY - 1 ) / SECONDS_PER_DAY; + uint64_t per_day_fee = fee_params.fee_per_day * days; + FC_ASSERT( days == 0 || per_day_fee / days == fee_params.fee_per_day, "Fee calculation overflow" ); + return fee_params.fee + per_day_fee; + } +} } diff --git a/libraries/chain/protocol/operations.cpp b/libraries/chain/protocol/operations.cpp index 40a37eba3a..48a65f6fed 100644 --- a/libraries/chain/protocol/operations.cpp +++ b/libraries/chain/protocol/operations.cpp @@ -32,6 +32,12 @@ uint64_t base_operation::calculate_data_fee( uint64_t bytes, uint64_t price_per_ return result.to_uint64(); } +fc::optional< fc::future > base_operation::validate_parallel( uint32_t skip )const +{ + validate(); + return fc::optional< fc::future >(); +} + void balance_claim_operation::validate()const { FC_ASSERT( fee == asset() ); diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 4bb7ce8378..1a1293ca76 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -60,19 +60,17 @@ void transaction::validate() const operation_validate(op); } -graphene::chain::transaction_id_type graphene::chain::transaction::id() const +const transaction_id_type& transaction::id() const { auto h = digest(); - transaction_id_type result; - memcpy(result._hash, h._hash, std::min(sizeof(result), sizeof(h))); - return result; + memcpy(_tx_id_buffer._hash, h._hash, std::min(sizeof(_tx_id_buffer), sizeof(h))); + return _tx_id_buffer; } const signature_type& graphene::chain::signed_transaction::sign(const private_key_type& key, const chain_id_type& chain_id) { digest_type h = sig_digest( chain_id ); signatures.push_back(key.sign_compact(h)); - signees.clear(); // Clear signees since it may be inconsistent after added a new signature return signatures.back(); } @@ -305,22 +303,17 @@ void verify_authority( const vector& ops, const flat_set& signed_transaction::get_signature_keys( const chain_id_type& chain_id )const { try { - // Strictly we should check whether the given chain ID is same as the one used to initialize the `signees` field. - // However, we don't pass in another chain ID so far, for better performance, we skip the check. - if( signees.empty() && !signatures.empty() ) + auto d = sig_digest( chain_id ); + flat_set result; + for( const auto& sig : signatures ) { - auto d = sig_digest( chain_id ); - flat_set result; - for( const auto& sig : signatures ) - { - GRAPHENE_ASSERT( - result.insert( fc::ecc::public_key(sig,d) ).second, + GRAPHENE_ASSERT( + result.insert( fc::ecc::public_key(sig,d) ).second, tx_duplicate_sig, "Duplicate Signature detected" ); - } - signees = std::move( result ); } - return signees; + _signees = std::move( result ); + return _signees; } FC_CAPTURE_AND_RETHROW() } @@ -386,6 +379,29 @@ set signed_transaction::minimize_required_signatures( return set( result.begin(), result.end() ); } +const transaction_id_type& precomputable_transaction::id()const +{ + if( !_tx_id_buffer._hash[0] ) + transaction::id(); + return _tx_id_buffer; +} + +void precomputable_transaction::validate() const +{ + if( _validated ) return; + transaction::validate(); + _validated = true; +} + +const flat_set& precomputable_transaction::get_signature_keys( const chain_id_type& chain_id )const +{ + // Strictly we should check whether the given chain ID is same as the one used to initialize the `signees` field. + // However, we don't pass in another chain ID so far, for better performance, we skip the check. + if( _signees.empty() ) + signed_transaction::get_signature_keys( chain_id ); + return _signees; +} + void signed_transaction::verify_authority( const chain_id_type& chain_id, const std::function& get_active, diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index bcf9b24f13..4b54862899 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -23,11 +23,14 @@ */ #pragma once #include + #include #include #include #include + #include +#include namespace graphene { namespace db { class object_database; @@ -190,6 +193,111 @@ namespace graphene { namespace db { object_database& _db; }; + /** @class direct_index + * @brief A secondary index that tracks objects in vectors indexed by object + * id. It is meant for fully (or almost fully) populated indexes only (will + * fail when loading an object_database with large gaps). + * + * WARNING! If any of the methods called on insertion, removal or + * modification throws, subsequent behaviour is undefined! Such exceptions + * indicate that this index type is not appropriate for the use-case. + */ + template + class direct_index : public secondary_index + { + static_assert( chunkbits < 64, "Do you really want arrays with more than 2^63 elements???" ); + + // private + static const size_t MAX_HOLE = 100; + static const size_t _mask = ((1 << chunkbits) - 1); + size_t next = 0; + vector< vector< const Object* > > content; + std::stack< object_id_type > ids_being_modified; + + public: + direct_index() { + FC_ASSERT( (1ULL << chunkbits) > MAX_HOLE, "Small chunkbits is inefficient." ); + } + + virtual ~direct_index(){} + + virtual void object_inserted( const object& obj ) + { + uint64_t instance = obj.id.instance(); + if( instance == next ) + { + if( !(next & _mask) ) + { + content.resize((next >> chunkbits) + 1); + content[next >> chunkbits].resize( 1 << chunkbits, nullptr ); + } + next++; + } + else if( instance < next ) + FC_ASSERT( !content[instance >> chunkbits][instance & _mask], "Overwriting insert at {id}!", ("id",obj.id) ); + else // instance > next, allow small "holes" + { + FC_ASSERT( instance <= next + MAX_HOLE, "Out-of-order insert: {id} > {next}!", ("id",obj.id)("next",next) ); + if( !(next & _mask) || (next & (~_mask)) != (instance & (~_mask)) ) + { + content.resize((instance >> chunkbits) + 1); + content[instance >> chunkbits].resize( 1 << chunkbits, nullptr ); + } + while( next <= instance ) + { + content[next >> chunkbits][next & _mask] = nullptr; + next++; + } + } + FC_ASSERT( nullptr != dynamic_cast(&obj), "Wrong object type!" ); + content[instance >> chunkbits][instance & _mask] = static_cast( &obj ); + } + + virtual void object_removed( const object& obj ) + { + FC_ASSERT( nullptr != dynamic_cast(&obj), "Wrong object type!" ); + uint64_t instance = obj.id.instance(); + FC_ASSERT( instance < next, "Removing out-of-range object: {id} > {next}!", ("id",obj.id)("next",next) ); + FC_ASSERT( content[instance >> chunkbits][instance & _mask], "Removing non-existent object {id}!", ("id",obj.id) ); + content[instance >> chunkbits][instance & _mask] = nullptr; + } + + virtual void about_to_modify( const object& before ) + { + ids_being_modified.emplace( before.id ); + } + + virtual void object_modified( const object& after ) + { + FC_ASSERT( ids_being_modified.top() == after.id, "Modification of ID is not supported!"); + ids_being_modified.pop(); + } + + template< typename object_id > + const Object* find( const object_id& id )const + { + static_assert( object_id::space_id == Object::space_id, "Space ID mismatch!" ); + static_assert( object_id::type_id == Object::type_id, "Type_ID mismatch!" ); + if( id.instance >= next ) return nullptr; + return content[id.instance.value >> chunkbits][id.instance.value & _mask]; + }; + + template< typename object_id > + const Object& get( const object_id& id )const + { + const Object* ptr = find( id ); + FC_ASSERT( ptr != nullptr, "Object not found!" ); + return *ptr; + }; + + const Object* find( const object_id_type& id )const + { + FC_ASSERT( id.space() == Object::space_id, "Space ID mismatch!" ); + FC_ASSERT( id.type() == Object::type_id, "Type_ID mismatch!" ); + if( id.instance() >= next ) return nullptr; + return content[id.instance() >> chunkbits][id.instance() & ((1 << chunkbits) - 1)]; + }; + }; /** * @class primary_index @@ -198,14 +306,18 @@ namespace graphene { namespace db { * * @see http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ - template + template class primary_index : public DerivedIndex, public base_primary_index { public: typedef typename DerivedIndex::object_type object_type; primary_index( object_database& db ) - :base_primary_index(db),_next_id(object_type::space_id,object_type::type_id,0) {} + :base_primary_index(db),_next_id(object_type::space_id,object_type::type_id,0) + { + if( DirectBits > 0 ) + _direct_by_id = add_secondary_index< direct_index< object_type, DirectBits > >(); + } virtual uint8_t object_space_id()const override { return object_type::space_id; } @@ -217,6 +329,14 @@ namespace graphene { namespace db { virtual void use_next_id()override { ++_next_id.number; } virtual void set_next_id( object_id_type id )override { _next_id = id; } + /** @return the object with id or nullptr if not found */ + virtual const object* find( object_id_type id )const override + { + if( DirectBits > 0 ) + return _direct_by_id->find( id ); + return DerivedIndex::find( id ); + } + fc::sha256 get_object_version()const { std::string desc = "1.0";//get_type_description(); @@ -234,14 +354,12 @@ namespace graphene { namespace db { fc::raw::unpack(ds, _next_id); fc::raw::unpack(ds, open_ver); FC_ASSERT( open_ver == get_object_version(), "Incompatible Version, the serialization of objects in this index has changed" ); - try { - vector tmp; - while( true ) - { - fc::raw::unpack( ds, tmp ); - load( tmp ); - } - } catch ( const fc::exception& ){} + vector tmp; + while( ds.remaining() > 0 ) + { + fc::raw::unpack( ds, tmp ); + load( tmp ); + } } virtual void save( const path& db ) override @@ -329,7 +447,8 @@ namespace graphene { namespace db { } private: - object_id_type _next_id; + object_id_type _next_id; + const direct_index< object_type, DirectBits >* _direct_by_id = nullptr; }; } } // graphene::db diff --git a/libraries/db/object_database.cpp b/libraries/db/object_database.cpp index fdde0fed6b..5b026c08cf 100644 --- a/libraries/db/object_database.cpp +++ b/libraries/db/object_database.cpp @@ -25,6 +25,7 @@ #include #include +#include #include namespace graphene { namespace db { @@ -72,14 +73,20 @@ void object_database::flush() { // ilog("Save object_database in ${d}", ("d", _data_dir)); fc::create_directories( _data_dir / "object_database.tmp" / "lock" ); + std::vector> tasks; + tasks.reserve(200); for( uint32_t space = 0; space < _index.size(); ++space ) { fc::create_directories( _data_dir / "object_database.tmp" / fc::to_string(space) ); const auto types = _index[space].size(); for( uint32_t type = 0; type < types; ++type ) if( _index[space][type] ) - _index[space][type]->save( _data_dir / "object_database.tmp" / fc::to_string(space)/fc::to_string(type) ); + tasks.push_back( fc::do_parallel( [this,space,type] () { + _index[space][type]->save( _data_dir / "object_database.tmp" / fc::to_string(space)/fc::to_string(type) ); + } ) ); } + for( auto& task : tasks ) + task.wait(); fc::remove_all( _data_dir / "object_database.tmp" / "lock" ); if( fc::exists( _data_dir / "object_database" ) ) fc::rename( _data_dir / "object_database", _data_dir / "object_database.old" ); @@ -103,11 +110,17 @@ void object_database::open(const fc::path& data_dir) wlog("Ignoring locked object_database"); return; } + std::vector> tasks; + tasks.reserve(200); ilog("Opening object database from ${d} ...", ("d", data_dir)); for( uint32_t space = 0; space < _index.size(); ++space ) for( uint32_t type = 0; type < _index[space].size(); ++type ) if( _index[space][type] ) - _index[space][type]->open( _data_dir / "object_database" / fc::to_string(space)/fc::to_string(type) ); + tasks.push_back( fc::do_parallel( [this,space,type] () { + _index[space][type]->open( _data_dir / "object_database" / fc::to_string(space)/fc::to_string(type) ); + } ) ); + for( auto& task : tasks ) + task.wait(); ilog( "Done opening object database." ); } FC_CAPTURE_AND_RETHROW( (data_dir) ) } diff --git a/libraries/net/include/graphene/net/core_messages.hpp b/libraries/net/include/graphene/net/core_messages.hpp index 8af0c3443c..76f74bd253 100644 --- a/libraries/net/include/graphene/net/core_messages.hpp +++ b/libraries/net/include/graphene/net/core_messages.hpp @@ -95,9 +95,9 @@ namespace graphene { namespace net { { static const core_message_type_enum type; - signed_transaction trx; + graphene::chain::precomputable_transaction trx; trx_message() {} - trx_message(signed_transaction transaction) : + trx_message(graphene::chain::signed_transaction transaction) : trx(std::move(transaction)) {} }; diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index b2eb5185cb..612529f93c 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -317,7 +317,7 @@ namespace graphene { namespace net { namespace detail { _maximum_blocks_per_peer_during_syncing(GRAPHENE_NET_MAX_BLOCKS_PER_PEER_DURING_SYNCING) { _rate_limiter.set_actual_rate_time_constant(fc::seconds(2)); - fc::rand_pseudo_bytes(&_node_id.data[0], (int)_node_id.size()); + fc::rand_bytes(&_node_id.data[0], (int)_node_id.size()); } node_impl::~node_impl() diff --git a/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp b/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp index 907d26ae9a..22c71236b5 100644 --- a/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp +++ b/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp @@ -58,7 +58,7 @@ class debug_witness_plugin : public graphene::app::plugin { boost::program_options::variables_map _options; - std::map _private_keys; + std::map _private_keys; std::shared_ptr< std::ofstream > _json_object_stream; boost::signals2::scoped_connection _applied_block_conn; diff --git a/libraries/plugins/delayed_node/delayed_node_plugin.cpp b/libraries/plugins/delayed_node/delayed_node_plugin.cpp index 24a46cc066..0329d018b2 100644 --- a/libraries/plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/plugins/delayed_node/delayed_node_plugin.cpp @@ -58,7 +58,7 @@ delayed_node_plugin::~delayed_node_plugin() void delayed_node_plugin::plugin_set_program_options(bpo::options_description& cli, bpo::options_description& cfg) { cli.add_options() - ("trusted-node", boost::program_options::value(), "RPC endpoint of a trusted validating node (required)") + ("trusted-node", boost::program_options::value(), "RPC endpoint of a trusted validating node (required for delayed_node)") ; cfg.add(cli); } @@ -103,8 +103,10 @@ void delayed_node_plugin::sync_with_trusted_node() while( remote_dpo.last_irreversible_block_num > db.head_block_num() ) { fc::optional block = my->database_api->get_block( db.head_block_num()+1 ); + // TODO: during sync, decouple requesting blocks from preprocessing + applying them FC_ASSERT(block, "Trusted node claims it has blocks it doesn't actually have."); ilog("Pushing block #${n}", ("n", block->block_num())); + db.precompute_parallel( *block, graphene::chain::database::skip_nothing ).wait(); db.push_block(*block); synced_blocks++; } diff --git a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp index dcffaea5f9..6af2df19ac 100644 --- a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp +++ b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp @@ -59,6 +59,7 @@ class elasticsearch_plugin_impl std::string _elasticsearch_basic_auth = ""; std::string _elasticsearch_index_prefix = "bitshares-"; bool _elasticsearch_operation_object = false; + uint32_t _elasticsearch_start_es_after_block = 0; // disabled CURL *curl; // curl handler vector bulk_lines; // vector of op lines vector prepare; @@ -74,7 +75,7 @@ class elasticsearch_plugin_impl std::string index_name; bool is_sync = false; private: - bool add_elasticsearch( const account_id_type account_id, const optional& oho ); + bool add_elasticsearch( const account_id_type account_id, const optional& oho, const uint32_t block_number ); const account_transaction_history_object& addNewEntry(const account_statistics_object& stats_obj, const account_id_type& account_id, const optional & oho); @@ -163,7 +164,7 @@ bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b for( auto& account_id : impacted ) { - if(!add_elasticsearch( account_id, oho )) + if(!add_elasticsearch( account_id, oho, b.block_num() )) return false; } } @@ -276,13 +277,16 @@ void elasticsearch_plugin_impl::doVisitor(const optional & oho) + const optional & oho, + const uint32_t block_number) { const auto &stats_obj = getStatsObject(account_id); const auto &ath = addNewEntry(stats_obj, account_id, oho); growStats(stats_obj, ath); - createBulkLine(ath); - prepareBulk(ath.id); + if(_elasticsearch_start_es_after_block == 0 || block_number > _elasticsearch_start_es_after_block) { + createBulkLine(ath); + prepareBulk(ath.id); + } cleanObjects(ath.id, account_id); if (curl && bulk_lines.size() >= limit_documents) { // we are in bulk time, ready to add data to elasticsearech @@ -428,6 +432,7 @@ void elasticsearch_plugin::plugin_set_program_options( ("elasticsearch-basic-auth", boost::program_options::value(), "Pass basic auth to elasticsearch database('')") ("elasticsearch-index-prefix", boost::program_options::value(), "Add a prefix to the index(bitshares-)") ("elasticsearch-operation-object", boost::program_options::value(), "Save operation as object(false)") + ("elasticsearch-start-es-after-block", boost::program_options::value(), "Start doing ES job after block(0)") ; cfg.add(cli); } @@ -435,11 +440,10 @@ void elasticsearch_plugin::plugin_set_program_options( void elasticsearch_plugin::plugin_initialize(const boost::program_options::variables_map& options) { database().applied_block.connect( [&]( const signed_block& b) { - if(!my->update_account_histories(b)) - { + if (!my->update_account_histories(b)) FC_THROW_EXCEPTION(graphene::chain::plugin_exception, "Error populating ES database, we are going to keep trying."); - } } ); + my->_oho_index = database().add_index< primary_index< operation_history_index > >(); database().add_index< primary_index< account_transaction_history_index > >(); @@ -464,6 +468,9 @@ void elasticsearch_plugin::plugin_initialize(const boost::program_options::varia if (options.count("elasticsearch-operation-object")) { my->_elasticsearch_operation_object = options["elasticsearch-operation-object"].as(); } + if (options.count("elasticsearch-start-es-after-block")) { + my->_elasticsearch_start_es_after_block = options["elasticsearch-start-es-after-block"].as(); + } } void elasticsearch_plugin::plugin_startup() diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp index d0c35010ce..5695064325 100644 --- a/libraries/plugins/es_objects/es_objects.cpp +++ b/libraries/plugins/es_objects/es_objects.cpp @@ -30,10 +30,11 @@ #include #include #include +#include +#include #include - namespace graphene { namespace es_objects { namespace detail @@ -72,12 +73,8 @@ class es_objects_plugin_impl fc::time_point_sec block_time; private: - void prepare_proposal(const proposal_object& proposal_object); - void prepare_account(const account_object& account_object); - void prepare_asset(const asset_object& asset_object); - void prepare_balance(const account_balance_object& account_balance_object); - void prepare_limit(const limit_order_object& limit_object); - void prepare_bitasset(const asset_bitasset_data_object& bitasset_object); + template + void prepareTemplate(T blockchain_object, string index_name); }; bool es_objects_plugin_impl::index_database( const vector& ids, std::string action) @@ -102,7 +99,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(p->id, "proposal"); else - prepare_proposal(*p); + prepareTemplate(*p, "proposal"); } } else if(value.is() && _es_objects_accounts) { @@ -112,7 +109,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(a->id, "account"); else - prepare_account(*a); + prepareTemplate(*a, "account"); } } else if(value.is() && _es_objects_assets) { @@ -122,7 +119,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(a->id, "asset"); else - prepare_asset(*a); + prepareTemplate(*a, "asset"); } } else if(value.is() && _es_objects_balances) { @@ -132,7 +129,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(b->id, "balance"); else - prepare_balance(*b); + prepareTemplate(*b, "balance"); } } else if(value.is() && _es_objects_limit_orders) { @@ -142,7 +139,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(l->id, "limitorder"); else - prepare_limit(*l); + prepareTemplate(*l, "limitorder"); } } else if(value.is() && _es_objects_asset_bitasset) { @@ -152,7 +149,7 @@ bool es_objects_plugin_impl::index_database( const vector& ids, if(action == "delete") remove_from_database(ba->id, "bitasset"); else - prepare_bitasset(*ba); + prepareTemplate(*ba, "bitasset"); } } } @@ -190,181 +187,33 @@ void es_objects_plugin_impl::remove_from_database( object_id_type id, std::strin } } -void es_objects_plugin_impl::prepare_proposal(const proposal_object& proposal_object) -{ - proposal_struct prop; - prop.object_id = proposal_object.id; - prop.block_time = block_time; - prop.block_number = block_number; - prop.expiration_time = proposal_object.expiration_time; - prop.review_period_time = proposal_object.review_period_time; - prop.proposed_transaction = fc::json::to_string(proposal_object.proposed_transaction); - prop.required_owner_approvals = fc::json::to_string(proposal_object.required_owner_approvals); - prop.available_owner_approvals = fc::json::to_string(proposal_object.available_owner_approvals); - prop.required_active_approvals = fc::json::to_string(proposal_object.required_active_approvals); - prop.available_key_approvals = fc::json::to_string(proposal_object.available_key_approvals); - prop.proposer = proposal_object.proposer; - - std::string data = fc::json::to_string(prop); - - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "proposal"; - bulk_header["_type"] = "data"; - if(_es_objects_keep_only_current) - { - bulk_header["_id"] = string(prop.object_id); - } - - prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); - std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); - prepare.clear(); -} - -void es_objects_plugin_impl::prepare_account(const account_object& account_object) -{ - account_struct acct; - acct.object_id = account_object.id; - acct.block_time = block_time; - acct.block_number = block_number; - acct.membership_expiration_date = account_object.membership_expiration_date; - acct.registrar = account_object.registrar; - acct.referrer = account_object.referrer; - acct.lifetime_referrer = account_object.lifetime_referrer; - acct.network_fee_percentage = account_object.network_fee_percentage; - acct.lifetime_referrer_fee_percentage = account_object.lifetime_referrer_fee_percentage; - acct.referrer_rewards_percentage = account_object.referrer_rewards_percentage; - acct.name = account_object.name; - acct.owner_account_auths = fc::json::to_string(account_object.owner.account_auths); - acct.owner_key_auths = fc::json::to_string(account_object.owner.key_auths); - acct.owner_address_auths = fc::json::to_string(account_object.owner.address_auths); - acct.active_account_auths = fc::json::to_string(account_object.active.account_auths); - acct.active_key_auths = fc::json::to_string(account_object.active.key_auths); - acct.active_address_auths = fc::json::to_string(account_object.active.address_auths); - acct.voting_account = account_object.options.voting_account; - acct.votes = fc::json::to_string(account_object.options.votes); - - std::string data = fc::json::to_string(acct); - - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "account"; - bulk_header["_type"] = "data"; - if(_es_objects_keep_only_current) - { - bulk_header["_id"] = string(acct.object_id); - } - - prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); - std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); - prepare.clear(); -} - -void es_objects_plugin_impl::prepare_asset(const asset_object& asset_object) -{ - asset_struct asset; - asset.object_id = asset_object.id; - asset.block_time = block_time; - asset.block_number = block_number; - asset.symbol = asset_object.symbol; - asset.issuer = asset_object.issuer; - asset.is_market_issued = asset_object.is_market_issued(); - asset.dynamic_asset_data_id = asset_object.dynamic_asset_data_id; - asset.bitasset_data_id = asset_object.bitasset_data_id; - - std::string data = fc::json::to_string(asset); - - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "asset"; - bulk_header["_type"] = "data"; - if(_es_objects_keep_only_current) - { - bulk_header["_id"] = string(asset.object_id); - } - - prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); - std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); - prepare.clear(); -} - -void es_objects_plugin_impl::prepare_balance(const account_balance_object& account_balance_object) +template +void es_objects_plugin_impl::prepareTemplate(T blockchain_object, string index_name) { - balance_struct balance; - balance.object_id = account_balance_object.id; - balance.block_time = block_time; - balance.block_number = block_number; - balance.owner = account_balance_object.owner; - balance.asset_type = account_balance_object.asset_type; - balance.balance = account_balance_object.balance; - balance.maintenance_flag = account_balance_object.maintenance_flag; - - std::string data = fc::json::to_string(balance); - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "balance"; + bulk_header["_index"] = _es_objects_index_prefix + index_name; bulk_header["_type"] = "data"; if(_es_objects_keep_only_current) { - bulk_header["_id"] = string(balance.object_id); + bulk_header["_id"] = string(blockchain_object.id); } - prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); - std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); - prepare.clear(); -} + adaptor_struct adaptor; + fc::variant blockchain_object_variant; + fc::to_variant( blockchain_object, blockchain_object_variant, GRAPHENE_NET_MAX_NESTED_OBJECTS ); + fc::mutable_variant_object o = adaptor.adapt(blockchain_object_variant.get_object()); -void es_objects_plugin_impl::prepare_limit(const limit_order_object& limit_object) -{ - limit_order_struct limit; - limit.object_id = limit_object.id; - limit.block_time = block_time; - limit.block_number = block_number; - limit.expiration = limit_object.expiration; - limit.seller = limit_object.seller; - limit.for_sale = limit_object.for_sale; - limit.sell_price = limit_object.sell_price; - limit.deferred_fee = limit_object.deferred_fee; - - std::string data = fc::json::to_string(limit); + o["object_id"] = string(blockchain_object.id); + o["block_time"] = block_time; + o["block_number"] = block_number; - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "limitorder"; - bulk_header["_type"] = "data"; - if(_es_objects_keep_only_current) - { - bulk_header["_id"] = string(limit.object_id); - } + string data = fc::json::to_string(o, fc::json::legacy_generator); prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); prepare.clear(); } -void es_objects_plugin_impl::prepare_bitasset(const asset_bitasset_data_object& bitasset_object) -{ - if(!bitasset_object.is_prediction_market) { - - bitasset_struct bitasset; - bitasset.object_id = bitasset_object.id; - bitasset.block_time = block_time; - bitasset.block_number = block_number; - bitasset.current_feed = fc::json::to_string(bitasset_object.current_feed); - bitasset.current_feed_publication_time = bitasset_object.current_feed_publication_time; - - std::string data = fc::json::to_string(bitasset); - - fc::mutable_variant_object bulk_header; - bulk_header["_index"] = _es_objects_index_prefix + "bitasset"; - bulk_header["_type"] = "data"; - if(_es_objects_keep_only_current) - { - bulk_header["_id"] = string(bitasset.object_id); - } - - prepare = graphene::utilities::createBulk(bulk_header, std::move(data)); - std::move(prepare.begin(), prepare.end(), std::back_inserter(bulk)); - prepare.clear(); - } -} - es_objects_plugin_impl::~es_objects_plugin_impl() { return; diff --git a/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp b/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp index 54ecbdc90d..fa91e3bde4 100644 --- a/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp +++ b/libraries/plugins/es_objects/include/graphene/es_objects/es_objects.hpp @@ -30,7 +30,6 @@ namespace graphene { namespace es_objects { using namespace chain; - namespace detail { class es_objects_plugin_impl; @@ -54,105 +53,61 @@ class es_objects_plugin : public graphene::app::plugin std::unique_ptr my; }; -struct proposal_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - time_point_sec expiration_time; - optional review_period_time; - string proposed_transaction; - string required_active_approvals; - string available_active_approvals; - string required_owner_approvals; - string available_owner_approvals; - string available_key_approvals; - account_id_type proposer; -}; -struct account_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - time_point_sec membership_expiration_date; - account_id_type registrar; - account_id_type referrer; - account_id_type lifetime_referrer; - uint16_t network_fee_percentage; - uint16_t lifetime_referrer_fee_percentage; - uint16_t referrer_rewards_percentage; - string name; - string owner_account_auths; - string owner_key_auths; - string owner_address_auths; - string active_account_auths; - string active_key_auths; - string active_address_auths; - account_id_type voting_account; - string votes; -}; -struct asset_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - string symbol; - account_id_type issuer; - bool is_market_issued; - asset_dynamic_data_id_type dynamic_asset_data_id; - optional bitasset_data_id; -}; -struct balance_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - account_id_type owner; - asset_id_type asset_type; - share_type balance; - bool maintenance_flag; -}; -struct limit_order_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - time_point_sec expiration; - account_id_type seller; - share_type for_sale; - price sell_price; - share_type deferred_fee; -}; -struct bitasset_struct { - object_id_type object_id; - fc::time_point_sec block_time; - uint32_t block_number; - string current_feed; - time_point_sec current_feed_publication_time; - time_point_sec feed_expiration_time; +struct adaptor_struct { + fc::mutable_variant_object adapt(const variant_object &obj) { + fc::mutable_variant_object o(obj); + vector keys_to_rename; + for (auto i = o.begin(); i != o.end(); ++i) { + auto &element = (*i).value(); + if (element.is_object()) { + const string &name = (*i).key(); + auto &vo = element.get_object(); + if (vo.contains(name.c_str())) + keys_to_rename.emplace_back(name); + element = adapt(vo); + } else if (element.is_array()) + adapt(element.get_array()); + } + for (const auto &i : keys_to_rename) { + string new_name = i + "_"; + o[new_name] = variant(o[i]); + o.erase(i); + } + if (o.find("owner") != o.end() && o["owner"].is_string()) + { + o["owner_"] = o["owner"].as_string(); + o.erase("owner"); + } + if (o.find("active_special_authority") != o.end()) + { + o["active_special_authority"] = fc::json::to_string(o["active_special_authority"]); + } + if (o.find("owner_special_authority") != o.end()) + { + o["owner_special_authority"] = fc::json::to_string(o["owner_special_authority"]); + } + if (o.find("feeds") != o.end()) + { + o["feeds"] = fc::json::to_string(o["feeds"]); + } + if (o.find("operations") != o.end()) + { + o["operations"] = fc::json::to_string(o["operations"]); + } + + return o; + } + + void adapt(fc::variants &v) { + for (auto &array_element : v) { + if (array_element.is_object()) + array_element = adapt(array_element.get_object()); + else if (array_element.is_array()) + adapt(array_element.get_array()); + else + array_element = array_element.as_string(); + } + } }; } } //graphene::es_objects - -FC_REFLECT( - graphene::es_objects::proposal_struct, - (object_id)(block_time)(block_number)(expiration_time)(review_period_time)(proposed_transaction)(required_active_approvals) - (available_active_approvals)(required_owner_approvals)(available_owner_approvals)(available_key_approvals)(proposer) -) -FC_REFLECT( - graphene::es_objects::account_struct, - (object_id)(block_time)(block_number)(membership_expiration_date)(registrar)(referrer)(lifetime_referrer) - (network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage)(name)(owner_account_auths) - (owner_key_auths)(owner_address_auths)(active_account_auths)(active_key_auths)(active_address_auths)(voting_account)(votes) -) -FC_REFLECT( - graphene::es_objects::asset_struct, - (object_id)(block_time)(block_number)(symbol)(issuer)(is_market_issued)(dynamic_asset_data_id)(bitasset_data_id) -) -FC_REFLECT( - graphene::es_objects::balance_struct, - (object_id)(block_time)(block_number)(owner)(asset_type)(balance)(maintenance_flag) -) -FC_REFLECT( - graphene::es_objects::limit_order_struct, - (object_id)(block_time)(block_number)(expiration)(seller)(for_sale)(sell_price)(deferred_fee) -) -FC_REFLECT( - graphene::es_objects::bitasset_struct, - (object_id)(block_time)(block_number)(current_feed)(current_feed_publication_time) -) \ No newline at end of file diff --git a/libraries/plugins/witness/include/graphene/witness/witness.hpp b/libraries/plugins/witness/include/graphene/witness/witness.hpp index 9292b55e9f..0d2eab27ac 100644 --- a/libraries/plugins/witness/include/graphene/witness/witness.hpp +++ b/libraries/plugins/witness/include/graphene/witness/witness.hpp @@ -81,7 +81,7 @@ class witness_plugin : public graphene::app::plugin { uint32_t _required_witness_participation = 33 * GRAPHENE_1_PERCENT; uint32_t _production_skip_flags = graphene::chain::database::skip_nothing; - std::map _private_keys; + std::map _private_keys; std::set _witnesses; fc::future _block_production_task; diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index ca13357f0c..e069fbcdc3 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -490,6 +490,13 @@ class wallet_api */ asset_bitasset_data_object get_bitasset_data(string asset_name_or_id)const; + /** + * Returns information about the given HTLC object. + * @param htlc_id the id of the HTLC object. + * @returns the information about the HTLC object + */ + variant get_htlc(string htlc_id) const; + /** Lookup the id of a named account. * @param account_name_or_id the name of the account to look up * @returns the id of the named account @@ -1448,6 +1455,44 @@ class wallet_api bool broadcast = false ); + /** + * Create a hashed time lock contract + * + * @param source The account that will reserve the funds (and pay the fee) + * @param destination The account that will receive the funds if the preimage is presented + * @param amount the amount of the asset that is to be traded + * @param asset_symbol The asset that is to be traded + * @param hash_algorithm the algorithm used to generate the hash from the preimage. Can be RIPEMD160, SHA1 or SHA256. + * @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 broadcast true if you wish to broadcast the transaction + */ + signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, + string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, + const uint32_t claim_period_seconds, bool broadcast = false ); + + /**** + * Update a hashed time lock contract + * + * @param htlc_id The object identifier of the HTLC on the blockchain + * @param issuer Who is performing this operation (and paying the fee) + * @param preimage the preimage that should evaluate to the preimage_hash + */ + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::string& preimage, + bool broadcast = false ); + + /***** + * Increase the timelock on an existing HTLC + * + * @param htlc_id The object identifier of the HTLC on the blockchain + * @param issuer Who is performing this operation (and paying the fee) + * @param seconds_to_add how many seconds to add to the existing timelock + * @param broadcast true to broadcast to the network + */ + signed_transaction htlc_extend(string htlc_id, string issuer, const uint32_t seconds_to_add, + bool broadcast = false); + /** * Get information about a vesting balance object. * @@ -1777,6 +1822,7 @@ FC_API( graphene::wallet::wallet_api, (update_asset) (update_asset_issuer) (update_bitasset) + (get_htlc) (update_asset_feed_producers) (publish_asset_feed) (issue_asset) @@ -1798,6 +1844,9 @@ FC_API( graphene::wallet::wallet_api, (update_witness) (create_worker) (update_worker_votes) + (htlc_create) + (htlc_redeem) + (htlc_extend) (get_vesting_balances) (withdraw_vesting) (vote_for_committee_member) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index ac33c180e9..0eabbe35d1 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -60,10 +60,12 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -114,14 +116,16 @@ struct operation_printer ostream& out; const wallet_api_impl& wallet; operation_result result; + operation_history_object hist; std::string fee(const asset& a) const; public: - operation_printer( ostream& out, const wallet_api_impl& wallet, const operation_result& r = operation_result() ) + operation_printer( ostream& out, const wallet_api_impl& wallet, const operation_history_object& obj ) : out(out), wallet(wallet), - result(r) + result(obj.result), + hist(obj) {} typedef std::string result_type; @@ -134,6 +138,8 @@ struct operation_printer std::string operator()(const account_create_operation& op)const; std::string operator()(const account_update_operation& op)const; 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; }; template @@ -259,6 +265,27 @@ struct op_prototype_visitor } }; +class htlc_hash_to_string_visitor +{ +public: + typedef string result_type; + + result_type operator()( const fc::ripemd160& hash )const + { + return "RIPEMD160 " + hash.str(); + } + + result_type operator()( const fc::sha1& hash )const + { + return "SHA1 " + hash.str(); + } + + result_type operator()( const fc::sha256& hash )const + { + return "SHA256 " + hash.str(); + } +}; + class wallet_api_impl { public: @@ -645,6 +672,15 @@ class wallet_api_impl return *opt; } + htlc_object get_htlc(string htlc_id) const + { + htlc_id_type id; + fc::from_variant(htlc_id, id); + auto obj = _remote_db->get_objects( { id }).front(); + htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); + return htlc; + } + asset_id_type get_asset_id(string asset_symbol_or_id) const { FC_ASSERT( asset_symbol_or_id.size() > 0 ); @@ -1735,6 +1771,95 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } + static htlc_hash do_hash( const string& algorithm, const std::string& hash ) + { + string name_upper; + std::transform( algorithm.begin(), algorithm.end(), std::back_inserter(name_upper), ::toupper); + if( name_upper == "RIPEMD160" ) + return fc::ripemd160( hash ); + if( name_upper == "SHA256" ) + return fc::sha256( hash ); + if( name_upper == "SHA1" ) + return fc::sha1( hash ); + FC_THROW_EXCEPTION( fc::invalid_arg_exception, "Unknown algorithm '${a}'", ("a",algorithm) ); + } + + signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, + string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, + const uint32_t claim_period_seconds, bool broadcast = false ) + { + try + { + FC_ASSERT( !self.is_locked() ); + fc::optional asset_obj = get_asset(asset_symbol); + FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); + + htlc_create_operation create_op; + create_op.from = get_account(source).id; + create_op.to = get_account(destination).id; + create_op.amount = asset_obj->amount_from_string(amount); + create_op.claim_period_seconds = claim_period_seconds; + create_op.preimage_hash = do_hash( hash_algorithm, preimage_hash ); + create_op.preimage_size = preimage_size; + + signed_transaction tx; + tx.operations.push_back(create_op); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } FC_CAPTURE_AND_RETHROW( (source)(destination)(amount)(asset_symbol)(hash_algorithm) + (preimage_hash)(preimage_size)(claim_period_seconds)(broadcast) ) + } + + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::vector& preimage, bool broadcast ) + { + try + { + FC_ASSERT( !self.is_locked() ); + fc::optional htlc_obj = get_htlc(htlc_id); + FC_ASSERT(htlc_obj, "Could not find HTLC matching ${htlc}", ("htlc", htlc_id)); + + account_object issuer_obj = get_account(issuer); + + htlc_redeem_operation update_op; + update_op.htlc_id = htlc_obj->id; + update_op.redeemer = issuer_obj.id; + update_op.preimage = preimage; + + signed_transaction tx; + tx.operations.push_back(update_op); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(preimage)(broadcast) ) + } + + signed_transaction htlc_extend ( string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast) + { + try + { + FC_ASSERT( !self.is_locked() ); + fc::optional htlc_obj = get_htlc(htlc_id); + FC_ASSERT(htlc_obj, "Could not find HTLC matching ${htlc}", ("htlc", htlc_id)); + + account_object issuer_obj = get_account(issuer); + + htlc_extend_operation update_op; + update_op.htlc_id = htlc_obj->id; + update_op.update_issuer = issuer_obj.id; + update_op.seconds_to_add = seconds_to_add; + + signed_transaction tx; + tx.operations.push_back(update_op); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction(tx, broadcast); + } FC_CAPTURE_AND_RETHROW( (htlc_id)(issuer)(seconds_to_add)(broadcast) ) + } + vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ) { try { fc::optional vbid = maybe_id( account_name ); @@ -2225,7 +2350,7 @@ class wallet_api_impl auto b = _remote_db->get_block_header(i.block_num); FC_ASSERT(b); ss << b->timestamp.to_iso_string() << " "; - i.op.visit(operation_printer(ss, *this, i.result)); + i.op.visit(operation_printer(ss, *this, i)); ss << " \n"; } @@ -2242,7 +2367,7 @@ class wallet_api_impl auto b = _remote_db->get_block_header(i.block_num); FC_ASSERT(b); ss << b->timestamp.to_iso_string() << " "; - i.op.visit(operation_printer(ss, *this, i.result)); + i.op.visit(operation_printer(ss, *this, i)); ss << " \n"; } @@ -2263,7 +2388,7 @@ class wallet_api_impl auto b = _remote_db->get_block_header(i.block_num); FC_ASSERT(b); ss << b->timestamp.to_iso_string() << " "; - i.op.visit(operation_printer(ss, *this, i.result)); + i.op.visit(operation_printer(ss, *this, i)); ss << " transaction_id : "; ss << d.transaction_id.str(); ss << " \n"; @@ -2305,7 +2430,7 @@ class wallet_api_impl { auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); std::stringstream ss; - r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); + r.trx.operations[0].visit( operation_printer( ss, *this, operation_history_object() ) ); ss << "\n"; for( const auto& out : r.outputs ) { @@ -2318,7 +2443,7 @@ class wallet_api_impl { auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); std::stringstream ss; - r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); + r.trx.operations[0].visit( operation_printer( ss, *this, operation_history_object() ) ); ss << "\n"; for( const auto& out : r.outputs ) { @@ -2882,6 +3007,41 @@ std::string operation_printer::operator()(const asset_create_operation& op) cons return fee(op.fee); } +std::string operation_printer::operator()(const htlc_redeem_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; + + auto fee_asset = wallet.get_asset( op.fee.asset_id ); + auto to = wallet.get_account( op.to ); + 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 ) << ")"; + // 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 ""; +} + std::string operation_result_printer::operator()(const void_result& x) const { return ""; @@ -3012,6 +3172,44 @@ uint64_t wallet_api::get_asset_count()const return my->_remote_db->get_asset_count(); } +signed_transaction wallet_api::htlc_create( string source, string destination, string amount, string asset_symbol, + string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, + const uint32_t claim_period_seconds, bool broadcast) +{ + return my->htlc_create(source, destination, amount, asset_symbol, hash_algorithm, preimage_hash, preimage_size, + claim_period_seconds, broadcast); +} + +variant wallet_api::get_htlc(std::string htlc_id) const +{ + static detail::htlc_hash_to_string_visitor vtor; + + graphene::chain::htlc_object obj = my->get_htlc(htlc_id); + fc::mutable_variant_object ret_val; + ret_val["database_id"] = (std::string)obj.id; + ret_val["from"] = (std::string)((graphene::db::object_id_type)obj.from); + ret_val["to"] = (std::string)((graphene::db::object_id_type)obj.to); + ret_val["amount"] = obj.amount.amount.value; + ret_val["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); + ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); + ret_val["preimage_hash"] = obj.preimage_hash.visit( vtor ); + ret_val["preimage_size"] = obj.preimage_size; + return ret_val; +} + +signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::string& preimage, + bool broadcast) +{ + + return my->htlc_redeem(htlc_id, issuer, std::vector(preimage.begin(), preimage.end()), broadcast); +} + +signed_transaction wallet_api::htlc_extend ( std::string htlc_id, std::string issuer, const uint32_t seconds_to_add, + bool broadcast) +{ + return my->htlc_extend(htlc_id, issuer, seconds_to_add, broadcast); +} + vector wallet_api::get_account_history(string name, int limit)const { vector result; @@ -3060,7 +3258,7 @@ vector wallet_api::get_account_history(string name, int limit) } } std::stringstream ss; - auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result)); + auto memo = o.op.visit(detail::operation_printer(ss, *my, o)); result.push_back( operation_detail{ memo, ss.str(), o } ); } @@ -3108,7 +3306,7 @@ vector wallet_api::get_relative_account_history( start); for (auto &o : current) { std::stringstream ss; - auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result)); + auto memo = o.op.visit(detail::operation_printer(ss, *my, o)); result.push_back(operation_detail{memo, ss.str(), o}); } if (current.size() < std::min(100, limit)) @@ -3152,7 +3350,7 @@ account_history_operation_detail wallet_api::get_account_history_by_operations( auto current = my->_remote_hist->get_account_history_by_operations(always_id, operation_types, start, min_limit); for (auto& obj : current.operation_history_objs) { std::stringstream ss; - auto memo = obj.op.visit(detail::operation_printer(ss, *my, obj.result)); + auto memo = obj.op.visit(detail::operation_printer(ss, *my, obj)); transaction_id_type transaction_id; auto block = get_block(obj.block_num); diff --git a/programs/build_helpers/member_enumerator.cpp b/programs/build_helpers/member_enumerator.cpp index 16452c5bf8..5dcc6156a3 100644 --- a/programs/build_helpers/member_enumerator.cpp +++ b/programs/build_helpers/member_enumerator.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index e955f8d7fa..24a4065e64 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/app/main.cpp b/tests/app/main.cpp index 848d60d664..b68cdf78a0 100644 --- a/tests/app/main.cpp +++ b/tests/app/main.cpp @@ -267,7 +267,7 @@ BOOST_AUTO_TEST_CASE( two_node_network ) BOOST_CHECK_EQUAL( db2->get_balance( GRAPHENE_NULL_ACCOUNT, asset_id_type() ).amount.value, 0 ); BOOST_TEST_MESSAGE( "Creating transfer tx" ); - graphene::chain::signed_transaction trx; + graphene::chain::precomputable_transaction trx; { account_id_type nathan_id = db2->get_index_type().indices().get().find( "nathan" )->id; fc::ecc::private_key nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 393dce4d83..7968f75349 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef _WIN32 #ifndef _WIN32_WINNT @@ -49,6 +50,7 @@ #include #include #endif +#include #include @@ -143,13 +145,15 @@ std::shared_ptr start_application(fc::temp_directory /////////// /// Send a block to the db /// @param app the application +/// @param returned_block the signed block /// @returns true on success /////////// -bool generate_block(std::shared_ptr app) { +bool generate_block(std::shared_ptr app, graphene::chain::signed_block& returned_block) +{ try { fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); auto db = app->chain_database(); - auto block_1 = db->generate_block( db->get_slot_time(1), + returned_block = db->generate_block( db->get_slot_time(1), db->get_scheduled_witness(1), committee_key, database::skip_nothing ); @@ -159,6 +163,12 @@ bool generate_block(std::shared_ptr app) { } } +bool generate_block(std::shared_ptr app) +{ + graphene::chain::signed_block returned_block; + return generate_block(app, returned_block); +} + /////////// /// @brief Skip intermediate blocks, and generate a maintenance block /// @param app the application @@ -566,3 +576,188 @@ BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture ) throw; } } + +/////////////////////// +// Start a server and connect using the same calls as the CLI +// Create an HTLC +/////////////////////// +BOOST_AUTO_TEST_CASE( cli_create_htlc ) +{ + 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); + + 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); + // 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"); + 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); + // 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); + // 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::sha256 preimage_md = fc::sha256::hash(preimage_string); + std::stringstream ss; + for(int 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", "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; + { + 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", "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; + { + 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(); +} diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 806bc0144f..d1af826d39 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -31,12 +31,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include @@ -56,6 +58,13 @@ namespace graphene { namespace chain { using std::cout; using std::cerr; +void clearable_block::clear() +{ + _calculated_merkle_root = checksum_type(); + _signee = fc::ecc::public_key(); + _block_id = block_id_type(); +} + database_fixture::database_fixture(const fc::time_point_sec &initial_timestamp) : app(), db( *app.chain_database() ) { @@ -228,7 +237,7 @@ void database_fixture::verify_asset_supplies( const database& db ) BOOST_CHECK(core_asset_data.fee_pool == 0); const auto& statistics_index = db.get_index_type().indices(); - const auto& balance_index = db.get_index_type().indices(); + const auto& acct_balance_index = db.get_index_type().indices(); const auto& settle_index = db.get_index_type().indices(); const auto& bids = db.get_index_type().indices(); map total_balances; @@ -236,7 +245,7 @@ void database_fixture::verify_asset_supplies( const database& db ) share_type core_in_orders; share_type reported_core_in_orders; - for( const account_balance_object& b : balance_index ) + for( const account_balance_object& b : acct_balance_index ) total_balances[b.asset_type] += b.balance; for( const force_settlement_object& s : settle_index ) total_balances[s.balance.asset_id] += s.balance.amount; @@ -278,6 +287,8 @@ void database_fixture::verify_asset_supplies( const database& db ) total_balances[ vbo.balance.asset_id ] += vbo.balance.amount; for( const fba_accumulator_object& fba : db.get_index_type< simple_index< fba_accumulator_object > >() ) total_balances[ asset_id_type() ] += fba.accumulated_fba_fees; + for( const balance_object& bo : db.get_index_type< balance_index >().indices() ) + total_balances[ bo.balance.asset_id ] += bo.balance.amount; total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget; @@ -286,6 +297,13 @@ void database_fixture::verify_asset_supplies( const database& db ) BOOST_CHECK_EQUAL(item.first(db).dynamic_asset_data_id(db).current_supply.value, item.second.value); } + // htlc + const auto& htlc_idx = db.get_index_type< htlc_index >().indices().get< by_id >(); + for( auto itr = htlc_idx.begin(); itr != htlc_idx.end(); ++itr ) + { + total_balances[itr->amount.asset_id] += itr->amount.amount; + } + for( const asset_object& asset_obj : db.get_index_type().indices() ) { BOOST_CHECK_EQUAL(total_balances[asset_obj.id].value, asset_obj.dynamic_asset_data_id(db).current_supply.value); @@ -454,7 +472,7 @@ const asset_object& database_fixture::create_bitasset( creator.bitasset_opts->short_backing_asset = backing_asset; trx.operations.push_back(std::move(creator)); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.operations.clear(); return db.get(ptx.operation_results[0].get()); } FC_CAPTURE_AND_RETHROW( (name)(flags) ) } @@ -485,7 +503,7 @@ const asset_object& database_fixture::create_prediction_market( creator.is_prediction_market = true; trx.operations.push_back(std::move(creator)); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.operations.clear(); return db.get(ptx.operation_results[0].get()); } FC_CAPTURE_AND_RETHROW( (name)(flags) ) } @@ -505,7 +523,7 @@ const asset_object& database_fixture::create_user_issued_asset( const string& na creator.common_options.issuer_permissions = charge_market_fee; trx.operations.push_back(std::move(creator)); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.operations.clear(); return db.get(ptx.operation_results[0].get()); } @@ -531,7 +549,7 @@ const asset_object& database_fixture::create_user_issued_asset( const string& na trx.operations.push_back(std::move(creator)); set_expiration( db, trx ); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.operations.clear(); return db.get(ptx.operation_results[0].get()); } @@ -544,7 +562,7 @@ void database_fixture::issue_uia( const account_object& recipient, asset amount op.asset_to_issue = amount; op.issue_to_account = recipient.id; trx.operations.push_back(op); - db.push_transaction( trx, ~0 ); + PUSH_TX( db, trx, ~0 ); trx.operations.clear(); } @@ -590,7 +608,7 @@ const account_object& database_fixture::create_account( { trx.operations.push_back(make_account(name, key)); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); auto& result = db.get(ptx.operation_results[0].get()); trx.operations.clear(); return result; @@ -609,7 +627,7 @@ const account_object& database_fixture::create_account( trx.operations.resize(1); trx.operations.back() = (make_account(name, registrar, referrer, referrer_percent, key)); trx.validate(); - auto r = db.push_transaction(trx, ~0); + auto r = PUSH_TX(db, trx, ~0); const auto& result = db.get(r.operation_results[0].get()); trx.operations.clear(); return result; @@ -643,7 +661,7 @@ const account_object& database_fixture::create_account( trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); const account_object& result = db.get(ptx.operation_results[0].get()); trx.operations.clear(); return result; @@ -657,7 +675,7 @@ const committee_member_object& database_fixture::create_committee_member( const op.committee_member_account = owner.id; trx.operations.push_back(op); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.operations.clear(); return db.get(ptx.operation_results[0].get()); } @@ -678,7 +696,7 @@ const witness_object& database_fixture::create_witness( const account_object& ow op.block_signing_key = signing_private_key.get_public_key(); trx.operations.push_back(op); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, skip_flags ); + processed_transaction ptx = PUSH_TX(db, trx, skip_flags ); trx.clear(); return db.get(ptx.operation_results[0].get()); } FC_CAPTURE_AND_RETHROW() } @@ -693,7 +711,7 @@ const worker_object& database_fixture::create_worker( const account_id_type owne op.work_end_date = op.work_begin_date + duration; trx.operations.push_back(op); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); trx.clear(); return db.get(ptx.operation_results[0].get()); } FC_CAPTURE_AND_RETHROW() } @@ -741,7 +759,7 @@ const limit_order_object* database_fixture::create_sell_order( const account_obj trx.operations.push_back(buy_order); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op, fee_core_exchange_rate); trx.validate(); - auto processed = db.push_transaction(trx, ~0); + auto processed = PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); return db.find( processed.operation_results[0].get() ); @@ -755,7 +773,7 @@ asset database_fixture::cancel_limit_order( const limit_order_object& order ) trx.operations.push_back(cancel_order); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - auto processed = db.push_transaction(trx, ~0); + auto processed = PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); return processed.operation_results[0].get(); @@ -791,7 +809,7 @@ void database_fixture::transfer( for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); } trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); verify_asset_supplies(db); trx.operations.clear(); } FC_CAPTURE_AND_RETHROW( (from.id)(to.id)(amount)(fee) ) @@ -809,7 +827,7 @@ void database_fixture::update_feed_producers( const asset_object& mia, flat_set< for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } FC_CAPTURE_AND_RETHROW( (mia)(producers) ) } @@ -829,7 +847,7 @@ void database_fixture::publish_feed( const asset_object& mia, const account_obje for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } @@ -877,7 +895,7 @@ void database_fixture::force_global_settle( const asset_object& what, const pric trx.operations.push_back(sop); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } FC_CAPTURE_AND_RETHROW( (what)(p) ) } @@ -892,7 +910,7 @@ operation_result database_fixture::force_settle( const account_object& who, asse trx.operations.push_back(sop); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - processed_transaction ptx = db.push_transaction(trx, ~0); + processed_transaction ptx = PUSH_TX(db, trx, ~0); const operation_result& op_result = ptx.operation_results.front(); trx.operations.clear(); verify_asset_supplies(db); @@ -912,7 +930,7 @@ const call_order_object* database_fixture::borrow( const account_object& who, as trx.operations.push_back(update); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); @@ -937,7 +955,7 @@ void database_fixture::cover(const account_object& who, asset what, asset collat trx.operations.push_back(update); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } FC_CAPTURE_AND_RETHROW( (who.name)(what)(collateral)(target_cr) ) } @@ -953,7 +971,7 @@ void database_fixture::bid_collateral(const account_object& who, const asset& to trx.operations.push_back(bid); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } FC_CAPTURE_AND_RETHROW( (who.name)(to_bid)(to_cover) ) } @@ -969,7 +987,7 @@ void database_fixture::fund_fee_pool( const account_object& from, const asset_ob for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); set_expiration( db, trx ); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); verify_asset_supplies(db); } @@ -996,7 +1014,7 @@ void database_fixture::upgrade_to_lifetime_member( const account_object& account op.upgrade_to_lifetime_member = true; op.fee = db.get_global_properties().parameters.current_fees->calculate_fee(op); trx.operations = {op}; - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); FC_ASSERT( op.account_to_upgrade(db).is_lifetime_member() ); trx.clear(); verify_asset_supplies(db); @@ -1016,7 +1034,7 @@ void database_fixture::upgrade_to_annual_member(const account_object& account) op.account_to_upgrade = account.get_id(); op.fee = db.get_global_properties().parameters.current_fees->calculate_fee(op); trx.operations = {op}; - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); FC_ASSERT( op.account_to_upgrade(db).is_member(db.head_block_time()) ); trx.clear(); verify_asset_supplies(db); @@ -1193,7 +1211,7 @@ bool _push_block( database& db, const signed_block& b, uint32_t skip_flags /* = processed_transaction _push_transaction( database& db, const signed_transaction& tx, uint32_t skip_flags /* = 0 */ ) { try { - auto pt = db.push_transaction( tx, skip_flags ); + auto pt = db.push_transaction( precomputable_transaction(tx), skip_flags ); database_fixture::verify_asset_supplies(db); return pt; } FC_CAPTURE_AND_RETHROW((tx)) } diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 13e4ead075..fee840cad6 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -170,6 +170,12 @@ extern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP; namespace graphene { namespace chain { +class clearable_block : public signed_block { +public: + /** @brief Clears internal cached values like ID, signing key, Merkle root etc. */ + void clear(); +}; + struct database_fixture { // the reason we use an app is to exercise the indexes of built-in // plugins @@ -333,6 +339,10 @@ struct database_fixture { void transfer( account_id_type from, account_id_type to, const asset& amount, const asset& fee = asset() ); void transfer( const account_object& from, const account_object& to, const asset& amount, const asset& fee = asset() ); void fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount ); + /** + * NOTE: This modifies the database directly. You will probably have to call this each time you + * finish creating a block + */ void enable_fees(); void change_fees( const flat_set< fee_parameters >& new_params, uint32_t new_scale = 0 ); void upgrade_to_lifetime_member( account_id_type account ); diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 813cff42d4..cf9df59142 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -498,7 +498,7 @@ BOOST_AUTO_TEST_CASE( committee_authority ) */ trx.operations.push_back(uop); sign( trx, committee_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 0); BOOST_CHECK(db.get(prop.id).is_authorized_to_execute(db)); @@ -1057,7 +1057,6 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture ) PUSH_TX( db, trx, skip ); trx.operations.push_back( xfer_op ); - trx.signees.clear(); // signees should be invalidated BOOST_TEST_MESSAGE( "Invalidating Alices Signature" ); // Alice's signature is now invalid GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception ); @@ -1169,7 +1168,7 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture ) op.owner = auth; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_transaction_signatures | database::skip_authority_check ); + PUSH_TX( db, tx, database::skip_transaction_signatures ); } ; auto get_active = [&]( @@ -1283,7 +1282,7 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) op.owner = auth; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_transaction_signatures | database::skip_authority_check ); + PUSH_TX( db, tx, database::skip_transaction_signatures ); } ; auto get_active = [&]( @@ -1370,7 +1369,7 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) op.owner = owner; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_transaction_signatures | database::skip_authority_check ); + PUSH_TX( db, tx, database::skip_transaction_signatures ); } ; auto set_auth = [&]( @@ -1462,7 +1461,7 @@ BOOST_FIXTURE_TEST_CASE( missing_owner_auth_test, database_fixture ) op.owner = owner; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_transaction_signatures | database::skip_authority_check ); + PUSH_TX( db, tx, database::skip_transaction_signatures ); } ; auto get_active = [&]( @@ -1677,4 +1676,107 @@ BOOST_AUTO_TEST_CASE( issue_214 ) BOOST_CHECK_EQUAL( top.amount.amount.value, get_balance( bob_id, top.amount.asset_id ) ); } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( irrelevant_signatures ) +{ try { + ACTORS( (alice)(bob) ); + fund( alice ); + + // PK: BTS4vsFgTXJcGQMKCFayF2hrNRfYcKjNZ6Mzk8aw9M4zuWfscPhzE, A: BTSGfxPKKLj6tdTUB7i3mHsd2m7QvPLPy2YA + const fc::ecc::private_key test2 = fc::ecc::private_key::regenerate( fc::sha256::hash( std::string( "test-2" ) ) ); + const public_key_type test2_pub( test2.get_public_key() ); + + // PK: BTS7FXC7S9UH7HEH8QiuJ8Xv1NRJJZd1GomALLm9ffjtH95Tb2ZQB, A: BTSBajRqmdrXqmDpZhJ8sgkGagdeXneHFVeM + const fc::ecc::private_key test3 = fc::ecc::private_key::regenerate( fc::sha256::hash( std::string( "test-3" ) ) ); + const public_key_type test3_pub( test3.get_public_key() ); + + BOOST_REQUIRE( test2_pub.key_data < test3_pub.key_data ); + BOOST_REQUIRE( address( test3_pub ) < address( test2_pub ) ); + + account_update_operation auo; + auo.account = alice_id; + auo.active = authority( 2, test2_pub, 2, test3_pub, 1 ); + + trx.clear(); + set_expiration( db, trx ); + trx.operations.push_back( auo ); + sign( trx, alice_private_key ); + PUSH_TX( db, trx ); + trx.clear(); + + transfer_operation to; + to.amount = asset( 1 ); + to.from = alice_id; + to.to = bob_id; + trx.operations.push_back( to ); + sign( trx, test2 ); + sign( trx, test3 ); + PUSH_TX( db, trx ); +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( self_approving_proposal ) +{ try { + ACTORS( (alice) ); + fund( alice ); + + generate_blocks( HARDFORK_CORE_1479_TIME ); + trx.clear(); + set_expiration( db, trx ); + + proposal_update_operation pup; + pup.fee_paying_account = alice_id; + pup.proposal = proposal_id_type(0); + pup.active_approvals_to_add.insert( alice_id ); + + proposal_create_operation pop; + pop.proposed_ops.emplace_back(pup); + pop.fee_paying_account = alice_id; + pop.expiration_time = db.head_block_time() + fc::days(1); + trx.operations.push_back(pop); + const proposal_id_type pid1 = PUSH_TX( db, trx, ~0 ).operation_results[0].get(); + trx.clear(); + BOOST_REQUIRE_EQUAL( 0, pid1.instance.value ); + db.get(pid1); + + trx.operations.push_back(pup); + PUSH_TX( db, trx, ~0 ); + + // Proposal failed and still exists + db.get(pid1); +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( self_deleting_proposal ) +{ try { + ACTORS( (alice) ); + fund( alice ); + + generate_blocks( HARDFORK_CORE_1479_TIME ); + trx.clear(); + set_expiration( db, trx ); + + proposal_delete_operation pdo; + pdo.fee_paying_account = alice_id; + pdo.proposal = proposal_id_type(0); + pdo.using_owner_authority = false; + + proposal_create_operation pop; + pop.proposed_ops.emplace_back( pdo ); + pop.fee_paying_account = alice_id; + pop.expiration_time = db.head_block_time() + fc::days(1); + trx.operations.push_back( pop ); + const proposal_id_type pid1 = PUSH_TX( db, trx, ~0 ).operation_results[0].get(); + trx.clear(); + BOOST_REQUIRE_EQUAL( 0, pid1.instance.value ); + db.get(pid1); + + proposal_update_operation pup; + pup.fee_paying_account = alice_id; + pup.proposal = proposal_id_type(0); + pup.active_approvals_to_add.insert( alice_id ); + trx.operations.push_back(pup); + PUSH_TX( db, trx, ~0 ); + + // Proposal failed and still exists + db.get(pid1); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/basic_tests.cpp b/tests/tests/basic_tests.cpp index 2157dde75a..97d03ed41b 100644 --- a/tests/tests/basic_tests.cpp +++ b/tests/tests/basic_tests.cpp @@ -408,7 +408,7 @@ BOOST_AUTO_TEST_CASE( scaled_precision ) BOOST_AUTO_TEST_CASE( merkle_root ) { - signed_block block; + clearable_block block; vector tx; vector t; const uint32_t num_tx = 10; @@ -444,6 +444,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dA = d(t[0], t[1]); block.transactions.push_back( tx[1] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dA) ); /* @@ -458,6 +459,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dI = d(dA, dB); block.transactions.push_back( tx[2] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dI) ); /* @@ -472,6 +474,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dI = d(dA, dB); block.transactions.push_back( tx[3] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dI) ); /* @@ -489,6 +492,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dM = d(dI, dJ); block.transactions.push_back( tx[4] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dM) ); /* @@ -506,6 +510,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dM = d(dI, dJ); block.transactions.push_back( tx[5] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dM) ); /* @@ -523,6 +528,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dM = d(dI, dJ); block.transactions.push_back( tx[6] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dM) ); /* @@ -540,6 +546,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dM = d(dI, dJ); block.transactions.push_back( tx[7] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dM) ); /* @@ -560,6 +567,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dO = d(dM, dN); block.transactions.push_back( tx[8] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dO) ); /* @@ -580,6 +588,7 @@ BOOST_AUTO_TEST_CASE( merkle_root ) dO = d(dM, dN); block.transactions.push_back( tx[9] ); + block.clear(); BOOST_CHECK( block.calculate_merkle_root() == c(dO) ); } diff --git a/tests/tests/bitasset_tests.cpp b/tests/tests/bitasset_tests.cpp index 759ea8744e..4fc6097650 100644 --- a/tests/tests/bitasset_tests.cpp +++ b/tests/tests/bitasset_tests.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -447,7 +448,6 @@ BOOST_AUTO_TEST_CASE( hf_890_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; generate_blocks(HARDFORK_615_TIME, true, skip); // get around Graphene issue #615 feed expiration bug @@ -917,7 +917,6 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; generate_blocks( HARDFORK_615_TIME, true, skip ); // get around Graphene issue #615 feed expiration bug diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index e71642726b..76d9c0b75c 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -80,11 +80,12 @@ BOOST_AUTO_TEST_CASE( block_database_test ) FC_ASSERT( !bdb.is_open() ); bdb.open( data_dir.path() ); - signed_block b; + clearable_block b; for( uint32_t i = 0; i < 5; ++i ) { if( i > 0 ) b.previous = b.id(); b.witness = witness_id_type(i+1); + b.clear(); bdb.store( b.id(), b ); auto fetch = bdb.fetch_by_number( b.block_num() ); @@ -601,11 +602,11 @@ BOOST_AUTO_TEST_CASE( undo_pending ) t.to = nathan_id; t.amount = asset(5000); trx.operations.push_back(t); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.clear(); set_expiration( db, trx ); trx.operations.push_back(t); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); BOOST_CHECK(db.get_balance(nathan_id, asset_id_type()).amount == 10000); db.clear_pending(); @@ -687,7 +688,7 @@ BOOST_AUTO_TEST_CASE( duplicate_transactions ) db2.open(dir2.path(), make_genesis, "TEST"); BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() ); - auto skip_sigs = database::skip_transaction_signatures | database::skip_authority_check; + auto skip_sigs = database::skip_transaction_signatures; auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); public_key_type init_account_pub_key = init_account_priv_key.get_public_key(); @@ -756,7 +757,7 @@ BOOST_AUTO_TEST_CASE( tapos ) cop.active = cop.owner; trx.operations.push_back(cop); trx.sign( init_account_priv_key, db1.get_chain_id() ); - db1.push_transaction(trx); + PUSH_TX(db1, trx); b = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); trx.clear(); @@ -766,11 +767,11 @@ BOOST_AUTO_TEST_CASE( tapos ) trx.operations.push_back(t); trx.sign( init_account_priv_key, db1.get_chain_id() ); //relative_expiration is 1, but ref block is 2 blocks old, so this should fail. - GRAPHENE_REQUIRE_THROW(PUSH_TX( db1, trx, database::skip_transaction_signatures | database::skip_authority_check ), fc::exception); + GRAPHENE_REQUIRE_THROW(PUSH_TX( db1, trx, database::skip_transaction_signatures ), fc::exception); set_expiration( db1, trx ); trx.clear_signatures(); trx.sign( init_account_priv_key, db1.get_chain_id() ); - db1.push_transaction(trx, database::skip_transaction_signatures | database::skip_authority_check); + PUSH_TX( db1, trx, database::skip_transaction_signatures ); } catch (fc::exception& e) { edump((e.to_detail_string())); throw; @@ -951,7 +952,7 @@ BOOST_FIXTURE_TEST_CASE( double_sign_check, database_fixture ) for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + PUSH_TX(db, trx, ~0); trx.operations.clear(); t.from = bob.id; @@ -962,22 +963,21 @@ BOOST_FIXTURE_TEST_CASE( double_sign_check, database_fixture ) trx.validate(); BOOST_TEST_MESSAGE( "Verify that not-signing causes an exception" ); - GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), fc::exception ); + GRAPHENE_REQUIRE_THROW( PUSH_TX(db, trx, 0), fc::exception ); BOOST_TEST_MESSAGE( "Verify that double-signing causes an exception" ); sign( trx, bob_private_key ); sign( trx, bob_private_key ); - GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), tx_duplicate_sig ); + GRAPHENE_REQUIRE_THROW( PUSH_TX(db, trx, 0), tx_duplicate_sig ); BOOST_TEST_MESSAGE( "Verify that signing with an extra, unused key fails" ); trx.signatures.pop_back(); sign( trx, generate_private_key("bogus" )); - GRAPHENE_REQUIRE_THROW( db.push_transaction(trx, 0), tx_irrelevant_sig ); + GRAPHENE_REQUIRE_THROW( PUSH_TX(db, trx, 0), tx_irrelevant_sig ); BOOST_TEST_MESSAGE( "Verify that signing once with the proper key passes" ); trx.signatures.pop_back(); - trx.signees.clear(); // signees should be invalidated - db.push_transaction(trx, 0); + PUSH_TX(db, trx, 0); } FC_LOG_AND_RETHROW() } @@ -998,7 +998,7 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) uop.new_parameters.block_interval = 1; cop.proposed_ops.emplace_back(uop); trx.operations.push_back(cop); - db.push_transaction(trx); + PUSH_TX(db, trx); } BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); { @@ -1019,7 +1019,7 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) sign( trx, get_account("init6" ).active.get_keys().front(),init_account_priv_key); sign( trx, get_account("init7" ).active.get_keys().front(),init_account_priv_key); */ - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); } BOOST_TEST_MESSAGE( "Verifying that the interval didn't change immediately" ); @@ -1056,7 +1056,6 @@ BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture ) uint32_t skip_flags = ( database::skip_witness_signature | database::skip_transaction_signatures - | database::skip_authority_check ); const asset_object& core = asset_id_type()(db); @@ -1231,8 +1230,8 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture ) }; // tx's created by ACTORS() have bogus authority, so we need to - // skip_authority_check in the block where they're included - signed_block b1 = generate_block(db, database::skip_authority_check); + // skip_transaction_signatures in the block where they're included + signed_block b1 = generate_block(db, database::skip_transaction_signatures); fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() ); @@ -1244,7 +1243,7 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture ) { optional< signed_block > b = db.fetch_block_by_number( db2.head_block_num()+1 ); db2.push_block(*b, database::skip_witness_signature - |database::skip_authority_check ); + |database::skip_transaction_signatures ); } BOOST_CHECK( db2.get( alice_id ).name == "alice" ); BOOST_CHECK( db2.get( bob_id ).name == "bob" ); @@ -1253,7 +1252,7 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture ) transfer( account_id_type(), alice_id, asset( 1000 ) ); transfer( account_id_type(), bob_id, asset( 1000 ) ); // need to skip authority check here as well for same reason as above - db2.push_block(generate_block(db, database::skip_authority_check), database::skip_authority_check); + db2.push_block(generate_block(db, database::skip_transaction_signatures), database::skip_transaction_signatures); BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 1000); BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 1000); @@ -1478,7 +1477,6 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture ) database::skip_transaction_dupe_check | database::skip_witness_signature | database::skip_transaction_signatures - | database::skip_authority_check ; // Sam is the creator of accounts @@ -1604,10 +1602,9 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture ) trx.operations.push_back( create_op ); // trx.sign( sam_key ); - processed_transaction ptx_create = db.push_transaction( trx, + processed_transaction ptx_create = PUSH_TX( db, trx, database::skip_transaction_dupe_check | - database::skip_transaction_signatures | - database::skip_authority_check + database::skip_transaction_signatures ); account_id_type alice_account_id = ptx_create.operation_results[0] @@ -1642,11 +1639,11 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture ) sign( trx, *owner_privkey[i] ); if( i < int(create_op.owner.weight_threshold-1) ) { - GRAPHENE_REQUIRE_THROW(db.push_transaction(trx), fc::exception); + GRAPHENE_REQUIRE_THROW(PUSH_TX(db, trx), fc::exception); } else { - db.push_transaction( trx, + PUSH_TX( db, trx, database::skip_transaction_dupe_check | database::skip_transaction_signatures ); } @@ -1830,11 +1827,6 @@ BOOST_FIXTURE_TEST_CASE( temp_account_balance, database_fixture ) top.to = GRAPHENE_COMMITTEE_ACCOUNT; trx.operations.push_back( top ); - sign( trx, alice_private_key ); - BOOST_CHECK_THROW( PUSH_TX( db, trx ), fc::assert_exception ); - - generate_blocks( HARDFORK_CORE_1040_TIME ); - set_expiration( db, trx ); trx.clear_signatures(); sign( trx, alice_private_key ); diff --git a/tests/tests/confidential_tests.cpp b/tests/tests/confidential_tests.cpp index bef651db5c..b5046dcdfe 100644 --- a/tests/tests/confidential_tests.cpp +++ b/tests/tests/confidential_tests.cpp @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE( confidential_test ) trx.operations = {to_blind}; sign( trx, dan_private_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); trx.clear_signatures(); BOOST_TEST_MESSAGE( "Transfering from blind to blind with change address" ); @@ -97,7 +97,7 @@ BOOST_AUTO_TEST_CASE( confidential_test ) blind_tr.validate(); trx.operations = {blind_tr}; sign( trx, owner2_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_TEST_MESSAGE( "Attempting to double spend the same commitments" ); blind_tr.fee = core.amount(11); @@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE( confidential_test ) out4.range_proof = fc::ecc::range_proof_sign( 0, out3.commitment, InB1, nonce1, 0, 0, 750-300-11 ); blind_tr.outputs = {out4,out3}; trx.operations = {blind_tr}; - BOOST_REQUIRE_THROW( db.push_transaction(trx, ~0), graphene::chain::blind_transfer_unknown_commitment ); + BOOST_REQUIRE_THROW( PUSH_TX(db, trx, ~0), graphene::chain::blind_transfer_unknown_commitment ); BOOST_TEST_MESSAGE( "Transfering from blind to nathan public" ); @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE( confidential_test ) from_blind.inputs.push_back( {out4.commitment, out4.owner} ); trx.operations = {from_blind}; trx.clear_signatures(); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_REQUIRE_EQUAL( get_balance( nathan, core ), 750-300-10-10 ); diff --git a/tests/tests/database_tests.cpp b/tests/tests/database_tests.cpp index 6b5ea8b96b..6158c2eb05 100644 --- a/tests/tests/database_tests.cpp +++ b/tests/tests/database_tests.cpp @@ -137,4 +137,89 @@ BOOST_AUTO_TEST_CASE( merge_test ) } } +BOOST_AUTO_TEST_CASE( direct_index_test ) +{ try { + try { + const graphene::db::primary_index< account_index, 6 > small_chunkbits( db ); + BOOST_FAIL( "Expected assertion failure!" ); + } catch( const fc::assert_exception& expected ) {} + + graphene::db::primary_index< account_index, 8 > my_accounts( db ); + const auto& direct = my_accounts.get_secondary_index>(); + BOOST_CHECK_EQUAL( 0, my_accounts.indices().size() ); + BOOST_CHECK( nullptr == direct.find( account_id_type( 1 ) ) ); + // BOOST_CHECK_THROW( direct.find( asset_id_type( 1 ) ), fc::assert_exception ); // compile-time error + BOOST_CHECK_THROW( direct.find( object_id_type( asset_id_type( 1 ) ) ), fc::assert_exception ); + BOOST_CHECK_THROW( direct.get( account_id_type( 1 ) ), fc::assert_exception ); + + account_object test_account; + test_account.id = account_id_type(1); + test_account.name = "account1"; + + my_accounts.load( fc::raw::pack( test_account ) ); + + BOOST_CHECK_EQUAL( 1, my_accounts.indices().size() ); + BOOST_CHECK( nullptr == direct.find( account_id_type( 0 ) ) ); + BOOST_CHECK( nullptr == direct.find( account_id_type( 2 ) ) ); + BOOST_CHECK( nullptr != direct.find( account_id_type( 1 ) ) ); + BOOST_CHECK_EQUAL( test_account.name, direct.get( test_account.id ).name ); + + // The following assumes that MAX_HOLE = 100 + test_account.id = account_id_type(102); + test_account.name = "account102"; + // highest insert was 1, direct.next is 2 => 102 is highest allowed instance + my_accounts.load( fc::raw::pack( test_account ) ); + BOOST_CHECK_EQUAL( test_account.name, direct.get( test_account.id ).name ); + + // direct.next is now 103, but index sequence counter is 0 + my_accounts.create( [] ( object& o ) { + account_object& acct = dynamic_cast< account_object& >( o ); + BOOST_CHECK_EQUAL( 0, acct.id.instance() ); + acct.name = "account0"; + } ); + + test_account.id = account_id_type(50); + test_account.name = "account50"; + my_accounts.load( fc::raw::pack( test_account ) ); + + // can handle nested modification + my_accounts.modify( direct.get( account_id_type(0) ), [&direct,&my_accounts] ( object& outer ) { + account_object& _outer = dynamic_cast< account_object& >( outer ); + my_accounts.modify( direct.get( account_id_type(50) ), [] ( object& inner ) { + account_object& _inner = dynamic_cast< account_object& >( inner ); + _inner.referrer = account_id_type(102); + }); + _outer.options.voting_account = GRAPHENE_PROXY_TO_SELF_ACCOUNT; + }); + + // direct.next is still 103, so 204 is not allowed + test_account.id = account_id_type(204); + test_account.name = "account204"; + GRAPHENE_REQUIRE_THROW( my_accounts.load( fc::raw::pack( test_account ) ), fc::assert_exception ); + // This is actually undefined behaviour. The object has been inserted into + // the primary index, but the secondary has refused to insert it! + BOOST_CHECK_EQUAL( 5, my_accounts.indices().size() ); + + uint32_t count = 0; + for( uint32_t i = 0; i < 250; i++ ) + { + const account_object* aptr = dynamic_cast< const account_object* >( my_accounts.find( account_id_type( i ) ) ); + if( aptr ) + { + count++; + BOOST_CHECK( aptr->id.instance() == 0 || aptr->id.instance() == 1 + || aptr->id.instance() == 50 || aptr->id.instance() == 102 ); + BOOST_CHECK_EQUAL( i, aptr->id.instance() ); + BOOST_CHECK_EQUAL( "account" + std::to_string( i ), aptr->name ); + } + } + BOOST_CHECK_EQUAL( count, my_accounts.indices().size() - 1 ); + + GRAPHENE_REQUIRE_THROW( my_accounts.modify( direct.get( account_id_type( 1 ) ), [] ( object& acct ) { + acct.id = account_id_type(2); + }), fc::assert_exception ); + // This is actually undefined behaviour. The object has been modified, but + // but the secondary has not updated its representation +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index 503c9ebc6e..587814815c 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -759,7 +759,6 @@ BOOST_AUTO_TEST_CASE( fee_refund_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -897,7 +896,6 @@ BOOST_AUTO_TEST_CASE( non_core_fee_refund_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -1284,7 +1282,6 @@ BOOST_AUTO_TEST_CASE( hf445_fee_refund_cross_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -1791,7 +1788,6 @@ BOOST_AUTO_TEST_CASE( bsip26_fee_refund_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -2349,7 +2345,6 @@ BOOST_AUTO_TEST_CASE( bsip26_fee_refund_cross_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp new file mode 100644 index 0000000000..98518f6484 --- /dev/null +++ b/tests/tests/htlc_tests.cpp @@ -0,0 +1,639 @@ +/* + * Copyright (c) 2018 jmjatlanta and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// below are for random bytes for htlc +#include +#include +#include +#include +#include +// for htlc timeout +#include +#include + +#include + +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE( htlc_tests, database_fixture ) + +void generate_random_preimage(uint16_t key_size, std::vector& vec) +{ + std::independent_bits_engine rbe; + std::generate(begin(vec), end(vec), std::ref(rbe)); + return; +} + +/**** + * Hash the preimage and put it in a vector + * @param preimage the preimage + * @returns a vector that cointains the sha256 hash of the preimage + */ +template +H hash_it(std::vector preimage) +{ + return H::hash( (char*)preimage.data(), preimage.size() ); +} + +flat_map< uint64_t, graphene::chain::fee_parameters > get_htlc_fee_parameters() +{ + flat_map ret_val; + + htlc_create_operation::fee_parameters_type create_param; + create_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + create_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + ret_val[((operation)htlc_create_operation()).which()] = create_param; + + htlc_redeem_operation::fee_parameters_type redeem_param; + redeem_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + redeem_param.fee_per_kb = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + ret_val[((operation)htlc_redeem_operation()).which()] = redeem_param; + + htlc_extend_operation::fee_parameters_type extend_param; + extend_param.fee = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + extend_param.fee_per_day = 2 * GRAPHENE_BLOCKCHAIN_PRECISION; + ret_val[((operation)htlc_extend_operation()).which()] = extend_param; + + return ret_val; +} + +/**** + * @brief push through a proposal that sets htlc parameters and fees + * @param db_fixture the database connection + */ +void set_committee_parameters(database_fixture* db_fixture) +{ + // htlc fees + // get existing fee_schedule + const chain_parameters& existing_params = db_fixture->db.get_global_properties().parameters; + const fee_schedule_type& existing_fee_schedule = *(existing_params.current_fees); + // create a new fee_shedule + fee_schedule_type new_fee_schedule; + new_fee_schedule.scale = GRAPHENE_100_PERCENT; + // replace the old with the new + flat_map params_map = get_htlc_fee_parameters(); + 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); + } + } + // htlc parameters + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db_fixture->db.get_global_properties().parameters, db_fixture->db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db_fixture->db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation uop; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 19200; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + uop.new_parameters.extensions.value.updatable_htlc_options = new_params; + uop.new_parameters.current_fees = new_fee_schedule; + cop.proposed_ops.emplace_back(uop); + + db_fixture->trx.operations.push_back(cop); + graphene::chain::processed_transaction proc_trx =db_fixture->db.push_transaction(db_fixture->trx); + proposal_id_type good_proposal_id = proc_trx.operation_results[0].get(); + + proposal_update_operation puo; + puo.proposal = good_proposal_id; + puo.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + puo.active_approvals_to_add = { + db_fixture->get_account("init0").get_id(), db_fixture->get_account("init1").get_id(), + db_fixture->get_account("init2").get_id(), db_fixture->get_account("init3").get_id(), + db_fixture->get_account("init4").get_id(), db_fixture->get_account("init5").get_id(), + db_fixture->get_account("init6").get_id(), db_fixture->get_account("init7").get_id()}; + db_fixture->trx.operations.push_back(puo); + db_fixture->sign( db_fixture->trx, db_fixture->init_account_priv_key ); + db_fixture->db.push_transaction(db_fixture->trx); + db_fixture->trx.clear(); + + db_fixture->generate_blocks( good_proposal_id( db_fixture->db ).expiration_time + 5 ); + db_fixture->generate_blocks( db_fixture->db.get_dynamic_global_properties().next_maintenance_time ); + db_fixture->generate_block(); // get the maintenance skip slots out of the way + +} + +void advance_past_hardfork(database_fixture* db_fixture) +{ + db_fixture->generate_blocks(HARDFORK_CORE_1468_TIME); + set_expiration(db_fixture->db, db_fixture->trx); + set_committee_parameters(db_fixture); + set_expiration(db_fixture->db, db_fixture->trx); +} + +BOOST_AUTO_TEST_CASE( htlc_expires ) +{ +try { + ACTORS((alice)(bob)); + + int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + advance_past_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 puts a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + BOOST_TEST_MESSAGE("Alice (who has 100 coins, is transferring 2 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); + 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); + + // let it expire (wait for timeout) + generate_blocks( db.head_block_time() + fc::seconds(120) ); + // verify funds return (minus the fees) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 96 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify Bob cannot execute the contract after the fact +} FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( htlc_fulfilled ) +{ +try { + ACTORS((alice)(bob)); + + int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); + + advance_past_hardfork(this); + + uint16_t preimage_size = 256; + std::vector pre_image(preimage_size); + generate_random_preimage(preimage_size, pre_image); + + graphene::chain::htlc_id_type alice_htlc_id; + // clear everything out + generate_block(); + trx.clear(); + // Alice puts a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 20 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 86400; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = preimage_size; + create_operation.from = alice_id; + create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); + 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(); + } + + // make sure Alice's money gets put on hold (100 - 20 - 4(fee) ) + BOOST_CHECK_EQUAL( get_balance( alice_id, graphene::chain::asset_id_type()), 76 * GRAPHENE_BLOCKCHAIN_PRECISION ); + + // extend the timeout so that Bob has more time + { + graphene::chain::htlc_extend_operation extend_operation; + extend_operation.htlc_id = alice_htlc_id; + extend_operation.seconds_to_add = 86400; + extend_operation.update_issuer = alice_id; + extend_operation.fee = db.current_fee_schedule().calculate_fee( extend_operation ); + trx.operations.push_back( extend_operation ); + sign( trx, alice_private_key ); + PUSH_TX( db, trx, ~0 ); + trx.clear(); + generate_blocks( db.head_block_time() + fc::seconds(87000) ); + set_expiration( db, trx ); + } + + // make sure Alice's money is still on hold, and account for extra fee + BOOST_CHECK_EQUAL( get_balance( alice_id, graphene::chain::asset_id_type()), 72 * GRAPHENE_BLOCKCHAIN_PRECISION ); + + // send a redeem operation to claim the funds + { + graphene::chain::htlc_redeem_operation update_operation; + update_operation.redeemer = bob_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, bob_private_key); + PUSH_TX( db, trx, ~0 ); + generate_block(); + trx.clear(); + } + // verify funds end up in Bob's account (100 + 20 - 4(fee) ) + BOOST_CHECK_EQUAL( get_balance(bob_id, graphene::chain::asset_id_type()), 116 * GRAPHENE_BLOCKCHAIN_PRECISION ); + // verify funds remain out of Alice's acount ( 100 - 20 - 4 ) + BOOST_CHECK_EQUAL( get_balance(alice_id, graphene::chain::asset_id_type()), 72 * GRAPHENE_BLOCKCHAIN_PRECISION ); +} FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( other_peoples_money ) +{ +try { + advance_past_hardfork(this); + + ACTORS((alice)(bob)); + + int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION ); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + 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(); + // Bob attempts to put a contract on the blockchain using Alice's funds + { + graphene::chain::htlc_create_operation create_operation; + create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 3; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = preimage_size; + create_operation.from = alice_id; + create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); + trx.operations.push_back(create_operation); + sign(trx, bob_private_key); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx ), fc::exception); + trx.clear(); + } + // now try the same but with Alice's signature (should work) + { + graphene::chain::htlc_create_operation create_operation; + create_operation.amount = graphene::chain::asset( 1 * GRAPHENE_BLOCKCHAIN_PRECISION ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 3; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = preimage_size; + create_operation.from = alice_id; + create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + PUSH_TX( db, trx ); + trx.clear(); + } +} FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( htlc_hardfork_test ) +{ + try { + { + // try to set committee parameters before hardfork + proposal_create_operation cop = proposal_create_operation::committee_proposal( + db.get_global_properties().parameters, db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + committee_member_update_global_parameters_operation cmuop; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 2048; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + cmuop.new_parameters.extensions.value.updatable_htlc_options = new_params; + cop.proposed_ops.emplace_back(cmuop); + trx.operations.push_back(cop); + // update with signatures + proposal_update_operation uop; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; + trx.operations.push_back(uop); + sign( trx, init_account_priv_key ); + BOOST_TEST_MESSAGE("Sending proposal."); + GRAPHENE_CHECK_THROW(db.push_transaction(trx), fc::exception); + BOOST_TEST_MESSAGE("Verifying that proposal did not succeeed."); + BOOST_CHECK(!db.get_global_properties().parameters.extensions.value.updatable_htlc_options.valid()); + trx.clear(); + } + + { + BOOST_TEST_MESSAGE("Attempting to set HTLC 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 + fee_schedule_type new_fee_schedule; + new_fee_schedule.scale = existing_fee_schedule.scale; + // replace the old with the new + flat_map params_map = get_htlc_fee_parameters(); + 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(); + } + + // now things should start working... + BOOST_TEST_MESSAGE("Advancing to HTLC hardfork time."); + advance_past_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" ); + { + // 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 + fee_schedule_type new_fee_schedule; + new_fee_schedule.scale = existing_fee_schedule.scale; + // replace the old with the new + flat_map params_map = get_htlc_fee_parameters(); + 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; + graphene::chain::htlc_options new_params; + new_params.max_preimage_size = 2048; + new_params.max_timeout_secs = 60 * 60 * 24 * 28; + uop.new_parameters.extensions.value.updatable_htlc_options = new_params; + uop.new_parameters.current_fees = new_fee_schedule; + cop.proposed_ops.emplace_back(uop); + trx.operations.push_back(cop); + graphene::chain::processed_transaction proc_trx =db.push_transaction(trx); + good_proposal_id = proc_trx.operation_results[0].get(); + } + + BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); + { + proposal_update_operation uop; + uop.proposal = good_proposal_id; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; + trx.operations.push_back(uop); + sign( trx, init_account_priv_key ); + db.push_transaction(trx); + BOOST_CHECK(good_proposal_id(db).is_authorized_to_execute(db)); + } + BOOST_TEST_MESSAGE( "Verifying that the parameters didn't change immediately" ); + + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u); + + BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); + generate_blocks(good_proposal_id(db).expiration_time + 5); + BOOST_TEST_MESSAGE( "Verify that the parameters still have not changed" ); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 19200u); + + BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" ); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); // get the maintenance skip slots out of the way + + BOOST_TEST_MESSAGE( "Verify that the change has been implemented" ); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.extensions.value.updatable_htlc_options->max_preimage_size, 2048u); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.current_fees->get().fee, 2 * GRAPHENE_BLOCKCHAIN_PRECISION); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( htlc_before_hardfork ) +{ try { + ACTORS((alice)(bob)); + + int64_t init_balance(100000); + + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + + 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; + // clear everything out + generate_block(); + trx.clear(); + + // Alice tries to put a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 10000 ); + 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; + trx.operations.push_back(create_operation); + sign(trx, alice_private_key); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx, ~0), fc::exception); + trx.clear(); + } + + // Propose htlc_create + { + proposal_create_operation pco; + pco.expiration_time = db.head_block_time() + fc::minutes(1); + pco.fee_paying_account = alice_id; + + graphene::chain::htlc_create_operation create_operation; + create_operation.amount = graphene::chain::asset( 10000 ); + 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; + + pco.proposed_ops.emplace_back( create_operation ); + trx.operations.push_back( pco ); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception ); + trx.clear(); + } + + // Propose htlc_redeem + { + proposal_create_operation pco; + pco.expiration_time = db.head_block_time() + fc::minutes(1); + pco.fee_paying_account = alice_id; + + graphene::chain::htlc_redeem_operation rop; + rop.redeemer = bob_id; + rop.htlc_id = alice_htlc_id; + string preimage_str = "Arglebargle"; + rop.preimage.insert( rop.preimage.begin(), preimage_str.begin(), preimage_str.end() ); + + pco.proposed_ops.emplace_back( rop ); + trx.operations.push_back( pco ); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception ); + trx.clear(); + } + + // Propose htlc_extend + { + proposal_create_operation pco; + pco.expiration_time = db.head_block_time() + fc::minutes(1); + pco.fee_paying_account = alice_id; + + graphene::chain::htlc_extend_operation xop; + xop.htlc_id = alice_htlc_id; + xop.seconds_to_add = 100; + xop.update_issuer = alice_id; + + pco.proposed_ops.emplace_back( xop ); + trx.operations.push_back( pco ); + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::assert_exception ); + trx.clear(); + } +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( fee_calculations ) +{ + // create + { + htlc_create_operation::fee_parameters_type create_fee; + create_fee.fee = 2; + create_fee.fee_per_day = 2; + htlc_create_operation create; + // no days + create.claim_period_seconds = 0; + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 2 ); + // exactly 1 day + create.claim_period_seconds = 60 * 60 * 24; + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 4 ); + // tad over a day + create.claim_period_seconds++; + BOOST_CHECK_EQUAL( create.calculate_fee(create_fee).value, 6 ); + } + // redeem + { + htlc_redeem_operation::fee_parameters_type redeem_fee; + redeem_fee.fee_per_kb = 2; + redeem_fee.fee = 2; + htlc_redeem_operation redeem; + // no preimage + redeem.preimage = std::vector(); + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 2 ) ; + // exactly 1KB + std::string test(1024, 'a'); + redeem.preimage = std::vector( test.begin(), test.end() ); + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 4 ) ; + // just 1 byte over 1KB + std::string larger(1025, 'a'); + redeem.preimage = std::vector( larger.begin(), larger.end() ); + BOOST_CHECK_EQUAL( redeem.calculate_fee( redeem_fee ).value, 6 ) ; + } + // extend + { + htlc_extend_operation::fee_parameters_type extend_fee; + extend_fee.fee = 2; + extend_fee.fee_per_day = 2; + htlc_extend_operation extend; + // no days + extend.seconds_to_add = 0; + BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 2 ); + // exactly 1 day + extend.seconds_to_add = 60 * 60 * 24; + BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 4 ); + // 1 day and 1 second + extend.seconds_to_add++; + BOOST_CHECK_EQUAL( extend.calculate_fee( extend_fee ).value, 6 ); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 5e4168562d..398eb42b11 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE( asset_settle_cancel_operation_test_after_hf588 ) pcop.proposed_ops.emplace_back(ascop); trx.operations.push_back(pcop); - BOOST_CHECK_EXCEPTION(db.push_transaction(trx), fc::assert_exception, + BOOST_CHECK_EXCEPTION(PUSH_TX(db, trx), fc::assert_exception, [](fc::assert_exception const &e) -> bool { std::cout << e.to_string() << std::endl; if (e.to_string().find("Virtual operation") != std::string::npos) @@ -333,7 +333,7 @@ BOOST_AUTO_TEST_CASE( asset_settle_cancel_operation_test_after_hf588 ) trx.operations.push_back(pcop); - BOOST_CHECK_EXCEPTION(db.push_transaction(trx), fc::assert_exception, + BOOST_CHECK_EXCEPTION(PUSH_TX(db, trx), fc::assert_exception, [](fc::assert_exception const &e) -> bool { std::cout << e.to_string() << std::endl; if (e.to_string().find("Virtual operation") != std::string::npos) @@ -656,7 +656,7 @@ BOOST_AUTO_TEST_CASE( call_order_update_target_cr_hardfork_time_test ) tx.operations.push_back( prop ); db.current_fee_schedule().set_fee( tx.operations.back() ); set_expiration( db, tx ); - db.push_transaction( tx, ~0 ); + PUSH_TX( db, tx, ~0 ); }; BOOST_TEST_MESSAGE( "bob tries to propose a proposal with target_cr set, " @@ -1940,7 +1940,7 @@ BOOST_AUTO_TEST_CASE( reserve_asset_test ) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } ; auto _issue_uia = [&]( const account_object& recipient, asset amount ) @@ -1952,7 +1952,7 @@ BOOST_AUTO_TEST_CASE( reserve_asset_test ) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } ; int64_t init_balance = 10000; @@ -2043,7 +2043,7 @@ BOOST_AUTO_TEST_CASE( cover_with_collateral_test ) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } ; // margin call requirement: 1.75x diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index ed43464ba6..15df1b2ece 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -138,7 +139,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_create_before_hardfork_23 ) trx.operations.back() = op; } sign( trx, nathan_private_key ); - db.push_transaction( trx ); + PUSH_TX( db, trx ); trx.clear(); } FC_LOG_AND_RETHROW() } @@ -183,7 +184,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_create_after_hardfork_23 ) trx.operations.back() = op; } sign( trx, nathan_private_key ); - db.push_transaction( trx ); + PUSH_TX( db, trx ); trx.clear(); } FC_LOG_AND_RETHROW() } @@ -564,7 +565,6 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_whitelist_asset_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -1027,7 +1027,7 @@ BOOST_AUTO_TEST_CASE( feed_limit_test ) op.issuer = bit_usd.issuer; trx.operations = {op}; sign( trx, nathan_private_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_TEST_MESSAGE("Checking current_feed is null"); BOOST_CHECK(bitasset.current_feed.settlement_price.is_null()); @@ -1037,7 +1037,7 @@ BOOST_AUTO_TEST_CASE( feed_limit_test ) trx.clear(); trx.operations = {op}; sign( trx, nathan_private_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_TEST_MESSAGE("Checking current_feed is not null"); BOOST_CHECK(!bitasset.current_feed.settlement_price.is_null()); @@ -1051,7 +1051,6 @@ BOOST_AUTO_TEST_CASE( witness_create ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; generate_block(skip); @@ -1237,7 +1236,6 @@ BOOST_AUTO_TEST_CASE( global_settle_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -1615,7 +1613,6 @@ BOOST_AUTO_TEST_CASE( force_settle_test ) | database::skip_transaction_dupe_check | database::skip_block_size_check | database::skip_tapos_check - | database::skip_authority_check | database::skip_merkle_check ; @@ -1910,13 +1907,13 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) trx.operations = {op}; _sign( trx, n_key ); // Fail because I'm claiming from an address which hasn't signed - GRAPHENE_CHECK_THROW(db.push_transaction(trx), tx_missing_other_auth); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), tx_missing_other_auth); trx.clear(); op.balance_to_claim = balance_id_type(); op.balance_owner_key = n_key.get_public_key(); trx.operations = {op}; _sign( trx, n_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); // Not using fixture's get_balance() here because it uses fixture's db, not my override BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 1); @@ -1944,7 +1941,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) _sign( trx, n_key ); _sign( trx, v1_key ); // Attempting to claim 1 from a balance with 0 available - GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_invalid_claim_amount); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_invalid_claim_amount); op.balance_to_claim = vesting_balance_2.id; op.total_claimed.amount = 151; @@ -1954,7 +1951,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) _sign( trx, n_key ); _sign( trx, v2_key ); // Attempting to claim 151 from a balance with 150 available - GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_invalid_claim_amount); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_invalid_claim_amount); op.balance_to_claim = vesting_balance_2.id; op.total_claimed.amount = 100; @@ -1963,7 +1960,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) trx.clear_signatures(); _sign( trx, n_key ); _sign( trx, v2_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 101); BOOST_CHECK_EQUAL(vesting_balance_2.balance.amount.value, 300); @@ -1973,7 +1970,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) _sign( trx, n_key ); _sign( trx, v2_key ); // Attempting to claim twice within a day - GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_claimed_too_often); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_claimed_too_often); db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, skip_flags); slot = db.get_slot_at_time(vesting_balance_1.vesting_policy->begin_timestamp + 60); @@ -1987,7 +1984,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) trx.clear_signatures(); _sign( trx, n_key ); _sign( trx, v1_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK(db.find_object(op.balance_to_claim) == nullptr); BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 601); @@ -1999,7 +1996,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) _sign( trx, n_key ); _sign( trx, v2_key ); // Attempting to claim twice within a day - GRAPHENE_CHECK_THROW(db.push_transaction(trx), balance_claim_claimed_too_often); + GRAPHENE_CHECK_THROW(PUSH_TX(db, trx), balance_claim_claimed_too_often); db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, skip_flags); slot = db.get_slot_at_time(db.head_block_time() + fc::days(1)); @@ -2011,7 +2008,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test ) trx.clear_signatures(); _sign( trx, n_key ); _sign( trx, v2_key ); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK(db.find_object(op.balance_to_claim) == nullptr); BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 901); } FC_LOG_AND_RETHROW() } @@ -2031,7 +2028,7 @@ BOOST_AUTO_TEST_CASE(transfer_with_memo) { op.memo->set_message(alice_private_key, bob_public_key, "Dear Bob,\n\nMoney!\n\nLove, Alice"); trx.operations = {op}; trx.sign(alice_private_key, db.get_chain_id()); - db.push_transaction(trx); + PUSH_TX(db, trx); BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 500); BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 500); @@ -2057,7 +2054,7 @@ BOOST_AUTO_TEST_CASE(zero_second_vbo) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } enable_fees(); upgrade_to_lifetime_member(alice_id); @@ -2148,6 +2145,14 @@ BOOST_AUTO_TEST_CASE(zero_second_vbo) BOOST_CHECK( vbid(db).get_allowed_withdraw(db.head_block_time()) == asset(0) ); generate_block(); BOOST_CHECK( vbid(db).get_allowed_withdraw(db.head_block_time()) == asset(10000) ); + + /* + db.get_index_type< simple_index >().inspect_all_objects( + [&](const object& o) + { + ilog( "budget: ${brec}", ("brec", static_cast(o)) ); + }); + */ } } FC_LOG_AND_RETHROW() } @@ -2424,18 +2429,18 @@ BOOST_AUTO_TEST_CASE( buyback ) sign( tx, philbin_private_key ); // Alice and Philbin signed, but asset issuer is invalid - GRAPHENE_CHECK_THROW( db.push_transaction(tx), account_create_buyback_incorrect_issuer ); + GRAPHENE_CHECK_THROW( PUSH_TX(db, tx), account_create_buyback_incorrect_issuer ); tx.clear_signatures(); tx.operations.back().get< account_create_operation >().extensions.value.buyback_options->asset_to_buy_issuer = izzy_id; sign( tx, philbin_private_key ); // Izzy didn't sign - GRAPHENE_CHECK_THROW( db.push_transaction(tx), tx_missing_active_auth ); + GRAPHENE_CHECK_THROW( PUSH_TX(db, tx), tx_missing_active_auth ); sign( tx, izzy_private_key ); // OK - processed_transaction ptx = db.push_transaction( tx ); + processed_transaction ptx = PUSH_TX( db, tx ); rex_id = ptx.operation_results.back().get< object_id_type >(); // Try to create another account rex2 which is bbo on same asset @@ -2443,7 +2448,7 @@ BOOST_AUTO_TEST_CASE( buyback ) tx.operations.back().get< account_create_operation >().name = "rex2"; sign( tx, izzy_private_key ); sign( tx, philbin_private_key ); - GRAPHENE_CHECK_THROW( db.push_transaction(tx), account_create_buyback_already_exists ); + GRAPHENE_CHECK_THROW( PUSH_TX(db, tx), account_create_buyback_already_exists ); } // issue some BUYME to Alice diff --git a/tests/tests/swan_tests.cpp b/tests/tests/swan_tests.cpp index f0d7ce9aab..5b9b5c3f20 100644 --- a/tests/tests/swan_tests.cpp +++ b/tests/tests/swan_tests.cpp @@ -491,4 +491,28 @@ BOOST_AUTO_TEST_CASE( revive_empty_with_bid ) } } +/** Creates a black swan, bids on more than outstanding debt + */ +BOOST_AUTO_TEST_CASE( overflow ) +{ try { + init_standard_swan( 700 ); + + wait_for_hf_core_216(); + + bid_collateral( borrower(), back().amount(2200), swan().amount(GRAPHENE_MAX_SHARE_SUPPLY - 1) ); + bid_collateral( borrower2(), back().amount(2100), swan().amount(1399) ); + set_feed(1, 2); + wait_for_maintenance(); + + auto& call_idx = db.get_index_type().indices().get(); + auto itr = call_idx.find( boost::make_tuple(_borrower, _swan) ); + BOOST_REQUIRE( itr != call_idx.end() ); + BOOST_CHECK_EQUAL( 1, itr->debt.value ); + itr = call_idx.find( boost::make_tuple(_borrower2, _swan) ); + BOOST_REQUIRE( itr != call_idx.end() ); + BOOST_CHECK_EQUAL( 1399, itr->debt.value ); + + BOOST_CHECK( !swan().bitasset_data(db).has_settlement() ); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/uia_tests.cpp b/tests/tests/uia_tests.cpp index 95b4fdf4ec..0efc4c5ef9 100644 --- a/tests/tests/uia_tests.cpp +++ b/tests/tests/uia_tests.cpp @@ -379,7 +379,7 @@ BOOST_AUTO_TEST_CASE( transfer_restricted_test ) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } ; const asset_object& uia = create_user_issued_asset( "TXRX", sam, transfer_restricted ); @@ -398,7 +398,7 @@ BOOST_AUTO_TEST_CASE( transfer_restricted_test ) transaction tx; tx.operations.push_back( op ); set_expiration( db, tx ); - PUSH_TX( db, tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ); } ; BOOST_TEST_MESSAGE( "Enable transfer_restricted, send fails" ); diff --git a/tests/tests/voting_tests.cpp b/tests/tests/voting_tests.cpp index 34f39e48c1..141c19e0f3 100644 --- a/tests/tests/voting_tests.cpp +++ b/tests/tests/voting_tests.cpp @@ -414,5 +414,130 @@ BOOST_AUTO_TEST_CASE(invalid_voting_account) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(last_voting_date) +{ + try + { + ACTORS((alice)); + + transfer(committee_account, alice_id, asset(100)); + + // we are going to vote for this witness + auto witness1 = witness_id_type(1)(db); + + auto stats_obj = db.get_account_stats_by_owner(alice_id); + BOOST_CHECK_EQUAL(stats_obj.last_vote_time.sec_since_epoch(), 0); + + // alice votes + graphene::chain::account_update_operation op; + op.account = alice_id; + op.new_options = alice.options; + op.new_options->votes.insert(witness1.vote_id); + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0 ); + + auto now = db.head_block_time().sec_since_epoch(); + + // last_vote_time is updated for alice + stats_obj = db.get_account_stats_by_owner(alice_id); + BOOST_CHECK_EQUAL(stats_obj.last_vote_time.sec_since_epoch(), now); + + } FC_LOG_AND_RETHROW() +} +BOOST_AUTO_TEST_CASE(last_voting_date_proxy) +{ + try + { + ACTORS((alice)(proxy)(bob)); + + transfer(committee_account, alice_id, asset(100)); + transfer(committee_account, bob_id, asset(200)); + transfer(committee_account, proxy_id, asset(300)); + + generate_block(); + + // witness to vote for + auto witness1 = witness_id_type(1)(db); + + // round1: alice changes proxy, this is voting activity + { + graphene::chain::account_update_operation op; + op.account = alice_id; + op.new_options = alice_id(db).options; + op.new_options->voting_account = proxy_id; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX( db, trx, ~0 ); + } + // alice last_vote_time is updated + auto alice_stats_obj = db.get_account_stats_by_owner(alice_id); + auto round1 = db.head_block_time().sec_since_epoch(); + BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1); + + generate_block(); + + // round 2: alice update account but no proxy or voting changes are done + { + graphene::chain::account_update_operation op; + op.account = alice_id; + op.new_options = alice_id(db).options; + trx.operations.push_back(op); + sign(trx, alice_private_key); + set_expiration( db, trx ); + PUSH_TX( db, trx, ~0 ); + } + // last_vote_time is not updated + auto round2 = db.head_block_time().sec_since_epoch(); + alice_stats_obj = db.get_account_stats_by_owner(alice_id); + BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1); + + generate_block(); + + // round 3: bob votes + { + graphene::chain::account_update_operation op; + op.account = bob_id; + op.new_options = bob_id(db).options; + op.new_options->votes.insert(witness1.vote_id); + trx.operations.push_back(op); + sign(trx, bob_private_key); + set_expiration( db, trx ); + PUSH_TX(db, trx, ~0); + } + + // last_vote_time for bob is updated as he voted + auto round3 = db.head_block_time().sec_since_epoch(); + auto bob_stats_obj = db.get_account_stats_by_owner(bob_id); + BOOST_CHECK_EQUAL(bob_stats_obj.last_vote_time.sec_since_epoch(), round3); + + generate_block(); + + // round 4: proxy votes + { + graphene::chain::account_update_operation op; + op.account = proxy_id; + op.new_options = proxy_id(db).options; + op.new_options->votes.insert(witness1.vote_id); + trx.operations.push_back(op); + sign(trx, proxy_private_key); + PUSH_TX(db, trx, ~0); + } + + // proxy just voted so the last_vote_time is updated + auto round4 = db.head_block_time().sec_since_epoch(); + auto proxy_stats_obj = db.get_account_stats_by_owner(proxy_id); + BOOST_CHECK_EQUAL(proxy_stats_obj.last_vote_time.sec_since_epoch(), round4); + + // alice haves proxy, proxy votes but last_vote_time is not updated for alice + alice_stats_obj = db.get_account_stats_by_owner(alice_id); + BOOST_CHECK_EQUAL(alice_stats_obj.last_vote_time.sec_since_epoch(), round1); + + // bob haves nothing to do with proxy so last_vote_time is not updated + bob_stats_obj = db.get_account_stats_by_owner(bob_id); + BOOST_CHECK_EQUAL(bob_stats_obj.last_vote_time.sec_since_epoch(), round3); + + } FC_LOG_AND_RETHROW() +} BOOST_AUTO_TEST_SUITE_END() From acab2a0bb766452efdb784659d763ad024c9ed14 Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Wed, 23 Jan 2019 16:59:13 +0300 Subject: [PATCH 106/161] Modified hash function - vbo_mfs_hash() --- .../include/graphene/chain/vesting_balance_object.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp index 226dbf0c0a..ec3ab0c850 100644 --- a/libraries/chain/include/graphene/chain/vesting_balance_object.hpp +++ b/libraries/chain/include/graphene/chain/vesting_balance_object.hpp @@ -199,20 +199,18 @@ namespace graphene { namespace chain { * @ingroup object_index */ struct by_account; + // by_vesting_type index MUST NOT be used for iterating because order is not well-defined. struct by_vesting_type; namespace detail { /** Calculate a hash for account_id_type and asset_id. - Use object_id.hash_value function to calculate sum of hashes from account_id_type and asset_id. - then subducts a result from std::numeric_limits::max(). - object_id.hash_value returns uint64_t but only 48 bits are significant - so std::numeric_limits::max() - (48 bits + 48 bits) always correct from point of natural numbers + Use 48 bit value (see object_id.hpp) for account_id and XOR it with 24 bit for asset_id */ inline uint64_t vbo_mfs_hash(const account_id_type& account_id, const asset_id_type& asset_id) { - return std::numeric_limits::max() - ( hash_value(account_id) + hash_value(asset_id) ); + return (asset_id.instance.value << 40) ^ account_id.instance.value; } /** From a2d5fac585a1436c1328ec711e82cbe5ce8896ea Mon Sep 17 00:00:00 2001 From: Alfredo Date: Thu, 24 Jan 2019 15:21:44 -0300 Subject: [PATCH 107/161] accurate block generation for hf1270 in market and swan tests --- tests/tests/market_tests.cpp | 60 +++++++++++++++++++++++++----------- tests/tests/swan_tests.cpp | 5 +-- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/tests/tests/market_tests.cpp b/tests/tests/market_tests.cpp index b08257fe24..933ed8cd00 100644 --- a/tests/tests/market_tests.cpp +++ b/tests/tests/market_tests.cpp @@ -232,10 +232,14 @@ BOOST_AUTO_TEST_CASE(issue_338_etc) BOOST_AUTO_TEST_CASE(hardfork_core_338_test) { try { + auto mi = db.get_global_properties().parameters.maintenance_interval; + if(hf1270) - generate_blocks(HARDFORK_CORE_1270_TIME); + generate_blocks(HARDFORK_CORE_1270_TIME - mi); else - generate_blocks(HARDFORK_CORE_343_TIME); + generate_blocks(HARDFORK_CORE_343_TIME - mi); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); set_expiration( db, trx ); @@ -411,10 +415,14 @@ BOOST_AUTO_TEST_CASE(hardfork_core_338_test) BOOST_AUTO_TEST_CASE(hardfork_core_453_test) { try { + auto mi = db.get_global_properties().parameters.maintenance_interval; + if(hf1270) - generate_blocks(HARDFORK_CORE_1270_TIME); + generate_blocks(HARDFORK_CORE_1270_TIME - mi); else - generate_blocks(HARDFORK_CORE_343_TIME); + generate_blocks(HARDFORK_CORE_343_TIME - mi); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); set_expiration( db, trx ); @@ -492,10 +500,14 @@ BOOST_AUTO_TEST_CASE(hardfork_core_453_test) BOOST_AUTO_TEST_CASE(hardfork_core_625_big_limit_order_test) { try { + auto mi = db.get_global_properties().parameters.maintenance_interval; + if(hf1270) - generate_blocks(HARDFORK_CORE_1270_TIME); + generate_blocks(HARDFORK_CORE_1270_TIME - mi); else - generate_blocks(HARDFORK_CORE_625_TIME); + generate_blocks(HARDFORK_CORE_625_TIME - mi); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); set_expiration( db, trx ); @@ -1203,10 +1215,14 @@ BOOST_AUTO_TEST_CASE(hard_fork_343_cross_test) BOOST_AUTO_TEST_CASE(target_cr_test_limit_call) { try { + auto mi = db.get_global_properties().parameters.maintenance_interval; + if(hf1270) - generate_blocks(HARDFORK_CORE_1270_TIME); + generate_blocks(HARDFORK_CORE_1270_TIME - mi); else - generate_blocks(HARDFORK_CORE_834_TIME); + generate_blocks(HARDFORK_CORE_834_TIME - mi); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); set_expiration( db, trx ); @@ -1383,10 +1399,14 @@ BOOST_AUTO_TEST_CASE(target_cr_test_limit_call) BOOST_AUTO_TEST_CASE(target_cr_test_call_limit) { try { + auto mi = db.get_global_properties().parameters.maintenance_interval; + if(hf1270) - generate_blocks(HARDFORK_CORE_1270_TIME); + generate_blocks(HARDFORK_CORE_1270_TIME - mi); else - generate_blocks(HARDFORK_CORE_834_TIME); + generate_blocks(HARDFORK_CORE_834_TIME - mi); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); set_expiration( db, trx ); @@ -1521,8 +1541,9 @@ BOOST_AUTO_TEST_CASE(target_cr_test_call_limit) BOOST_AUTO_TEST_CASE(mcr_bug_increase_before1270) { try { - generate_blocks(HARDFORK_CORE_453_TIME); - + auto mi = db.get_global_properties().parameters.maintenance_interval; + generate_blocks(HARDFORK_CORE_453_TIME - mi); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); generate_block(); set_expiration( db, trx ); @@ -1585,8 +1606,9 @@ BOOST_AUTO_TEST_CASE(mcr_bug_increase_before1270) BOOST_AUTO_TEST_CASE(mcr_bug_increase_after1270) { try { - generate_blocks(HARDFORK_CORE_1270_TIME); - + auto mi = db.get_global_properties().parameters.maintenance_interval; + generate_blocks(HARDFORK_CORE_1270_TIME - mi); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); generate_block(); set_expiration( db, trx ); @@ -1650,8 +1672,9 @@ BOOST_AUTO_TEST_CASE(mcr_bug_increase_after1270) BOOST_AUTO_TEST_CASE(mcr_bug_decrease_before1270) { try { - generate_blocks(HARDFORK_CORE_453_TIME); - + auto mi = db.get_global_properties().parameters.maintenance_interval; + generate_blocks(HARDFORK_CORE_453_TIME - mi); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); generate_block(); set_expiration( db, trx ); @@ -1719,8 +1742,9 @@ BOOST_AUTO_TEST_CASE(mcr_bug_decrease_before1270) BOOST_AUTO_TEST_CASE(mcr_bug_decrease_after1270) { try { - generate_blocks(HARDFORK_CORE_1270_TIME); - + auto mi = db.get_global_properties().parameters.maintenance_interval; + generate_blocks(HARDFORK_CORE_1270_TIME - mi); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); generate_block(); set_expiration( db, trx ); diff --git a/tests/tests/swan_tests.cpp b/tests/tests/swan_tests.cpp index eb36bb67d7..25b59f232e 100644 --- a/tests/tests/swan_tests.cpp +++ b/tests/tests/swan_tests.cpp @@ -115,8 +115,9 @@ struct swan_fixture : database_fixture { generate_block(); } void wait_for_hf_core_1270() { - generate_blocks( HARDFORK_CORE_1270_TIME ); - generate_block(); + auto mi = db.get_global_properties().parameters.maintenance_interval; + generate_blocks(HARDFORK_CORE_1270_TIME - mi); + wait_for_maintenance(); } void wait_for_maintenance() { From 8466e198badff212eb95ad7254157630459630eb Mon Sep 17 00:00:00 2001 From: Alfredo Date: Fri, 25 Jan 2019 17:03:34 -0300 Subject: [PATCH 108/161] changes in bitasset_tests/hf_935_test --- tests/tests/bitasset_tests.cpp | 67 ++++++++++------------------------ 1 file changed, 20 insertions(+), 47 deletions(-) diff --git a/tests/tests/bitasset_tests.cpp b/tests/tests/bitasset_tests.cpp index 017e20a94a..30466a9beb 100644 --- a/tests/tests/bitasset_tests.cpp +++ b/tests/tests/bitasset_tests.cpp @@ -928,26 +928,22 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); generate_block( skip ); - for( int i = 0; i < 8; ++i ) + for( int i = 0; i < 6; ++i ) { idump( (i) ); int blocks = 0; auto mi = db.get_global_properties().parameters.maintenance_interval; - if( i == 2 ) // go beyond hard fork 890 + if( i == 2 ) // go beyond hard fork 890, 343, 935 { generate_blocks( HARDFORK_CORE_868_890_TIME - mi, true, skip ); - generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); } - else if( i == 4 ) // go beyond hard fork 935 + else if( i == 4 ) // go beyond hard fork 1270 { - generate_blocks( HARDFORK_CORE_935_TIME - mi, true, skip ); - generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); - } - else if( i == 6 ) // go beyond hard fork 1270 - { - generate_blocks( HARDFORK_CORE_1270_TIME, true, skip ); + generate_blocks( HARDFORK_CORE_1270_TIME - mi, true, skip ); } + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); + set_expiration( db, trx ); ACTORS( (seller)(borrower)(feedproducer)(feedproducer2)(feedproducer3) ); @@ -1066,45 +1062,12 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) trx.clear(); } - bool affected_by_hf_343 = false; - - // check - if( i / 2 == 0 ) // before hard fork 890 + if( db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_935_TIME ) { // median feed won't change (issue 890) BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 1750 ); BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1100 ); - // limit order is still there - BOOST_CHECK( db.find( sell_id ) ); - - // go beyond hard fork 890 - blocks += generate_blocks( HARDFORK_CORE_868_890_TIME - mi, true, skip ); - bool was_before_hf_343 = ( db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_343_TIME ); - - blocks += generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); - bool now_after_hf_343 = ( db.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_343_TIME ); - - if( was_before_hf_343 && now_after_hf_343 ) // if hf 343 executed at same maintenance interval, actually after hf 890 - affected_by_hf_343 = true; - } - - // after hard fork 890, if it's before hard fork 935 - if( db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_935_TIME ) - { - // median should have changed - BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); - if( i % 2 == 0 ) // MCR test, MCR should be 350% - BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500 ); - else // MSSR test, MSSR should be 125% - BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1250 ); - - if( affected_by_hf_343 ) // if updated bitasset before hf 890, and hf 343 executed after hf 890 - // the limit order should have been filled - BOOST_CHECK( !db.find( sell_id ) ); - else // if not affected by hf 343 - // the limit order should be still there, because `check_call_order` was incorrectly skipped - BOOST_CHECK( db.find( sell_id ) ); // go beyond hard fork 935 blocks += generate_blocks(HARDFORK_CORE_935_TIME - mi, true, skip); @@ -1113,16 +1076,21 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) // after hard fork 935, the limit order is filled only for the MSSR test if( db.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_935_TIME && - db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_1270_TIME ) + db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_1270_TIME && i > 4) { // check median BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); - if( i % 2 != 0 ) { // MSSR test, MSSR should be 125%, order filled + if( i % 2 == 0 ) { // MCR bug + BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500); + BOOST_CHECK(db.find(sell_id)); // order still here + } + else { // MSSR test, MSSR should be 125%, order filled BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1250); BOOST_CHECK(!db.find(sell_id)); } // go beyond hard fork 1270 - blocks += generate_blocks(HARDFORK_CORE_1270_TIME, true, skip); + blocks += generate_blocks(HARDFORK_CORE_1270_TIME - mi, true, skip); + blocks += generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip); } // after hard fork 1270 @@ -1134,11 +1102,16 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500); BOOST_CHECK(!db.find(sell_id)); // MCR bug fixed } + else { // MSSR test, MSSR should be 125%, order filled + BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1250); + BOOST_CHECK(!db.find(sell_id)); + } } // undo above tx's and reset generate_block( skip ); ++blocks; + while( blocks > 0 ) { db.pop_block(); From ab4dfeb59cac1ea7d48511515e80a713006ad6a2 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 25 Jan 2019 15:48:24 -0500 Subject: [PATCH 109/161] use newly added member in all places --- libraries/chain/market_evaluator.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index d63d1be06e..df3c6cb53e 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -169,10 +169,14 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.", ("sym", _debt_asset->symbol) ); + _dynamic_data_obj = &_debt_asset->dynamic_asset_data_id(d); FC_ASSERT( next_maintenance_time <= HARDFORK_CORE_1465_TIME - || _debt_asset->dynamic_data(d).current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply, + || _dynamic_data_obj->current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply, "Borrowing this quantity would exceed MAX_SUPPLY" ); + FC_ASSERT( _dynamic_data_obj->current_supply + o.delta_debt.amount >= 0, + "This transaction would bring current supply below zero."); + _bitasset_data = &_debt_asset->bitasset_data(d); /// if there is a settlement for this asset, then no further margin positions may be taken and @@ -188,10 +192,6 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope else if( _bitasset_data->current_feed.settlement_price.is_null() ) FC_THROW_EXCEPTION(insufficient_feeds, "Cannot borrow asset with no price feed."); - _dynamic_data_obj = &_debt_asset->dynamic_asset_data_id(d); - FC_ASSERT( _dynamic_data_obj->current_supply + o.delta_debt.amount >= 0, - "This transaction would bring current supply below zero."); - // Note: there was code here checking whether the account has enough balance to increase delta collateral, // which is now removed since the check is implicitly done later by `adjust_balance()` in `do_apply()`. From cce205c8a58f4058f4f2a5e78e5113538a78119c Mon Sep 17 00:00:00 2001 From: OpenLedgerApp Date: Mon, 28 Jan 2019 13:51:27 +0300 Subject: [PATCH 110/161] Update hardfork date into far future --- libraries/chain/hardfork.d/CORE_1268.hf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/hardfork.d/CORE_1268.hf b/libraries/chain/hardfork.d/CORE_1268.hf index 6872db2fb2..352463c043 100644 --- a/libraries/chain/hardfork.d/CORE_1268.hf +++ b/libraries/chain/hardfork.d/CORE_1268.hf @@ -1,4 +1,4 @@ // #1268 Distribute Asset Market Fees to Referral Program #ifndef HARDFORK_1268_TIME -#define HARDFORK_1268_TIME (fc::time_point_sec( 1530705600 )) // Wednesday, July 4, 2018 12:00:00 PM +#define HARDFORK_1268_TIME (fc::time_point_sec( 1577880000 )) // Wednesday, January 1, 2020 12:00:00 PM #endif From b191bd735324dc096228d1bb0dedfa94bf3a5342 Mon Sep 17 00:00:00 2001 From: abitmore Date: Tue, 14 Aug 2018 10:10:36 -0400 Subject: [PATCH 111/161] Added allow_non_immediate_owner param to methods --- libraries/app/database_api.cpp | 12 +++++- libraries/chain/db_block.cpp | 7 +++- libraries/chain/hardfork.d/CORE_584.hf | 5 +++ .../graphene/chain/protocol/transaction.hpp | 37 ++++++++++++++++++- libraries/chain/proposal_object.cpp | 3 ++ libraries/chain/protocol/transaction.cpp | 14 ++++++- tests/tests/authority_tests.cpp | 26 +++++++++---- 7 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 libraries/chain/hardfork.d/CORE_584.hf diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index f067a101e8..6d7581b3d0 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -1971,10 +1972,12 @@ set database_api::get_required_signatures( const signed_transac set database_api_impl::get_required_signatures( const signed_transaction& trx, const flat_set& available_keys )const { + bool allow_non_immediate_owner = ( _db.head_block_time() >= HARDFORK_CORE_584_TIME ); auto result = trx.get_required_signatures( _db.get_chain_id(), available_keys, [&]( account_id_type id ){ return &id(_db).active; }, [&]( account_id_type id ){ return &id(_db).owner; }, + allow_non_immediate_owner, _db.get_global_properties().parameters.max_authority_depth ); return result; } @@ -1990,6 +1993,7 @@ set
database_api::get_potential_address_signatures( const signed_transa set database_api_impl::get_potential_signatures( const signed_transaction& trx )const { + bool allow_non_immediate_owner = ( _db.head_block_time() >= HARDFORK_CORE_584_TIME ); set result; trx.get_required_signatures( _db.get_chain_id(), @@ -2008,6 +2012,7 @@ set database_api_impl::get_potential_signatures( const signed_t result.insert(k); return &auth; }, + allow_non_immediate_owner, _db.get_global_properties().parameters.max_authority_depth ); @@ -2055,10 +2060,12 @@ bool database_api::verify_authority( const signed_transaction& trx )const bool database_api_impl::verify_authority( const signed_transaction& trx )const { + bool allow_non_immediate_owner = ( _db.head_block_time() >= HARDFORK_CORE_584_TIME ); trx.verify_authority( _db.get_chain_id(), [this]( account_id_type id ){ return &id(_db).active; }, [this]( account_id_type id ){ return &id(_db).owner; }, - _db.get_global_properties().parameters.max_authority_depth ); + allow_non_immediate_owner, + _db.get_global_properties().parameters.max_authority_depth ); return true; } @@ -2080,7 +2087,8 @@ bool database_api_impl::verify_account_authority( const string& account_name_or_ { graphene::chain::verify_authority(ops, keys, [this]( account_id_type id ){ return &id(_db).active; }, - [this]( account_id_type id ){ return &id(_db).owner; } ); + [this]( account_id_type id ){ return &id(_db).owner; }, + true ); } catch (fc::exception& ex) { diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 24618dbd34..0882230fec 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -637,9 +637,14 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx if( !(skip & skip_transaction_signatures) ) { + bool allow_non_immediate_owner = ( head_block_time() >= HARDFORK_CORE_584_TIME ); auto get_active = [&]( account_id_type id ) { return &id(*this).active; }; auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; }; - trx.verify_authority( chain_id, get_active, get_owner, get_global_properties().parameters.max_authority_depth ); + trx.verify_authority( chain_id, + get_active, + get_owner, + allow_non_immediate_owner, + get_global_properties().parameters.max_authority_depth ); } //Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is diff --git a/libraries/chain/hardfork.d/CORE_584.hf b/libraries/chain/hardfork.d/CORE_584.hf new file mode 100644 index 0000000000..e570010a94 --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_584.hf @@ -0,0 +1,5 @@ +// bitshares-core issue #584 Owner keys of non-immediately required accounts can not authorize a transaction +// https://github.com/bitshares/bitshares-core/issues/584 +#ifndef HARDFORK_CORE_584_TIME +#define HARDFORK_CORE_584_TIME (fc::time_point_sec( 1580000000 )) // a temporary date in the future +#endif diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 84234afb9e..5af573532b 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -111,7 +111,9 @@ namespace graphene { namespace chain { return results; } - void get_required_authorities( flat_set& active, flat_set& owner, vector& other )const; + void get_required_authorities( flat_set& active, + flat_set& owner, + vector& other )const; protected: // Calculate the digest used for signature validation @@ -146,13 +148,27 @@ namespace graphene { namespace chain { const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, + bool allow_non_immediate_owner, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const; + /** + * Checks whether signatures in this signed transaction are sufficient to authorize the transaction. + * Throws an exception when failed. + * + * @param chain_id the ID of a block chain + * @param get_active callback function to retrieve active authority of a given account + * @param get_active callback function to retrieve owner authority of a given account + * @param allow_non_immediate_owner whether to allow owner authrity of non-immediately + * required accounts to authorize operations in the transaction + * @param max_recursion maximum level of recursion when verifying, since an account + * can have another account in active authority and/or owner authority + */ void verify_authority( const chain_id_type& chain_id, const std::function& get_active, const std::function& get_owner, + bool allow_non_immediate_owner, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const; /** @@ -161,12 +177,12 @@ namespace graphene { namespace chain { * some cases where get_required_signatures() returns a * non-minimal set. */ - set minimize_required_signatures( const chain_id_type& chain_id, const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, + bool allow_non_immediate_owner, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH ) const; @@ -213,9 +229,26 @@ namespace graphene { namespace chain { mutable bool _validated = false; }; + /** + * Checks whether given public keys and approvals are sufficient to authorize given operations. + * Throws an exception when failed. + * + * @param ops a vector of operations + * @param sigs a set of public keys + * @param get_active callback function to retrieve active authority of a given account + * @param get_active callback function to retrieve owner authority of a given account + * @param allow_non_immediate_owner whether to allow owner authrity of non-immediately + * required accounts to authorize operations + * @param max_recursion maximum level of recursion when verifying, since an account + * can have another account in active authority and/or owner authority + * @param allow_committee whether to allow the special "committee account" to authorize the operations + * @param active_approvals accounts that approved the operations with their active authories + * @param owner_approvals accounts that approved the operations with their owner authories + */ void verify_authority( const vector& ops, const flat_set& sigs, const std::function& get_active, const std::function& get_owner, + bool allow_non_immediate_owner, uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH, bool allow_committe = false, const flat_set& active_aprovals = flat_set(), diff --git a/libraries/chain/proposal_object.cpp b/libraries/chain/proposal_object.cpp index 7d7884e1c4..47d4d0db4a 100644 --- a/libraries/chain/proposal_object.cpp +++ b/libraries/chain/proposal_object.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include +#include #include namespace graphene { namespace chain { @@ -31,10 +32,12 @@ bool proposal_object::is_authorized_to_execute(database& db) const transaction_evaluation_state dry_run_eval(&db); try { + bool allow_non_immediate_owner = ( db.head_block_time() >= HARDFORK_CORE_584_TIME ); verify_authority( proposed_transaction.operations, available_key_approvals, [&]( account_id_type id ){ return &id(db).active; }, [&]( account_id_type id ){ return &id(db).owner; }, + allow_non_immediate_owner, db.get_global_properties().parameters.max_authority_depth, true, /* allow committeee */ available_active_approvals, diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 1a1293ca76..c61924dda5 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -249,6 +249,7 @@ struct sign_state void verify_authority( const vector& ops, const flat_set& sigs, const std::function& get_active, const std::function& get_owner, + bool allow_non_immediate_owner, uint32_t max_recursion_depth, bool allow_committe, const flat_set& active_aprovals, @@ -265,6 +266,7 @@ void verify_authority( const vector& ops, const flat_set signed_transaction::get_required_signatures( const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, + bool allow_non_immediate_owner, uint32_t max_recursion_depth )const { flat_set required_active; @@ -357,6 +360,7 @@ set signed_transaction::minimize_required_signatures( const flat_set& available_keys, const std::function& get_active, const std::function& get_owner, + bool allow_non_immediate_owner, uint32_t max_recursion ) const { @@ -368,7 +372,7 @@ set signed_transaction::minimize_required_signatures( result.erase( k ); try { - graphene::chain::verify_authority( operations, result, get_active, get_owner, max_recursion ); + graphene::chain::verify_authority( operations, result, get_active, get_owner, allow_non_immediate_owner, max_recursion ); continue; // element stays erased if verify_authority is ok } catch( const tx_missing_owner_auth& e ) {} @@ -406,9 +410,15 @@ void signed_transaction::verify_authority( const chain_id_type& chain_id, const std::function& get_active, const std::function& get_owner, + bool allow_non_immediate_owner, uint32_t max_recursion )const { try { - graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, max_recursion ); + graphene::chain::verify_authority( operations, + get_signature_keys( chain_id ), + get_active, + get_owner, + allow_non_immediate_owner, + max_recursion ); } FC_CAPTURE_AND_RETHROW( (*this) ) } } } // graphene::chain diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index cf9df59142..d7c8ec8139 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1192,7 +1192,9 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner ); + // TODO add test case for post hard fork + set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, + get_active, get_owner, false ); //wdump( (result_set)(ref_set) ); return result_set == ref_set; } ; @@ -1306,7 +1308,9 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner ); + // TODO add test case for post hard fork + set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, + get_active, get_owner, false ); //wdump( (result_set)(ref_set) ); return result_set == ref_set; } ; @@ -1318,7 +1322,9 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - set result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner ); + // TODO add test case for post hard fork + set result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, + get_active, get_owner, false ); //wdump( (result_set)(ref_set) ); return result_set == ref_set; } ; @@ -1337,9 +1343,10 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) BOOST_CHECK( chk( tx, { alice_public_key, bob_public_key }, { alice_public_key, bob_public_key } ) ); BOOST_CHECK( chk_min( tx, { alice_public_key, bob_public_key }, { alice_public_key } ) ); - GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner ), fc::exception ); + // TODO add test case for post hard fork + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); sign( tx, alice_private_key ); - tx.verify_authority( db.get_chain_id(), get_active, get_owner ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); } catch(fc::exception& e) { @@ -1401,7 +1408,9 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner ); + // TODO add test case for post hard fork + set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, + get_active, get_owner, false ); //wdump( (result_set)(ref_set) ); return result_set == ref_set; } ; @@ -1424,11 +1433,12 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) BOOST_CHECK( chk( tx, { alice_owner_pub }, { } ) ); BOOST_CHECK( chk( tx, { alice_active_pub, alice_owner_pub }, { alice_active_pub } ) ); sign( tx, alice_owner_key ); - GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner ), fc::exception ); + // TODO add test case for post hard fork + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); tx.clear_signatures(); sign( tx, alice_active_key ); - tx.verify_authority( db.get_chain_id(), get_active, get_owner ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); } catch(fc::exception& e) From a393c2afa59c2318d33ee8d32a31790a418087ab Mon Sep 17 00:00:00 2001 From: abitmore Date: Tue, 14 Aug 2018 13:02:07 -0400 Subject: [PATCH 112/161] Check owner authorities in sign_state --- libraries/chain/protocol/transaction.cpp | 35 +++++++++++++++--------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index c61924dda5..e70beec8c2 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -93,7 +93,9 @@ void transaction::set_reference_block( const block_id_type& reference_block ) ref_block_prefix = reference_block._hash[1]; } -void transaction::get_required_authorities( flat_set& active, flat_set& owner, vector& other )const +void transaction::get_required_authorities( flat_set& active, + flat_set& owner, + vector& other )const { for( const auto& op : operations ) operation_get_required_authorities( op, active, owner, other ); @@ -102,7 +104,6 @@ void transaction::get_required_authorities( flat_set& active, f } - const flat_set empty_keyset; struct sign_state @@ -162,7 +163,7 @@ struct sign_state bool check_authority( account_id_type id ) { if( approved_by.find(id) != approved_by.end() ) return true; - return check_authority( get_active(id) ); + return check_authority( get_active(id) ) || ( allow_non_immediate_owner && check_authority( get_owner(id) ) ); } /** @@ -197,7 +198,8 @@ struct sign_state { if( depth == max_recursion ) continue; - if( check_authority( get_active( a.first ), depth+1 ) ) + if( check_authority( get_active( a.first ), depth+1 ) + || ( allow_non_immediate_owner && check_authority( get_owner( a.first ), depth+1 ) ) ) { approved_by.insert( a.first ); total_weight += a.second; @@ -228,9 +230,16 @@ struct sign_state } sign_state( const flat_set& sigs, - const std::function& a, + const std::function& active, + const std::function& owner, + bool allow_owner, + uint32_t max_recursion_depth = GRAPHENE_MAX_SIG_CHECK_DEPTH, const flat_set& keys = empty_keyset ) - :get_active(a),available_keys(keys) + : get_active(active), + get_owner(owner), + allow_non_immediate_owner(allow_owner), + max_recursion(max_recursion_depth), + available_keys(keys) { for( const auto& key : sigs ) provided_signatures[ key ] = false; @@ -238,11 +247,14 @@ struct sign_state } const std::function& get_active; - const flat_set& available_keys; + const std::function& get_owner; + + const bool allow_non_immediate_owner; + const uint32_t max_recursion; + const flat_set& available_keys; flat_map provided_signatures; flat_set approved_by; - uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH; }; @@ -266,9 +278,7 @@ void verify_authority( const vector& ops, const flat_set signed_transaction::get_required_signatures( get_required_authorities( required_active, required_owner, other ); const flat_set& signature_keys = get_signature_keys( chain_id ); - sign_state s( signature_keys, get_active, available_keys ); - s.max_recursion = max_recursion_depth; + sign_state s( signature_keys, get_active, get_owner, allow_non_immediate_owner, max_recursion_depth, available_keys ); for( const auto& auth : other ) s.check_authority(&auth); From 745a248286a85f62a97a41812360e22ea1d2fa48 Mon Sep 17 00:00:00 2001 From: abitmore Date: Tue, 14 Aug 2018 13:26:03 -0400 Subject: [PATCH 113/161] Added tests for non-immediate owner authorities --- tests/tests/authority_tests.cpp | 38 ++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index d7c8ec8139..65ab72d9b8 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1192,11 +1192,12 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - // TODO add test case for post hard fork set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, false ); - //wdump( (result_set)(ref_set) ); - return result_set == ref_set; + set result_set2 = tx.get_required_signatures( db.get_chain_id(), available_keys, + get_active, get_owner, true ); + //wdump( (result_set)(result_set2)(ref_set) ); + return result_set == ref_set && result_set2 == ref_set; } ; set_auth( well_id, authority( 60, alice_id, 50, bob_id, 50 ) ); @@ -1308,11 +1309,12 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - // TODO add test case for post hard fork set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, false ); - //wdump( (result_set)(ref_set) ); - return result_set == ref_set; + set result_set2 = tx.get_required_signatures( db.get_chain_id(), available_keys, + get_active, get_owner, true ); + //wdump( (result_set)(result_set2)(ref_set) ); + return result_set == ref_set && result_set2 == ref_set; } ; auto chk_min = [&]( @@ -1322,11 +1324,12 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) ) -> bool { //wdump( (tx)(available_keys) ); - // TODO add test case for post hard fork set result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner, false ); - //wdump( (result_set)(ref_set) ); - return result_set == ref_set; + set result_set2 = tx.minimize_required_signatures( db.get_chain_id(), available_keys, + get_active, get_owner, true ); + //wdump( (result_set)(result_set2)(ref_set) ); + return result_set == ref_set && result_set2 == ref_set; } ; set_auth( roco_id, authority( 2, styx_id, 1, thud_id, 2 ) ); @@ -1343,8 +1346,8 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) BOOST_CHECK( chk( tx, { alice_public_key, bob_public_key }, { alice_public_key, bob_public_key } ) ); BOOST_CHECK( chk_min( tx, { alice_public_key, bob_public_key }, { alice_public_key } ) ); - // TODO add test case for post hard fork GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ), fc::exception ); sign( tx, alice_private_key ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); } @@ -1403,14 +1406,14 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) auto chk = [&]( const signed_transaction& tx, + bool after_hf_584, flat_set available_keys, set ref_set ) -> bool { //wdump( (tx)(available_keys) ); - // TODO add test case for post hard fork set result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, - get_active, get_owner, false ); + get_active, get_owner, after_hf_584 ); //wdump( (result_set)(ref_set) ); return result_set == ref_set; } ; @@ -1430,15 +1433,20 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) tx.operations.push_back( op ); // https://github.com/bitshares/bitshares-core/issues/584 - BOOST_CHECK( chk( tx, { alice_owner_pub }, { } ) ); - BOOST_CHECK( chk( tx, { alice_active_pub, alice_owner_pub }, { alice_active_pub } ) ); + BOOST_CHECK( chk( tx, false, { alice_owner_pub }, { } ) ); + BOOST_CHECK( chk( tx, false, { alice_active_pub, alice_owner_pub }, { alice_active_pub } ) ); + + BOOST_CHECK( chk( tx, true, { alice_owner_pub }, { alice_owner_pub } ) ); + BOOST_CHECK( chk( tx, true, { alice_active_pub, alice_owner_pub }, { alice_active_pub } ) ); + sign( tx, alice_owner_key ); - // TODO add test case for post hard fork GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, alice_active_key ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); } catch(fc::exception& e) From 451ca3dbca0637b4417691662ea7753410f548d0 Mon Sep 17 00:00:00 2001 From: abitmore Date: Thu, 16 Aug 2018 12:47:42 -0400 Subject: [PATCH 114/161] Added more tests for non-immediate owner authority --- tests/tests/authority_tests.cpp | 192 +++++++++++++++++++++++++++++--- 1 file changed, 179 insertions(+), 13 deletions(-) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 65ab72d9b8..594e2444b0 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1358,15 +1358,40 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) } } +/* + * Active vs Owner https://github.com/bitshares/bitshares-core/issues/584 + * + * All weights and all thresholds are 1, so every single key should be able to sign if within max_depth + * + * Bob --+--(a)--+-- Alice --+--(a)--+-- Daisy --(a/o)-- Daisy_active_key / Daisy_owner_key + * | | | | + * | | | +-- Alice_active_key + * | | | + * | | +--(o)--+-- Cindy --(a/o)-- Cindy_active_key / Cindy_owner_key + * | | | + * | | +-- Alice_owner_key + * | | + * | +-- Bob_active_key + * | + * +--(o)--+-- Edwin --+--(a)--+-- Gavin --(a/o)-- Gavin_active_key / Gavin_owner_key + * | | | + * | | +-- Edwin_active_key + * | | + * | +--(o)--+-- Frank --(a/o)-- Frank_active_key / Frank_owner_key + * | | + * | +-- Edwin_owner_key + * | + * +-- Bob_owner_key + */ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) { try { ACTORS( - (alice)(bob) + (alice)(bob)(cindy)(daisy)(edwin)(frank)(gavin) ); - auto set_auth2 = [&]( + auto set_auth = [&]( account_id_type aid, const authority& active, const authority& owner @@ -1382,14 +1407,6 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) PUSH_TX( db, tx, database::skip_transaction_signatures ); } ; - auto set_auth = [&]( - account_id_type aid, - const authority& auth - ) - { - set_auth2( aid, auth, auth ); - } ; - auto get_active = [&]( account_id_type aid ) -> const authority* @@ -1420,10 +1437,44 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) fc::ecc::private_key alice_active_key = fc::ecc::private_key::regenerate(fc::digest("alice_active")); fc::ecc::private_key alice_owner_key = fc::ecc::private_key::regenerate(fc::digest("alice_owner")); + fc::ecc::private_key bob_active_key = fc::ecc::private_key::regenerate(fc::digest("bob_active")); + fc::ecc::private_key bob_owner_key = fc::ecc::private_key::regenerate(fc::digest("bob_owner")); + fc::ecc::private_key cindy_active_key = fc::ecc::private_key::regenerate(fc::digest("cindy_active")); + fc::ecc::private_key cindy_owner_key = fc::ecc::private_key::regenerate(fc::digest("cindy_owner")); + fc::ecc::private_key daisy_active_key = fc::ecc::private_key::regenerate(fc::digest("daisy_active")); + fc::ecc::private_key daisy_owner_key = fc::ecc::private_key::regenerate(fc::digest("daisy_owner")); + fc::ecc::private_key edwin_active_key = fc::ecc::private_key::regenerate(fc::digest("edwin_active")); + fc::ecc::private_key edwin_owner_key = fc::ecc::private_key::regenerate(fc::digest("edwin_owner")); + fc::ecc::private_key frank_active_key = fc::ecc::private_key::regenerate(fc::digest("frank_active")); + fc::ecc::private_key frank_owner_key = fc::ecc::private_key::regenerate(fc::digest("frank_owner")); + fc::ecc::private_key gavin_active_key = fc::ecc::private_key::regenerate(fc::digest("gavin_active")); + fc::ecc::private_key gavin_owner_key = fc::ecc::private_key::regenerate(fc::digest("gavin_owner")); + public_key_type alice_active_pub( alice_active_key.get_public_key() ); public_key_type alice_owner_pub( alice_owner_key.get_public_key() ); - set_auth2( alice_id, authority( 1, alice_active_pub, 1 ), authority( 1, alice_owner_pub, 1 ) ); - set_auth( bob_id, authority( 1, alice_id, 1 ) ); + public_key_type bob_active_pub( bob_active_key.get_public_key() ); + public_key_type bob_owner_pub( bob_owner_key.get_public_key() ); + public_key_type cindy_active_pub( cindy_active_key.get_public_key() ); + public_key_type cindy_owner_pub( cindy_owner_key.get_public_key() ); + public_key_type daisy_active_pub( daisy_active_key.get_public_key() ); + public_key_type daisy_owner_pub( daisy_owner_key.get_public_key() ); + public_key_type edwin_active_pub( edwin_active_key.get_public_key() ); + public_key_type edwin_owner_pub( edwin_owner_key.get_public_key() ); + public_key_type frank_active_pub( frank_active_key.get_public_key() ); + public_key_type frank_owner_pub( frank_owner_key.get_public_key() ); + public_key_type gavin_active_pub( gavin_active_key.get_public_key() ); + public_key_type gavin_owner_pub( gavin_owner_key.get_public_key() ); + + set_auth( alice_id, authority( 1, alice_active_pub, 1, daisy_id, 1 ), authority( 1, alice_owner_pub, 1, cindy_id, 1 ) ); + set_auth( bob_id, authority( 1, bob_active_pub, 1, alice_id, 1 ), authority( 1, bob_owner_pub, 1, edwin_id, 1 ) ); + + set_auth( cindy_id, authority( 1, cindy_active_pub, 1 ), authority( 1, cindy_owner_pub, 1 ) ); + set_auth( daisy_id, authority( 1, daisy_active_pub, 1 ), authority( 1, daisy_owner_pub, 1 ) ); + + set_auth( edwin_id, authority( 1, edwin_active_pub, 1, gavin_id, 1 ), authority( 1, edwin_owner_pub, 1, frank_id, 1 ) ); + + set_auth( frank_id, authority( 1, frank_active_pub, 1 ), authority( 1, frank_owner_pub, 1 ) ); + set_auth( gavin_id, authority( 1, gavin_active_pub, 1 ), authority( 1, gavin_owner_pub, 1 ) ); signed_transaction tx; transfer_operation op; @@ -1433,20 +1484,135 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) tx.operations.push_back( op ); // https://github.com/bitshares/bitshares-core/issues/584 + // If not allow non-immediate owner to authorize BOOST_CHECK( chk( tx, false, { alice_owner_pub }, { } ) ); + BOOST_CHECK( chk( tx, false, { alice_active_pub }, { alice_active_pub } ) ); BOOST_CHECK( chk( tx, false, { alice_active_pub, alice_owner_pub }, { alice_active_pub } ) ); + BOOST_CHECK( chk( tx, false, { bob_owner_pub }, { bob_owner_pub } ) ); + BOOST_CHECK( chk( tx, false, { bob_active_pub }, { bob_active_pub } ) ); + BOOST_CHECK( chk( tx, false, { bob_active_pub, bob_owner_pub }, { bob_active_pub } ) ); + + BOOST_CHECK( chk( tx, false, { cindy_owner_pub }, { } ) ); + BOOST_CHECK( chk( tx, false, { cindy_active_pub }, { } ) ); + BOOST_CHECK( chk( tx, false, { cindy_active_pub, cindy_owner_pub }, { } ) ); + + BOOST_CHECK( chk( tx, false, { daisy_owner_pub }, { } ) ); + BOOST_CHECK( chk( tx, false, { daisy_active_pub }, { daisy_active_pub } ) ); + BOOST_CHECK( chk( tx, false, { daisy_active_pub, daisy_owner_pub }, { daisy_active_pub } ) ); + + BOOST_CHECK( chk( tx, false, { edwin_owner_pub }, { } ) ); + BOOST_CHECK( chk( tx, false, { edwin_active_pub }, { edwin_active_pub } ) ); + BOOST_CHECK( chk( tx, false, { edwin_active_pub, edwin_owner_pub }, { edwin_active_pub } ) ); + + BOOST_CHECK( chk( tx, false, { frank_owner_pub }, { } ) ); + BOOST_CHECK( chk( tx, false, { frank_active_pub }, { } ) ); + BOOST_CHECK( chk( tx, false, { frank_active_pub, frank_owner_pub }, { } ) ); + + BOOST_CHECK( chk( tx, false, { gavin_owner_pub }, { } ) ); + BOOST_CHECK( chk( tx, false, { gavin_active_pub }, { gavin_active_pub } ) ); + BOOST_CHECK( chk( tx, false, { gavin_active_pub, gavin_owner_pub }, { gavin_active_pub } ) ); + + // If allow non-immediate owner to authorize BOOST_CHECK( chk( tx, true, { alice_owner_pub }, { alice_owner_pub } ) ); + BOOST_CHECK( chk( tx, true, { alice_active_pub }, { alice_active_pub } ) ); BOOST_CHECK( chk( tx, true, { alice_active_pub, alice_owner_pub }, { alice_active_pub } ) ); + BOOST_CHECK( chk( tx, true, { bob_owner_pub }, { bob_owner_pub } ) ); + BOOST_CHECK( chk( tx, true, { bob_active_pub }, { bob_active_pub } ) ); + BOOST_CHECK( chk( tx, true, { bob_active_pub, bob_owner_pub }, { bob_active_pub } ) ); + + BOOST_CHECK( chk( tx, true, { cindy_owner_pub }, { cindy_owner_pub } ) ); + BOOST_CHECK( chk( tx, true, { cindy_active_pub }, { cindy_active_pub } ) ); + BOOST_CHECK( chk( tx, true, { cindy_active_pub, cindy_owner_pub }, { cindy_active_pub } ) ); + + BOOST_CHECK( chk( tx, true, { daisy_owner_pub }, { daisy_owner_pub } ) ); + BOOST_CHECK( chk( tx, true, { daisy_active_pub }, { daisy_active_pub } ) ); + BOOST_CHECK( chk( tx, true, { daisy_active_pub, daisy_owner_pub }, { daisy_active_pub } ) ); + + BOOST_CHECK( chk( tx, true, { edwin_owner_pub }, { edwin_owner_pub } ) ); + BOOST_CHECK( chk( tx, true, { edwin_active_pub }, { edwin_active_pub } ) ); + BOOST_CHECK( chk( tx, true, { edwin_active_pub, edwin_owner_pub }, { edwin_active_pub } ) ); + + BOOST_CHECK( chk( tx, true, { frank_owner_pub }, { frank_owner_pub } ) ); + BOOST_CHECK( chk( tx, true, { frank_active_pub }, { frank_active_pub } ) ); + BOOST_CHECK( chk( tx, true, { frank_active_pub, frank_owner_pub }, { frank_active_pub } ) ); + + BOOST_CHECK( chk( tx, true, { gavin_owner_pub }, { gavin_owner_pub } ) ); + BOOST_CHECK( chk( tx, true, { gavin_active_pub }, { gavin_active_pub } ) ); + BOOST_CHECK( chk( tx, true, { gavin_active_pub, gavin_owner_pub }, { gavin_active_pub } ) ); + sign( tx, alice_owner_key ); GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); - tx.clear_signatures(); + sign( tx, alice_active_key ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, bob_owner_key ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, bob_active_key ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, cindy_owner_key ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, cindy_active_key ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, daisy_owner_key ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, daisy_active_key ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, edwin_owner_key ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, edwin_active_key ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, frank_owner_key ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, frank_active_key ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, gavin_owner_key ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + sign( tx, gavin_active_key ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); + tx.clear_signatures(); + + // TODO add proposal tests } catch(fc::exception& e) From 3d7904e21f4838c184545198f8dc5e4e6a2e11d6 Mon Sep 17 00:00:00 2001 From: abitmore Date: Thu, 16 Aug 2018 13:56:15 -0400 Subject: [PATCH 115/161] Proposal and hf tests for non-immediate owner auth --- tests/tests/authority_tests.cpp | 237 +++++++++++++++++++++++++++++++- 1 file changed, 236 insertions(+), 1 deletion(-) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 594e2444b0..2541d287a2 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1391,6 +1391,8 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) (alice)(bob)(cindy)(daisy)(edwin)(frank)(gavin) ); + transfer( account_id_type(), bob_id, asset(100000) ); + auto set_auth = [&]( account_id_type aid, const authority& active, @@ -1476,12 +1478,15 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) set_auth( frank_id, authority( 1, frank_active_pub, 1 ), authority( 1, frank_owner_pub, 1 ) ); set_auth( gavin_id, authority( 1, gavin_active_pub, 1 ), authority( 1, gavin_owner_pub, 1 ) ); + generate_block(); + signed_transaction tx; transfer_operation op; op.from = bob_id; op.to = alice_id; op.amount = asset(1); tx.operations.push_back( op ); + set_expiration( db, tx ); // https://github.com/bitshares/bitshares-core/issues/584 // If not allow non-immediate owner to authorize @@ -1544,76 +1549,306 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) sign( tx, alice_owner_key ); GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, alice_active_key ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, bob_owner_key ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, bob_active_key ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, cindy_owner_key ); GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, cindy_active_key ); GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, daisy_owner_key ); GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, daisy_active_key ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, edwin_owner_key ); GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, edwin_active_key ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, frank_owner_key ); GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, frank_active_key ); GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, gavin_owner_key ); GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception ); + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); sign( tx, gavin_active_key ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); tx.clear_signatures(); - // TODO add proposal tests + // proposal tests + auto new_proposal = [&]() -> proposal_id_type { + signed_transaction ptx; + + proposal_create_operation pop; + pop.proposed_ops.emplace_back(op); + pop.fee_paying_account = bob_id; + pop.expiration_time = db.head_block_time() + fc::days(1); + ptx.operations.push_back(pop); + set_expiration( db, ptx ); + sign( ptx, bob_active_key ); + + return PUSH_TX( db, ptx, database::skip_transaction_dupe_check ).operation_results[0].get(); + }; + + auto approve_proposal = [&]( + proposal_id_type proposal, + account_id_type account, + bool approve_with_owner, + fc::ecc::private_key key + ) + { + signed_transaction ptx; + + proposal_update_operation pup; + pup.fee_paying_account = account; + pup.proposal = proposal; + if( approve_with_owner ) + pup.owner_approvals_to_add.insert( account ); + else + pup.active_approvals_to_add.insert( account ); + ptx.operations.push_back(pup); + set_expiration( db, ptx ); + sign( ptx, key ); + PUSH_TX( db, ptx, database::skip_transaction_dupe_check ); + }; + + proposal_id_type pid; + + pid = new_proposal(); + approve_proposal( pid, alice_id, true, alice_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, alice_id, false, alice_active_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, bob_id, true, bob_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, bob_id, false, bob_active_key ); + BOOST_CHECK( !db.find( pid ) ); + + // Cindy's approval doesn't work + pid = new_proposal(); + approve_proposal( pid, cindy_id, true, cindy_owner_key ); + BOOST_CHECK( db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, cindy_id, false, cindy_active_key ); + BOOST_CHECK( db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, daisy_id, true, daisy_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, daisy_id, false, daisy_active_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, edwin_id, true, edwin_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, edwin_id, false, edwin_active_key ); + BOOST_CHECK( !db.find( pid ) ); + + // Frank's approval doesn't work + pid = new_proposal(); + approve_proposal( pid, frank_id, true, frank_owner_key ); + BOOST_CHECK( db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, frank_id, false, frank_active_key ); + BOOST_CHECK( db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, gavin_id, true, gavin_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, gavin_id, false, gavin_active_key ); + BOOST_CHECK( !db.find( pid ) ); + + generate_block( database::skip_transaction_dupe_check ); + + // pass the hard fork time + generate_blocks( HARDFORK_CORE_584_TIME, true, database::skip_transaction_dupe_check ); + set_expiration( db, tx ); + + // signing tests + sign( tx, alice_owner_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, alice_active_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, bob_owner_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, bob_active_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, cindy_owner_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, cindy_active_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, daisy_owner_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, daisy_active_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, edwin_owner_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, edwin_active_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, frank_owner_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, frank_active_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, gavin_owner_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + sign( tx, gavin_active_key ); + PUSH_TX( db, tx, database::skip_transaction_dupe_check ); + tx.signatures.clear(); + + // proposal tests + pid = new_proposal(); + approve_proposal( pid, alice_id, true, alice_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, alice_id, false, alice_active_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, bob_id, true, bob_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, bob_id, false, bob_active_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, cindy_id, true, cindy_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, cindy_id, false, cindy_active_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, daisy_id, true, daisy_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, daisy_id, false, daisy_active_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, edwin_id, true, edwin_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, edwin_id, false, edwin_active_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, frank_id, true, frank_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, frank_id, false, frank_active_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, gavin_id, true, gavin_owner_key ); + BOOST_CHECK( !db.find( pid ) ); + + pid = new_proposal(); + approve_proposal( pid, gavin_id, false, gavin_active_key ); + BOOST_CHECK( !db.find( pid ) ); + generate_block( database::skip_transaction_dupe_check ); } catch(fc::exception& e) { From 9a50898a7bad969ddf280bdf218e23bfda28d9ab Mon Sep 17 00:00:00 2001 From: abitmore Date: Thu, 16 Aug 2018 15:21:18 -0400 Subject: [PATCH 116/161] Fixed test cases --- tests/tests/authority_tests.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 2541d287a2..8bb619e225 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1350,6 +1350,7 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture ) GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ), fc::exception ); sign( tx, alice_private_key ); tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); } catch(fc::exception& e) { @@ -1911,23 +1912,29 @@ BOOST_FIXTURE_TEST_CASE( missing_owner_auth_test, database_fixture ) tx.operations.push_back( op ); // not signed, should throw tx_missing_owner_auth - GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner ), + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), + graphene::chain::tx_missing_owner_auth ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ), graphene::chain::tx_missing_owner_auth ); // signed with alice's active key, should throw tx_missing_owner_auth sign( tx, alice_active_key ); - GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner ), + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), + graphene::chain::tx_missing_owner_auth ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ), graphene::chain::tx_missing_owner_auth ); // signed with alice's owner key, should not throw tx.clear_signatures(); sign( tx, alice_owner_key ); - tx.verify_authority( db.get_chain_id(), get_active, get_owner ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); // signed with both alice's owner key and active key, // it does not throw due to https://github.com/bitshares/bitshares-core/issues/580 sign( tx, alice_active_key ); - tx.verify_authority( db.get_chain_id(), get_active, get_owner ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); // creating a transaction that needs active permission tx.clear(); @@ -1936,21 +1943,27 @@ BOOST_FIXTURE_TEST_CASE( missing_owner_auth_test, database_fixture ) tx.operations.push_back( op ); // not signed, should throw tx_missing_active_auth - GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner ), + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), + graphene::chain::tx_missing_active_auth ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ), graphene::chain::tx_missing_active_auth ); // signed with alice's active key, should not throw sign( tx, alice_active_key ); - tx.verify_authority( db.get_chain_id(), get_active, get_owner ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); // signed with alice's owner key, should not throw tx.clear_signatures(); sign( tx, alice_owner_key ); - tx.verify_authority( db.get_chain_id(), get_active, get_owner ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ); + tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ); // signed with both alice's owner key and active key, should throw tx_irrelevant_sig sign( tx, alice_active_key ); - GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner ), + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), + graphene::chain::tx_irrelevant_sig ); + GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ), graphene::chain::tx_irrelevant_sig ); } From 7cac2800e611fb5cb8b194eb20ead965b77b635c Mon Sep 17 00:00:00 2001 From: abitmore Date: Sun, 19 Aug 2018 10:41:04 -0400 Subject: [PATCH 117/161] Updated coding style --- tests/tests/database_api_tests.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index 1eeb177b42..9ad79899b3 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -36,7 +36,8 @@ using namespace graphene::chain::test; BOOST_FIXTURE_TEST_SUITE(database_api_tests, database_fixture) -BOOST_AUTO_TEST_CASE(is_registered) { +BOOST_AUTO_TEST_CASE(is_registered) +{ try { /*** * Arrange @@ -71,7 +72,8 @@ BOOST_AUTO_TEST_CASE(is_registered) { } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE( get_potential_signatures_owner_and_active ) { +BOOST_AUTO_TEST_CASE( get_potential_signatures_owner_and_active ) +{ try { fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest("key1")); fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2")); @@ -118,7 +120,8 @@ BOOST_AUTO_TEST_CASE( get_potential_signatures_owner_and_active ) { } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE( get_potential_signatures_other ) { +BOOST_AUTO_TEST_CASE( get_potential_signatures_other ) +{ try { fc::ecc::private_key priv_key1 = fc::ecc::private_key::regenerate(fc::digest("key1")); public_key_type pub_key1( priv_key1.get_public_key() ); @@ -138,7 +141,8 @@ BOOST_AUTO_TEST_CASE( get_potential_signatures_other ) { } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE( get_required_signatures_owner_or_active ) { +BOOST_AUTO_TEST_CASE( get_required_signatures_owner_or_active ) +{ try { fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest("key1")); fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2")); @@ -211,7 +215,8 @@ BOOST_AUTO_TEST_CASE( get_required_signatures_owner_or_active ) { } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE( get_required_signatures_partially_signed_or_not ) { +BOOST_AUTO_TEST_CASE( get_required_signatures_partially_signed_or_not ) +{ try { fc::ecc::private_key morgan_key = fc::ecc::private_key::regenerate(fc::digest("morgan_key")); fc::ecc::private_key nathan_key = fc::ecc::private_key::regenerate(fc::digest("nathan_key")); @@ -602,7 +607,8 @@ BOOST_AUTO_TEST_CASE( get_required_signatures_partially_signed_or_not ) { } FC_LOG_AND_RETHROW() } -BOOST_AUTO_TEST_CASE( set_subscribe_callback_disable_notify_all_test ) { +BOOST_AUTO_TEST_CASE( set_subscribe_callback_disable_notify_all_test ) +{ try { ACTORS( (alice) ); @@ -792,7 +798,7 @@ BOOST_AUTO_TEST_CASE(get_account_limit_orders) limit_order_id_type(o.id), o.sell_price); BOOST_CHECK(results.size() == 71); BOOST_CHECK(results.front().id > o.id); - // NOTE 3: because of NOTE 1, here should be little than + // NOTE 3: because of NOTE 1, here should be little than BOOST_CHECK(results.front().sell_price < o.sell_price); for (size_t i = 0 ; i < results.size() - 1 ; ++i) { From f2c608ef5b78e4118c436f801e50a06cf565099e Mon Sep 17 00:00:00 2001 From: abitmore Date: Sun, 19 Aug 2018 11:24:15 -0400 Subject: [PATCH 118/161] Added database_api tests for non-immediate owner --- tests/tests/database_api_tests.cpp | 147 +++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index 9ad79899b3..fc30d6f000 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -25,6 +25,7 @@ #include #include +#include #include @@ -120,6 +121,152 @@ BOOST_AUTO_TEST_CASE( get_potential_signatures_owner_and_active ) } FC_LOG_AND_RETHROW() } +/// Testing get_potential_signatures and get_required_signatures for non-immediate owner authority issue. +/// https://github.com/bitshares/bitshares-core/issues/584 +BOOST_AUTO_TEST_CASE( get_signatures_non_immediate_owner ) +{ + try { + fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest("key1")); + fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2")); + fc::ecc::private_key ashley_key1 = fc::ecc::private_key::regenerate(fc::digest("akey1")); + fc::ecc::private_key ashley_key2 = fc::ecc::private_key::regenerate(fc::digest("akey2")); + fc::ecc::private_key oliver_key1 = fc::ecc::private_key::regenerate(fc::digest("okey1")); + fc::ecc::private_key oliver_key2 = fc::ecc::private_key::regenerate(fc::digest("okey2")); + public_key_type pub_key_active( nathan_key1.get_public_key() ); + public_key_type pub_key_owner( nathan_key2.get_public_key() ); + public_key_type a_pub_key_active( ashley_key1.get_public_key() ); + public_key_type a_pub_key_owner( ashley_key2.get_public_key() ); + public_key_type o_pub_key_active( oliver_key1.get_public_key() ); + public_key_type o_pub_key_owner( oliver_key2.get_public_key() ); + const account_object& nathan = create_account("nathan", nathan_key1.get_public_key() ); + const account_object& ashley = create_account("ashley", ashley_key1.get_public_key() ); + const account_object& oliver = create_account("oliver", oliver_key1.get_public_key() ); + account_id_type nathan_id = nathan.id; + account_id_type ashley_id = ashley.id; + account_id_type oliver_id = oliver.id; + + try { + account_update_operation op; + op.account = nathan_id; + op.active = authority(1, pub_key_active, 1, ashley_id, 1); + op.owner = authority(1, pub_key_owner, 1, oliver_id, 1); + trx.operations.push_back(op); + sign(trx, nathan_key1); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); + trx.operations.clear(); + trx.signatures.clear(); + + op.account = ashley_id; + op.active = authority(1, a_pub_key_active, 1); + op.owner = authority(1, a_pub_key_owner, 1); + trx.operations.push_back(op); + sign(trx, ashley_key1); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); + trx.operations.clear(); + trx.signatures.clear(); + + op.account = oliver_id; + op.active = authority(1, o_pub_key_active, 1); + op.owner = authority(1, o_pub_key_owner, 1); + trx.operations.push_back(op); + sign(trx, oliver_key1); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); + trx.operations.clear(); + trx.signatures.clear(); + } FC_CAPTURE_AND_RETHROW ((nathan.active)) + + // this transaction requires active + signed_transaction trx_a; + transfer_operation op; + op.from = nathan_id; + op.to = account_id_type(); + trx_a.operations.push_back(op); + + // get potential signatures + graphene::app::database_api db_api(db); + set pub_keys = db_api.get_potential_signatures( trx_a ); + + BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() ); + BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() ); + BOOST_CHECK( pub_keys.find( a_pub_key_active ) != pub_keys.end() ); + // doesn't work due to https://github.com/bitshares/bitshares-core/issues/584 + BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() ); + BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() ); + // doesn't work due to https://github.com/bitshares/bitshares-core/issues/584 + BOOST_CHECK( pub_keys.find( o_pub_key_owner ) == pub_keys.end() ); + + // get required signatures + pub_keys = db_api.get_required_signatures( trx_a, { a_pub_key_owner, o_pub_key_owner } ); + BOOST_CHECK( pub_keys.empty() ); + + // this op requires owner + signed_transaction trx_o; + account_update_operation auop; + auop.account = nathan_id; + auop.owner = authority(1, pub_key_owner, 1); + trx_o.operations.push_back(auop); + + // get potential signatures + pub_keys = db_api.get_potential_signatures( trx_o ); + + // active authorities doesn't help in this case + BOOST_CHECK( pub_keys.find( pub_key_active ) == pub_keys.end() ); + BOOST_CHECK( pub_keys.find( a_pub_key_active ) == pub_keys.end() ); + BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() ); + + // owner authorities should be ok + BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() ); + BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() ); + // doesn't work due to https://github.com/bitshares/bitshares-core/issues/584 + BOOST_CHECK( pub_keys.find( o_pub_key_owner ) == pub_keys.end() ); + + // get required signatures + pub_keys = db_api.get_required_signatures( trx_o, { a_pub_key_owner, o_pub_key_owner } ); + BOOST_CHECK( pub_keys.empty() ); + + // go beyond hard fork + generate_blocks( HARDFORK_CORE_584_TIME, true ); + + // for the transaction that requires active + // get potential signatures + pub_keys = db_api.get_potential_signatures( trx_a ); + + // all authorities should be ok + BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() ); + BOOST_CHECK( pub_keys.find( a_pub_key_active ) != pub_keys.end() ); + BOOST_CHECK( pub_keys.find( a_pub_key_owner ) != pub_keys.end() ); + BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() ); + BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() ); + BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() ); + + // get required signatures + pub_keys = db_api.get_required_signatures( trx_a, { a_pub_key_owner } ); + BOOST_CHECK( pub_keys.find( a_pub_key_owner ) != pub_keys.end() ); + pub_keys = db_api.get_required_signatures( trx_a, { o_pub_key_owner } ); + BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() ); + + // for the transaction that requires owner + // get potential signatures + pub_keys = db_api.get_potential_signatures( trx_o ); + + // active authorities doesn't help in this case + BOOST_CHECK( pub_keys.find( pub_key_active ) == pub_keys.end() ); + BOOST_CHECK( pub_keys.find( a_pub_key_active ) == pub_keys.end() ); + BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() ); + + // owner authorities should help + BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() ); + BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() ); + BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() ); + + // get required signatures + pub_keys = db_api.get_required_signatures( trx_o, { a_pub_key_owner, o_pub_key_owner } ); + BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() ); + BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() ); + + } FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_CASE( get_potential_signatures_other ) { try { From ac310b1e8551eab346a18f6378a7937875ab3c38 Mon Sep 17 00:00:00 2001 From: abitmore Date: Thu, 13 Sep 2018 18:03:42 -0400 Subject: [PATCH 119/161] Fixed test cases after rebase --- tests/tests/authority_tests.cpp | 28 ++++++++++++++-------------- tests/tests/database_api_tests.cpp | 9 +++------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/tests/tests/authority_tests.cpp b/tests/tests/authority_tests.cpp index 8bb619e225..22fb6bc710 100644 --- a/tests/tests/authority_tests.cpp +++ b/tests/tests/authority_tests.cpp @@ -1738,59 +1738,59 @@ BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture ) // signing tests sign( tx, alice_owner_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, alice_active_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, bob_owner_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, bob_active_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, cindy_owner_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, cindy_active_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, daisy_owner_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, daisy_active_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, edwin_owner_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, edwin_active_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, frank_owner_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, frank_active_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, gavin_owner_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); sign( tx, gavin_active_key ); PUSH_TX( db, tx, database::skip_transaction_dupe_check ); - tx.signatures.clear(); + tx.clear_signatures(); // proposal tests pid = new_proposal(); diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index fc30d6f000..2db747d0ce 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -153,8 +153,7 @@ BOOST_AUTO_TEST_CASE( get_signatures_non_immediate_owner ) trx.operations.push_back(op); sign(trx, nathan_key1); PUSH_TX( db, trx, database::skip_transaction_dupe_check ); - trx.operations.clear(); - trx.signatures.clear(); + trx.clear(); op.account = ashley_id; op.active = authority(1, a_pub_key_active, 1); @@ -162,8 +161,7 @@ BOOST_AUTO_TEST_CASE( get_signatures_non_immediate_owner ) trx.operations.push_back(op); sign(trx, ashley_key1); PUSH_TX( db, trx, database::skip_transaction_dupe_check ); - trx.operations.clear(); - trx.signatures.clear(); + trx.clear(); op.account = oliver_id; op.active = authority(1, o_pub_key_active, 1); @@ -171,8 +169,7 @@ BOOST_AUTO_TEST_CASE( get_signatures_non_immediate_owner ) trx.operations.push_back(op); sign(trx, oliver_key1); PUSH_TX( db, trx, database::skip_transaction_dupe_check ); - trx.operations.clear(); - trx.signatures.clear(); + trx.clear(); } FC_CAPTURE_AND_RETHROW ((nathan.active)) // this transaction requires active From ece99b6d1def2bb50d8323b2bdda315a7de54915 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Tue, 29 Jan 2019 15:14:32 -0300 Subject: [PATCH 120/161] workaround hf_935_test --- tests/tests/bitasset_tests.cpp | 79 ++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 18 deletions(-) diff --git a/tests/tests/bitasset_tests.cpp b/tests/tests/bitasset_tests.cpp index 30466a9beb..c25e27bd10 100644 --- a/tests/tests/bitasset_tests.cpp +++ b/tests/tests/bitasset_tests.cpp @@ -928,22 +928,30 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); generate_block( skip ); - for( int i = 0; i < 6; ++i ) + // need additional block to avoid prev: popping block would leave head block null error + generate_block( skip ); + + for( int i = 0; i < 8; ++i ) { idump( (i) ); int blocks = 0; auto mi = db.get_global_properties().parameters.maintenance_interval; - if( i == 2 ) // go beyond hard fork 890, 343, 935 + if( i == 2 ) // go beyond hard fork 890 { generate_blocks( HARDFORK_CORE_868_890_TIME - mi, true, skip ); + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); + } + else if( i == 4 ) // go beyond hard fork 935 + { + generate_blocks( HARDFORK_CORE_935_TIME - mi, true, skip ); + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); } - else if( i == 4 ) // go beyond hard fork 1270 + else if( i == 6 ) // go beyond hard fork 1270 { generate_blocks( HARDFORK_CORE_1270_TIME - mi, true, skip ); + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); } - generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); - set_expiration( db, trx ); ACTORS( (seller)(borrower)(feedproducer)(feedproducer2)(feedproducer3) ); @@ -1062,12 +1070,48 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) trx.clear(); } - if( db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_935_TIME ) + bool affected_by_hf_343 = false; + + // check + if( i / 2 == 0 ) // before hard fork 890 { // median feed won't change (issue 890) BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 1750 ); BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1100 ); + // limit order is still there + BOOST_CHECK( db.find( sell_id ) ); + + // go beyond hard fork 890 + blocks += generate_blocks( HARDFORK_CORE_868_890_TIME - mi, true, skip ); + bool was_before_hf_343 = ( db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_343_TIME ); + + blocks += generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); + bool now_after_hf_343 = ( db.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_343_TIME ); + + // order is filled here + BOOST_CHECK( !db.find( sell_id ) ); + + if( was_before_hf_343 && now_after_hf_343 ) // if hf 343 executed at same maintenance interval, actually after hf 890 + affected_by_hf_343 = true; + } + + // after hard fork 890, if it's before hard fork 935 + if( db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_935_TIME ) + { + // median should have changed + BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); + if( i % 2 == 0 ) // MCR test, MCR should be 350% + BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500 ); + else // MSSR test, MSSR should be 125% + BOOST_CHECK_EQUAL( usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1250 ); + + if( affected_by_hf_343 ) // if updated bitasset before hf 890, and hf 343 executed after hf 890 + // the limit order should have been filled + BOOST_CHECK( !db.find( sell_id ) ); + else // if not affected by hf 343 + // the limit order should be still there, because `check_call_order` was incorrectly skipped + BOOST_CHECK( db.find( sell_id ) ); // go beyond hard fork 935 blocks += generate_blocks(HARDFORK_CORE_935_TIME - mi, true, skip); @@ -1076,34 +1120,34 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) // after hard fork 935, the limit order is filled only for the MSSR test if( db.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_935_TIME && - db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_1270_TIME && i > 4) + db.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_1270_TIME) { // check median BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); - if( i % 2 == 0 ) { // MCR bug + if( i % 2 == 0) { // MCR test, median MCR should be 350% and order will not be filled except when i = 0 BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500); - BOOST_CHECK(db.find(sell_id)); // order still here + if(i / 2 == 0) // order is filled with i = 0 at HARDFORK_CORE_343_TIME + mi + BOOST_CHECK(!db.find(sell_id)); + else + BOOST_CHECK(db.find(sell_id)); // MCR bug, order still there } - else { // MSSR test, MSSR should be 125%, order filled + else { // MSSR test, MSSR should be 125% and order is filled BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1250); - BOOST_CHECK(!db.find(sell_id)); + BOOST_CHECK(!db.find(sell_id)); // order filled } + // go beyond hard fork 1270 blocks += generate_blocks(HARDFORK_CORE_1270_TIME - mi, true, skip); blocks += generate_blocks(db.get_dynamic_global_properties().next_maintenance_time, true, skip); } - // after hard fork 1270 + // after hard fork 1270, the limit order should be filled for MCR test if( db.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_1270_TIME) { // check median BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); - if( i % 2 == 0 ) { // MCR test, median MCR should be 350%, order filled + if( i % 2 == 0 ) { // MCR test, order filled BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500); - BOOST_CHECK(!db.find(sell_id)); // MCR bug fixed - } - else { // MSSR test, MSSR should be 125%, order filled - BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maximum_short_squeeze_ratio, 1250); BOOST_CHECK(!db.find(sell_id)); } } @@ -1111,7 +1155,6 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) // undo above tx's and reset generate_block( skip ); ++blocks; - while( blocks > 0 ) { db.pop_block(); From 17b389470a5a0efd3979c3d7aaa137747a6f701a Mon Sep 17 00:00:00 2001 From: abitmore Date: Wed, 30 Jan 2019 11:14:17 -0500 Subject: [PATCH 121/161] Fix typo in docs --- .../include/graphene/chain/protocol/transaction.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 5af573532b..3f09f58fa8 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -157,12 +157,12 @@ namespace graphene { namespace chain { * Throws an exception when failed. * * @param chain_id the ID of a block chain - * @param get_active callback function to retrieve active authority of a given account - * @param get_active callback function to retrieve owner authority of a given account + * @param get_active callback function to retrieve active authorities of a given account + * @param get_owner callback function to retrieve owner authorities of a given account * @param allow_non_immediate_owner whether to allow owner authrity of non-immediately * required accounts to authorize operations in the transaction * @param max_recursion maximum level of recursion when verifying, since an account - * can have another account in active authority and/or owner authority + * can have another account in active authorities and/or owner authorities */ void verify_authority( const chain_id_type& chain_id, @@ -235,12 +235,12 @@ namespace graphene { namespace chain { * * @param ops a vector of operations * @param sigs a set of public keys - * @param get_active callback function to retrieve active authority of a given account - * @param get_active callback function to retrieve owner authority of a given account + * @param get_active callback function to retrieve active authorities of a given account + * @param get_owner callback function to retrieve owner authorities of a given account * @param allow_non_immediate_owner whether to allow owner authrity of non-immediately * required accounts to authorize operations * @param max_recursion maximum level of recursion when verifying, since an account - * can have another account in active authority and/or owner authority + * can have another account in active authorities and/or owner authorities * @param allow_committee whether to allow the special "committee account" to authorize the operations * @param active_approvals accounts that approved the operations with their active authories * @param owner_approvals accounts that approved the operations with their owner authories From 1aad691e29bcbf4a0759b6529caac0fdcf52a688 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Wed, 30 Jan 2019 14:28:13 -0300 Subject: [PATCH 122/161] use affected_by_hf_343 flag --- tests/tests/bitasset_tests.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/tests/bitasset_tests.cpp b/tests/tests/bitasset_tests.cpp index c25e27bd10..c9f3a4d85b 100644 --- a/tests/tests/bitasset_tests.cpp +++ b/tests/tests/bitasset_tests.cpp @@ -1089,9 +1089,6 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) blocks += generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); bool now_after_hf_343 = ( db.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_343_TIME ); - // order is filled here - BOOST_CHECK( !db.find( sell_id ) ); - if( was_before_hf_343 && now_after_hf_343 ) // if hf 343 executed at same maintenance interval, actually after hf 890 affected_by_hf_343 = true; } @@ -1126,7 +1123,7 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) BOOST_CHECK( usd_id(db).bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price ); if( i % 2 == 0) { // MCR test, median MCR should be 350% and order will not be filled except when i = 0 BOOST_CHECK_EQUAL(usd_id(db).bitasset_data(db).current_feed.maintenance_collateral_ratio, 3500); - if(i / 2 == 0) // order is filled with i = 0 at HARDFORK_CORE_343_TIME + mi + if( affected_by_hf_343 ) BOOST_CHECK(!db.find(sell_id)); else BOOST_CHECK(db.find(sell_id)); // MCR bug, order still there From ca1e045bbf19365aa61c6fdb5c398db5c69845f3 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Wed, 30 Jan 2019 15:03:20 -0300 Subject: [PATCH 123/161] remove trailing white spaces --- tests/tests/market_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/market_tests.cpp b/tests/tests/market_tests.cpp index 933ed8cd00..413ffd42f9 100644 --- a/tests/tests/market_tests.cpp +++ b/tests/tests/market_tests.cpp @@ -1308,7 +1308,7 @@ BOOST_AUTO_TEST_CASE(target_cr_test_limit_call) BOOST_CHECK_LT( call2_to_cover.value, call2_id(db).debt.value ); // even though call2 has a higher CR, since call's TCR is less than call2's TCR, so we expect call will cover less when called BOOST_CHECK_LT( call_to_cover.value, call2_to_cover.value ); - + // Create a big sell order slightly below the call price, will be matched with several orders BOOST_CHECK( !create_sell_order(seller, bitusd.amount(700*4), core.amount(5900*4) ) ); From b4946090fa47d683a82363e67ddc701df47729fd Mon Sep 17 00:00:00 2001 From: abitmore Date: Fri, 1 Feb 2019 05:46:47 -0500 Subject: [PATCH 124/161] Revert to by_price index when globally settling Because all positions will be closed anyway, using which index doesn't matter in terms of amounts, however, using another index may change the ID of historical virtual operations, specifically, `fill_order_operation` -- we usually try to avoid this. --- libraries/chain/db_market.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index f6b9f24c57..3d42f9abc9 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -62,14 +62,14 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett const asset_dynamic_data_object& mia_dyn = mia.dynamic_asset_data_id(*this); auto original_mia_supply = mia_dyn.current_supply; - const auto& call_index = get_index_type().indices().get(); + const auto& call_price_index = get_index_type().indices().get(); auto maint_time = get_dynamic_global_properties().next_maintenance_time; bool before_core_hardfork_342 = ( maint_time <= HARDFORK_CORE_342_TIME ); // better rounding // cancel all call orders and accumulate it into collateral_gathered - auto call_itr = call_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) ); - auto call_end = call_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) ); + auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) ); + auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) ); asset pays; while( call_itr != call_end ) { @@ -94,7 +94,7 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett } modify( bitasset, [&mia,original_mia_supply,&collateral_gathered]( asset_bitasset_data_object& obj ){ - obj.settlement_price = mia.amount(original_mia_supply) / collateral_gathered; //settlement_price; + obj.settlement_price = mia.amount(original_mia_supply) / collateral_gathered; obj.settlement_fund = collateral_gathered.amount; }); From a22f92e576bb1e1028e732247e2b9c7794a9869e Mon Sep 17 00:00:00 2001 From: abitmore Date: Mon, 4 Feb 2019 15:31:38 -0500 Subject: [PATCH 125/161] Simplify lambda --- libraries/chain/market_object.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libraries/chain/market_object.cpp b/libraries/chain/market_object.cpp index 0edfcc363c..0c2e464906 100644 --- a/libraries/chain/market_object.cpp +++ b/libraries/chain/market_object.cpp @@ -25,6 +25,8 @@ #include +#include + using namespace graphene::chain; /* @@ -135,20 +137,18 @@ share_type call_order_object::get_max_debt_to_cover( price match_price, FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt ); // Check whether the collateral ratio after filled is high enough - auto result_is_good = [after_core_hardfork_1270,this,&to_cover,&to_pay,tcr,feed_price,target_collateralization]() -> bool - { - // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization(). - if( !after_core_hardfork_1270 ) // before core-1270 hard fork - { - price new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr ); - return ( new_call_price > feed_price ); - } - else // after core-1270 hard fork + // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization(). + std::function result_is_good = after_core_hardfork_1270 ? + std::function( [this,&to_cover,&to_pay,target_collateralization]() -> bool { price new_collateralization = ( get_collateral() - to_pay ) / ( get_debt() - to_cover ); return ( new_collateralization > target_collateralization ); - } - }; + }) : + std::function( [this,&to_cover,&to_pay,tcr,feed_price]() -> bool + { + price new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr ); + return ( new_call_price > feed_price ); + }); // if the result is good, we return. if( result_is_good() ) From bdcb0ea626855e8a5ffb7accb2078588859a9932 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Wed, 6 Feb 2019 13:46:19 +0100 Subject: [PATCH 126/161] Fixed typos --- .../chain/include/graphene/chain/protocol/transaction.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 3f09f58fa8..d43a65ecc9 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -159,7 +159,7 @@ namespace graphene { namespace chain { * @param chain_id the ID of a block chain * @param get_active callback function to retrieve active authorities of a given account * @param get_owner callback function to retrieve owner authorities of a given account - * @param allow_non_immediate_owner whether to allow owner authrity of non-immediately + * @param allow_non_immediate_owner whether to allow owner authority of non-immediately * required accounts to authorize operations in the transaction * @param max_recursion maximum level of recursion when verifying, since an account * can have another account in active authorities and/or owner authorities @@ -237,7 +237,7 @@ namespace graphene { namespace chain { * @param sigs a set of public keys * @param get_active callback function to retrieve active authorities of a given account * @param get_owner callback function to retrieve owner authorities of a given account - * @param allow_non_immediate_owner whether to allow owner authrity of non-immediately + * @param allow_non_immediate_owner whether to allow owner authority of non-immediately * required accounts to authorize operations * @param max_recursion maximum level of recursion when verifying, since an account * can have another account in active authorities and/or owner authorities From a8da5f180bfb5ab96f8fd9d81a0fcaacd0b30a59 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 9 Feb 2019 12:43:03 -0500 Subject: [PATCH 127/161] merged in softfork code --- libraries/chain/hardfork.d/CORE_1465.hf | 1 + libraries/chain/market_evaluator.cpp | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/libraries/chain/hardfork.d/CORE_1465.hf b/libraries/chain/hardfork.d/CORE_1465.hf index bdedbf8052..90039b9b3d 100644 --- a/libraries/chain/hardfork.d/CORE_1465.hf +++ b/libraries/chain/hardfork.d/CORE_1465.hf @@ -1,4 +1,5 @@ // bitshares-core issue #1465 check max_supply before processing call_order_update #ifndef HARDFORK_CORE_1465_TIME #define HARDFORK_CORE_1465_TIME (fc::time_point_sec( 1600000000 )) // 2020-09-13 12:26:40 +#define SOFTFORK_CORE_1465_TIME (fc::time_point_sec( 1545350400 )) // 2018-12-21 00:00:00 #endif diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index f14119410d..467683e301 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -170,10 +170,25 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope ("sym", _debt_asset->symbol) ); _dynamic_data_obj = &_debt_asset->dynamic_asset_data_id(d); - FC_ASSERT( next_maintenance_time <= HARDFORK_CORE_1465_TIME - || _dynamic_data_obj->current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply, - "Borrowing this quantity would exceed MAX_SUPPLY" ); + /*** + * We have softfork code already in production to prevent exceeding MAX_SUPPLY between 2018-12-21 until HF 1465. + * But we must allow this in replays until 2018-12-21. The HF 1465 code will correct the problem. + * After HF 1465, we MAY be able to remove the cleanup code IF it never executes. We MAY be able to clean + * up the softfork code IF it never executes. We MAY be able to turn the hardfork code into regular code IF + * noone ever attempted this before HF 1465. + */ + if (next_maintenance_time <= SOFTFORK_CORE_1465_TIME) + { + if ( _dynamic_data_obj->current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply ) + ilog("Issue 1465... Borrowing and exceeding MAX_SUPPLY. Will be corrected at hardfork time.") + } + else + { + FC_ASSERT( _dynamic_data_obj->current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply, + "Borrowing this quantity would exceed MAX_SUPPLY" ); + } + FC_ASSERT( _dynamic_data_obj->current_supply + o.delta_debt.amount >= 0, "This transaction would bring current supply below zero."); From dfbb508b484575fa23f3887239a2a1a0d0e3c12b Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 9 Feb 2019 13:18:51 -0500 Subject: [PATCH 128/161] syntax error --- libraries/chain/market_evaluator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index 467683e301..3616b34083 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -181,7 +181,7 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope if (next_maintenance_time <= SOFTFORK_CORE_1465_TIME) { if ( _dynamic_data_obj->current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply ) - ilog("Issue 1465... Borrowing and exceeding MAX_SUPPLY. Will be corrected at hardfork time.") + ilog("Issue 1465... Borrowing and exceeding MAX_SUPPLY. Will be corrected at hardfork time."); } else { From 2e69219fa42191c912ad24cfa9022293017570db Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 9 Feb 2019 13:35:59 -0500 Subject: [PATCH 129/161] Merge softfork & hardfork 1479 --- libraries/chain/proposal_evaluator.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 348688d605..9e8ef4daf4 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -127,8 +127,23 @@ struct proposal_operation_hardfork_visitor } // loop and self visit in proposals void operator()(const graphene::chain::proposal_create_operation &v) const { + bool contains_proposal_update = false; + for (const op_wrapper &op : v.proposed_ops) + { op.op.visit(*this); + /** + * The softfork code before HF 1479 is more restrictive than HF1479. Therefore, both + * must be kept until HF 1479, and the softfork code must not execute after HF 1479. + * If the softfork code never executes before HF 1479, it can be removed. + */ + if ( block_time < HARDFORK_CORE_1479_TIME + && op.op.which() == operation::tag().value ) + { + FC_ASSERT( !contains_proposal_update, "At most one proposal update can be nested in a proposal!" ); + contains_proposal_update = true; + } + } } }; From dc901237c53c7af6f66f95e8227c0ab17032b5fd Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 11 Feb 2019 06:19:26 -0500 Subject: [PATCH 130/161] Added missing softfork code --- libraries/chain/proposal_evaluator.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 348688d605..645b73a135 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -38,18 +38,27 @@ namespace detail { struct proposal_operation_hardfork_visitor { typedef void result_type; + const database& db; const fc::time_point_sec block_time; const fc::time_point_sec next_maintenance_time; - proposal_operation_hardfork_visitor( const fc::time_point_sec bt, const fc::time_point_sec nmt ) - : block_time(bt), next_maintenance_time(nmt) {} + proposal_operation_hardfork_visitor( const database& _db, const fc::time_point_sec bt ) + : db( _db ), block_time(bt), next_maintenance_time( db.get_dynamic_global_properties().next_maintenance_time ) {} template void operator()(const T &v) const {} - // TODO review and cleanup code below after hard fork - // hf_834 void operator()(const graphene::chain::call_order_update_operation &v) const { + + // TODO If this never ASSERTs before HF 1465, it can be removed + FC_ASSERT( block_time > SOFTFORK_CORE_1465_TIME && block_time < HARDFORK_CORE_1465_TIME + && v.delta_debt.amount > 0 && v.delta_debt.asset_id != asset_id_type(113) // CNY + && v.delta_debt.asset_id( db ).bitasset_data_id + && !(*( v.delta_debt.asset_id( db ).bitasset_data_id))( db ).is_prediction_market + , "Soft fork - preventing proposal with call_order_update!" ); + + // TODO review and cleanup code below after hard fork + // hf_834 if (next_maintenance_time <= HARDFORK_CORE_834_TIME) { FC_ASSERT( !v.extensions.value.target_collateral_ratio.valid(), "Can not set target_collateral_ratio in call_order_update_operation before hardfork 834." ); @@ -171,8 +180,7 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati // Calling the proposal hardfork visitor const fc::time_point_sec block_time = d.head_block_time(); - const fc::time_point_sec next_maint_time = d.get_dynamic_global_properties().next_maintenance_time; - proposal_operation_hardfork_visitor vtor( block_time, next_maint_time ); + proposal_operation_hardfork_visitor vtor( d, block_time ); vtor( o ); if( block_time < HARDFORK_CORE_214_TIME ) { // cannot be removed after hf, unfortunately From 8727a3cf546c8c9237fe9bacbdda8baef188ba6a Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 11 Feb 2019 06:52:10 -0500 Subject: [PATCH 131/161] Remove dates as this assert always applies --- libraries/chain/proposal_evaluator.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 9e8ef4daf4..6cefd86282 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -127,21 +127,16 @@ struct proposal_operation_hardfork_visitor } // loop and self visit in proposals void operator()(const graphene::chain::proposal_create_operation &v) const { - bool contains_proposal_update = false; + bool already_contains_proposal_update = false; for (const op_wrapper &op : v.proposed_ops) { op.op.visit(*this); - /** - * The softfork code before HF 1479 is more restrictive than HF1479. Therefore, both - * must be kept until HF 1479, and the softfork code must not execute after HF 1479. - * If the softfork code never executes before HF 1479, it can be removed. - */ - if ( block_time < HARDFORK_CORE_1479_TIME - && op.op.which() == operation::tag().value ) + // Do not allow more than 1 proposal_update in a proposal + if ( op.op.which() == operation::tag().value ) { - FC_ASSERT( !contains_proposal_update, "At most one proposal update can be nested in a proposal!" ); - contains_proposal_update = true; + FC_ASSERT( !already_contains_proposal_update, "At most one proposal update can be nested in a proposal!" ); + already_contains_proposal_update = true; } } } From a71f373284a65164e20293d5fc0d39db3753d7e8 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 11 Feb 2019 07:44:48 -0500 Subject: [PATCH 132/161] reverse ASSERT logic --- libraries/chain/proposal_evaluator.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 645b73a135..b39ba05dc0 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -51,10 +51,11 @@ struct proposal_operation_hardfork_visitor void operator()(const graphene::chain::call_order_update_operation &v) const { // TODO If this never ASSERTs before HF 1465, it can be removed - FC_ASSERT( block_time > SOFTFORK_CORE_1465_TIME && block_time < HARDFORK_CORE_1465_TIME - && v.delta_debt.amount > 0 && v.delta_debt.asset_id != asset_id_type(113) // CNY - && v.delta_debt.asset_id( db ).bitasset_data_id - && !(*( v.delta_debt.asset_id( db ).bitasset_data_id))( db ).is_prediction_market + FC_ASSERT( block_time > HARDFORK_CORE_1465_TIME + || v.delta_debt.asset_id == asset_id_type(113) // CNY + || v.delta_debt.amount < 0 + || (v.delta_debt.asset_id( db ).bitasset_data_id + && (*(v.delta_debt.asset_id( db ).bitasset_data_id))( db ).is_prediction_market ) , "Soft fork - preventing proposal with call_order_update!" ); // TODO review and cleanup code below after hard fork From ff65ab786057ce5beb1f4df3683039a9e6487b5e Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 11 Feb 2019 07:53:46 -0500 Subject: [PATCH 133/161] fix test --- tests/tests/operation_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 771074267a..0d2dffda79 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -664,7 +664,7 @@ BOOST_AUTO_TEST_CASE( call_order_update_target_cr_hardfork_time_test ) GRAPHENE_REQUIRE_THROW( call_update_proposal( bob, alice, bitusd.amount(10), core.amount(40), 1750 ), fc::assert_exception ); GRAPHENE_REQUIRE_THROW( call_update_proposal( bob, alice, bitusd.amount(10), core.amount(40), 65535 ), fc::assert_exception ); - generate_blocks( db.get_dynamic_global_properties().next_maintenance_time ); + generate_blocks( HARDFORK_CORE_1465_TIME + 10 ); set_expiration( db, trx ); BOOST_TEST_MESSAGE( "bob tries to propose a proposal with target_cr set, " From d04634082f97a88c3d7564ea83e5ba53e5157501 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 11 Feb 2019 17:53:02 -0500 Subject: [PATCH 134/161] Added softfork window --- libraries/chain/proposal_evaluator.cpp | 3 ++- tests/tests/operation_tests.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index b39ba05dc0..e1a5cde051 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -51,7 +51,8 @@ struct proposal_operation_hardfork_visitor void operator()(const graphene::chain::call_order_update_operation &v) const { // TODO If this never ASSERTs before HF 1465, it can be removed - FC_ASSERT( block_time > HARDFORK_CORE_1465_TIME + FC_ASSERT( block_time < SOFTFORK_CORE_1465_TIME + || block_time > HARDFORK_CORE_1465_TIME || v.delta_debt.asset_id == asset_id_type(113) // CNY || v.delta_debt.amount < 0 || (v.delta_debt.asset_id( db ).bitasset_data_id diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 0d2dffda79..771074267a 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -664,7 +664,7 @@ BOOST_AUTO_TEST_CASE( call_order_update_target_cr_hardfork_time_test ) GRAPHENE_REQUIRE_THROW( call_update_proposal( bob, alice, bitusd.amount(10), core.amount(40), 1750 ), fc::assert_exception ); GRAPHENE_REQUIRE_THROW( call_update_proposal( bob, alice, bitusd.amount(10), core.amount(40), 65535 ), fc::assert_exception ); - generate_blocks( HARDFORK_CORE_1465_TIME + 10 ); + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time ); set_expiration( db, trx ); BOOST_TEST_MESSAGE( "bob tries to propose a proposal with target_cr set, " From 50fd49207bfefa4f367f01aa556f17c057822b22 Mon Sep 17 00:00:00 2001 From: abitmore Date: Tue, 12 Feb 2019 13:08:19 -0500 Subject: [PATCH 135/161] Reduce number of db queries in get_call_orders API --- libraries/app/database_api.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index c1520f4b41..41c3d6a841 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1273,10 +1273,9 @@ vector database_api_impl::get_call_orders(const std::string& { FC_ASSERT( limit <= 300 ); - const asset_id_type asset_a_id = get_asset_from_string(a)->id; + const asset_object* mia = get_asset_from_string(a); const auto& call_index = _db.get_index_type().indices().get(); - const asset_object& mia = _db.get(asset_a_id); - price index_price = price::min( mia.bitasset_data(_db).options.short_backing_asset, mia.get_id() ); + price index_price = price::min( mia->bitasset_data(_db).options.short_backing_asset, mia->get_id() ); vector< call_order_object> result; auto itr_min = call_index.lower_bound(index_price); From edf1d0501044347fcc9cfd56c863346c69149661 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 14 Feb 2019 18:27:23 -0500 Subject: [PATCH 136/161] Prevent transactions with size over the set limit --- libraries/chain/db_block.cpp | 3 +++ libraries/chain/hardfork.d/CORE_1573.hf | 4 ++++ tests/tests/network_broadcast_api_tests.cpp | 26 +++++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 libraries/chain/hardfork.d/CORE_1573.hf diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 3763900e09..63e09c661a 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -657,6 +657,9 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx FC_ASSERT( trx.expiration <= now + chain_parameters.maximum_time_until_expiration, "", ("trx.expiration",trx.expiration)("now",now)("max_til_exp",chain_parameters.maximum_time_until_expiration)); FC_ASSERT( now <= trx.expiration, "", ("now",now)("trx.exp",trx.expiration) ); + FC_ASSERT( head_block_time() <= HARDFORK_CORE_1573_TIME + || fc::raw::pack_size(trx) <= chain_parameters.maximum_transaction_size, + "Transaction exceeds maximum transaction size." ); } //Insert transaction into unique transactions database. diff --git a/libraries/chain/hardfork.d/CORE_1573.hf b/libraries/chain/hardfork.d/CORE_1573.hf new file mode 100644 index 0000000000..6a09e88402 --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_1573.hf @@ -0,0 +1,4 @@ +// bitshares-core issue #1573 check transaction size +#ifndef HARDFORK_CORE_1573_TIME +#define HARDFORK_CORE_1573_TIME (fc::time_point_sec( 1600000000 )) +#endif \ No newline at end of file diff --git a/tests/tests/network_broadcast_api_tests.cpp b/tests/tests/network_broadcast_api_tests.cpp index a566750b67..a40c112662 100644 --- a/tests/tests/network_broadcast_api_tests.cpp +++ b/tests/tests/network_broadcast_api_tests.cpp @@ -25,6 +25,7 @@ #include #include +#include #include @@ -71,4 +72,29 @@ BOOST_AUTO_TEST_CASE( broadcast_transaction_with_callback_test ) { } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( broadcast_transaction_too_large ) { + try { + + fc::ecc::private_key cid_key = fc::ecc::private_key::regenerate( fc::digest("key") ); + const account_id_type cid_id = create_account( "cid", cid_key.get_public_key() ).id; + fund( cid_id(db) ); + + auto nb_api = std::make_shared< graphene::app::network_broadcast_api >( app ); + + generate_blocks( HARDFORK_CORE_1573_TIME + 10 ); + + set_expiration( db, trx ); + transfer_operation trans; + trans.from = cid_id; + trans.to = account_id_type(); + trans.amount = asset(1); + for(int i = 0; i < 250; ++i ) + trx.operations.push_back( trans ); + sign( trx, cid_key ); + + BOOST_CHECK_THROW( nb_api->broadcast_transaction( trx ), fc::exception ); + + } FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_SUITE_END() From 959ef67db17516df16ef94b1fa27b517fa397dbe Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 14 Feb 2019 18:30:38 -0500 Subject: [PATCH 137/161] crlf to end file --- libraries/chain/hardfork.d/CORE_1573.hf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/hardfork.d/CORE_1573.hf b/libraries/chain/hardfork.d/CORE_1573.hf index 6a09e88402..72016a5d7c 100644 --- a/libraries/chain/hardfork.d/CORE_1573.hf +++ b/libraries/chain/hardfork.d/CORE_1573.hf @@ -1,4 +1,4 @@ // bitshares-core issue #1573 check transaction size #ifndef HARDFORK_CORE_1573_TIME #define HARDFORK_CORE_1573_TIME (fc::time_point_sec( 1600000000 )) -#endif \ No newline at end of file +#endif From 8aa19af075304d2e2240237d3291ab6d84a79b74 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 15 Feb 2019 11:54:26 -0500 Subject: [PATCH 138/161] precompute transaction size --- libraries/chain/db_block.cpp | 2 +- .../include/graphene/chain/protocol/transaction.hpp | 4 ++++ libraries/chain/protocol/transaction.cpp | 13 +++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 63e09c661a..b1f2e65480 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -658,7 +658,7 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx ("trx.expiration",trx.expiration)("now",now)("max_til_exp",chain_parameters.maximum_time_until_expiration)); FC_ASSERT( now <= trx.expiration, "", ("now",now)("trx.exp",trx.expiration) ); FC_ASSERT( head_block_time() <= HARDFORK_CORE_1573_TIME - || fc::raw::pack_size(trx) <= chain_parameters.maximum_transaction_size, + || trx.get_packed_size() <= chain_parameters.maximum_transaction_size, "Transaction exceeds maximum transaction size." ); } diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index d43a65ecc9..3d1e81c461 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -115,6 +115,8 @@ namespace graphene { namespace chain { flat_set& owner, vector& other )const; + virtual uint64_t get_packed_size()const; + protected: // Calculate the digest used for signature validation digest_type sig_digest( const chain_id_type& chain_id )const; @@ -225,8 +227,10 @@ namespace graphene { namespace chain { virtual const transaction_id_type& id()const override; virtual void validate()const override; virtual const flat_set& get_signature_keys( const chain_id_type& chain_id )const override; + virtual uint64_t get_packed_size()const override; protected: mutable bool _validated = false; + mutable uint64_t _packed_size = 0; }; /** diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 14b9dc31a9..39a3c4590e 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -59,6 +59,11 @@ void transaction::validate() const operation_validate(op); } +uint64_t transaction::get_packed_size() const +{ + return fc::raw::pack_size(*this); +} + const transaction_id_type& transaction::id() const { auto h = digest(); @@ -402,9 +407,17 @@ void precomputable_transaction::validate() const { if( _validated ) return; transaction::validate(); + get_packed_size(); _validated = true; } +uint64_t precomputable_transaction::get_packed_size()const +{ + if( _packed_size == 0 ) + _packed_size = transaction::get_packed_size(); + return _packed_size; +} + const flat_set& precomputable_transaction::get_signature_keys( const chain_id_type& chain_id )const { // Strictly we should check whether the given chain ID is same as the one used to initialize the `signees` field. From 19c9d733318141d8535d111a3650524f65db02a5 Mon Sep 17 00:00:00 2001 From: jmjatlanta Date: Fri, 15 Feb 2019 16:57:28 -0500 Subject: [PATCH 139/161] use platform agnostic int --- libraries/wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index e069fbcdc3..d99d8bf0b5 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1469,7 +1469,7 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, - string hash_algorithm, const std::string& preimage_hash, size_t preimage_size, + string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, const uint32_t claim_period_seconds, bool broadcast = false ); /**** diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index db36a601eb..938a3e0d99 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1791,7 +1791,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, size_t preimage_size, + string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, const uint32_t claim_period_seconds, bool broadcast = false ) { try @@ -3179,7 +3179,7 @@ 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, size_t preimage_size, + string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, const uint32_t claim_period_seconds, bool broadcast) { return my->htlc_create(source, destination, amount, asset_symbol, hash_algorithm, preimage_hash, preimage_size, From f5db37dc0b56305f04f5819479bb98d3f871221b Mon Sep 17 00:00:00 2001 From: Alfredo Date: Fri, 15 Feb 2019 23:18:47 -0300 Subject: [PATCH 140/161] fix failing tests in testnet --- tests/tests/bitasset_tests.cpp | 7 +++++++ tests/tests/market_tests.cpp | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/tests/bitasset_tests.cpp b/tests/tests/bitasset_tests.cpp index f4b9c334c5..4dfd0427b5 100644 --- a/tests/tests/bitasset_tests.cpp +++ b/tests/tests/bitasset_tests.cpp @@ -930,6 +930,13 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) // need additional block to avoid prev: popping block would leave head block null error generate_block( skip ); + // testnet needs a lot more blocks(35) here to pass + if(strcmp(GRAPHENE_SYMBOL, "TEST") == 0) { + for(int b = 0; b<35; b++) + generate_block( skip ); + } + + for( int i = 0; i < 8; ++i ) { idump( (i) ); diff --git a/tests/tests/market_tests.cpp b/tests/tests/market_tests.cpp index 413ffd42f9..6000605dc5 100644 --- a/tests/tests/market_tests.cpp +++ b/tests/tests/market_tests.cpp @@ -1817,13 +1817,13 @@ BOOST_AUTO_TEST_CASE(mcr_bug_cross1270) auto mi = db.get_global_properties().parameters.maintenance_interval; generate_blocks(HARDFORK_CORE_1270_TIME - mi); - const asset_object& core = get_asset("BTS"); + const asset_object& core = get_asset(GRAPHENE_SYMBOL); const asset_object& bitusd = get_asset("USDBIT"); const asset_id_type bitusd_id = bitusd.id; const account_object& feedproducer = get_account("feedproducer"); // feed is expired - BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio, 1750); + BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio, GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).feed_is_expired(db.head_block_time()), false); // should be true? // make new feed From 71641eb27cfd99c583baf723a394152ae49dd47f Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 16 Feb 2019 06:57:49 -0500 Subject: [PATCH 141/161] incorrect sign on log message --- libraries/chain/market_evaluator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index 90d09af884..17d1164362 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -183,7 +183,7 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope */ if (next_maintenance_time <= SOFTFORK_CORE_1465_TIME) { - if ( _dynamic_data_obj->current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply ) + if ( _dynamic_data_obj->current_supply + o.delta_debt.amount > _debt_asset->options.max_supply ) ilog("Issue 1465... Borrowing and exceeding MAX_SUPPLY. Will be corrected at hardfork time."); } else From db1699c5ee8c84bcf604483709aafbd76af26171 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sun, 17 Feb 2019 10:21:41 -0300 Subject: [PATCH 142/161] remove feed is expired checks --- tests/tests/market_tests.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/tests/market_tests.cpp b/tests/tests/market_tests.cpp index 6000605dc5..22d0444468 100644 --- a/tests/tests/market_tests.cpp +++ b/tests/tests/market_tests.cpp @@ -1824,7 +1824,6 @@ BOOST_AUTO_TEST_CASE(mcr_bug_cross1270) // feed is expired BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio, GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); - BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).feed_is_expired(db.head_block_time()), false); // should be true? // make new feed price_feed current_feed; @@ -1834,7 +1833,6 @@ BOOST_AUTO_TEST_CASE(mcr_bug_cross1270) publish_feed( bitusd, feedproducer, current_feed ); BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio, 2000); - BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).feed_is_expired(db.head_block_time()), false); // pass hardfork generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); From b0c55b2bb829aafb6c7a5036492505e630052a34 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Sun, 17 Feb 2019 11:05:55 -0300 Subject: [PATCH 143/161] increase number of witnesses to hf_935_test --- tests/common/database_fixture.cpp | 6 +++++- tests/tests/bitasset_tests.cpp | 10 ---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 7d50b8f065..47d5755f89 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -86,7 +86,11 @@ database_fixture::database_fixture(const fc::time_point_sec &initial_timestamp) genesis_state.initial_timestamp = initial_timestamp; - genesis_state.initial_active_witnesses = 10; + if(boost::unit_test::framework::current_test_case().p_name.value == "hf_935_test") + genesis_state.initial_active_witnesses = 20; + else + genesis_state.initial_active_witnesses = 10; + for( unsigned int i = 0; i < genesis_state.initial_active_witnesses; ++i ) { auto name = "init"+fc::to_string(i); diff --git a/tests/tests/bitasset_tests.cpp b/tests/tests/bitasset_tests.cpp index 4dfd0427b5..03062fbe6e 100644 --- a/tests/tests/bitasset_tests.cpp +++ b/tests/tests/bitasset_tests.cpp @@ -927,16 +927,6 @@ BOOST_AUTO_TEST_CASE( hf_935_test ) generate_blocks( db.get_dynamic_global_properties().next_maintenance_time, true, skip ); generate_block( skip ); - // need additional block to avoid prev: popping block would leave head block null error - generate_block( skip ); - - // testnet needs a lot more blocks(35) here to pass - if(strcmp(GRAPHENE_SYMBOL, "TEST") == 0) { - for(int b = 0; b<35; b++) - generate_block( skip ); - } - - for( int i = 0; i < 8; ++i ) { idump( (i) ); From 81de0c56b124168e423edf64df1c2ce81339d715 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sun, 17 Feb 2019 22:15:32 -0500 Subject: [PATCH 144/161] Call get_packed_size at appropriate time. --- libraries/chain/db_block.cpp | 1 + libraries/chain/protocol/transaction.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index b1f2e65480..2f60296950 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -752,6 +752,7 @@ void database::_precompute_parallel( const Trx* trx, const size_t count, const u for( size_t i = 0; i < count; ++i, ++trx ) { trx->validate(); // TODO - parallelize wrt confidential operations + trx->get_packed_size(); if( !(skip&skip_transaction_dupe_check) ) trx->id(); if( !(skip&skip_transaction_signatures) ) diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 39a3c4590e..6b4d7f3235 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -407,7 +407,6 @@ void precomputable_transaction::validate() const { if( _validated ) return; transaction::validate(); - get_packed_size(); _validated = true; } From 7d51919fce74305edb7d0f049fdcb61823fab6e9 Mon Sep 17 00:00:00 2001 From: Alfredo Date: Mon, 18 Feb 2019 09:51:20 -0300 Subject: [PATCH 145/161] wrap lines --- tests/tests/market_tests.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/tests/market_tests.cpp b/tests/tests/market_tests.cpp index 22d0444468..c30505f87f 100644 --- a/tests/tests/market_tests.cpp +++ b/tests/tests/market_tests.cpp @@ -1823,7 +1823,8 @@ BOOST_AUTO_TEST_CASE(mcr_bug_cross1270) const account_object& feedproducer = get_account("feedproducer"); // feed is expired - BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio, GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); + auto mcr = (*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio; + BOOST_CHECK_EQUAL(mcr, GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); // make new feed price_feed current_feed; @@ -1832,14 +1833,16 @@ BOOST_AUTO_TEST_CASE(mcr_bug_cross1270) current_feed.maximum_short_squeeze_ratio = 1100; publish_feed( bitusd, feedproducer, current_feed ); - BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio, 2000); + mcr = (*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio; + BOOST_CHECK_EQUAL(mcr, 2000); // pass hardfork generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); generate_block(); // feed is still valid - BOOST_CHECK_EQUAL((*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio, 2000); + mcr = (*bitusd_id(db).bitasset_data_id)(db).current_feed.maintenance_collateral_ratio; + BOOST_CHECK_EQUAL(mcr, 2000); // margin call is traded print_market(asset_id_type(1)(db).symbol, asset_id_type()(db).symbol); From c8ba1f289e88634c2f1cb5c77c90ea7e72a16e22 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 20 Feb 2019 14:32:58 -0500 Subject: [PATCH 146/161] fix HTLC tests --- tests/tests/htlc_tests.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index fa5181da1f..cd8bff5a59 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -136,16 +136,13 @@ void set_committee_parameters(database_fixture* db_fixture) db_fixture->trx.operations.push_back(cop); graphene::chain::processed_transaction proc_trx =db_fixture->db.push_transaction(db_fixture->trx); + db_fixture->trx.clear(); proposal_id_type good_proposal_id = proc_trx.operation_results[0].get(); proposal_update_operation puo; puo.proposal = good_proposal_id; puo.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; - puo.active_approvals_to_add = { - db_fixture->get_account("init0").get_id(), db_fixture->get_account("init1").get_id(), - db_fixture->get_account("init2").get_id(), db_fixture->get_account("init3").get_id(), - db_fixture->get_account("init4").get_id(), db_fixture->get_account("init5").get_id(), - db_fixture->get_account("init6").get_id(), db_fixture->get_account("init7").get_id()}; + puo.key_approvals_to_add.emplace( db_fixture->init_account_priv_key.get_public_key() ); db_fixture->trx.operations.push_back(puo); db_fixture->sign( db_fixture->trx, db_fixture->init_account_priv_key ); db_fixture->db.push_transaction(db_fixture->trx); From ba05bd076802ecc390a38ea0d3421c58f47a0f8f Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 25 Feb 2019 09:53:48 -0300 Subject: [PATCH 147/161] bump database in hardfork --- 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 e97bacc032..867bc5adc4 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -121,7 +121,7 @@ #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 -#define GRAPHENE_CURRENT_DB_VERSION "BTS2.181221" +#define GRAPHENE_CURRENT_DB_VERSION "BTS2.190225" #define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT) From 7cd6e318133168740ab7a5066eb04752e30c0a26 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 15 Mar 2019 09:36:06 -0500 Subject: [PATCH 148/161] Look at white/blacklists on htlc_create --- libraries/chain/htlc_evaluator.cpp | 11 ++- tests/tests/htlc_tests.cpp | 121 +++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 7ecc8ac98e..ec1578a405 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -36,6 +37,7 @@ namespace graphene { void_result htlc_create_evaluator::do_evaluate(const htlc_create_operation& o) { + graphene::chain::database& d = db(); optional htlc_options = get_committee_htlc_options(db()); FC_ASSERT(htlc_options, "HTLC Committee options are not set."); @@ -45,7 +47,14 @@ namespace graphene { // make sure the preimage length is reasonable 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( db().get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; + FC_ASSERT( d.get_balance( o.from, o.amount.asset_id) >= (o.amount), "Insufficient funds") ; + const auto& asset_to_transfer = o.amount.asset_id(d); + const auto& from_account = o.from(d); + const auto& to_account = o.to(d); + 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) ); return void_result(); } diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index cd8bff5a59..fbaca186d6 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -633,4 +633,125 @@ BOOST_AUTO_TEST_CASE( fee_calculations ) } } +BOOST_AUTO_TEST_CASE( htlc_blacklist ) +{ +try { + ACTORS((nathan)(alice)(bob)); + + upgrade_to_lifetime_member( nathan ); + + // create a UIA + const asset_id_type uia_id = create_user_issued_asset( "NATHANCOIN", nathan, white_list ).id; + // Make a whitelist authority + { + BOOST_TEST_MESSAGE( "Changing the whitelist authority" ); + asset_update_operation uop; + uop.issuer = nathan_id; + uop.asset_to_update = uia_id; + uop.new_options = uia_id(db).options; + uop.new_options.blacklist_authorities.insert(nathan_id); + trx.operations.push_back(uop); + PUSH_TX( db, trx, ~0 ); + trx.operations.clear(); + } + + + int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); + transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); + transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); + + advance_past_hardfork(this); + + // blacklist bob + { + graphene::chain::account_whitelist_operation op; + op.authorizing_account = nathan_id; + op.account_to_list = bob_id; + op.new_listing = graphene::chain::account_whitelist_operation::account_listing::black_listed; + op.fee = db.current_fee_schedule().calculate_fee( op ); + trx.operations.push_back( op ); + sign( trx, nathan_private_key ); + PUSH_TX( db, trx, ~0 ); + trx.clear(); + generate_block(); + } + + issue_uia( alice_id, asset( init_balance, uia_id ) ); + + uint16_t preimage_size = 256; + std::vector pre_image(preimage_size); + generate_random_preimage(preimage_size, pre_image); + + // Alice attempts to put a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 20 * GRAPHENE_BLOCKCHAIN_PRECISION, uia_id ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 86400; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = preimage_size; + create_operation.from = alice_id; + create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); + trx.operations.push_back( create_operation ); + sign(trx, alice_private_key); + // bob cannot accept it, so it fails + GRAPHENE_CHECK_THROW( PUSH_TX( db, trx, ~0 ), fc::exception ); + trx.clear(); + } + + // unblacklist Bob + { + graphene::chain::account_whitelist_operation op; + op.authorizing_account = nathan_id; + op.account_to_list = bob_id; + op.new_listing = graphene::chain::account_whitelist_operation::account_listing::no_listing; + op.fee = db.current_fee_schedule().calculate_fee( op ); + trx.operations.push_back( op ); + sign( trx, nathan_private_key ); + PUSH_TX( db, trx, ~0 ); + trx.clear(); + generate_block(); + } + + graphene::chain::htlc_id_type alice_htlc_id; + + // Alice again attempts to put a contract on the blockchain + { + graphene::chain::htlc_create_operation create_operation; + + create_operation.amount = graphene::chain::asset( 20 * GRAPHENE_BLOCKCHAIN_PRECISION, uia_id ); + create_operation.to = bob_id; + create_operation.claim_period_seconds = 86400; + create_operation.preimage_hash = hash_it( pre_image ); + create_operation.preimage_size = preimage_size; + create_operation.from = alice_id; + create_operation.fee = db.current_fee_schedule().calculate_fee( create_operation ); + trx.operations.push_back( create_operation ); + sign(trx, alice_private_key); + // bob can now accept it, so it works + 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(); + } + + // bob can redeem + { + graphene::chain::htlc_redeem_operation update_operation; + update_operation.redeemer = bob_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, bob_private_key); + PUSH_TX( db, trx, ~0 ); + generate_block(); + trx.clear(); + } + +} FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_SUITE_END() From ef6dc13b27b6745494a0f5eee079861ae1eeca5f Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 15 Mar 2019 09:39:39 -0500 Subject: [PATCH 149/161] Fix spacing --- libraries/chain/htlc_evaluator.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index ec1578a405..d318c40203 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -48,13 +48,15 @@ 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") ; - const auto& asset_to_transfer = o.amount.asset_id(d); - const auto& from_account = o.from(d); - const auto& to_account = o.to(d); + const auto& asset_to_transfer = o.amount.asset_id( d ); + const auto& from_account = o.from( d ); + const auto& to_account = o.to( d ); 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) ); + "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) ); + "Asset ${asset} is not authorized for account ${acct}.", + ( "asset", asset_to_transfer.id )( "acct", to_account.id ) ); return void_result(); } From 11f9f1be02a7ce67476692ce58a7167c97f5c4ef Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 15 Mar 2019 09:50:39 -0500 Subject: [PATCH 150/161] Change format of get_htlc output --- libraries/wallet/wallet.cpp | 61 +++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 938a3e0d99..de91c0a081 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -264,24 +264,54 @@ struct op_prototype_visitor } }; +class htlc_hash_to_mutable_variant_visitor +{ +public: + typedef fc::mutable_variant_object result_type; + + result_type operator()( const fc::ripemd160& hash )const + { + fc::mutable_variant_object ret_val; + ret_val["preimage_hash"] = hash.str(); + ret_val["hash_algo"] = "RIPEMD160"; + return ret_val; + } + + result_type operator()( const fc::sha1& hash )const + { + fc::mutable_variant_object ret_val; + ret_val["preimage_hash"] = hash.str(); + ret_val["hash_algo"] = "SHA1"; + return ret_val; + } + + result_type operator()( const fc::sha256& hash )const + { + fc::mutable_variant_object ret_val; + ret_val["preimage_hash"] = hash.str(); + ret_val["hash_algo"] = "SHA256"; + return ret_val; + } +}; + class htlc_hash_to_string_visitor { public: - typedef string result_type; + typedef std::string result_type; result_type operator()( const fc::ripemd160& hash )const { - return "RIPEMD160 " + hash.str(); + return "RIPEMD160 " + hash.str(); } result_type operator()( const fc::sha1& hash )const { - return "SHA1 " + hash.str(); + return "SHA1 " + hash.str(); } result_type operator()( const fc::sha256& hash )const { - return "SHA256 " + hash.str(); + return "SHA256 " + hash.str(); } }; @@ -3188,18 +3218,25 @@ signed_transaction wallet_api::htlc_create( string source, string destination, s variant wallet_api::get_htlc(std::string htlc_id) const { - static detail::htlc_hash_to_string_visitor vtor; + static detail::htlc_hash_to_mutable_variant_visitor vtor; graphene::chain::htlc_object obj = my->get_htlc(htlc_id); fc::mutable_variant_object ret_val; ret_val["database_id"] = (std::string)obj.id; - ret_val["from"] = (std::string)((graphene::db::object_id_type)obj.from); - ret_val["to"] = (std::string)((graphene::db::object_id_type)obj.to); - ret_val["amount"] = obj.amount.amount.value; - ret_val["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); - ret_val["expiration"] = fc::get_approximate_relative_time_string(obj.expiration); - ret_val["preimage_hash"] = obj.preimage_hash.visit( vtor ); - ret_val["preimage_size"] = obj.preimage_size; + fc::mutable_variant_object transfer; + transfer["from"] = (std::string)((graphene::db::object_id_type)obj.from); + transfer["to"] = (std::string)((graphene::db::object_id_type)obj.to); + transfer["amount"] = obj.amount.amount.value; + transfer["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); + ret_val["transfer"] = transfer; + fc::mutable_variant_object conditions; + fc::mutable_variant_object hash_lock = obj.preimage_hash.visit( vtor ); + hash_lock["preimage_length"] = obj.preimage_size; + conditions["hash_lock"] = hash_lock; + fc::mutable_variant_object time_lock; + time_lock["expiration"] = obj.expiration.to_iso_string(); + conditions["time_lock"] = time_lock; + ret_val["conditions"] = conditions; return ret_val; } From bd7d0122f3fd6291f4988f4127f6d74476eeab7a Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 15 Mar 2019 14:02:35 -0500 Subject: [PATCH 151/161] allow for no results --- .../wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 51 +++++++++++-------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index d99d8bf0b5..1e7d90c234 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -495,7 +495,7 @@ class wallet_api * @param htlc_id the id of the HTLC object. * @returns the information about the HTLC object */ - variant get_htlc(string htlc_id) const; + optional get_htlc(string htlc_id) const; /** Lookup the id of a named account. * @param account_name_or_id the name of the account to look up diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index de91c0a081..75577983df 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -708,13 +708,14 @@ class wallet_api_impl return *opt; } - htlc_object get_htlc(string htlc_id) const + optional get_htlc(string htlc_id) const { htlc_id_type id; fc::from_variant(htlc_id, id); auto obj = _remote_db->get_objects( { id }).front(); - htlc_object htlc = obj.template as(GRAPHENE_MAX_NESTED_OBJECTS); - return htlc; + if (!obj.is_null()) + return optional(obj.template as(GRAPHENE_MAX_NESTED_OBJECTS)); + return optional(); } asset_id_type get_asset_id(string asset_symbol_or_id) const @@ -3216,28 +3217,34 @@ signed_transaction wallet_api::htlc_create( string source, string destination, s claim_period_seconds, broadcast); } -variant wallet_api::get_htlc(std::string htlc_id) const +optional wallet_api::get_htlc(std::string htlc_id) const { static detail::htlc_hash_to_mutable_variant_visitor vtor; - graphene::chain::htlc_object obj = my->get_htlc(htlc_id); - fc::mutable_variant_object ret_val; - ret_val["database_id"] = (std::string)obj.id; - fc::mutable_variant_object transfer; - transfer["from"] = (std::string)((graphene::db::object_id_type)obj.from); - transfer["to"] = (std::string)((graphene::db::object_id_type)obj.to); - transfer["amount"] = obj.amount.amount.value; - transfer["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); - ret_val["transfer"] = transfer; - fc::mutable_variant_object conditions; - fc::mutable_variant_object hash_lock = obj.preimage_hash.visit( vtor ); - hash_lock["preimage_length"] = obj.preimage_size; - conditions["hash_lock"] = hash_lock; - fc::mutable_variant_object time_lock; - time_lock["expiration"] = obj.expiration.to_iso_string(); - conditions["time_lock"] = time_lock; - ret_val["conditions"] = conditions; - return ret_val; + optional opt_obj = my->get_htlc(htlc_id); + optional return_val; + if (opt_obj) + { + return_val = fc::mutable_variant_object(); + const graphene::chain::htlc_object& obj = *opt_obj; + fc::mutable_variant_object& ret_val = *return_val; + ret_val["database_id"] = (std::string)obj.id; + fc::mutable_variant_object transfer; + transfer["from"] = (std::string)((graphene::db::object_id_type)obj.from); + transfer["to"] = (std::string)((graphene::db::object_id_type)obj.to); + transfer["amount"] = obj.amount.amount.value; + transfer["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); + ret_val["transfer"] = transfer; + fc::mutable_variant_object conditions; + fc::mutable_variant_object hash_lock = obj.preimage_hash.visit( vtor ); + hash_lock["preimage_length"] = obj.preimage_size; + conditions["hash_lock"] = hash_lock; + fc::mutable_variant_object time_lock; + time_lock["expiration"] = obj.expiration.to_iso_string(); + conditions["time_lock"] = time_lock; + ret_val["conditions"] = conditions; + } + return return_val; } signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::string& preimage, From 4ca72d8257b14e4c1307c9f39b7ea0d2c515108a Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 15 Mar 2019 14:03:10 -0500 Subject: [PATCH 152/161] fix long to int compiler warning --- tests/cli/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index d3e26943cf..7075a8a84f 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -680,7 +680,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) std::string preimage_string = "My Secret"; fc::sha256 preimage_md = fc::sha256::hash(preimage_string); std::stringstream ss; - for(int i = 0; i < preimage_md.data_size(); i++) + for(size_t i = 0; i < preimage_md.data_size(); i++) { char d = preimage_md.data()[i]; unsigned char uc = static_cast(d); From cfba7ade6474f981b1d1dfdddba30ab966c6698e Mon Sep 17 00:00:00 2001 From: John Jones Date: Sat, 16 Mar 2019 15:50:44 -0500 Subject: [PATCH 153/161] adjust htlc_object struct --- libraries/chain/db_notify.cpp | 4 +- libraries/chain/db_update.cpp | 6 +- libraries/chain/htlc_evaluator.cpp | 24 ++++---- .../include/graphene/chain/htlc_object.hpp | 55 +++++++++++++---- .../wallet/include/graphene/wallet/wallet.hpp | 4 +- libraries/wallet/wallet.cpp | 59 +------------------ tests/common/database_fixture.cpp | 2 +- 7 files changed, 68 insertions(+), 86 deletions(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index fafaa3bf00..1347026428 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -369,8 +369,8 @@ void get_relevant_accounts( const object* obj, flat_set& accoun } case htlc_object_type:{ const auto& htlc_obj = dynamic_cast(obj); FC_ASSERT( htlc_obj != nullptr ); - accounts.insert( htlc_obj->from ); - accounts.insert( htlc_obj->to ); + accounts.insert( htlc_obj->transfer.from ); + accounts.insert( htlc_obj->transfer.to ); break; } } diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 29856165d7..48dea9fb49 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -581,12 +581,12 @@ void database::clear_expired_htlcs() { const auto& htlc_idx = get_index_type().indices().get(); while ( htlc_idx.begin() != htlc_idx.end() - && htlc_idx.begin()->expiration <= head_block_time() ) + && htlc_idx.begin()->conditions.time_lock.expiration <= head_block_time() ) { const htlc_object& obj = *htlc_idx.begin(); - adjust_balance( obj.from, obj.amount ); + adjust_balance( obj.transfer.from, asset(obj.transfer.amount, obj.transfer.asset_id) ); // virtual op - htlc_refund_operation vop( obj.id, obj.from ); + htlc_refund_operation vop( obj.id, obj.transfer.from ); vop.htlc_id = htlc_idx.begin()->id; push_applied_operation( vop ); diff --git a/libraries/chain/htlc_evaluator.cpp b/libraries/chain/htlc_evaluator.cpp index 7ecc8ac98e..fc02dc234c 100644 --- a/libraries/chain/htlc_evaluator.cpp +++ b/libraries/chain/htlc_evaluator.cpp @@ -56,12 +56,13 @@ namespace graphene { dbase.adjust_balance( o.from, -o.amount ); const htlc_object& esc = db().create([&dbase,&o]( htlc_object& esc ) { - esc.from = o.from; - esc.to = o.to; - esc.amount = o.amount; - esc.preimage_hash = o.preimage_hash; - esc.preimage_size = o.preimage_size; - esc.expiration = dbase.head_block_time() + o.claim_period_seconds; + esc.transfer.from = o.from; + esc.transfer.to = o.to; + esc.transfer.amount = o.amount.amount; + 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; }); return esc.id; @@ -89,19 +90,20 @@ namespace graphene { { htlc_obj = &db().get(o.htlc_id); - FC_ASSERT(o.preimage.size() == htlc_obj->preimage_size, "Preimage size mismatch."); + 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->preimage_hash.visit( vtor ), "Provided preimage does not generate correct hash."); + FC_ASSERT( htlc_obj->conditions.hash_lock.preimage_hash.visit( vtor ), "Provided preimage does not generate correct hash."); return void_result(); } void_result htlc_redeem_evaluator::do_apply(const htlc_redeem_operation& o) { - db().adjust_balance(htlc_obj->to, htlc_obj->amount); + 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->from, htlc_obj->to, htlc_obj->amount ); + htlc_redeemed_operation virt_op( htlc_obj->id, htlc_obj->transfer.from, htlc_obj->transfer.to, + asset(htlc_obj->transfer.amount, htlc_obj->transfer.asset_id ) ); db().push_applied_operation( virt_op ); db().remove(*htlc_obj); return void_result(); @@ -116,7 +118,7 @@ namespace graphene { void_result htlc_extend_evaluator::do_apply(const htlc_extend_operation& o) { db().modify(*htlc_obj, [&o](htlc_object& db_obj) { - db_obj.expiration += o.seconds_to_add; + db_obj.conditions.time_lock.expiration += o.seconds_to_add; }); return void_result(); diff --git a/libraries/chain/include/graphene/chain/htlc_object.hpp b/libraries/chain/include/graphene/chain/htlc_object.hpp index e9c4515042..5d41eda392 100644 --- a/libraries/chain/include/graphene/chain/htlc_object.hpp +++ b/libraries/chain/include/graphene/chain/htlc_object.hpp @@ -45,12 +45,38 @@ namespace graphene { namespace chain { static const uint8_t space_id = protocol_ids; static const uint8_t type_id = htlc_object_type; - account_id_type from; - account_id_type to; - asset amount; - fc::time_point_sec expiration; - htlc_hash preimage_hash; - uint16_t preimage_size; + struct transfer_info { + account_id_type from; + account_id_type to; + share_type amount; + asset_id_type asset_id; + } transfer; + struct condition_info { + struct hash_lock_info { + htlc_hash preimage_hash; + uint16_t preimage_size; + } hash_lock; + struct time_lock_info { + fc::time_point_sec expiration; + } time_lock; + } conditions; + + /**** + * Index helper for timelock + */ + struct timelock_extractor { + typedef fc::time_point_sec result_type; + const result_type& operator()(const htlc_object& o)const { return o.conditions.time_lock.expiration; } + }; + + /***** + * Index helper for from + */ + struct from_extractor { + typedef account_id_type result_type; + const result_type& operator()(const htlc_object& o)const { return o.transfer.from; } + }; + }; struct by_from_id; @@ -61,13 +87,13 @@ namespace graphene { namespace chain { ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, ordered_unique< tag< by_expiration >, - composite_key< htlc_object, - member< htlc_object, fc::time_point_sec, &htlc_object::expiration >, + composite_key< htlc_object, + htlc_object::timelock_extractor, member< object, object_id_type, &object::id > > >, ordered_unique< tag< by_from_id >, composite_key< htlc_object, - member< htlc_object, account_id_type, &htlc_object::from >, + htlc_object::from_extractor, member< object, object_id_type, &object::id > > > > @@ -77,6 +103,13 @@ namespace graphene { namespace chain { } } // namespace graphene::chain +FC_REFLECT( graphene::chain::htlc_object::transfer_info, + (from) (to) (amount) (asset_id) ) +FC_REFLECT( graphene::chain::htlc_object::condition_info::hash_lock_info, + (preimage_hash) (preimage_size) ) +FC_REFLECT( graphene::chain::htlc_object::condition_info::time_lock_info, + (expiration) ) +FC_REFLECT( graphene::chain::htlc_object::condition_info, + (hash_lock)(time_lock) ) FC_REFLECT_DERIVED( graphene::chain::htlc_object, (graphene::db::object), - (from)(to)(amount)(expiration) - (preimage_hash)(preimage_size) ); + (transfer) (conditions) ) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 1e7d90c234..14404c47a8 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -23,6 +23,8 @@ */ #pragma once +#include +#include #include #include @@ -495,7 +497,7 @@ class wallet_api * @param htlc_id the id of the HTLC object. * @returns the information about the HTLC object */ - optional get_htlc(string htlc_id) const; + optional get_htlc(string htlc_id) const; /** Lookup the id of a named account. * @param account_name_or_id the name of the account to look up diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 75577983df..2902bbc871 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -264,36 +264,6 @@ struct op_prototype_visitor } }; -class htlc_hash_to_mutable_variant_visitor -{ -public: - typedef fc::mutable_variant_object result_type; - - result_type operator()( const fc::ripemd160& hash )const - { - fc::mutable_variant_object ret_val; - ret_val["preimage_hash"] = hash.str(); - ret_val["hash_algo"] = "RIPEMD160"; - return ret_val; - } - - result_type operator()( const fc::sha1& hash )const - { - fc::mutable_variant_object ret_val; - ret_val["preimage_hash"] = hash.str(); - ret_val["hash_algo"] = "SHA1"; - return ret_val; - } - - result_type operator()( const fc::sha256& hash )const - { - fc::mutable_variant_object ret_val; - ret_val["preimage_hash"] = hash.str(); - ret_val["hash_algo"] = "SHA256"; - return ret_val; - } -}; - class htlc_hash_to_string_visitor { public: @@ -3217,34 +3187,9 @@ signed_transaction wallet_api::htlc_create( string source, string destination, s claim_period_seconds, broadcast); } -optional wallet_api::get_htlc(std::string htlc_id) const +optional wallet_api::get_htlc(std::string htlc_id) const { - static detail::htlc_hash_to_mutable_variant_visitor vtor; - - optional opt_obj = my->get_htlc(htlc_id); - optional return_val; - if (opt_obj) - { - return_val = fc::mutable_variant_object(); - const graphene::chain::htlc_object& obj = *opt_obj; - fc::mutable_variant_object& ret_val = *return_val; - ret_val["database_id"] = (std::string)obj.id; - fc::mutable_variant_object transfer; - transfer["from"] = (std::string)((graphene::db::object_id_type)obj.from); - transfer["to"] = (std::string)((graphene::db::object_id_type)obj.to); - transfer["amount"] = obj.amount.amount.value; - transfer["asset"] = (std::string)((graphene::db::object_id_type)obj.amount.asset_id); - ret_val["transfer"] = transfer; - fc::mutable_variant_object conditions; - fc::mutable_variant_object hash_lock = obj.preimage_hash.visit( vtor ); - hash_lock["preimage_length"] = obj.preimage_size; - conditions["hash_lock"] = hash_lock; - fc::mutable_variant_object time_lock; - time_lock["expiration"] = obj.expiration.to_iso_string(); - conditions["time_lock"] = time_lock; - ret_val["conditions"] = conditions; - } - return return_val; + return my->get_htlc(htlc_id); } signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::string& preimage, diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 47d5755f89..b58ba374ca 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -318,7 +318,7 @@ void database_fixture::verify_asset_supplies( const database& db ) const auto& htlc_idx = db.get_index_type< htlc_index >().indices().get< by_id >(); for( auto itr = htlc_idx.begin(); itr != htlc_idx.end(); ++itr ) { - total_balances[itr->amount.asset_id] += itr->amount.amount; + total_balances[itr->transfer.asset_id] += itr->transfer.amount; } for( const asset_object& asset_obj : db.get_index_type().indices() ) From 5b5e6ad39ea153778e9ee0144f48dadd334acba5 Mon Sep 17 00:00:00 2001 From: John Jones Date: Sun, 17 Mar 2019 06:52:35 -0500 Subject: [PATCH 154/161] Prove bob redeems although blacklisted --- tests/tests/htlc_tests.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index fbaca186d6..826cc7e1a2 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -657,8 +657,8 @@ try { int64_t init_balance(100 * GRAPHENE_BLOCKCHAIN_PRECISION); - transfer( committee_account, alice_id, graphene::chain::asset(init_balance) ); - transfer( committee_account, bob_id, graphene::chain::asset(init_balance) ); + fund( alice, graphene::chain::asset(init_balance) ); + fund( bob, graphene::chain::asset(init_balance) ); advance_past_hardfork(this); @@ -737,7 +737,21 @@ try { alice_htlc_id = alice_trx.operation_results[0].get(); } - // bob can redeem + // blacklist bob + { + graphene::chain::account_whitelist_operation op; + op.authorizing_account = nathan_id; + op.account_to_list = bob_id; + op.new_listing = graphene::chain::account_whitelist_operation::account_listing::black_listed; + op.fee = db.current_fee_schedule().calculate_fee( op ); + trx.operations.push_back( op ); + sign( trx, nathan_private_key ); + PUSH_TX( db, trx, ~0 ); + trx.clear(); + generate_block(); + } + + // bob can redeem even though he's blacklisted { graphene::chain::htlc_redeem_operation update_operation; update_operation.redeemer = bob_id; From c63d21d7ea89fcbb506321a89f1f8da5851798e0 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 18 Mar 2019 09:49:55 -0500 Subject: [PATCH 155/161] Pretty-format HTLC output --- .../wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 58 +++++++++++++++++-- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 14404c47a8..8ec275c60f 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -497,7 +497,7 @@ class wallet_api * @param htlc_id the id of the HTLC object. * @returns the information about the HTLC object */ - optional get_htlc(string htlc_id) const; + fc::optional get_htlc(string htlc_id) const; /** Lookup the id of a named account. * @param account_name_or_id the name of the account to look up diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 2902bbc871..5e9690b261 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -678,14 +678,16 @@ class wallet_api_impl return *opt; } - optional get_htlc(string htlc_id) const + fc::optional get_htlc(string htlc_id) const { htlc_id_type id; fc::from_variant(htlc_id, id); auto obj = _remote_db->get_objects( { id }).front(); if (!obj.is_null()) - return optional(obj.template as(GRAPHENE_MAX_NESTED_OBJECTS)); - return optional(); + { + return fc::optional(obj.template as(GRAPHENE_MAX_NESTED_OBJECTS)); + } + return fc::optional(); } asset_id_type get_asset_id(string asset_symbol_or_id) const @@ -3187,9 +3189,55 @@ signed_transaction wallet_api::htlc_create( string source, string destination, s claim_period_seconds, broadcast); } -optional wallet_api::get_htlc(std::string htlc_id) const +fc::optional wallet_api::get_htlc(std::string htlc_id) const { - return my->get_htlc(htlc_id); + fc::optional optional_obj = my->get_htlc(htlc_id); + if (optional_obj) + { + const htlc_object& obj = *optional_obj; + // convert to formatted variant + fc::mutable_variant_object transfer; + const auto& from = my->get_account( obj.transfer.from ); + transfer["from"] = from.name; + const auto& to = my->get_account( obj.transfer.to ); + transfer["to"] = to.name; + const auto& asset = my->get_asset( obj.transfer.asset_id ); + transfer["asset"] = asset.symbol; + transfer["amount"] = obj.transfer.amount.value; + class htlc_hash_to_variant_visitor + { + public: + typedef fc::mutable_variant_object result_type; + + result_type operator()(const fc::ripemd160& obj)const + { return convert("RIPEMD160", obj.str()); } + result_type operator()(const fc::sha1& obj)const + { return convert("SHA1", obj.str()); } + result_type operator()(const fc::sha256& obj)const + { return convert("SHA256", obj.str()); } + private: + result_type convert(const std::string& type, const std::string& hash)const + { + fc::mutable_variant_object ret_val; + ret_val["hash_algo"] = type; + ret_val["preimage_hash"] = hash; + return ret_val; + } + }; + static htlc_hash_to_variant_visitor hash_visitor; + fc::mutable_variant_object htlc_lock = obj.conditions.hash_lock.preimage_hash.visit(hash_visitor); + htlc_lock["preimage_size"] = obj.conditions.hash_lock.preimage_size; + fc::mutable_variant_object time_lock; + time_lock["expiration"] = obj.conditions.time_lock.expiration; + fc::mutable_variant_object conditions; + conditions["htlc_lock"] = htlc_lock; + conditions["time_lock"] = time_lock; + fc::mutable_variant_object result; + result["transfer"] = transfer; + result["conditions"] = conditions; + return fc::optional(result); + } + return fc::optional(); } signed_transaction wallet_api::htlc_redeem( std::string htlc_id, std::string issuer, const std::string& preimage, From 5f0e40016b0384c1b784e5d324d711aa8994b81e Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 19 Mar 2019 09:16:13 -0500 Subject: [PATCH 156/161] format of amount to include decimal --- libraries/wallet/wallet.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 5e9690b261..f45f30230f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -63,6 +63,7 @@ #include #include +#include #include #include #include @@ -3203,7 +3204,7 @@ fc::optional wallet_api::get_htlc(std::string htlc_id) const transfer["to"] = to.name; const auto& asset = my->get_asset( obj.transfer.asset_id ); transfer["asset"] = asset.symbol; - transfer["amount"] = obj.transfer.amount.value; + transfer["amount"] = graphene::app::uint128_amount_to_string( obj.transfer.amount.value, asset.precision ); class htlc_hash_to_variant_visitor { public: From db1383858a25d3ccf6d3dcb6fee8f2c56d2b7536 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 19 Mar 2019 10:02:06 -0500 Subject: [PATCH 157/161] bump DB_VERSION --- 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 867bc5adc4..5b0811e968 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -121,7 +121,7 @@ #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 -#define GRAPHENE_CURRENT_DB_VERSION "BTS2.190225" +#define GRAPHENE_CURRENT_DB_VERSION "20190319" #define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT) From c51220beac1f23a500f6440d2ce1424d776d0a2f Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 20 Mar 2019 11:02:42 -0500 Subject: [PATCH 158/161] Added time left to data returned from get_htlc --- libraries/wallet/wallet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index f45f30230f..308b5be7c0 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3230,6 +3230,7 @@ fc::optional wallet_api::get_htlc(std::string htlc_id) const htlc_lock["preimage_size"] = obj.conditions.hash_lock.preimage_size; fc::mutable_variant_object time_lock; time_lock["expiration"] = obj.conditions.time_lock.expiration; + time_lock["time_left"] = fc::get_approximate_relative_time_string(obj.conditions.time_lock.expiration); fc::mutable_variant_object conditions; conditions["htlc_lock"] = htlc_lock; conditions["time_lock"] = time_lock; From 07d10cb95dbe40ca70a720533d381a8d8ed3e384 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 21 Mar 2019 13:16:07 -0500 Subject: [PATCH 159/161] using the more expresive optional<>.valid() --- libraries/wallet/wallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 308b5be7c0..462a5b91c5 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -684,7 +684,7 @@ class wallet_api_impl htlc_id_type id; fc::from_variant(htlc_id, id); auto obj = _remote_db->get_objects( { id }).front(); - if (!obj.is_null()) + if ( !obj.is_null() ) { return fc::optional(obj.template as(GRAPHENE_MAX_NESTED_OBJECTS)); } @@ -3193,7 +3193,7 @@ signed_transaction wallet_api::htlc_create( string source, string destination, s fc::optional wallet_api::get_htlc(std::string htlc_id) const { fc::optional optional_obj = my->get_htlc(htlc_id); - if (optional_obj) + if ( optional_obj.valid() ) { const htlc_object& obj = *optional_obj; // convert to formatted variant From de73829a49b62e924a4518d16bc4b5ffe593fe01 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 21 Mar 2019 14:30:54 -0500 Subject: [PATCH 160/161] Increase max_trx_size for confidential_tx test --- tests/cli/main.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index d3e26943cf..4347ba0cd3 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -461,7 +461,12 @@ BOOST_FIXTURE_TEST_CASE( cli_confidential_tx_test, cli_fixture ) { using namespace graphene::wallet; try { - std::vector import_txs; + // we need to increase the default max transaction size to run this test. + this->app1->chain_database()->modify( + this->app1->chain_database()->get_global_properties(), + []( global_property_object& p) { + p.parameters.maximum_transaction_size = 8192; + }); std::vector import_txs; BOOST_TEST_MESSAGE("Importing nathan's balance"); import_txs = con.wallet_api_ptr->import_balance("nathan", nathan_keys, true); @@ -680,7 +685,7 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) std::string preimage_string = "My Secret"; fc::sha256 preimage_md = fc::sha256::hash(preimage_string); std::stringstream ss; - for(int i = 0; i < preimage_md.data_size(); i++) + for(size_t i = 0; i < preimage_md.data_size(); i++) { char d = preimage_md.data()[i]; unsigned char uc = static_cast(d); From db045a20fa1b92a42abd632c43f6dcf20772d523 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 21 Mar 2019 14:35:05 -0500 Subject: [PATCH 161/161] Fixed spacing --- tests/cli/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 4347ba0cd3..b091f298fc 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -466,7 +466,8 @@ BOOST_FIXTURE_TEST_CASE( cli_confidential_tx_test, cli_fixture ) this->app1->chain_database()->get_global_properties(), []( global_property_object& p) { p.parameters.maximum_transaction_size = 8192; - }); std::vector import_txs; + }); + std::vector import_txs; BOOST_TEST_MESSAGE("Importing nathan's balance"); import_txs = con.wallet_api_ptr->import_balance("nathan", nathan_keys, true);