From 3af94b787847cbd9b547a6b1cd8b6be37bb2f917 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 4 Jan 2016 10:24:09 -0500 Subject: [PATCH 01/43] application.cpp: Handle exception in open() by re-indexing #492 --- libraries/app/application.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 0245ebcf..5be743a3 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -328,9 +328,27 @@ namespace detail { fc::read_file_contents( _data_dir / "db_version", version_str ); return (version_str != GRAPHENE_CURRENT_DB_VERSION); }; - if( !is_new() && is_outdated() ) + + bool need_reindex = (!is_new() && is_outdated()); + std::string reindex_reason = "version upgrade"; + + if( !need_reindex ) { - ilog("Replaying blockchain due to version upgrade"); + try + { + _chain_db->open(_data_dir / "blockchain", initial_state); + } + catch( const fc::exception& e ) + { + ilog( "caught exception ${e} in open()", ("e", e.to_detail_string()) ); + need_reindex = true; + reindex_reason = "exception in open()"; + } + } + + if( need_reindex ) + { + ilog("Replaying blockchain due to ${reason}", ("reason", reindex_reason) ); fc::remove_all( _data_dir / "db_version" ); _chain_db->reindex(_data_dir / "blockchain", initial_state()); @@ -346,8 +364,6 @@ namespace detail { db_version.write( version_string.c_str(), version_string.size() ); db_version.close(); } - } else { - _chain_db->open(_data_dir / "blockchain", initial_state); } } else { wlog("Detected unclean shutdown. Replaying blockchain..."); From a1ee326b55ebdc62f0b43ff2921355eb383b82c9 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Fri, 22 Jan 2016 15:57:53 -0500 Subject: [PATCH 02/43] Improve vote counting implementation #533 --- libraries/chain/db_maint.cpp | 32 +++++++-- libraries/chain/hardfork.d/533.hf | 4 ++ .../include/graphene/chain/vote_count.hpp | 70 +++++++++++++++++++ 3 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 libraries/chain/hardfork.d/533.hf create mode 100644 libraries/chain/include/graphene/chain/vote_count.hpp diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 58ce0b7a..92fa0335 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include @@ -183,7 +185,10 @@ void database::update_active_witnesses() } // Update witness authority - modify( get(GRAPHENE_WITNESS_ACCOUNT), [&]( account_object& a ) { + modify( get(GRAPHENE_WITNESS_ACCOUNT), [&]( account_object& a ) + { + if( head_block_time() < HARDFORK_533_TIME ) + { uint64_t total_votes = 0; map weights; a.active.weight_threshold = 0; @@ -208,7 +213,15 @@ void database::update_active_witnesses() a.active.weight_threshold /= 2; a.active.weight_threshold += 1; - }); + } + else + { + vote_counter vc; + for( const witness_object& wit : wits ) + vc.add( wit.witness_account, _vote_tally_buffer[wit.vote_id] ); + vc.finish( a.active ); + } + } ); modify(gpo, [&]( global_property_object& gp ){ gp.active_witnesses.clear(); @@ -256,7 +269,10 @@ void database::update_active_committee_members() // Update committee authorities if( !committee_members.empty() ) { - modify(get(GRAPHENE_COMMITTEE_ACCOUNT), [&](account_object& a) { + modify(get(GRAPHENE_COMMITTEE_ACCOUNT), [&](account_object& a) + { + if( head_block_time() < HARDFORK_533_TIME ) + { uint64_t total_votes = 0; map weights; a.active.weight_threshold = 0; @@ -281,7 +297,15 @@ void database::update_active_committee_members() a.active.weight_threshold /= 2; a.active.weight_threshold += 1; - }); + } + else + { + vote_counter vc; + for( const committee_member_object& cm : committee_members ) + vc.add( cm.committee_member_account, _vote_tally_buffer[cm.vote_id] ); + vc.finish( a.active ); + } + } ); modify(get(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT), [&](account_object& a) { a.active = get(GRAPHENE_COMMITTEE_ACCOUNT).active; }); diff --git a/libraries/chain/hardfork.d/533.hf b/libraries/chain/hardfork.d/533.hf new file mode 100644 index 00000000..3830028b --- /dev/null +++ b/libraries/chain/hardfork.d/533.hf @@ -0,0 +1,4 @@ +// #533 Improve vote counting implementation +#ifndef HARDFORK_533_TIME +#define HARDFORK_533_TIME (fc::time_point_sec( 1455127200 )) +#endif diff --git a/libraries/chain/include/graphene/chain/vote_count.hpp b/libraries/chain/include/graphene/chain/vote_count.hpp new file mode 100644 index 00000000..ad14e22d --- /dev/null +++ b/libraries/chain/include/graphene/chain/vote_count.hpp @@ -0,0 +1,70 @@ +/* + * 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 + +namespace graphene { namespace chain { + +/** + * Keep track of vote totals in internal authority object. See #533. + */ +struct vote_counter +{ + template< typename Component > + void add( Component who, uint64_t votes ) + { + assert( votes <= last_votes ); + last_votes = votes; + if( bitshift == -1 ) + bitshift = std::max(int(boost::multiprecision::detail::find_msb( votes )) - 15, 0); + uint64_t scaled_votes = std::max( votes >> bitshift, uint64_t(1) ); + assert( scaled_votes <= std::numeric_limits::max() ); + total_votes += scaled_votes; + assert( total_votes <= std::numeric_limits::max() ); + auth.add_authority( who, weight_type( scaled_votes ) ); + } + + /** + * Write into out_auth, but only if we have at least one member. + */ + void finish( authority& out_auth ) + { + if( total_votes == 0 ) + return; + assert( total_votes <= std::numeric_limits::max() ); + uint32_t weight = uint32_t( total_votes ); + weight = (weight >> 1)+1; + auth.weight_threshold = weight; + out_auth = auth; + } + + uint64_t last_votes = std::numeric_limits::max(); + uint64_t total_votes = 0; + int8_t bitshift = -1; + authority auth; +}; + +} } // graphene::chain From ef68375fabad138fb12283db61901746fbd39899 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Fri, 22 Jan 2016 16:14:50 -0500 Subject: [PATCH 03/43] Fix indentation #533 --- libraries/chain/db_maint.cpp | 84 ++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 92fa0335..b473a22a 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -189,30 +189,30 @@ void database::update_active_witnesses() { if( head_block_time() < HARDFORK_533_TIME ) { - uint64_t total_votes = 0; - map weights; - a.active.weight_threshold = 0; - a.active.clear(); + uint64_t total_votes = 0; + map weights; + a.active.weight_threshold = 0; + a.active.clear(); - for( const witness_object& wit : wits ) - { - weights.emplace(wit.witness_account, _vote_tally_buffer[wit.vote_id]); - total_votes += _vote_tally_buffer[wit.vote_id]; - } + for( const witness_object& wit : wits ) + { + weights.emplace(wit.witness_account, _vote_tally_buffer[wit.vote_id]); + total_votes += _vote_tally_buffer[wit.vote_id]; + } - // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, - // then I want to keep the most significant 16 bits of what's left. - int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); - for( const auto& weight : weights ) - { - // Ensure that everyone has at least one vote. Zero weights aren't allowed. - uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) ); - a.active.account_auths[weight.first] += votes; - a.active.weight_threshold += votes; - } + // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, + // then I want to keep the most significant 16 bits of what's left. + int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); + for( const auto& weight : weights ) + { + // Ensure that everyone has at least one vote. Zero weights aren't allowed. + uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) ); + a.active.account_auths[weight.first] += votes; + a.active.weight_threshold += votes; + } - a.active.weight_threshold /= 2; - a.active.weight_threshold += 1; + a.active.weight_threshold /= 2; + a.active.weight_threshold += 1; } else { @@ -273,30 +273,30 @@ void database::update_active_committee_members() { if( head_block_time() < HARDFORK_533_TIME ) { - uint64_t total_votes = 0; - map weights; - a.active.weight_threshold = 0; - a.active.clear(); + uint64_t total_votes = 0; + map weights; + a.active.weight_threshold = 0; + a.active.clear(); - for( const committee_member_object& del : committee_members ) - { - weights.emplace(del.committee_member_account, _vote_tally_buffer[del.vote_id]); - total_votes += _vote_tally_buffer[del.vote_id]; - } + for( const committee_member_object& del : committee_members ) + { + weights.emplace(del.committee_member_account, _vote_tally_buffer[del.vote_id]); + total_votes += _vote_tally_buffer[del.vote_id]; + } - // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, - // then I want to keep the most significant 16 bits of what's left. - int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); - for( const auto& weight : weights ) - { - // Ensure that everyone has at least one vote. Zero weights aren't allowed. - uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) ); - a.active.account_auths[weight.first] += votes; - a.active.weight_threshold += votes; - } + // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits, + // then I want to keep the most significant 16 bits of what's left. + int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); + for( const auto& weight : weights ) + { + // Ensure that everyone has at least one vote. Zero weights aren't allowed. + uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) ); + a.active.account_auths[weight.first] += votes; + a.active.weight_threshold += votes; + } - a.active.weight_threshold /= 2; - a.active.weight_threshold += 1; + a.active.weight_threshold /= 2; + a.active.weight_threshold += 1; } else { From a1e8fc07417407525482924df6b125d95c38a06f Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Wed, 27 Jan 2016 10:30:32 -0500 Subject: [PATCH 04/43] Implement top_n special authority #516 --- libraries/app/api.cpp | 2 + libraries/chain/CMakeLists.txt | 1 + libraries/chain/account_evaluator.cpp | 63 +++++- libraries/chain/db_init.cpp | 2 + libraries/chain/db_maint.cpp | 63 +++++- libraries/chain/hardfork.d/516.hf | 4 + .../include/graphene/chain/account_object.hpp | 14 +- .../chain/include/graphene/chain/config.hpp | 2 +- .../graphene/chain/protocol/account.hpp | 45 +++- .../include/graphene/chain/protocol/ext.hpp | 200 ++++++++++++++++++ .../chain/protocol/special_authority.hpp | 50 +++++ .../include/graphene/chain/protocol/types.hpp | 7 +- .../graphene/chain/special_authority.hpp | 34 +++ .../chain/special_authority_object.hpp | 70 ++++++ .../include/graphene/chain/vote_count.hpp | 2 + libraries/chain/protocol/account.cpp | 21 +- libraries/chain/special_authority.cpp | 70 ++++++ tests/tests/operation_tests2.cpp | 151 +++++++++++++ 18 files changed, 784 insertions(+), 17 deletions(-) create mode 100644 libraries/chain/hardfork.d/516.hf create mode 100644 libraries/chain/include/graphene/chain/protocol/ext.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/special_authority.hpp create mode 100644 libraries/chain/include/graphene/chain/special_authority.hpp create mode 100644 libraries/chain/include/graphene/chain/special_authority_object.hpp create mode 100644 libraries/chain/special_authority.cpp diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index c807a3f0..2080aa2e 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -344,6 +344,8 @@ namespace graphene { namespace app { break; case impl_budget_record_object_type: break; + case impl_special_authority_object_type: + break; } } return result; diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 876c8c1d..2ce2f99d 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -76,6 +76,7 @@ add_library( graphene_chain withdraw_permission_evaluator.cpp worker_evaluator.cpp confidential_evaluator.cpp + special_authority.cpp account_object.cpp asset_object.cpp diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 28623544..f1c77d10 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include @@ -53,6 +55,12 @@ void verify_authority_accounts( const database& db, const authority& a ) void_result account_create_evaluator::do_evaluate( const account_create_operation& op ) { try { database& d = db(); + if( d.head_block_time() < HARDFORK_516_TIME ) + { + FC_ASSERT( !op.extensions.value.owner_special_authority.valid() ); + FC_ASSERT( !op.extensions.value.active_special_authority.valid() ); + } + FC_ASSERT( d.find_object(op.options.voting_account), "Invalid proxy account specified." ); FC_ASSERT( fee_paying_account->is_lifetime_member(), "Only Lifetime members may register an account." ); FC_ASSERT( op.referrer(d).is_member(d.head_block_time()), "The referrer must be either a lifetime or annual subscriber." ); @@ -68,6 +76,11 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_create_max_auth_exceeded ) GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_create_auth_account_not_found ) + if( op.extensions.value.owner_special_authority.valid() ) + evaluate_special_authority( d, *op.extensions.value.owner_special_authority ); + if( op.extensions.value.active_special_authority.valid() ) + evaluate_special_authority( d, *op.extensions.value.active_special_authority ); + uint32_t max_vote_id = global_props.next_available_vote_id; FC_ASSERT( op.options.num_witness <= chain_params.maximum_witness_count, @@ -136,6 +149,11 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio obj.active = o.active; obj.options = o.options; obj.statistics = db().create([&](account_statistics_object& s){s.owner = obj.id;}).id; + + if( o.extensions.value.owner_special_authority.valid() ) + obj.owner_special_authority = *(o.extensions.value.owner_special_authority); + if( o.extensions.value.active_special_authority.valid() ) + obj.active_special_authority = *(o.extensions.value.active_special_authority); }); if( has_small_percent ) @@ -159,6 +177,15 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio p.parameters.current_fees->get().basic_fee <<= p.parameters.account_fee_scale_bitshifts; }); + if( o.extensions.value.owner_special_authority.valid() + || o.extensions.value.active_special_authority.valid() ) + { + db().create< special_authority_object >( [&]( special_authority_object& sa ) + { + sa.account = new_acnt_object.id; + } ); + } + return new_acnt_object.id; } FC_CAPTURE_AND_RETHROW((o)) } @@ -166,6 +193,11 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio void_result account_update_evaluator::do_evaluate( const account_update_operation& o ) { try { database& d = db(); + if( d.head_block_time() < HARDFORK_516_TIME ) + { + FC_ASSERT( !o.extensions.value.owner_special_authority.valid() ); + FC_ASSERT( !o.extensions.value.active_special_authority.valid() ); + } const auto& chain_params = d.get_global_properties().parameters; @@ -177,6 +209,11 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio GRAPHENE_RECODE_EXC( internal_verify_auth_max_auth_exceeded, account_update_max_auth_exceeded ) GRAPHENE_RECODE_EXC( internal_verify_auth_account_not_found, account_update_auth_account_not_found ) + if( o.extensions.value.owner_special_authority.valid() ) + evaluate_special_authority( d, *o.extensions.value.owner_special_authority ); + if( o.extensions.value.active_special_authority.valid() ) + evaluate_special_authority( d, *o.extensions.value.active_special_authority ); + acnt = &o.account(d); if( o.new_options ) @@ -195,11 +232,35 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio void_result account_update_evaluator::do_apply( const account_update_operation& o ) { try { - db().modify( *acnt, [&](account_object& a){ + database& d = db(); + bool sa_before, sa_after; + d.modify( *acnt, [&](account_object& a){ if( o.owner ) a.owner = *o.owner; if( o.active ) a.active = *o.active; if( o.new_options ) a.options = *o.new_options; + sa_before = a.has_special_authority(); + if( o.extensions.value.owner_special_authority.valid() ) + a.owner_special_authority = *(o.extensions.value.owner_special_authority); + if( o.extensions.value.active_special_authority.valid() ) + a.active_special_authority = *(o.extensions.value.active_special_authority); + sa_after = a.has_special_authority(); }); + + if( sa_before & (!sa_after) ) + { + const auto& sa_idx = d.get_index_type< special_authority_index >().indices().get(); + auto sa_it = sa_idx.find( o.account ); + assert( sa_it != sa_idx.end() ); + d.remove( *sa_it ); + } + else if( (!sa_before) & sa_after ) + { + d.create< special_authority_object >( [&]( special_authority_object& sa ) + { + sa.account = o.account; + } ); + } + return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index cdc4182c..e603b3eb 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -208,6 +209,7 @@ void database::initialize_indexes() add_index< primary_index > >(); add_index< primary_index > >(); add_index< primary_index > >(); + add_index< primary_index< special_authority_index > >(); } void database::init_genesis(const genesis_state_type& genesis_state) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index b473a22a..c00a4226 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -465,6 +466,62 @@ void database::process_budget() FC_CAPTURE_AND_RETHROW() } +template< typename Visitor > +void visit_special_authorities( const database& db, Visitor visit ) +{ + const auto& sa_idx = db.get_index_type< special_authority_index >().indices().get(); + + for( const special_authority_object& sao : sa_idx ) + { + const account_object& acct = sao.account(db); + if( acct.owner_special_authority.which() != special_authority::tag< no_special_authority >::value ) + { + visit( acct, true, acct.owner_special_authority ); + } + if( acct.active_special_authority.which() != special_authority::tag< no_special_authority >::value ) + { + visit( acct, false, acct.active_special_authority ); + } + } +} + +void update_top_n_authorities( database& db ) +{ + visit_special_authorities( db, + [&]( const account_object& acct, bool is_owner, const special_authority& auth ) + { + if( auth.which() == special_authority::tag< top_holders_special_authority >::value ) + { + // use index to grab the top N holders of the asset and vote_counter to obtain the weights + + const top_holders_special_authority& tha = auth.get< top_holders_special_authority >(); + vote_counter vc; + const auto& bal_idx = db.get_index_type< account_balance_index >().indices().get< by_asset_balance >(); + uint8_t num_needed = tha.num_top_holders; + if( num_needed == 0 ) + return; + + // find accounts + const auto range = bal_idx.equal_range( boost::make_tuple( tha.asset ) ); + for( const account_balance_object& bal : boost::make_iterator_range( range.first, range.second ) ) + { + assert( bal.asset_type == tha.asset ); + if( bal.owner == acct.id ) + continue; + vc.add( bal.owner, bal.balance.value ); + --num_needed; + if( num_needed == 0 ) + break; + } + + db.modify( acct, [&]( account_object& a ) + { + vc.finish( is_owner ? a.owner : a.active ); + } ); + } + } ); +} + void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props) { const auto& gpo = get_global_properties(); @@ -545,7 +602,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g } } fee_helper(*this, gpo); - perform_account_maintenance(std::tie(tally_helper, fee_helper)); + perform_account_maintenance(std::tie( + tally_helper, + fee_helper + )); struct clear_canary { clear_canary(vector& target): target(target){} @@ -557,6 +617,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g b(_committee_count_histogram_buffer), c(_vote_tally_buffer); + update_top_n_authorities(*this); update_active_witnesses(); update_active_committee_members(); update_worker_votes(); diff --git a/libraries/chain/hardfork.d/516.hf b/libraries/chain/hardfork.d/516.hf new file mode 100644 index 00000000..445e2871 --- /dev/null +++ b/libraries/chain/hardfork.d/516.hf @@ -0,0 +1,4 @@ +// #516 Special authorities +#ifndef HARDFORK_516_TIME +#define HARDFORK_516_TIME (fc::time_point_sec( 1455127200 )) +#endif diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index a3f2b3ab..3039784e 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -204,6 +204,16 @@ namespace graphene { namespace chain { * Vesting balance which receives cashback_reward deposits. */ optional cashback_vb; + + special_authority owner_special_authority = no_special_authority(); + special_authority active_special_authority = no_special_authority(); + + bool has_special_authority()const + { + return (owner_special_authority.which() != special_authority::tag< no_special_authority >::value) + || (active_special_authority.which() != special_authority::tag< no_special_authority >::value); + } + template const vesting_balance_object& cashback_balance(const DB& db)const { @@ -352,7 +362,9 @@ FC_REFLECT_DERIVED( graphene::chain::account_object, (network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage) (name)(owner)(active)(options)(statistics)(whitelisting_accounts)(blacklisting_accounts) (whitelisting_accounts)(blacklisted_accounts) - (cashback_vb) ) + (cashback_vb) + (owner_special_authority)(active_special_authority) + ) FC_REFLECT_DERIVED( graphene::chain::account_balance_object, (graphene::db::object), diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index f178675c..f1bbc3f7 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -143,7 +143,7 @@ #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 -#define GRAPHENE_CURRENT_DB_VERSION "GPH2.4" +#define GRAPHENE_CURRENT_DB_VERSION "GPH2.5" #define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT) diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index 30ab0a29..987568b1 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -23,15 +23,18 @@ */ #pragma once #include +#include +#include +#include #include -namespace graphene { namespace chain { +namespace graphene { namespace chain { bool is_valid_name( const string& s ); bool is_cheap_name( const string& n ); /// These are the fields which can be updated by the active authority. - struct account_options + struct account_options { /// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non- /// validated account activities. This field is here to prevent confusion if the active authority has zero or @@ -61,7 +64,15 @@ namespace graphene { namespace chain { */ struct account_create_operation : public base_operation { - struct fee_parameters_type { + struct ext + { + optional< void_t > null_ext; + optional< special_authority > owner_special_authority; + optional< special_authority > active_special_authority; + }; + + struct fee_parameters_type + { uint64_t basic_fee = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account uint64_t premium_fee = 2000*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION; @@ -82,7 +93,7 @@ namespace graphene { namespace chain { authority active; account_options options; - extensions_type extensions; + extension< ext > extensions; account_id_type fee_payer()const { return registrar; } void validate()const; @@ -98,8 +109,16 @@ namespace graphene { namespace chain { */ struct account_update_operation : public base_operation { - struct fee_parameters_type { - share_type fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; + struct ext + { + optional< void_t > null_ext; + optional< special_authority > owner_special_authority; + optional< special_authority > active_special_authority; + }; + + struct fee_parameters_type + { + share_type fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION; }; @@ -114,17 +133,20 @@ namespace graphene { namespace chain { /// New account options optional new_options; - extensions_type extensions; + extension< ext > extensions; account_id_type fee_payer()const { return account; } void validate()const; share_type calculate_fee( const fee_parameters_type& k )const; + bool is_owner_update()const + { return owner || extensions.value.owner_special_authority.valid(); } + void get_required_owner_authorities( flat_set& a )const - { if( owner ) a.insert( account ); } + { if( is_owner_update() ) a.insert( account ); } void get_required_active_authorities( flat_set& a )const - { if( !owner ) a.insert( account ); } + { if( !is_owner_update() ) a.insert( account ); } }; /** @@ -236,16 +258,19 @@ FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listi 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) ) FC_REFLECT( graphene::chain::account_create_operation, (fee)(registrar) (referrer)(referrer_percent) (name)(owner)(active)(options)(extensions) ) + +FC_REFLECT(graphene::chain::account_update_operation::ext, (null_ext)(owner_special_authority)(active_special_authority) ) FC_REFLECT( graphene::chain::account_update_operation, (fee)(account)(owner)(active)(new_options)(extensions) ) -FC_REFLECT( graphene::chain::account_upgrade_operation, +FC_REFLECT( graphene::chain::account_upgrade_operation, (fee)(account_to_upgrade)(upgrade_to_lifetime_member)(extensions) ) FC_REFLECT( graphene::chain::account_whitelist_operation, (fee)(authorizing_account)(account_to_list)(new_listing)(extensions)) diff --git a/libraries/chain/include/graphene/chain/protocol/ext.hpp b/libraries/chain/include/graphene/chain/protocol/ext.hpp new file mode 100644 index 00000000..fda4feee --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/ext.hpp @@ -0,0 +1,200 @@ +/* + * 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 + +namespace graphene { namespace chain { + +template< typename T > +struct extension +{ + extension() {} + + T value; +}; + +template< typename T > +struct graphene_extension_pack_count_visitor +{ + graphene_extension_pack_count_visitor( const T& v ) : value(v) {} + + template + void operator()( const char* name )const + { + count += ((value.*member).valid()) ? 1 : 0; + } + + const T& value; + mutable uint32_t count = 0; +}; + +template< typename Stream, typename T > +struct graphene_extension_pack_read_visitor +{ + graphene_extension_pack_read_visitor( Stream& s, const T& v ) : stream(s), value(v) {} + + template + void operator()( const char* name )const + { + if( (value.*member).valid() ) + { + fc::raw::pack( stream, unsigned_int( which ) ); + fc::raw::pack( stream, *(value.*member) ); + } + ++which; + } + + Stream& stream; + const T& value; + mutable uint32_t which = 0; +}; + +template< typename Stream, class T > +void operator<<( Stream& stream, const graphene::chain::extension& value ) +{ + graphene_extension_pack_count_visitor count_vtor( value.value ); + fc::reflector::visit( count_vtor ); + fc::raw::pack( stream, unsigned_int( count_vtor.count ) ); + graphene_extension_pack_read_visitor read_vtor( stream, value.value ); + fc::reflector::visit( read_vtor ); +} + +template< typename Stream, typename T > +struct graphene_extension_unpack_visitor +{ + graphene_extension_unpack_visitor( Stream& s, T& v ) : stream(s), value(v) + { + unsigned_int c; + fc::raw::unpack( stream, c ); + count_left = c.value; + maybe_read_next_which(); + } + + void maybe_read_next_which()const + { + if( count_left > 0 ) + { + unsigned_int w; + fc::raw::unpack( stream, w ); + next_which = w.value; + } + } + + template< typename Member, class Class, Member (Class::*member)> + void operator()( const char* name )const + { + if( (count_left > 0) && (which == next_which) ) + { + Member temp; + fc::raw::unpack( stream, temp ); + (value.*member) = temp; + --count_left; + maybe_read_next_which(); + } + else + (value.*member).reset(); + ++which; + } + + mutable uint32_t which = 0; + mutable uint32_t next_which = 0; + mutable uint32_t count_left = 0; + + Stream& stream; + T& value; +}; + +template< typename Stream, typename T > +void operator>>( Stream& s, graphene::chain::extension& value ) +{ + graphene_extension_unpack_visitor vtor( s, value.value ); + fc::reflector::visit( vtor ); + FC_ASSERT( vtor.count_left == 0 ); // unrecognized extension throws here +} + +} } // graphene::chain + +namespace fc { + +template< typename T > +struct graphene_extension_from_variant_visitor +{ + graphene_extension_from_variant_visitor( const variant_object& v, T& val ) + : vo( v ), value( val ) + { + count_left = vo.size(); + } + + template + void operator()( const char* name )const + { + auto it = vo.find(name); + if( it != vo.end() ) + { + from_variant( it->value(), (value.*member) ); + assert( count_left > 0 ); // x.find(k) returns true for n distinct values of k only if x.size() >= n + --count_left; + } + } + + const variant_object& vo; + T& value; + mutable uint32_t count_left = 0; +}; + +template< typename T > +void from_variant( const fc::variant& var, graphene::chain::extension& value ) +{ + graphene_extension_from_variant_visitor vtor( var.get_object(), value.value ); + fc::reflector::visit( vtor ); + FC_ASSERT( vtor.count_left == 0 ); // unrecognized extension throws here +} + +template< typename T > +struct graphene_extension_to_variant_visitor +{ + graphene_extension_to_variant_visitor( const T& v ) : value(v) {} + + template + void operator()( const char* name )const + { + if( (value.*member).valid() ) + mvo[ name ] = (value.*member); + } + + const T& value; + mutable mutable_variant_object mvo; +}; + +template< typename T > +void to_variant( const graphene::chain::extension& value, fc::variant& var ) +{ + graphene_extension_to_variant_visitor vtor( value.value ); + fc::reflector::visit( vtor ); + var = vtor.mvo; +} + +} // fc diff --git a/libraries/chain/include/graphene/chain/protocol/special_authority.hpp b/libraries/chain/include/graphene/chain/protocol/special_authority.hpp new file mode 100644 index 00000000..3ee6f15f --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/special_authority.hpp @@ -0,0 +1,50 @@ +/* + * 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 + +namespace graphene { namespace chain { + +struct no_special_authority {}; + +struct top_holders_special_authority +{ + asset_id_type asset; + uint8_t num_top_holders = 1; +}; + +typedef static_variant< + no_special_authority, + top_holders_special_authority + > special_authority; + +void validate_special_authority( const special_authority& auth ); + +} } // graphene::chain + +FC_REFLECT( graphene::chain::no_special_authority, ) +FC_REFLECT( graphene::chain::top_holders_special_authority, (asset)(num_top_holders) ) +FC_REFLECT_TYPENAME( graphene::chain::special_authority ) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 3652ef5e..009a7980 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -152,7 +152,8 @@ namespace graphene { namespace chain { impl_blinded_balance_object_type, impl_chain_property_object_type, impl_witness_schedule_object_type, - impl_budget_record_object_type + impl_budget_record_object_type, + impl_special_authority_object_type }; //typedef fc::unsigned_int object_id_type; @@ -201,6 +202,7 @@ namespace graphene { namespace chain { class chain_property_object; class witness_schedule_object; class budget_record_object; + class special_authority_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; @@ -218,6 +220,7 @@ namespace graphene { namespace chain { 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 fc::array symbol_type; typedef fc::ripemd160 block_id_type; @@ -347,6 +350,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type, (impl_chain_property_object_type) (impl_witness_schedule_object_type) (impl_budget_record_object_type) + (impl_special_authority_object_type) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) @@ -375,6 +379,7 @@ 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( graphene::chain::void_t, ) FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags, diff --git a/libraries/chain/include/graphene/chain/special_authority.hpp b/libraries/chain/include/graphene/chain/special_authority.hpp new file mode 100644 index 00000000..f091f736 --- /dev/null +++ b/libraries/chain/include/graphene/chain/special_authority.hpp @@ -0,0 +1,34 @@ +/* + * 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 + +namespace graphene { namespace chain { + +class database; + +void evaluate_special_authority( const database& db, const special_authority& auth ); + +} } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/special_authority_object.hpp b/libraries/chain/include/graphene/chain/special_authority_object.hpp new file mode 100644 index 00000000..da9ecc5e --- /dev/null +++ b/libraries/chain/include/graphene/chain/special_authority_object.hpp @@ -0,0 +1,70 @@ +/* + * 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 { + +/** + * special_authority_object only exists to help with a specific indexing problem. + * We want to be able to iterate over all accounts that contain a special authority. + * However, accounts which have a special_authority are very rare. So rather + * than indexing account_object by the special_authority fields (requiring additional + * bookkeeping for every account), we instead maintain a special_authority_object + * pointing to each account which has special_authority (requiring additional + * bookkeeping only for every account which has special_authority). + * + * This class is an implementation detail. + */ + +class special_authority_object : public graphene::db::abstract_object +{ + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_special_authority_object_type; + + account_id_type account; +}; + +struct by_account; + +typedef multi_index_container< + special_authority_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, member< special_authority_object, account_id_type, &special_authority_object::account> > + > +> special_authority_multi_index_type; + +typedef generic_index< special_authority_object, special_authority_multi_index_type > special_authority_index; + +} } // graphene::chain + +FC_REFLECT_DERIVED( + graphene::chain::special_authority_object, + (graphene::db::object), + (account) +) diff --git a/libraries/chain/include/graphene/chain/vote_count.hpp b/libraries/chain/include/graphene/chain/vote_count.hpp index ad14e22d..bb23b840 100644 --- a/libraries/chain/include/graphene/chain/vote_count.hpp +++ b/libraries/chain/include/graphene/chain/vote_count.hpp @@ -36,6 +36,8 @@ struct vote_counter template< typename Component > void add( Component who, uint64_t votes ) { + if( votes == 0 ) + return; assert( votes <= last_votes ); last_votes = votes; if( bitshift == -1 ) diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index 810328dd..1c3fa007 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -186,6 +186,10 @@ void account_create_operation::validate()const FC_ASSERT( !owner.is_impossible(), "cannot create an account with an imposible owner authority threshold" ); FC_ASSERT( !active.is_impossible(), "cannot create an account with an imposible active authority threshold" ); options.validate(); + if( extensions.value.owner_special_authority.valid() ) + validate_special_authority( *extensions.value.owner_special_authority ); + if( extensions.value.active_special_authority.valid() ) + validate_special_authority( *extensions.value.active_special_authority ); } @@ -204,7 +208,17 @@ void account_update_operation::validate()const FC_ASSERT( account != GRAPHENE_TEMP_ACCOUNT ); FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( account != account_id_type() ); - FC_ASSERT( owner || active || new_options ); + + bool has_action = ( + owner.valid() + || active.valid() + || new_options.valid() + || extensions.value.owner_special_authority.valid() + || extensions.value.active_special_authority.valid() + ); + + FC_ASSERT( has_action ); + if( owner ) { FC_ASSERT( owner->num_auths() != 0 ); @@ -220,9 +234,12 @@ void account_update_operation::validate()const if( new_options ) new_options->validate(); + if( extensions.value.owner_special_authority.valid() ) + validate_special_authority( *extensions.value.owner_special_authority ); + if( extensions.value.active_special_authority.valid() ) + validate_special_authority( *extensions.value.active_special_authority ); } - share_type account_upgrade_operation::calculate_fee(const fee_parameters_type& k) const { if( upgrade_to_lifetime_member ) diff --git a/libraries/chain/special_authority.cpp b/libraries/chain/special_authority.cpp new file mode 100644 index 00000000..ca974f30 --- /dev/null +++ b/libraries/chain/special_authority.cpp @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#include +#include + +namespace graphene { namespace chain { + +struct special_authority_validate_visitor +{ + typedef void result_type; + + void operator()( const no_special_authority& a ) {} + + void operator()( const top_holders_special_authority& a ) + { + FC_ASSERT( a.num_top_holders > 0 ); + } +}; + +void validate_special_authority( const special_authority& a ) +{ + special_authority_validate_visitor vtor; + a.visit( vtor ); +} + +struct special_authority_evaluate_visitor +{ + typedef void result_type; + + special_authority_evaluate_visitor( const database& d ) : db(d) {} + + void operator()( const no_special_authority& a ) {} + + void operator()( const top_holders_special_authority& a ) + { + a.asset(db); // require asset to exist + } + + const database& db; +}; + +void evaluate_special_authority( const database& db, const special_authority& a ) +{ + special_authority_evaluate_visitor vtor( db ); + a.visit( vtor ); +} + +} } // graphene::chain diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index e13eda19..7adf3a1c 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -1332,4 +1333,154 @@ BOOST_AUTO_TEST_CASE(zero_second_vbo) // TODO: Write linear VBO tests +BOOST_AUTO_TEST_CASE( top_n_special ) +{ + ACTORS( (alice)(bob)(chloe)(dan)(izzy)(stan) ); + + generate_blocks( HARDFORK_516_TIME ); + + try + { + { + // + // Izzy (issuer) + // Stan (special authority) + // Alice, Bob, Chloe, Dan (ABCD) + // + + asset_id_type topn_id = create_user_issued_asset( "TOPN", izzy_id(db), 0 ).id; + authority stan_owner_auth = stan_id(db).owner; + authority stan_active_auth = stan_id(db).active; + + // set SA, wait for maint interval + // TODO: account_create_operation + // TODO: multiple accounts with different n for same asset + + { + top_holders_special_authority top2, top3; + + top2.num_top_holders = 2; + top2.asset = topn_id; + + top3.num_top_holders = 3; + top3.asset = topn_id; + + account_update_operation op; + op.account = stan_id; + op.extensions.value.active_special_authority = top3; + op.extensions.value.owner_special_authority = top2; + + signed_transaction tx; + tx.operations.push_back( op ); + + set_expiration( db, tx ); + sign( tx, stan_private_key ); + + PUSH_TX( db, tx ); + + // TODO: Check special_authority is properly set + // TODO: Do it in steps + } + + // wait for maint interval + // make sure we don't have any authority as account hasn't gotten distributed yet + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + BOOST_CHECK( stan_id(db).owner == stan_owner_auth ); + BOOST_CHECK( stan_id(db).active == stan_active_auth ); + + // issue some to Alice, make sure she gets control of Stan + + // we need to set_expiration() before issue_uia() because the latter doens't call it #11 + set_expiration( db, trx ); // #11 + issue_uia( alice_id, asset( 1000, topn_id ) ); + + BOOST_CHECK( stan_id(db).owner == stan_owner_auth ); + BOOST_CHECK( stan_id(db).active == stan_active_auth ); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + /* NOTE - this was an old check from an earlier implementation that only allowed SA for LTM's + // no boost yet, we need to upgrade to LTM before mechanics apply to Stan + BOOST_CHECK( stan_id(db).owner == stan_owner_auth ); + BOOST_CHECK( stan_id(db).active == stan_active_auth ); + + set_expiration( db, trx ); // #11 + upgrade_to_lifetime_member(stan_id); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + */ + + BOOST_CHECK( stan_id(db).owner == authority( 501, alice_id, 1000 ) ); + BOOST_CHECK( stan_id(db).active == authority( 501, alice_id, 1000 ) ); + + // give asset to Stan, make sure owner doesn't change at all + set_expiration( db, trx ); // #11 + transfer( alice_id, stan_id, asset( 1000, topn_id ) ); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + BOOST_CHECK( stan_id(db).owner == authority( 501, alice_id, 1000 ) ); + BOOST_CHECK( stan_id(db).active == authority( 501, alice_id, 1000 ) ); + + set_expiration( db, trx ); // #11 + issue_uia( chloe_id, asset( 131000, topn_id ) ); + + // now Chloe has 131,000 and Stan has 1k. Make sure change occurs at next maintenance interval. + // NB, 131072 is a power of 2; the number 131000 was chosen so that we need a bitshift, but + // if we put the 1000 from Stan's balance back into play, we need a different bitshift. + + // we use Chloe so she can be displaced by Bob later (showing the tiebreaking logic). + + // Check Alice is still in control, because we're deferred to next maintenance interval + BOOST_CHECK( stan_id(db).owner == authority( 501, alice_id, 1000 ) ); + BOOST_CHECK( stan_id(db).active == authority( 501, alice_id, 1000 ) ); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + BOOST_CHECK( stan_id(db).owner == authority( 32751, chloe_id, 65500 ) ); + BOOST_CHECK( stan_id(db).active == authority( 32751, chloe_id, 65500 ) ); + + // put Alice's stake back in play + set_expiration( db, trx ); // #11 + transfer( stan_id, alice_id, asset( 1000, topn_id ) ); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + BOOST_CHECK( stan_id(db).owner == authority( 33001, alice_id, 500, chloe_id, 65500 ) ); + BOOST_CHECK( stan_id(db).active == authority( 33001, alice_id, 500, chloe_id, 65500 ) ); + + // issue 200,000 to Dan to cause another bitshift. + set_expiration( db, trx ); // #11 + issue_uia( dan_id, asset( 200000, topn_id ) ); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // 200000 Dan + // 131000 Chloe + // 1000 Alice + + BOOST_CHECK( stan_id(db).owner == authority( 41376, chloe_id, 32750, dan_id, 50000 ) ); + BOOST_CHECK( stan_id(db).active == authority( 41501, alice_id, 250, chloe_id, 32750, dan_id, 50000 ) ); + + // have Alice send all but 1 back to Stan, verify that we clamp Alice at one vote + set_expiration( db, trx ); // #11 + transfer( alice_id, stan_id, asset( 999, topn_id ) ); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + BOOST_CHECK( stan_id(db).owner == authority( 41376, chloe_id, 32750, dan_id, 50000 ) ); + BOOST_CHECK( stan_id(db).active == authority( 41376, alice_id, 1, chloe_id, 32750, dan_id, 50000 ) ); + + // send 131k to Bob so he's tied with Chloe, verify he displaces Chloe in top2 + set_expiration( db, trx ); // #11 + issue_uia( bob_id, asset( 131000, topn_id ) ); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + BOOST_CHECK( stan_id(db).owner == authority( 41376, bob_id, 32750, dan_id, 50000 ) ); + BOOST_CHECK( stan_id(db).active == authority( 57751, bob_id, 32750, chloe_id, 32750, dan_id, 50000 ) ); + + // TODO more rounding checks + } + + } FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_SUITE_END() From 10a4dce501d4dd6a5ed7c9d07b3f5f5379401c61 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 28 Jan 2016 18:14:10 -0500 Subject: [PATCH 05/43] db_update.cpp: Don't update bitasset_data_object force_settled_volume every block unless needed #540 --- libraries/chain/db_update.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 2b6ba2e7..8fc29dd2 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -357,9 +357,12 @@ void database::clear_expired_orders() break; } } - modify(mia, [settled](asset_bitasset_data_object& b) { - b.force_settled_volume = settled.amount; - }); + if( mia.force_settled_volume != settled.amount ) + { + modify(mia, [settled](asset_bitasset_data_object& b) { + b.force_settled_volume = settled.amount; + }); + } } } } From 84fd629d36d52e364517ec56948375290809d776 Mon Sep 17 00:00:00 2001 From: James Calfee Date: Fri, 29 Jan 2016 15:12:55 -0600 Subject: [PATCH 06/43] Add crypto API call to derive a public key child. #536 --- libraries/app/api.cpp | 5 +++++ libraries/app/include/graphene/app/api.hpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index f262cca7..0d07ef2c 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -464,6 +464,11 @@ namespace graphene { namespace app { { return fc::ecc::range_proof_sign( min_value, commit, commit_blind, nonce, base10_exp, min_bits, actual_value ); } + + fc::ecc::public_key crypto_api::child(fc::ecc::public_key public_key, fc::sha256 child) + { + return public_key_type( public_key.child( child ) ); + } verify_range_proof_rewind_result crypto_api::verify_range_proof_rewind( const blind_factor_type& nonce, const commitment_type& commit, diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index a6ffe0e9..ebca000f 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -222,6 +222,7 @@ namespace graphene { namespace app { uint8_t min_bits, uint64_t actual_value ); + fc::ecc::public_key child(fc::ecc::public_key public_key, fc::sha256 child); verify_range_proof_rewind_result verify_range_proof_rewind( const blind_factor_type& nonce, const fc::ecc::commitment_type& commit, @@ -313,6 +314,7 @@ FC_API(graphene::app::crypto_api, (verify_sum) (verify_range) (range_proof_sign) + (child) (verify_range_proof_rewind) (range_get_info) ) From da598c82aa80e3836c012297fcc5a370b46bc1ca Mon Sep 17 00:00:00 2001 From: James Calfee Date: Tue, 2 Feb 2016 13:11:54 -0600 Subject: [PATCH 07/43] Revert "Add crypto API call to derive a public key child. #536" This reverts commit 84fd629d36d52e364517ec56948375290809d776. --- libraries/app/api.cpp | 5 ----- libraries/app/include/graphene/app/api.hpp | 2 -- 2 files changed, 7 deletions(-) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 0d07ef2c..f262cca7 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -464,11 +464,6 @@ namespace graphene { namespace app { { return fc::ecc::range_proof_sign( min_value, commit, commit_blind, nonce, base10_exp, min_bits, actual_value ); } - - fc::ecc::public_key crypto_api::child(fc::ecc::public_key public_key, fc::sha256 child) - { - return public_key_type( public_key.child( child ) ); - } verify_range_proof_rewind_result crypto_api::verify_range_proof_rewind( const blind_factor_type& nonce, const commitment_type& commit, diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index ebca000f..a6ffe0e9 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -222,7 +222,6 @@ namespace graphene { namespace app { uint8_t min_bits, uint64_t actual_value ); - fc::ecc::public_key child(fc::ecc::public_key public_key, fc::sha256 child); verify_range_proof_rewind_result verify_range_proof_rewind( const blind_factor_type& nonce, const fc::ecc::commitment_type& commit, @@ -314,7 +313,6 @@ FC_API(graphene::app::crypto_api, (verify_sum) (verify_range) (range_proof_sign) - (child) (verify_range_proof_rewind) (range_get_info) ) From 10fca25acc6bb13b724bb31760df27afdad761b3 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 8 Feb 2016 01:23:01 -0500 Subject: [PATCH 08/43] database_fixture.cpp: Fix integer overflow bug waiting for zero blocks #559 --- tests/common/database_fixture.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index f7431f40..50c6ffcd 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -318,8 +318,10 @@ void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_i if( miss_intermediate_blocks ) { generate_block(); - auto slots_to_miss = db.get_slot_at_time(timestamp) - 1; - if( slots_to_miss <= 0 ) return; + auto slots_to_miss = db.get_slot_at_time(timestamp); + if( slots_to_miss <= 1 ) + return; + --slots_to_miss; generate_block(~0, init_account_priv_key, slots_to_miss); return; } From ad339d2729d2331c562b264ef7f4ed7beff8c390 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Fri, 5 Feb 2016 17:29:13 -0500 Subject: [PATCH 09/43] Defer something-for-nothing culling for taker orders until the order is unmatched #555 --- libraries/chain/db_market.cpp | 54 +++++++++++++------ libraries/chain/hardfork.d/555.hf | 4 ++ .../chain/include/graphene/chain/database.hpp | 2 +- 3 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 libraries/chain/hardfork.d/555.hf diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index edcfc2c3..2c8251ca 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -132,6 +132,27 @@ void database::cancel_order( const limit_order_object& order, bool create_virtua remove(order); } +bool maybe_cull_small_order( database& db, const limit_order_object& order ) +{ + /** + * There are times when the AMOUNT_FOR_SALE * SALE_PRICE == 0 which means that we + * have hit the limit where the seller is asking for nothing in return. When this + * happens we must refund any balance back to the seller, it is too small to be + * sold at the sale price. + * + * If the order is a taker order (as opposed to a maker order), so the price is + * set by the counterparty, this check is deferred until the order becomes unmatched + * (see #555) -- however, detecting this condition is the responsibility of the caller. + */ + if( order.amount_to_receive().amount == 0 ) + { + ilog( "applied epsilon logic" ); + db.cancel_order(order); + return true; + } + return false; +} + bool database::apply_order(const limit_order_object& new_order_object, bool allow_black_swan) { auto order_id = new_order_object.id; @@ -171,7 +192,15 @@ bool database::apply_order(const limit_order_object& new_order_object, bool allo check_call_orders(sell_asset, allow_black_swan); check_call_orders(receive_asset, allow_black_swan); - return find_object(order_id) == nullptr; + const limit_order_object* updated_order_object = find< limit_order_object >( order_id ); + if( updated_order_object == nullptr ) + return true; + if( head_block_time() <= HARDFORK_555_TIME ) + return false; + // before #555 we would have done maybe_cull_small_order() logic as a result of fill_order() being called by match() above + // however after #555 we need to get rid of small orders -- #555 hardfork defers logic that was done too eagerly before, and + // this is the point it's deferred to. + return maybe_cull_small_order( *this, *updated_order_object ); } /** @@ -218,8 +247,8 @@ int database::match( const limit_order_object& usd, const OrderType& core, const core_pays == core.amount_for_sale() ); int result = 0; - result |= fill_order( usd, usd_pays, usd_receives ); - result |= fill_order( core, core_pays, core_receives ) << 1; + result |= fill_order( usd, usd_pays, usd_receives, false ); + result |= fill_order( core, core_pays, core_receives, true ) << 1; assert( result != 0 ); return result; } @@ -263,8 +292,10 @@ asset database::match( const call_order_object& call, return call_receives; } FC_CAPTURE_AND_RETHROW( (call)(settle)(match_price)(max_settlement) ) } -bool database::fill_order( const limit_order_object& order, const asset& pays, const asset& receives ) +bool database::fill_order( const limit_order_object& order, const asset& pays, const asset& receives, bool cull_if_small ) { try { + cull_if_small |= (head_block_time() < HARDFORK_555_TIME); + FC_ASSERT( order.amount_for_sale().asset_id == pays.asset_id ); FC_ASSERT( pays.asset_id != receives.asset_id ); @@ -297,17 +328,8 @@ bool database::fill_order( const limit_order_object& order, const asset& pays, c b.for_sale -= pays.amount; b.deferred_fee = 0; }); - /** - * There are times when the AMOUNT_FOR_SALE * SALE_PRICE == 0 which means that we - * have hit the limit where the seller is asking for nothing in return. When this - * happens we must refund any balance back to the seller, it is too small to be - * sold at the sale price. - */ - if( order.amount_to_receive().amount == 0 ) - { - cancel_order(order); - return true; - } + if( cull_if_small ) + return maybe_cull_small_order( *this, order ); return false; } } FC_CAPTURE_AND_RETHROW( (order)(pays)(receives) ) } @@ -517,7 +539,7 @@ bool database::check_call_orders(const asset_object& mia, bool enable_black_swan fill_order(*old_call_itr, call_pays, call_receives); auto old_limit_itr = filled_limit ? limit_itr++ : limit_itr; - fill_order(*old_limit_itr, order_pays, order_receives); + fill_order(*old_limit_itr, order_pays, order_receives, true); } // whlie call_itr != call_end diff --git a/libraries/chain/hardfork.d/555.hf b/libraries/chain/hardfork.d/555.hf new file mode 100644 index 00000000..28959f11 --- /dev/null +++ b/libraries/chain/hardfork.d/555.hf @@ -0,0 +1,4 @@ +// #555 Buyback accounts +#ifndef HARDFORK_555_TIME +#define HARDFORK_555_TIME (fc::time_point_sec( 1455127200 )) +#endif diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index e5956fec..a4dfdfc6 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -370,7 +370,7 @@ namespace graphene { namespace chain { /** * @return true if the order was completely filled and thus freed. */ - bool fill_order( const limit_order_object& order, const asset& pays, const asset& receives ); + bool fill_order( const limit_order_object& order, const asset& pays, const asset& receives, bool cull_if_small ); bool fill_order( const call_order_object& order, const asset& pays, const asset& receives ); bool fill_order( const force_settlement_object& settle, const asset& pays, const asset& receives ); From fe67b180657a7b79bd2b54ea21b2e89bc1ed4371 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 8 Feb 2016 15:28:39 -0500 Subject: [PATCH 10/43] js_operation_serializer: Add missing includes #466 #561 --- programs/js_operation_serializer/main.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 9dec2f6b..8b9ca362 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -23,14 +23,19 @@ */ #include #include -#include -#include -#include -#include -#include + #include +#include #include #include +#include +#include +#include +#include +#include +#include +#include + #include #include From a5620c3064362d3d3d83bba5660ebbc50939be5a Mon Sep 17 00:00:00 2001 From: abitmore Date: Tue, 2 Feb 2016 22:01:32 +0100 Subject: [PATCH 11/43] Expose whitelisted_accounts, fix #489 --- libraries/chain/include/graphene/chain/account_object.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index 5067292d..6d765f4b 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -331,7 +331,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_object, (membership_expiration_date)(registrar)(referrer)(lifetime_referrer) (network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage) (name)(owner)(active)(options)(statistics)(whitelisting_accounts)(blacklisting_accounts) - (whitelisting_accounts)(blacklisted_accounts) + (whitelisted_accounts)(blacklisted_accounts) (cashback_vb) ) FC_REFLECT_DERIVED( graphene::chain::account_balance_object, From ba2f388f22ab8bbbacf1531ed2357e0c09eecebc Mon Sep 17 00:00:00 2001 From: abitmore Date: Thu, 28 Jan 2016 11:20:43 +0100 Subject: [PATCH 12/43] Fix cancel_order: set fee after adding operation --- 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 3858f4c1..eec7d840 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1914,8 +1914,8 @@ public: limit_order_cancel_operation op; op.fee_paying_account = get_object(order_id).seller; op.order = order_id; - set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees); trx.operations = {op}; + set_operation_fees( trx, _remote_db->get_global_properties().parameters.current_fees); trx.validate(); return sign_transaction(trx, broadcast); From 8d2fa3863ee3eaa65b96ad8ef7cd825d62da0d5d Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 7 Feb 2016 16:58:29 +0100 Subject: [PATCH 13/43] Fix for #557: check BTC/PTS addresses on balance import including compressed/uncompressed versions --- libraries/wallet/wallet.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 3858f4c1..7e441e1a 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3356,7 +3356,17 @@ vector< signed_transaction > wallet_api_impl::import_balance( string name_or_id, { optional< private_key_type > key = wif_to_key( wif_key ); FC_ASSERT( key.valid(), "Invalid private key" ); - addrs.push_back( key->get_public_key() ); + fc::ecc::public_key pk = key->get_public_key(); + addrs.push_back( pk ); + keys[addrs.back()] = *key; + // see chain/balance_evaluator.cpp + addrs.push_back( pts_address( pk, false, 56 ) ); + keys[addrs.back()] = *key; + addrs.push_back( pts_address( pk, true, 56 ) ); + keys[addrs.back()] = *key; + addrs.push_back( pts_address( pk, false, 0 ) ); + keys[addrs.back()] = *key; + addrs.push_back( pts_address( pk, true, 0 ) ); keys[addrs.back()] = *key; } } From 915ebb58156079d519302e332c4740f9f0c503d5 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 4 Feb 2016 00:20:36 -0500 Subject: [PATCH 14/43] Implement member_enumerator to have FC dump class members #562 --- programs/build_helpers/CMakeLists.txt | 9 + programs/build_helpers/member_enumerator.cpp | 214 +++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 programs/build_helpers/member_enumerator.cpp diff --git a/programs/build_helpers/CMakeLists.txt b/programs/build_helpers/CMakeLists.txt index 678663e2..7a625b25 100644 --- a/programs/build_helpers/CMakeLists.txt +++ b/programs/build_helpers/CMakeLists.txt @@ -6,3 +6,12 @@ endif() # we only actually need Boost, but link against FC for now so we don't duplicate it. target_link_libraries( cat-parts PRIVATE fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + +add_executable( member_enumerator member_enumerator.cpp ) +if( UNIX AND NOT APPLE ) + set(rt_library rt ) +endif() + +# we only actually need Boost, but link against FC for now so we don't duplicate it. +target_link_libraries( member_enumerator PRIVATE fc graphene_app graphene_net graphene_chain graphene_egenesis_brief graphene_utilities graphene_wallet ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + diff --git a/programs/build_helpers/member_enumerator.cpp b/programs/build_helpers/member_enumerator.cpp new file mode 100644 index 00000000..001b47bd --- /dev/null +++ b/programs/build_helpers/member_enumerator.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2015 Cryptonomex, Inc., and contributors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Any modified source or binaries are used only with the BitShares network. + * + * 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace graphene::chain; + +namespace graphene { namespace member_enumerator { + +struct class_processor +{ + class_processor( std::map< std::string, std::vector< std::string > >& r ) : result(r) {} + + template< typename T > + void process_class( const T* dummy ); + + template< typename... T > + void process_class( const static_variant< T... >* dummy ); + + template< typename T > + void process_class( const std::vector< T >* dummy ); + + template< typename K, typename V > + void process_class( const std::map< K, V >* dummy ); + + template< typename T > + void process_class( const fc::flat_set< T >* dummy ); + + template< typename K, typename V > + void process_class( const fc::flat_map< K, V >* dummy ); + + template< typename T > + void process_class( const fc::optional< T >* dummy ); + + template< typename T > + static void process_class( std::map< std::string, std::vector< std::string > >& result ); + + std::map< std::string, std::vector< std::string > >& result; +}; + +template< typename T > +struct member_visitor +{ + member_visitor( class_processor* p ) : proc(p) {} + + template + void operator()( const char* name )const + { + members.emplace_back( name ); + proc->process_class( (const Member*) nullptr ); + } + + class_processor* proc; + mutable std::vector< std::string > members; +}; + +struct static_variant_visitor +{ + static_variant_visitor( class_processor* p ) : proc(p) {} + + typedef void result_type; + + template + void operator()( const T& element )const + { + proc->process_class( (const T*) nullptr ); + } + + class_processor* proc; +}; + +template< typename... T > +void class_processor::process_class( const static_variant< T... >* dummy ) +{ + static_variant dummy2; + static_variant_visitor vtor( this ); + + for( int w=0; w +struct if_reflected +{ + template< typename T > + static void process_class( class_processor* proc, const T* dummy ) + { + std::string tname = fc::get_typename::name(); + std::cerr << "skipping non-reflected class " << tname << std::endl; + } +}; + +template<> +struct if_reflected +{ + template< typename T > + static void process_class( class_processor* proc, const T* dummy ) + { + std::string tname = fc::get_typename::name(); + if( proc->result.find( tname ) != proc->result.end() ) + return; + ilog( "processing class ${c}", ("c", tname) ); + // need this to keep from recursing on same class + proc->result.emplace( tname, std::vector< std::string >() ); + + member_visitor vtor( proc ); + fc::reflector::visit( vtor ); + ilog( "members of class ${c} are ${m}", ("c", tname)("m", vtor.members) ); + proc->result[tname] = vtor.members; + } +}; + +template< typename T > +void class_processor::process_class( const T* dummy ) +{ + if_reflected< typename fc::reflector::is_defined >::process_class( this, dummy ); +} + +template< typename T > +void class_processor::process_class( const std::vector< T >* dummy ) +{ + process_class( (T*) nullptr ); +} + +template< typename K, typename V > +void class_processor::process_class( const std::map< K, V >* dummy ) +{ + process_class( (K*) nullptr ); + process_class( (V*) nullptr ); +} + +template< typename T > +void class_processor::process_class( const fc::flat_set< T >* dummy ) +{ + process_class( (T*) nullptr ); +} + +template< typename K, typename V > +void class_processor::process_class( const fc::flat_map< K, V >* dummy ) +{ + process_class( (K*) nullptr ); + process_class( (V*) nullptr ); +} + +template< typename T > +void class_processor::process_class( const fc::optional< T >* dummy ) +{ + process_class( (T*) nullptr ); +} + +template< typename T > +void class_processor::process_class( std::map< std::string, std::vector< std::string > >& result ) +{ + class_processor proc(result); + proc.process_class( (T*) nullptr ); +} + +} } + +int main( int argc, char** argv ) +{ + try + { + std::map< std::string, std::vector< std::string > > result; + graphene::member_enumerator::class_processor::process_class(result); + //graphene::member_enumerator::process_class(result); + + fc::mutable_variant_object mvo; + for( const std::pair< std::string, std::vector< std::string > >& e : result ) + { + variant v; + to_variant( e.second, v ); + mvo.set( e.first, v ); + } + + std::cout << fc::json::to_string( mvo ) << std::endl; + } + catch ( const fc::exception& e ) + { + edump((e.to_detail_string())); + } + return 0; +} From 2822aca94d8a20c99ba0b9150ed036d39d809888 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 9 Feb 2016 03:43:52 -0500 Subject: [PATCH 15/43] Implement rough Python regular expression based reflection checker #562 Ironically this doesn't use the member_enumerator implemented in previous commit --- programs/build_helpers/check_reflect.py | 132 ++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100755 programs/build_helpers/check_reflect.py diff --git a/programs/build_helpers/check_reflect.py b/programs/build_helpers/check_reflect.py new file mode 100755 index 00000000..d655b846 --- /dev/null +++ b/programs/build_helpers/check_reflect.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 + +import json +import os +import re +import xml.etree.ElementTree as etree + +def process_node(path, node): + """ + if node.tag == "TestCase": + if node.attrib.get("result", "UNKNOWN") != "passed": + failset.add(node) + return + if node.tag in ["TestResult", "TestSuite"]: + for child in node: + cpath = path+"/"+child.attrib["name"] + process_node(cpath, child) + return + """ + #print("unknown node", node.tag) + print(node.tag) + return + +name2members_doxygen = {} + +def process_class_node(node): + result = {"name":"", "vmembers":[]} + for child in node: + if child.tag == "name": + result["name"] = child.text + elif child.tag == "member": + kind = child.attrib.get("kind") + if kind == "variable": + result["vmembers"].append(child[0].text) + name2members_doxygen[result["name"]] = result["vmembers"] + return + +tree = etree.parse("doxygen/xml/index.xml") +root = tree.getroot() +for child in root: + if (child.tag == "compound") and (child.attrib.get("kind") in ["struct", "class"]): + process_class_node(child) + +s_static_names = set(["space_id", "type_id"]) + +for k, v in name2members_doxygen.items(): + name2members_doxygen[k] = [x for x in v if x not in s_static_names] + +#with open("stuff/member_enumerator.out", "r") as f: +# name2members_fc = json.load(f) + +# scan for FC_REFLECT( graphene::... in all cpp,hpp files under libraries/ programs/ tests/ + +re_reflect = re.compile(r""" +FC_REFLECT\s*[(] +\s*(graphene::[a-zA-Z0-9_:]+) +\s*, +((?:\s*[(]\s*[a-zA-Z0-9_]+\s*[)])*) +""", re.VERBOSE) + +re_reflect_derived = re.compile(r""" +FC_REFLECT_DERIVED\s*[(] +\s*(graphene::[a-zA-Z0-9_:]+) +\s*, +\s*[(]\s*(graphene::[a-zA-Z0-9_:]+)\s*[)] +\s*, +((?:\s*[(]\s*[a-zA-Z0-9_]+\s*[)])*) +""", re.VERBOSE) + +re_bubble_item = re.compile(r"\s*[(]\s*([a-zA-Z0-9_]+)\s*") + +def bubble_list(x): + return [re_bubble_item.match(e).group(1) for e in x.split(")")[:-1]] + +name2members_re = {} + +for root, dirs, files in os.walk("."): + if root == ".": + dirs[:] = ["libraries", "programs", "tests"] + for filename in files: + if not (filename.endswith(".cpp") or filename.endswith(".hpp")): + continue + try: + with open( os.path.join(root, filename), "r" ) as f: + content = f.read() + for m in re_reflect.finditer(content): + cname = m.group(1) + members = bubble_list(m.group(2)) + name2members_re[cname] = members + for m in re_reflect_derived.finditer(content): + cname = m.group(1) + members = bubble_list(m.group(3)) + name2members_re[cname] = members + except OSError as e: + pass + +def validate_members(name2members_ref, name2members_test): + ok_items = [] + ne_items = [] + error_items = [] + + for name in sorted(name2members_ref.keys()): + if name not in name2members_test: + ne_items.append(name) + elif sorted(name2members_ref[name]) != sorted(name2members_test[name]): + error_items.append(name) + print("") + print("error in", name) + print("doxygen:", name2members_ref[name]) + print("fc :", name2members_test[name]) + else: + ok_items.append(name) + return + +""" +print("") +print("ok:") +for item in ok_items: + print(item) + +print("") +print("not evaluated:") +for item in ne_items: + print(item) + +print("") +print("error:") +for item in error_items: + print(item) +""" + +validate_members(name2members_doxygen, name2members_re) From 19cf1b135b3cf74210ca57b7c7d34346f1efa7e0 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 9 Feb 2016 04:01:06 -0500 Subject: [PATCH 16/43] Fix withdraw_permission_object.hpp reflection #562 --- .../chain/include/graphene/chain/withdraw_permission_object.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp b/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp index 97ef23b4..172f4e28 100644 --- a/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp +++ b/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp @@ -93,4 +93,5 @@ FC_REFLECT_DERIVED( graphene::chain::withdraw_permission_object, (graphene::db:: (withdrawal_period_sec) (period_start_time) (expiration) + (claimed_this_period) ) From 3a968332e8c2922b5d14737ecf422b3a77ddda5e Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 9 Feb 2016 04:06:01 -0500 Subject: [PATCH 17/43] Remove active_witnesses from global_property_object #562 --- libraries/chain/db_init.cpp | 1 - libraries/chain/db_maint.cpp | 7 ------- .../include/graphene/chain/global_property_object.hpp | 1 - tests/tests/operation_tests.cpp | 5 +++-- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index ba2d07a6..2bb10b30 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -636,7 +636,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) for( uint32_t i = 1; i <= genesis_state.initial_active_witnesses; ++i ) { p.active_witnesses.insert(i); - p.witness_accounts.insert(get(witness_id_type(i)).witness_account); } }); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 807254a9..8403f181 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -213,13 +213,6 @@ void database::update_active_witnesses() [](const witness_object& w) { return w.id; }); - gp.witness_accounts.clear(); - gp.witness_accounts.reserve(wits.size()); - std::transform(wits.begin(), wits.end(), - std::inserter(gp.witness_accounts, gp.witness_accounts.end()), - [](const witness_object& w) { - return w.witness_account; - }); }); } FC_CAPTURE_AND_RETHROW() } diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 61aecf97..6374b548 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -49,7 +49,6 @@ namespace graphene { namespace chain { vector active_committee_members; // updated once per maintenance interval flat_set active_witnesses; // updated once per maintenance interval // n.b. witness scheduling is done by witness_schedule object - flat_set witness_accounts; // updated once per maintenance interval }; /** diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index b85b3161..15f7e845 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -1137,8 +1137,9 @@ BOOST_AUTO_TEST_CASE( witness_feeds ) generate_block(); const asset_object& bit_usd = get_asset("USDBIT"); auto& global_props = db.get_global_properties(); - const vector active_witnesses(global_props.witness_accounts.begin(), - global_props.witness_accounts.end()); + vector active_witnesses; + for( const witness_id_type& wit_id : global_props.active_witnesses ) + active_witnesses.push_back( wit_id(db).witness_account ); BOOST_REQUIRE_EQUAL(active_witnesses.size(), 10); asset_publish_feed_operation op; From e8aeacc29331e7c301ce8a76c880dfe9153a179d Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 9 Feb 2016 04:36:41 -0500 Subject: [PATCH 18/43] operations.cpp: Remove unused ancient implementation of operation_get_required_authorities #537 --- libraries/chain/protocol/operations.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/libraries/chain/protocol/operations.cpp b/libraries/chain/protocol/operations.cpp index 26dbaaa1..366d62f7 100644 --- a/libraries/chain/protocol/operations.cpp +++ b/libraries/chain/protocol/operations.cpp @@ -35,29 +35,6 @@ void balance_claim_operation::validate()const FC_ASSERT( balance_owner_key != public_key_type() ); } -struct required_auth_visitor -{ - typedef void result_type; - - vector& result; - - required_auth_visitor( vector& r ):result(r){} - - /** for most operations this is a no-op */ - template - void operator()(const T& )const {} - - void operator()( const balance_claim_operation& o )const - { - result.push_back( authority( 1, o.balance_owner_key, 1 ) ); - } -}; - -void operation_get_required_authorities( const operation& op, vector& result ) -{ - op.visit( required_auth_visitor( result ) ); -} - /** * @brief Used to validate operations in a polymorphic manner */ From 39d0946b50d76db0237b68ff73a2cc2f096453ed Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Tue, 9 Feb 2016 04:44:11 -0500 Subject: [PATCH 19/43] Remove evaluation_observer #550 --- .../chain/include/graphene/chain/database.hpp | 8 -- .../include/graphene/chain/evaluator.hpp | 94 +------------------ 2 files changed, 1 insertion(+), 101 deletions(-) diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 7197d47c..66d85451 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -269,14 +269,6 @@ namespace graphene { namespace chain { operation::tag::value].reset( new op_evaluator_impl() ); } - template - void register_evaluation_observer( evaluation_observer& observer ) - { - unique_ptr& op_eval = _operation_evaluators[operation::tag::value]; - op_eval->eval_observers.push_back( &observer ); - return; - } - //////////////////// db_balance.cpp //////////////////// /** diff --git a/libraries/chain/include/graphene/chain/evaluator.hpp b/libraries/chain/include/graphene/chain/evaluator.hpp index 015f10a5..33d6338c 100644 --- a/libraries/chain/include/graphene/chain/evaluator.hpp +++ b/libraries/chain/include/graphene/chain/evaluator.hpp @@ -29,47 +29,6 @@ namespace graphene { namespace chain { class generic_evaluator; class transaction_evaluation_state; - /** - * Observes evaluation events, providing - * pre- and post-evaluation hooks. - * - * Every call to pre_evaluate() is followed by - * a call to either post_evaluate() or evaluation_failed(). - * - * A subclass which needs to do a "diff" can gather some - * "before" state into its members in pre_evaluate(), - * then post_evaluate() will have both "before" - * and "after" state, and will be able to do the diff. - * - * evaluation_failed() is a cleanup method which notifies - * the subclass to "throw away" the diff. - */ - class evaluation_observer - { - public: - virtual ~evaluation_observer(){} - - virtual void pre_evaluate(const transaction_evaluation_state& eval_state, - const operation& op, - bool apply, - generic_evaluator* ge) - {} - - virtual void post_evaluate(const transaction_evaluation_state& eval_state, - const operation& op, - bool apply, - generic_evaluator* ge, - const operation_result& result) - {} - - virtual void evaluation_failed(const transaction_evaluation_state& eval_state, - const operation& op, - bool apply, - generic_evaluator* ge, - const operation_result& result) - {} - }; - class generic_evaluator { public: @@ -118,8 +77,6 @@ namespace graphene { namespace chain { public: virtual ~op_evaluator(){} virtual operation_result evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply) = 0; - - vector eval_observers; }; template @@ -128,57 +85,8 @@ namespace graphene { namespace chain { public: virtual operation_result evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply = true) override { - // fc::exception from observers are suppressed. - // fc::exception from evaluation is deferred (re-thrown - // after all observers receive evaluation_failed) - T eval; - shared_ptr evaluation_exception; - size_t observer_count = 0; - operation_result result; - - for( const auto& obs : eval_observers ) - { - try - { - obs->pre_evaluate(eval_state, op, apply, &eval); - } - catch( const fc::exception& e ) - { - elog( "suppressed exception in observer pre method:\n${e}", ( "e", e.to_detail_string() ) ); - } - observer_count++; - } - - try - { - result = eval.start_evaluate(eval_state, op, apply); - } - catch( const fc::exception& e ) - { - evaluation_exception = e.dynamic_copy_exception(); - } - - while( observer_count > 0 ) - { - --observer_count; - const auto& obs = eval_observers[observer_count]; - try - { - if( evaluation_exception ) - obs->post_evaluate(eval_state, op, apply, &eval, result); - else - obs->evaluation_failed(eval_state, op, apply, &eval, result); - } - catch( const fc::exception& e ) - { - elog( "suppressed exception in observer post method:\n${e}", ( "e", e.to_detail_string() ) ); - } - } - - if( evaluation_exception ) - evaluation_exception->dynamic_rethrow_exception(); - return result; + return eval.start_evaluate(eval_state, op, apply); } }; From f1be8574410662e5dee1c762d1aff953cbe9cb7d Mon Sep 17 00:00:00 2001 From: James Calfee Date: Tue, 9 Feb 2016 07:22:36 -0600 Subject: [PATCH 20/43] Updated fc submodule version (fixes compile) #500 --- libraries/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fc b/libraries/fc index ad7ecbf0..d74eee81 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit ad7ecbf061ae74c944c81da993a020c05d53c7b6 +Subproject commit d74eee811018f5f0b562b12af3a5afaa59cf60cb From ababf24e694d1716d020250be603a003f10bb723 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 9 Feb 2016 07:59:22 -0500 Subject: [PATCH 21/43] Fix #523 relevant to #452 --- libraries/wallet/include/graphene/wallet/wallet.hpp | 2 +- libraries/wallet/wallet.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 952b1b33..c13bac9f 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -759,7 +759,7 @@ class wallet_api blind_confirmation transfer_to_blind( string from_account_id_or_name, string asset_symbol, /** map from key or label to amount */ - map to_amounts, + vector> to_amounts, bool broadcast = false ); /** diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 3858f4c1..aeebfa84 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3787,7 +3787,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name, string asset_symbol, /** map from key or label to amount */ - map to_amounts, + vector> to_amounts, bool broadcast ) { try { FC_ASSERT( !is_locked() ); From fa439e97323cdddd28c604bb89d5748343d94772 Mon Sep 17 00:00:00 2001 From: James Calfee Date: Tue, 9 Feb 2016 12:37:54 -0600 Subject: [PATCH 22/43] Saves change address in the wallet when transfering from blind to an account (close #564) --- .../wallet/include/graphene/wallet/wallet.hpp | 3 +- libraries/wallet/wallet.cpp | 39 ++++++++++++++++--- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 02582de4..76bada67 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -765,9 +765,10 @@ class wallet_api * who sent it. * * @param opt_from - if not empty and the sender is a unknown public key, then the unknown public key will be given the label opt_from + * @param owner - optional, when owner is emtpy (owner == authority()) this receipt must be on the blockchain. Transfers may save their confirmation prior to broadcast. * @param confirmation_receipt - a base58 encoded stealth confirmation */ - blind_receipt receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ); + blind_receipt receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo, optional owner ); /** * Transfers a public balance from @from to one or more blinded balances using a diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index ed93724a..163a7899 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3676,7 +3676,7 @@ blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_ke auto conf = blind_transfer_help( from_blind_account_key_or_label, from_blind_account_key_or_label, - blind_in, symbol, false, true ); + blind_in, symbol, false, true/*to_temp*/ ); FC_ASSERT( conf.outputs.size() > 0 ); auto to_account = my->get_account( to_account_id_or_name ); @@ -3690,6 +3690,24 @@ blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_ke conf.trx.operations.push_back(from_blind); ilog( "about to validate" ); conf.trx.validate(); + + if( broadcast && conf.outputs.size() == 2 ) { + + // Save the change + blind_confirmation::output conf_output; + blind_confirmation::output change_output = conf.outputs[0]; + + // The wallet must have a private key for confirmation.to, this is used to decrypt the memo + public_key_type from_key = get_public_key(from_blind_account_key_or_label); + conf_output.confirmation.to = from_key; + conf_output.confirmation.one_time_key = change_output.confirmation.one_time_key; + conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo; + conf_output.confirmation_receipt = conf_output.confirmation; + //try { + receive_blind_transfer( conf_output.confirmation_receipt, from_blind_account_key_or_label, "@"+to_account.name, change_output.auth ); + //} catch ( ... ){} + } + ilog( "about to broadcast" ); conf.trx = sign_transaction( conf.trx, broadcast ); @@ -3864,7 +3882,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, { for( const auto& out : confirm.outputs ) { - try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "" ); } catch ( ... ){} + try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "", optional() ); } catch ( ... ){} } } @@ -3953,14 +3971,14 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name { for( const auto& out : confirm.outputs ) { - try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name ); } catch ( ... ){} + try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name, optional() ); } catch ( ... ){} } } return confirm; } FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) } -blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ) +blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo, optional owner ) { FC_ASSERT( !is_locked() ); stealth_confirmation conf(confirmation_receipt); @@ -4009,8 +4027,17 @@ blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, s auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value ); FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) ); - auto bbal = my->_remote_db->get_blinded_balances( {memo.commitment} ); - FC_ASSERT( bbal.size(), "commitment not found in blockchain", ("memo",memo) ); + vector bbal; + if( owner.valid() ) + { + // When owner is provided, do not require the commitment to be on the blockchain yet... This allows the receipt to be saved before it is broadcasted. + blinded_balance_object b; + b.owner = *owner; + bbal.push_back(b); + } else { + bbal = my->_remote_db->get_blinded_balances( {memo.commitment} ); + FC_ASSERT( bbal.size(), "commitment not found in blockchain", ("memo",memo) ); + } blind_balance bal; bal.amount = memo.amount; From a0cc5b775d21785d0ac87acf227276e73a87be8c Mon Sep 17 00:00:00 2001 From: James Calfee Date: Tue, 9 Feb 2016 13:43:56 -0600 Subject: [PATCH 23/43] Saves change address (without changing wallet_api::receive_blind_transfer API) #564 --- .../wallet/include/graphene/wallet/wallet.hpp | 3 +- libraries/wallet/wallet.cpp | 36 ++++++------------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 76bada67..02582de4 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -765,10 +765,9 @@ class wallet_api * who sent it. * * @param opt_from - if not empty and the sender is a unknown public key, then the unknown public key will be given the label opt_from - * @param owner - optional, when owner is emtpy (owner == authority()) this receipt must be on the blockchain. Transfers may save their confirmation prior to broadcast. * @param confirmation_receipt - a base58 encoded stealth confirmation */ - blind_receipt receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo, optional owner ); + blind_receipt receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ); /** * Transfers a public balance from @from to one or more blinded balances using a diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 163a7899..0e93f668 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3704,7 +3704,7 @@ blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_ke conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo; conf_output.confirmation_receipt = conf_output.confirmation; //try { - receive_blind_transfer( conf_output.confirmation_receipt, from_blind_account_key_or_label, "@"+to_account.name, change_output.auth ); + receive_blind_transfer( conf_output.confirmation_receipt, from_blind_account_key_or_label, "@"+to_account.name ); //} catch ( ... ){} } @@ -3882,7 +3882,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, { for( const auto& out : confirm.outputs ) { - try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "", optional() ); } catch ( ... ){} + try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "" ); } catch ( ... ){} } } @@ -3971,14 +3971,14 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name { for( const auto& out : confirm.outputs ) { - try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name, optional() ); } catch ( ... ){} + try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name ); } catch ( ... ){} } } return confirm; } FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) } -blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo, optional owner ) +blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ) { FC_ASSERT( !is_locked() ); stealth_confirmation conf(confirmation_receipt); @@ -4003,7 +4003,6 @@ blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, s auto plain_memo = fc::aes_decrypt( secret, conf.encrypted_memo ); auto memo = fc::raw::unpack( plain_memo ); - result.to_key = *conf.to; result.to_label = get_key_label( result.to_key ); if( memo.from ) @@ -4027,18 +4026,6 @@ blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, s auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value ); FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) ); - vector bbal; - if( owner.valid() ) - { - // When owner is provided, do not require the commitment to be on the blockchain yet... This allows the receipt to be saved before it is broadcasted. - blinded_balance_object b; - b.owner = *owner; - bbal.push_back(b); - } else { - bbal = my->_remote_db->get_blinded_balances( {memo.commitment} ); - FC_ASSERT( bbal.size(), "commitment not found in blockchain", ("memo",memo) ); - } - blind_balance bal; bal.amount = memo.amount; bal.to = *conf.to; @@ -4048,21 +4035,20 @@ blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, s bal.commitment = memo.commitment; bal.used = false; - result.control_authority = bbal.front().owner; + auto child_pubkey = child_priv_key.get_public_key(); + auto owner = authority(1, public_key_type(child_pubkey), 1); + result.control_authority = owner; result.data = memo; - - auto child_key_itr = bbal.front().owner.key_auths.find( child_priv_key.get_public_key() ); - - if( child_key_itr != bbal.front().owner.key_auths.end() ) + auto child_key_itr = owner.key_auths.find( child_pubkey ); + if( child_key_itr != owner.key_auths.end() ) my->_keys[child_key_itr->first] = key_to_wif( child_priv_key ); - - + // my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal ); result.date = fc::time_point::now(); my->_wallet.blind_receipts.insert( result ); - my->_keys[child_priv_key.get_public_key()] = key_to_wif( child_priv_key ); + my->_keys[child_pubkey] = key_to_wif( child_priv_key ); save_wallet_file(); From 68a0ffa1aa7ffd66940c10578c300e61ad6aa71f Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Wed, 10 Feb 2016 16:24:56 -0500 Subject: [PATCH 24/43] Make is_authorized_asset a free-floating method #566 The following sed commands were used to port existing call sites to the new interface: sed -i -e 's/\([a-zA-Z0-9_]\+\)->is_authorized_asset[(] \([a-zA-Z0-9_*]\+\), d [)]/is_authorized_asset( d, *\1, \2 )/' libraries/chain/*.cpp sed -i -e 's/\([a-zA-Z0-9_]\+\)[.]is_authorized_asset[(] \([a-zA-Z0-9_*]\+\), d [)]/is_authorized_asset( d, \1, \2 )/' libraries/chain/*.cpp sed -i -e 's/\([a-zA-Z0-9_]\+\)[(]db[)][.]is_authorized_asset[(]\([a-zA-Z0-9_*]\+\)[(]db[)], db[)]/is_authorized_asset( db, \1(db), \2(db) )/' tests/tests/uia_tests.cpp sed -i -e 's/\([a-zA-Z0-9_]\+\)[.]is_authorized_asset[(]\([a-zA-Z0-9_*]\+\), db[)]/is_authorized_asset( db, \1, \2 )/' tests/tests/uia_tests.cpp No new functionality is added by this commit, it is simply re-organizing the existing code in a different place. --- libraries/chain/CMakeLists.txt | 2 + libraries/chain/account_object.cpp | 29 --------- libraries/chain/asset_evaluator.cpp | 5 +- libraries/chain/evaluator.cpp | 3 +- .../include/graphene/chain/account_object.hpp | 6 -- .../graphene/chain/is_authorized_asset.hpp | 39 +++++++++++ libraries/chain/is_authorized_asset.cpp | 65 +++++++++++++++++++ libraries/chain/market_evaluator.cpp | 15 +++-- libraries/chain/transfer_evaluator.cpp | 13 ++-- .../chain/withdraw_permission_evaluator.cpp | 9 +-- tests/tests/uia_tests.cpp | 9 +-- 11 files changed, 137 insertions(+), 58 deletions(-) create mode 100644 libraries/chain/include/graphene/chain/is_authorized_asset.hpp create mode 100644 libraries/chain/is_authorized_asset.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 876c8c1d..fdd2bb00 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -84,6 +84,8 @@ add_library( graphene_chain block_database.cpp + is_authorized_asset.cpp + ${HEADERS} "${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp" ) diff --git a/libraries/chain/account_object.cpp b/libraries/chain/account_object.cpp index 1006cdcb..90d97692 100644 --- a/libraries/chain/account_object.cpp +++ b/libraries/chain/account_object.cpp @@ -42,35 +42,6 @@ share_type cut_fee(share_type a, uint16_t p) return r.to_uint64(); } -bool account_object::is_authorized_asset(const asset_object& asset_obj, const database& d) const -{ - if( d.head_block_time() > HARDFORK_416_TIME ) - { - if( !(asset_obj.options.flags & white_list) ) - return true; - } - - for( const auto id : blacklisting_accounts ) - { - if( asset_obj.options.blacklist_authorities.find(id) != asset_obj.options.blacklist_authorities.end() ) - return false; - } - - if( d.head_block_time() > HARDFORK_415_TIME ) - { - if( asset_obj.options.whitelist_authorities.size() == 0 ) - return true; - } - - for( const auto id : whitelisting_accounts ) - { - if( asset_obj.options.whitelist_authorities.find(id) != asset_obj.options.whitelist_authorities.end() ) - return true; - } - - return false; -} - void account_balance_object::adjust_balance(const asset& delta) { assert(delta.asset_id == asset_type); diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 17113d9c..30f5b83a 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -155,7 +156,7 @@ void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o ) if( a.options.flags & white_list ) { - FC_ASSERT( to_account->is_authorized_asset( a, d ) ); + FC_ASSERT( is_authorized_asset( d, *to_account, a ) ); } asset_dyn_data = &a.dynamic_asset_data_id(d); @@ -191,7 +192,7 @@ void_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation& if( a.options.flags & white_list ) { - FC_ASSERT( from_account->is_authorized_asset( a, d ) ); + FC_ASSERT( is_authorized_asset( d, *from_account, a ) ); } asset_dyn_data = &a.dynamic_asset_data_id(d); diff --git a/libraries/chain/evaluator.cpp b/libraries/chain/evaluator.cpp index 041e413a..bbf7521c 100644 --- a/libraries/chain/evaluator.cpp +++ b/libraries/chain/evaluator.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -61,7 +62,7 @@ database& generic_evaluator::db()const { return trx_state->db(); } if( d.head_block_time() > HARDFORK_419_TIME ) { - FC_ASSERT( fee_paying_account->is_authorized_asset( *fee_asset, d ), "Account ${acct} '${name}' attempted to pay fee by using asset ${a} '${sym}', which is unauthorized due to whitelist / blacklist", + FC_ASSERT( is_authorized_asset( d, *fee_paying_account, *fee_asset ), "Account ${acct} '${name}' attempted to pay fee by using asset ${a} '${sym}', which is unauthorized due to whitelist / blacklist", ("acct", fee_paying_account->id)("name", fee_paying_account->name)("a", fee_asset->id)("sym", fee_asset->symbol) ); } diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index a3f2b3ab..fd5da61f 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -233,12 +233,6 @@ namespace graphene { namespace chain { return !is_basic_account(now); } - /** - * @return true if this account is whitelisted and not blacklisted to transact in the provided asset; false - * otherwise. - */ - bool is_authorized_asset(const asset_object& asset_obj, const database& d)const; - account_id_type get_id()const { return id; } }; diff --git a/libraries/chain/include/graphene/chain/is_authorized_asset.hpp b/libraries/chain/include/graphene/chain/is_authorized_asset.hpp new file mode 100644 index 00000000..1c7569c0 --- /dev/null +++ b/libraries/chain/include/graphene/chain/is_authorized_asset.hpp @@ -0,0 +1,39 @@ +/* + * 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 + +namespace graphene { namespace chain { + +class account_object; +class asset_object; +class database; + +/** + * @return true if the account is whitelisted and not blacklisted to transact in the provided asset; false + * otherwise. + */ + +bool is_authorized_asset(const database& d, const account_object& acct, const asset_object& asset_obj); + +} } diff --git a/libraries/chain/is_authorized_asset.cpp b/libraries/chain/is_authorized_asset.cpp new file mode 100644 index 00000000..0dda6ece --- /dev/null +++ b/libraries/chain/is_authorized_asset.cpp @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#include +#include + +#include +#include + +namespace graphene { namespace chain { + +bool is_authorized_asset( + const database& d, + const account_object& acct, + const asset_object& asset_obj) +{ + if( d.head_block_time() > HARDFORK_416_TIME ) + { + if( !(asset_obj.options.flags & white_list) ) + return true; + } + + for( const auto id : acct.blacklisting_accounts ) + { + if( asset_obj.options.blacklist_authorities.find(id) != asset_obj.options.blacklist_authorities.end() ) + return false; + } + + if( d.head_block_time() > HARDFORK_415_TIME ) + { + if( asset_obj.options.whitelist_authorities.size() == 0 ) + return true; + } + + for( const auto id : acct.whitelisting_accounts ) + { + if( asset_obj.options.whitelist_authorities.find(id) != asset_obj.options.whitelist_authorities.end() ) + return true; + } + + return false; +} + +} } diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index 10e2cb98..d21f6fe0 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -23,11 +23,14 @@ */ #include #include +#include + +#include + #include #include #include -#include -#include +#include #include @@ -51,13 +54,13 @@ void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_o if( d.head_block_time() <= HARDFORK_416_TIME ) { - if( _sell_asset->options.flags & white_list ) FC_ASSERT( _seller->is_authorized_asset( *_sell_asset, d ) ); - if( _receive_asset->options.flags & white_list ) FC_ASSERT( _seller->is_authorized_asset( *_receive_asset, d ) ); + if( _sell_asset->options.flags & white_list ) FC_ASSERT( is_authorized_asset( d, *_seller, *_sell_asset ) ); + if( _receive_asset->options.flags & white_list ) FC_ASSERT( is_authorized_asset( d, *_seller, *_receive_asset ) ); } else { - FC_ASSERT( _seller->is_authorized_asset( *_sell_asset, d ) ); - FC_ASSERT( _seller->is_authorized_asset( *_receive_asset, d ) ); + FC_ASSERT( is_authorized_asset( d, *_seller, *_sell_asset ) ); + FC_ASSERT( is_authorized_asset( d, *_seller, *_receive_asset ) ); } FC_ASSERT( d.get_balance( *_seller, *_sell_asset ) >= op.amount_to_sell, "insufficient balance", diff --git a/libraries/chain/transfer_evaluator.cpp b/libraries/chain/transfer_evaluator.cpp index 25304963..0f6e2419 100644 --- a/libraries/chain/transfer_evaluator.cpp +++ b/libraries/chain/transfer_evaluator.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace graphene { namespace chain { void_result transfer_evaluator::do_evaluate( const transfer_operation& op ) @@ -42,14 +43,14 @@ void_result transfer_evaluator::do_evaluate( const transfer_operation& op ) if( asset_type.options.flags & white_list ) { GRAPHENE_ASSERT( - from_account.is_authorized_asset( asset_type, d ), + is_authorized_asset( d, from_account, asset_type ), transfer_from_account_not_whitelisted, "'from' account ${from} is not whitelisted for asset ${asset}", ("from",op.from) ("asset",op.amount.asset_id) ); GRAPHENE_ASSERT( - to_account.is_authorized_asset( asset_type, d ), + is_authorized_asset( d, to_account, asset_type ), transfer_to_account_not_whitelisted, "'to' account ${to} is not whitelisted for asset ${asset}", ("to",op.to) @@ -60,7 +61,7 @@ void_result transfer_evaluator::do_evaluate( const transfer_operation& op ) if( d.head_block_time() <= HARDFORK_419_TIME ) { if( fee_asset_type.options.flags & white_list ) - FC_ASSERT( from_account.is_authorized_asset( asset_type, d ) ); + FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) ); } // the above becomes no-op after hardfork because this check will then be performed in evaluator @@ -112,14 +113,14 @@ void_result override_transfer_evaluator::do_evaluate( const override_transfer_op if( asset_type.options.flags & white_list ) { - FC_ASSERT( to_account.is_authorized_asset( asset_type, d ) ); - FC_ASSERT( from_account.is_authorized_asset( asset_type, d ) ); + FC_ASSERT( is_authorized_asset( d, to_account, asset_type ) ); + FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) ); } if( d.head_block_time() <= HARDFORK_419_TIME ) { if( fee_asset_type.options.flags & white_list ) - FC_ASSERT( from_account.is_authorized_asset( asset_type, d ) ); + FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) ); } // the above becomes no-op after hardfork because this check will then be performed in evaluator diff --git a/libraries/chain/withdraw_permission_evaluator.cpp b/libraries/chain/withdraw_permission_evaluator.cpp index 0fdc4607..9014282d 100644 --- a/libraries/chain/withdraw_permission_evaluator.cpp +++ b/libraries/chain/withdraw_permission_evaluator.cpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -75,16 +76,16 @@ void_result withdraw_permission_claim_evaluator::do_evaluate(const withdraw_perm { const account_object& from = op.withdraw_to_account(d); const account_object& to = permit.authorized_account(d); - FC_ASSERT( to.is_authorized_asset( _asset, d ) ); - FC_ASSERT( from.is_authorized_asset( _asset, d ) ); + FC_ASSERT( is_authorized_asset( d, to, _asset ) ); + FC_ASSERT( is_authorized_asset( d, from, _asset ) ); } } else { const account_object& from = op.withdraw_to_account(d); const account_object& to = permit.authorized_account(d); - FC_ASSERT( to.is_authorized_asset( _asset, d ) ); - FC_ASSERT( from.is_authorized_asset( _asset, d ) ); + FC_ASSERT( is_authorized_asset( d, to, _asset ) ); + FC_ASSERT( is_authorized_asset( d, from, _asset ) ); } return void_result(); diff --git a/tests/tests/uia_tests.cpp b/tests/tests/uia_tests.cpp index 8876f23e..442d5080 100644 --- a/tests/tests/uia_tests.cpp +++ b/tests/tests/uia_tests.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -161,7 +162,7 @@ BOOST_AUTO_TEST_CASE( issue_whitelist_uia ) } PUSH_TX( db, trx, ~0 ); - BOOST_CHECK(nathan_id(db).is_authorized_asset(uia_id(db), db)); + BOOST_CHECK(is_authorized_asset( db, nathan_id(db), uia_id(db) )); BOOST_CHECK_EQUAL(get_balance(nathan_id, uia_id), 1000); // Make a whitelist, now it should fail @@ -265,7 +266,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia ) wop.account_to_list = nathan.id; trx.operations.back() = wop; PUSH_TX( db, trx, ~0 ); - BOOST_CHECK( !(nathan.is_authorized_asset(advanced, db)) ); + BOOST_CHECK( !(is_authorized_asset( db, nathan, advanced )) ); BOOST_TEST_MESSAGE( "Attempting to transfer from nathan after blacklisting, should fail" ); op.amount = advanced.amount(50); @@ -323,7 +324,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia ) trx.operations.back() = op; //Fail because nathan is blacklisted - BOOST_CHECK(!nathan.is_authorized_asset(advanced, db)); + BOOST_CHECK(!is_authorized_asset( db, nathan, advanced )); GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); //Remove nathan from committee's whitelist, add him to dan's. This should not authorize him to hold ADVANCED. @@ -340,7 +341,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia ) trx.operations.back() = op; //Fail because nathan is not whitelisted - BOOST_CHECK(!nathan.is_authorized_asset(advanced, db)); + BOOST_CHECK(!is_authorized_asset( db, nathan, advanced )); GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); burn.payer = dan.id; From 130b54116cce52fc718f5490e9d328e80ee00302 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 11 Feb 2016 02:38:18 -0500 Subject: [PATCH 25/43] Inline fast path of is_authorized_asset() #566 --- .../graphene/chain/is_authorized_asset.hpp | 17 ++++++++++++++++- libraries/chain/is_authorized_asset.cpp | 8 ++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/graphene/chain/is_authorized_asset.hpp b/libraries/chain/include/graphene/chain/is_authorized_asset.hpp index 1c7569c0..5062136e 100644 --- a/libraries/chain/include/graphene/chain/is_authorized_asset.hpp +++ b/libraries/chain/include/graphene/chain/is_authorized_asset.hpp @@ -29,11 +29,26 @@ class account_object; class asset_object; class database; +namespace detail { + +bool _is_authorized_asset(const database& d, const account_object& acct, const asset_object& asset_obj); + +} + /** * @return true if the account is whitelisted and not blacklisted to transact in the provided asset; false * otherwise. */ -bool is_authorized_asset(const database& d, const account_object& acct, const asset_object& asset_obj); +inline bool is_authorized_asset(const database& d, const account_object& acct, const asset_object& asset_obj) +{ + bool fast_check = !(asset_obj.options.flags & white_list); + + if( fast_check ) + return true; + + bool slow_check = detail::_is_authorized_asset( d, acct, asset_obj ); + return slow_check; +} } } diff --git a/libraries/chain/is_authorized_asset.cpp b/libraries/chain/is_authorized_asset.cpp index 0dda6ece..585b1da6 100644 --- a/libraries/chain/is_authorized_asset.cpp +++ b/libraries/chain/is_authorized_asset.cpp @@ -30,7 +30,9 @@ namespace graphene { namespace chain { -bool is_authorized_asset( +namespace detail { + +bool _is_authorized_asset( const database& d, const account_object& acct, const asset_object& asset_obj) @@ -62,4 +64,6 @@ bool is_authorized_asset( return false; } -} } +} // detail + +} } // graphene::chain From bb47f4c71c5e0d64962033100a1f9d0c4081ddd4 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 11 Feb 2016 02:57:03 -0500 Subject: [PATCH 26/43] Remove now-redundant white_list bit check from _is_authorized_asset() #566 --- libraries/chain/is_authorized_asset.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libraries/chain/is_authorized_asset.cpp b/libraries/chain/is_authorized_asset.cpp index 585b1da6..8168a505 100644 --- a/libraries/chain/is_authorized_asset.cpp +++ b/libraries/chain/is_authorized_asset.cpp @@ -37,12 +37,6 @@ bool _is_authorized_asset( const account_object& acct, const asset_object& asset_obj) { - if( d.head_block_time() > HARDFORK_416_TIME ) - { - if( !(asset_obj.options.flags & white_list) ) - return true; - } - for( const auto id : acct.blacklisting_accounts ) { if( asset_obj.options.blacklist_authorities.find(id) != asset_obj.options.blacklist_authorities.end() ) From 6f7f2605c1568464f852e930c75b5510f2d51337 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 11 Feb 2016 03:12:18 -0500 Subject: [PATCH 27/43] Remove now-redundant white_list bit check from evaluators #566 --- libraries/chain/asset_evaluator.cpp | 12 +---- libraries/chain/market_evaluator.cpp | 12 +---- libraries/chain/transfer_evaluator.cpp | 44 ++++++++----------- .../chain/withdraw_permission_evaluator.cpp | 21 ++------- 4 files changed, 26 insertions(+), 63 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 30f5b83a..9a467bec 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -153,11 +153,7 @@ void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o ) FC_ASSERT( !a.is_market_issued(), "Cannot manually issue a market-issued asset." ); to_account = &o.issue_to_account(d); - - if( a.options.flags & white_list ) - { - FC_ASSERT( is_authorized_asset( d, *to_account, a ) ); - } + FC_ASSERT( is_authorized_asset( d, *to_account, a ) ); asset_dyn_data = &a.dynamic_asset_data_id(d); FC_ASSERT( (asset_dyn_data->current_supply + o.asset_to_issue.amount) <= a.options.max_supply ); @@ -189,11 +185,7 @@ void_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation& ); from_account = &o.payer(d); - - if( a.options.flags & white_list ) - { - FC_ASSERT( is_authorized_asset( d, *from_account, a ) ); - } + FC_ASSERT( is_authorized_asset( d, *from_account, a ) ); asset_dyn_data = &a.dynamic_asset_data_id(d); FC_ASSERT( (asset_dyn_data->current_supply - o.amount_to_reserve.amount) >= 0 ); diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index d21f6fe0..27c31ae4 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -52,16 +52,8 @@ void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_o if( _sell_asset->options.blacklist_markets.size() ) FC_ASSERT( _sell_asset->options.blacklist_markets.find(_receive_asset->id) == _sell_asset->options.blacklist_markets.end() ); - if( d.head_block_time() <= HARDFORK_416_TIME ) - { - if( _sell_asset->options.flags & white_list ) FC_ASSERT( is_authorized_asset( d, *_seller, *_sell_asset ) ); - if( _receive_asset->options.flags & white_list ) FC_ASSERT( is_authorized_asset( d, *_seller, *_receive_asset ) ); - } - else - { - FC_ASSERT( is_authorized_asset( d, *_seller, *_sell_asset ) ); - FC_ASSERT( is_authorized_asset( d, *_seller, *_receive_asset ) ); - } + FC_ASSERT( is_authorized_asset( d, *_seller, *_sell_asset ) ); + FC_ASSERT( is_authorized_asset( d, *_seller, *_receive_asset ) ); FC_ASSERT( d.get_balance( *_seller, *_sell_asset ) >= op.amount_to_sell, "insufficient balance", ("balance",d.get_balance(*_seller,*_sell_asset))("amount_to_sell",op.amount_to_sell) ); diff --git a/libraries/chain/transfer_evaluator.cpp b/libraries/chain/transfer_evaluator.cpp index 0f6e2419..4ec9e3e9 100644 --- a/libraries/chain/transfer_evaluator.cpp +++ b/libraries/chain/transfer_evaluator.cpp @@ -40,28 +40,24 @@ void_result transfer_evaluator::do_evaluate( const transfer_operation& op ) try { - if( asset_type.options.flags & white_list ) - { - GRAPHENE_ASSERT( - is_authorized_asset( d, from_account, asset_type ), - transfer_from_account_not_whitelisted, - "'from' account ${from} is not whitelisted for asset ${asset}", - ("from",op.from) - ("asset",op.amount.asset_id) - ); - GRAPHENE_ASSERT( - is_authorized_asset( d, to_account, asset_type ), - transfer_to_account_not_whitelisted, - "'to' account ${to} is not whitelisted for asset ${asset}", - ("to",op.to) - ("asset",op.amount.asset_id) - ); - } + GRAPHENE_ASSERT( + is_authorized_asset( d, from_account, asset_type ), + transfer_from_account_not_whitelisted, + "'from' account ${from} is not whitelisted for asset ${asset}", + ("from",op.from) + ("asset",op.amount.asset_id) + ); + GRAPHENE_ASSERT( + is_authorized_asset( d, to_account, asset_type ), + transfer_to_account_not_whitelisted, + "'to' account ${to} is not whitelisted for asset ${asset}", + ("to",op.to) + ("asset",op.amount.asset_id) + ); if( d.head_block_time() <= HARDFORK_419_TIME ) { - if( fee_asset_type.options.flags & white_list ) - FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) ); + FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) ); } // the above becomes no-op after hardfork because this check will then be performed in evaluator @@ -111,16 +107,12 @@ void_result override_transfer_evaluator::do_evaluate( const override_transfer_op const account_object& to_account = op.to(d); const asset_object& fee_asset_type = op.fee.asset_id(d); - if( asset_type.options.flags & white_list ) - { - FC_ASSERT( is_authorized_asset( d, to_account, asset_type ) ); - FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) ); - } + FC_ASSERT( is_authorized_asset( d, to_account, asset_type ) ); + FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) ); if( d.head_block_time() <= HARDFORK_419_TIME ) { - if( fee_asset_type.options.flags & white_list ) - FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) ); + FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) ); } // the above becomes no-op after hardfork because this check will then be performed in evaluator diff --git a/libraries/chain/withdraw_permission_evaluator.cpp b/libraries/chain/withdraw_permission_evaluator.cpp index 9014282d..d001b441 100644 --- a/libraries/chain/withdraw_permission_evaluator.cpp +++ b/libraries/chain/withdraw_permission_evaluator.cpp @@ -70,23 +70,10 @@ void_result withdraw_permission_claim_evaluator::do_evaluate(const withdraw_perm const asset_object& _asset = op.amount_to_withdraw.asset_id(d); if( _asset.is_transfer_restricted() ) FC_ASSERT( _asset.issuer == permit.authorized_account || _asset.issuer == permit.withdraw_from_account ); - if( d.head_block_time() <= HARDFORK_416_TIME ) - { - if( _asset.options.flags & white_list ) - { - const account_object& from = op.withdraw_to_account(d); - const account_object& to = permit.authorized_account(d); - FC_ASSERT( is_authorized_asset( d, to, _asset ) ); - FC_ASSERT( is_authorized_asset( d, from, _asset ) ); - } - } - else - { - const account_object& from = op.withdraw_to_account(d); - const account_object& to = permit.authorized_account(d); - FC_ASSERT( is_authorized_asset( d, to, _asset ) ); - FC_ASSERT( is_authorized_asset( d, from, _asset ) ); - } + const account_object& from = op.withdraw_to_account(d); + const account_object& to = permit.authorized_account(d); + FC_ASSERT( is_authorized_asset( d, to, _asset ) ); + FC_ASSERT( is_authorized_asset( d, from, _asset ) ); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } From e4f7483ec3c6016b065cfc58a5ec346cbce7e1cc Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 11 Feb 2016 03:23:15 -0500 Subject: [PATCH 28/43] transfer_evaluator.cpp: Remove unused variable and redundant check #566 This check was the pre-419 check of whether the fee asset is authorized, which was (due to a typo) buggily checking the sent asset rather then the fee asset. --- libraries/chain/transfer_evaluator.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/libraries/chain/transfer_evaluator.cpp b/libraries/chain/transfer_evaluator.cpp index 4ec9e3e9..accc6ca3 100644 --- a/libraries/chain/transfer_evaluator.cpp +++ b/libraries/chain/transfer_evaluator.cpp @@ -36,7 +36,6 @@ void_result transfer_evaluator::do_evaluate( const transfer_operation& op ) const account_object& from_account = op.from(d); const account_object& to_account = op.to(d); const asset_object& asset_type = op.amount.asset_id(d); - const asset_object& fee_asset_type = op.fee.asset_id(d); try { @@ -55,12 +54,6 @@ void_result transfer_evaluator::do_evaluate( const transfer_operation& op ) ("asset",op.amount.asset_id) ); - if( d.head_block_time() <= HARDFORK_419_TIME ) - { - FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) ); - } - // the above becomes no-op after hardfork because this check will then be performed in evaluator - if( asset_type.is_transfer_restricted() ) { GRAPHENE_ASSERT( @@ -105,7 +98,6 @@ void_result override_transfer_evaluator::do_evaluate( const override_transfer_op const account_object& from_account = op.from(d); const account_object& to_account = op.to(d); - const asset_object& fee_asset_type = op.fee.asset_id(d); FC_ASSERT( is_authorized_asset( d, to_account, asset_type ) ); FC_ASSERT( is_authorized_asset( d, from_account, asset_type ) ); From c33fe35e4eca74b90045a191bc5925987b1dc987 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 11 Feb 2016 04:59:28 -0500 Subject: [PATCH 29/43] Implement buyback accounts #538 --- libraries/app/api.cpp | 2 + libraries/chain/CMakeLists.txt | 1 + libraries/chain/account_evaluator.cpp | 25 ++++++ libraries/chain/buyback.cpp | 45 +++++++++++ libraries/chain/db_init.cpp | 2 + libraries/chain/db_maint.cpp | 80 +++++++++++++++++++ libraries/chain/hardfork.d/538.hf | 4 + .../include/graphene/chain/account_object.hpp | 8 ++ .../include/graphene/chain/asset_object.hpp | 3 + .../chain/include/graphene/chain/buyback.hpp | 34 ++++++++ .../include/graphene/chain/buyback_object.hpp | 67 ++++++++++++++++ .../chain/include/graphene/chain/config.hpp | 1 + .../chain/include/graphene/chain/database.hpp | 6 +- .../include/graphene/chain/exceptions.hpp | 3 + .../graphene/chain/is_authorized_asset.hpp | 1 + .../graphene/chain/protocol/account.hpp | 12 ++- .../graphene/chain/protocol/authority.hpp | 5 ++ .../graphene/chain/protocol/buyback.hpp | 52 ++++++++++++ .../include/graphene/chain/protocol/types.hpp | 6 +- libraries/chain/is_authorized_asset.cpp | 7 ++ libraries/chain/protocol/account.cpp | 13 +++ 21 files changed, 373 insertions(+), 4 deletions(-) create mode 100644 libraries/chain/buyback.cpp create mode 100644 libraries/chain/hardfork.d/538.hf create mode 100644 libraries/chain/include/graphene/chain/buyback.hpp create mode 100644 libraries/chain/include/graphene/chain/buyback_object.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/buyback.hpp diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 2080aa2e..d8793423 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -346,6 +346,8 @@ namespace graphene { namespace app { break; case impl_special_authority_object_type: break; + case impl_buyback_object_type: + break; } } return result; diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index dce52c29..d0cbc20b 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -77,6 +77,7 @@ add_library( graphene_chain worker_evaluator.cpp confidential_evaluator.cpp special_authority.cpp + buyback.cpp account_object.cpp asset_object.cpp diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index f1c77d10..03fe26c7 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -25,6 +25,8 @@ #include #include +#include +#include #include #include #include @@ -80,6 +82,8 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio evaluate_special_authority( d, *op.extensions.value.owner_special_authority ); if( op.extensions.value.active_special_authority.valid() ) evaluate_special_authority( d, *op.extensions.value.active_special_authority ); + if( op.extensions.value.buyback_options.valid() ) + evaluate_buyback_account_options( d, *op.extensions.value.buyback_options ); uint32_t max_vote_id = global_props.next_available_vote_id; @@ -115,6 +119,7 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio object_id_type account_create_evaluator::do_apply( const account_create_operation& o ) { try { + database& d = db(); uint16_t referrer_percent = o.referrer_percent; bool has_small_percent = ( (db().head_block_time() <= HARDFORK_453_TIME) @@ -154,6 +159,11 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio obj.owner_special_authority = *(o.extensions.value.owner_special_authority); if( o.extensions.value.active_special_authority.valid() ) obj.active_special_authority = *(o.extensions.value.active_special_authority); + if( o.extensions.value.buyback_options.valid() ) + { + obj.allowed_assets = o.extensions.value.buyback_options->markets; + obj.allowed_assets->emplace( o.extensions.value.buyback_options->asset_to_buy ); + } }); if( has_small_percent ) @@ -186,6 +196,21 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio } ); } + if( o.extensions.value.buyback_options.valid() ) + { + asset_id_type asset_to_buy = o.extensions.value.buyback_options->asset_to_buy; + + d.create< buyback_object >( [&]( buyback_object& bo ) + { + bo.asset_to_buy = asset_to_buy; + } ); + + d.modify( asset_to_buy(d), [&]( asset_object& a ) + { + a.buyback_account = new_acnt_object.id; + } ); + } + return new_acnt_object.id; } FC_CAPTURE_AND_RETHROW((o)) } diff --git a/libraries/chain/buyback.cpp b/libraries/chain/buyback.cpp new file mode 100644 index 00000000..09341fe7 --- /dev/null +++ b/libraries/chain/buyback.cpp @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#include +#include +#include +#include + +namespace graphene { namespace chain { + +void evaluate_buyback_account_options( const database& db, const buyback_account_options& bbo ) +{ + FC_ASSERT( db.head_block_time() >= HARDFORK_538_TIME ); + const asset_object& a = bbo.asset_to_buy(db); + GRAPHENE_ASSERT( a.issuer == bbo.asset_to_buy_issuer, + account_create_buyback_incorrect_issuer, "Incorrect asset issuer specified in buyback_account_options", ("asset", a)("bbo", bbo) ); + GRAPHENE_ASSERT( !a.buyback_account.valid(), + account_create_buyback_already_exists, "Cannot create buyback for asset which already has buyback", ("asset", a)("bbo", bbo) ); + // TODO: Replace with chain parameter #554 + GRAPHENE_ASSERT( bbo.markets.size() < GRAPHENE_DEFAULT_MAX_BUYBACK_MARKETS, + account_create_buyback_too_many_markets, "Too many buyback markets", ("asset", a)("bbo", bbo) ); +} + +} } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index e603b3eb..1b9a37c0 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -210,6 +211,7 @@ void database::initialize_indexes() add_index< primary_index > >(); add_index< primary_index > >(); add_index< primary_index< special_authority_index > >(); + add_index< primary_index< buyback_index > >(); } void database::init_genesis(const genesis_state_type& genesis_state) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index c00a4226..9721ae45 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -33,9 +33,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -522,10 +524,88 @@ void update_top_n_authorities( 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 >(); + + for( const buyback_object& bbo : bbo_idx ) + { + const asset_object& asset_to_buy = bbo.asset_to_buy(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() ) + { + wlog( "skipping buyback account ${b} at block ${n} because allowed_assets does not exist", ("b", buyback_account)("n", db.head_block_num()) ); + continue; + } + + while( true ) + { + 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; + 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 ) + continue; + if( buyback_account.allowed_assets->find( asset_to_sell ) == buyback_account.allowed_assets->end() ) + { + wlog( "buyback account ${b} not selling disallowed holdings of asset ${a} at block ${n}", ("b", buyback_account)("a", asset_to_sell)("n", db.head_block_num()) ); + continue; + } + + try + { + transaction_evaluation_state buyback_context(&db); + buyback_context.skip_fee_schedule_check = true; + + limit_order_create_operation create_vop; + create_vop.fee = asset( 0, asset_id_type() ); + create_vop.seller = buyback_account.id; + create_vop.amount_to_sell = asset( amount_to_sell, asset_to_sell ); + create_vop.min_to_receive = asset( 1, asset_to_buy.id ); + create_vop.expiration = time_point_sec::maximum(); + create_vop.fill_or_kill = false; + + limit_order_id_type order_id = db.apply_operation( buyback_context, create_vop ).get< object_id_type >(); + + if( db.find( order_id ) != nullptr ) + { + limit_order_cancel_operation cancel_vop; + cancel_vop.fee = asset( 0, asset_id_type() ); + cancel_vop.order = order_id; + cancel_vop.fee_paying_account = buyback_account.id; + + db.apply_operation( buyback_context, cancel_vop ); + } + } + catch( const fc::exception& e ) + { + // we can in fact get here, e.g. if asset issuer of buy/sell asset blacklists/whitelists the buyback account + wlog( "Skipping buyback processing selling ${as} for ${ab} for buyback account ${b} at block ${n}; exception was ${e}", + ("as", asset_to_sell)("ab", asset_to_buy)("b", buyback_account)("n", db.head_block_num())("e", e.to_detail_string()) ); + continue; + } + } + } + return; +} + void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props) { const auto& gpo = get_global_properties(); + create_buyback_orders(*this); + struct vote_tally_helper { database& d; const global_property_object& props; diff --git a/libraries/chain/hardfork.d/538.hf b/libraries/chain/hardfork.d/538.hf new file mode 100644 index 00000000..68da2c43 --- /dev/null +++ b/libraries/chain/hardfork.d/538.hf @@ -0,0 +1,4 @@ +// #538 Buyback accounts +#ifndef HARDFORK_538_TIME +#define HARDFORK_538_TIME (fc::time_point_sec( 1455127200 )) +#endif diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index e2d74363..0af36acc 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -208,6 +208,13 @@ namespace graphene { namespace chain { special_authority owner_special_authority = no_special_authority(); special_authority active_special_authority = no_special_authority(); + /** + * This is a set of assets which the account is allowed to have. + * This is utilized to restrict buyback accounts to the assets that trade in their markets. + * In the future we may expand this to allow accounts to e.g. voluntarily restrict incoming transfers. + */ + optional< flat_set > allowed_assets; + bool has_special_authority()const { return (owner_special_authority.which() != special_authority::tag< no_special_authority >::value) @@ -358,6 +365,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_object, (whitelisting_accounts)(blacklisted_accounts) (cashback_vb) (owner_special_authority)(active_special_authority) + (allowed_assets) ) FC_REFLECT_DERIVED( graphene::chain::account_balance_object, diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index ea127026..e9a0b9bb 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -130,6 +130,8 @@ namespace graphene { namespace chain { /// Extra data associated with BitAssets. This field is non-null if and only if is_market_issued() returns true optional bitasset_data_id; + optional buyback_account; + asset_id_type get_id()const { return id; } void validate()const @@ -261,4 +263,5 @@ FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object), (options) (dynamic_asset_data_id) (bitasset_data_id) + (buyback_account) ) diff --git a/libraries/chain/include/graphene/chain/buyback.hpp b/libraries/chain/include/graphene/chain/buyback.hpp new file mode 100644 index 00000000..5b7a8746 --- /dev/null +++ b/libraries/chain/include/graphene/chain/buyback.hpp @@ -0,0 +1,34 @@ +/* + * 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 + +namespace graphene { namespace chain { + +class database; + +void evaluate_buyback_account_options( const database& db, const buyback_account_options& auth ); + +} } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/buyback_object.hpp b/libraries/chain/include/graphene/chain/buyback_object.hpp new file mode 100644 index 00000000..de84b0e2 --- /dev/null +++ b/libraries/chain/include/graphene/chain/buyback_object.hpp @@ -0,0 +1,67 @@ +/* + * 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 { + +/** + * buyback_authority_object only exists to help with a specific indexing problem. + * We want to be able to iterate over all assets that have a buyback program. + * However, assets which have a buyback program are very rare. So rather + * than indexing asset_object by the buyback field (requiring additional + * bookkeeping for every asset), we instead maintain a buyback_object + * pointing to each asset which has buyback (requiring additional + * bookkeeping only for every asset which has buyback). + * + * This class is an implementation detail. + */ + +class buyback_object : public graphene::db::abstract_object< buyback_object > +{ + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_buyback_object_type; + + asset_id_type asset_to_buy; +}; + +struct by_asset; + +typedef multi_index_container< + buyback_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, member< buyback_object, asset_id_type, &buyback_object::asset_to_buy> > + > +> buyback_multi_index_type; + +typedef generic_index< buyback_object, buyback_multi_index_type > buyback_index; + +} } // graphene::chain + +FC_REFLECT( graphene::chain::buyback_object, (asset_to_buy) ) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index f1bbc3f7..5f8772d1 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -107,6 +107,7 @@ #define GRAPHENE_DEFAULT_FEE_LIQUIDATION_THRESHOLD GRAPHENE_BLOCKCHAIN_PRECISION * 100; #define GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE 1000 #define GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS 4 +#define GRAPHENE_DEFAULT_MAX_BUYBACK_MARKETS 4 #define GRAPHENE_MAX_WORKER_NAME_LENGTH 63 diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index a4dfdfc6..25d1b2c9 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -412,12 +412,14 @@ namespace graphene { namespace chain { //////////////////// db_block.cpp //////////////////// + public: + // these were formerly private, but they have a fairly well-defined API, so let's make them public 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 ); - operation_result apply_operation( transaction_evaluation_state& eval_state, const operation& op ); - ///Steps involved in applying a new block ///@{ diff --git a/libraries/chain/include/graphene/chain/exceptions.hpp b/libraries/chain/include/graphene/chain/exceptions.hpp index e739b761..2e07ca26 100644 --- a/libraries/chain/include/graphene/chain/exceptions.hpp +++ b/libraries/chain/include/graphene/chain/exceptions.hpp @@ -104,6 +104,9 @@ namespace graphene { namespace chain { GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_create ); GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_create, 1, "Exceeds max authority fan-out" ) GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_create, 2, "Auth account not found" ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_incorrect_issuer, account_create, 3, "Incorrect issuer specified for account" ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_already_exists, account_create, 4, "Cannot create buyback for asset which already has buyback" ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_too_many_markets, account_create, 5, "Too many buyback markets" ) GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_update ); GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_update, 1, "Exceeds max authority fan-out" ) diff --git a/libraries/chain/include/graphene/chain/is_authorized_asset.hpp b/libraries/chain/include/graphene/chain/is_authorized_asset.hpp index 5062136e..3d99ae0f 100644 --- a/libraries/chain/include/graphene/chain/is_authorized_asset.hpp +++ b/libraries/chain/include/graphene/chain/is_authorized_asset.hpp @@ -43,6 +43,7 @@ bool _is_authorized_asset(const database& d, const account_object& acct, const a inline bool is_authorized_asset(const database& d, const account_object& acct, const asset_object& asset_obj) { bool fast_check = !(asset_obj.options.flags & white_list); + fast_check &= !(acct.allowed_assets.valid()); if( fast_check ) return true; diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index 987568b1..4e5fdaed 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -23,6 +23,7 @@ */ #pragma once #include +#include #include #include #include @@ -69,6 +70,7 @@ namespace graphene { namespace chain { optional< void_t > null_ext; optional< special_authority > owner_special_authority; optional< special_authority > active_special_authority; + optional< buyback_account_options > buyback_options; }; struct fee_parameters_type @@ -98,6 +100,14 @@ namespace graphene { namespace chain { account_id_type fee_payer()const { return registrar; } void validate()const; share_type calculate_fee(const fee_parameters_type& )const; + + void get_required_active_authorities( flat_set& a )const + { + // registrar should be required anyway as it is the fee_payer(), but we insert it here just to be sure + a.insert( registrar ); + if( extensions.value.buyback_options.valid() ) + a.insert( extensions.value.buyback_options->asset_to_buy_issuer ); + } }; /** @@ -258,7 +268,7 @@ FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listi 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) ) +FC_REFLECT(graphene::chain::account_create_operation::ext, (null_ext)(owner_special_authority)(active_special_authority)(buyback_options) ) FC_REFLECT( graphene::chain::account_create_operation, (fee)(registrar) (referrer)(referrer_percent) diff --git a/libraries/chain/include/graphene/chain/protocol/authority.hpp b/libraries/chain/include/graphene/chain/protocol/authority.hpp index e144f6c3..b6ef60d7 100644 --- a/libraries/chain/include/graphene/chain/protocol/authority.hpp +++ b/libraries/chain/include/graphene/chain/protocol/authority.hpp @@ -109,6 +109,11 @@ namespace graphene { namespace chain { uint32_t num_auths()const { return account_auths.size() + key_auths.size() + address_auths.size(); } void clear() { account_auths.clear(); key_auths.clear(); } + static authority null_authority() + { + return authority( 1, GRAPHENE_NULL_ACCOUNT, 1 ); + } + uint32_t weight_threshold = 0; flat_map account_auths; flat_map key_auths; diff --git a/libraries/chain/include/graphene/chain/protocol/buyback.hpp b/libraries/chain/include/graphene/chain/protocol/buyback.hpp new file mode 100644 index 00000000..6adad52d --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/buyback.hpp @@ -0,0 +1,52 @@ +/* + * 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 + +namespace graphene { namespace chain { + +struct buyback_account_options +{ + /** + * The asset to buy. + */ + asset_id_type asset_to_buy; + + /** + * Issuer of the asset. Must sign the transaction, must match issuer + * of specified asset. + */ + account_id_type asset_to_buy_issuer; + + /** + * What assets the account is willing to buy with. + * Other assets will just sit there since the account has null authority. + */ + flat_set< asset_id_type > markets; +}; + +} } + +FC_REFLECT( graphene::chain::buyback_account_options, (asset_to_buy)(asset_to_buy_issuer)(markets) ); diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 009a7980..fac66d7a 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -153,7 +153,8 @@ namespace graphene { namespace chain { impl_chain_property_object_type, impl_witness_schedule_object_type, impl_budget_record_object_type, - impl_special_authority_object_type + impl_special_authority_object_type, + impl_buyback_object_type }; //typedef fc::unsigned_int object_id_type; @@ -203,6 +204,7 @@ namespace graphene { namespace chain { class witness_schedule_object; class budget_record_object; class special_authority_object; + class buyback_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; @@ -221,6 +223,7 @@ namespace graphene { namespace chain { 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; typedef fc::array symbol_type; typedef fc::ripemd160 block_id_type; @@ -351,6 +354,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type, (impl_witness_schedule_object_type) (impl_budget_record_object_type) (impl_special_authority_object_type) + (impl_buyback_object_type) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) diff --git a/libraries/chain/is_authorized_asset.cpp b/libraries/chain/is_authorized_asset.cpp index 8168a505..6ec9643d 100644 --- a/libraries/chain/is_authorized_asset.cpp +++ b/libraries/chain/is_authorized_asset.cpp @@ -37,6 +37,13 @@ bool _is_authorized_asset( const account_object& acct, const asset_object& asset_obj) { + if( acct.allowed_assets.valid() ) + { + if( acct.allowed_assets->find( asset_obj.id ) == acct.allowed_assets->end() ) + return false; + // must still pass other checks even if it is in allowed_assets + } + for( const auto id : acct.blacklisting_accounts ) { if( asset_obj.options.blacklist_authorities.find(id) != asset_obj.options.blacklist_authorities.end() ) diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index 1c3fa007..b3ad9e00 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -190,6 +190,19 @@ void account_create_operation::validate()const validate_special_authority( *extensions.value.owner_special_authority ); if( extensions.value.active_special_authority.valid() ) validate_special_authority( *extensions.value.active_special_authority ); + if( extensions.value.buyback_options.valid() ) + { + FC_ASSERT( !(extensions.value.owner_special_authority.valid()) ); + FC_ASSERT( !(extensions.value.active_special_authority.valid()) ); + FC_ASSERT( owner == authority::null_authority() ); + FC_ASSERT( active == authority::null_authority() ); + size_t n_markets = extensions.value.buyback_options->markets.size(); + FC_ASSERT( n_markets > 0 ); + for( const asset_id_type m : extensions.value.buyback_options->markets ) + { + FC_ASSERT( m != extensions.value.buyback_options->asset_to_buy ); + } + } } From 1a51d873293e0ecc8fd198daabec0e6ef7b43e14 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 11 Feb 2016 04:59:35 -0500 Subject: [PATCH 30/43] Implement test for buyback accounts #538 --- tests/tests/operation_tests2.cpp | 137 +++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 7adf3a1c..6275a64e 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -1483,4 +1483,141 @@ BOOST_AUTO_TEST_CASE( top_n_special ) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( buyback ) +{ + ACTORS( (alice)(bob)(chloe)(dan)(izzy)(philbin) ); + upgrade_to_lifetime_member(philbin_id); + + generate_blocks( HARDFORK_538_TIME ); + generate_blocks( HARDFORK_555_TIME ); + + try + { + { + // + // Izzy (issuer) + // Alice, Bob, Chloe, Dan (ABCD) + // Rex (recycler -- buyback account) + // Philbin (registrar) + // + + asset_id_type nono_id = create_user_issued_asset( "NONO", izzy_id(db), 0 ).id; + asset_id_type buyme_id = create_user_issued_asset( "BUYME", izzy_id(db), 0 ).id; + + // Create a buyback account + account_id_type rex_id; + { + buyback_account_options bbo; + bbo.asset_to_buy = buyme_id; + bbo.asset_to_buy_issuer = izzy_id; + bbo.markets.emplace( asset_id_type() ); + account_create_operation create_op = make_account( "rex" ); + create_op.registrar = philbin_id; + create_op.extensions.value.buyback_options = bbo; + create_op.owner = authority::null_authority(); + create_op.active = authority::null_authority(); + + // Let's break it... + + signed_transaction tx; + tx.operations.push_back( create_op ); + set_expiration( db, tx ); + + tx.operations.back().get< account_create_operation >().extensions.value.buyback_options->asset_to_buy_issuer = alice_id; + sign( tx, alice_private_key ); + 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 ); + + tx.signatures.clear(); + 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 ); + sign( tx, izzy_private_key ); + + // OK + processed_transaction ptx = db.push_transaction( tx ); + rex_id = ptx.operation_results.back().get< object_id_type >(); + + // Try to create another account rex2 which is bbo on same asset + tx.signatures.clear(); + 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 ); + } + + // issue some BUYME to Alice + // we need to set_expiration() before issue_uia() because the latter doens't call it #11 + set_expiration( db, trx ); // #11 + issue_uia( alice_id, asset( 1000, buyme_id ) ); + issue_uia( alice_id, asset( 1000, nono_id ) ); + + // Alice wants to sell 100 BUYME for 1000 BTS, a middle price. + limit_order_id_type order_id_mid = create_sell_order( alice_id, asset( 100, buyme_id ), asset( 1000, asset_id_type() ) )->id; + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + // no success because buyback has none for sale + BOOST_CHECK( order_id_mid(db).for_sale == 100 ); + + // but we can send some to buyback + fund( rex_id(db), asset( 100, asset_id_type() ) ); + // no action until next maint + BOOST_CHECK( order_id_mid(db).for_sale == 100 ); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + // partial fill, Alice now sells 90 BUYME for 900 BTS. + BOOST_CHECK( order_id_mid(db).for_sale == 90 ); + + // TODO check burn amount + + // aagh more state in trx + set_expiration( db, trx ); // #11 + + // Selling 10 BUYME for 50 BTS, a low price. + limit_order_id_type order_id_low = create_sell_order( alice_id, asset( 10, buyme_id ), asset( 50, asset_id_type() ) )->id; + // Selling 10 BUYME for 150 BTS, a high price. + limit_order_id_type order_id_high = create_sell_order( alice_id, asset( 10, buyme_id ), asset( 150, asset_id_type() ) )->id; + + fund( rex_id(db), asset( 250, asset_id_type() ) ); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + BOOST_CHECK( db.find( order_id_low ) == nullptr ); + BOOST_CHECK( db.find( order_id_mid ) != nullptr ); + BOOST_CHECK( db.find( order_id_high ) != nullptr ); + + // 250 CORE in rex 90 BUYME in mid order 10 BUYME in low order + // 50 CORE goes to low order, buy 10 for 50 CORE + // 200 CORE goes to mid order, buy 20 for 200 CORE + // 70 BUYME in mid order 0 BUYME in low order + + idump( (order_id_mid(db)) ); + BOOST_CHECK( order_id_mid(db).for_sale == 70 ); + BOOST_CHECK( order_id_high(db).for_sale == 10 ); + + BOOST_CHECK( get_balance( rex_id, asset_id_type() ) == 0 ); + + // clear out the books -- 700 left on mid order, 150 left on high order, so 2000 BTS should result in 1150 left over + + fund( rex_id(db), asset( 2000, asset_id_type() ) ); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + idump( (get_balance( rex_id, asset_id_type() )) ); + + BOOST_CHECK( get_balance( rex_id, asset_id_type() ) == 1150 ); + + GRAPHENE_CHECK_THROW( transfer( alice_id, rex_id, asset( 1, nono_id ) ), fc::exception ); + // TODO: Check cancellation works for account which is BTS-restricted + } + + } FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_SUITE_END() From 146c0c4d8e24ce20f375b4c978995901464a8dae Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Thu, 17 Dec 2015 11:32:22 -0500 Subject: [PATCH 31/43] Clean up some non-determisitic behavior, maybe fix #485 1. Replace ordered_non_unique indexes with composite keys / ordered_unique, using object_id as tiebreaker. 2. Make some casts more explicit. This commit was rebased by theoreticalbts due to conflicts with the patches for #466 #562 including bugfixes - Fix flipped comparison operator - Implement operator> and operator!= for object_id_type --- libraries/app/database_api.cpp | 4 +- libraries/chain/db_init.cpp | 18 +++---- .../include/graphene/chain/asset_object.hpp | 7 ++- .../include/graphene/chain/market_object.hpp | 30 +++++++---- .../chain/withdraw_permission_object.hpp | 22 ++++++-- libraries/chain/protocol/asset_ops.cpp | 2 +- libraries/chain/protocol/fee_schedule.cpp | 2 +- .../db/include/graphene/db/object_id.hpp | 37 ++++++++++++-- libraries/wallet/wallet.cpp | 8 +-- tests/common/database_fixture.cpp | 8 +-- tests/tests/basic_tests.cpp | 51 ++++++++++--------- tests/tests/operation_tests.cpp | 6 +-- tests/tests/operation_tests2.cpp | 4 +- tests/tests/uia_tests.cpp | 2 +- 14 files changed, 134 insertions(+), 67 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 59b03acd..ddd63832 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -960,8 +960,8 @@ vector database_api_impl::get_margin_positions( const account { const auto& idx = _db.get_index_type(); const auto& aidx = idx.indices().get(); - auto start = aidx.lower_bound( boost::make_tuple( id, 0 ) ); - auto end = aidx.lower_bound( boost::make_tuple( id+1, 0 ) ); + auto start = aidx.lower_bound( boost::make_tuple( id, asset_id_type(0) ) ); + auto end = aidx.lower_bound( boost::make_tuple( id+1, asset_id_type(0) ) ); vector result; while( start != end ) { diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index f04c370e..85c3cc92 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -316,7 +316,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.statistics = create([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; - a.registrar = a.lifetime_referrer = a.referrer = id; + a.registrar = a.lifetime_referrer = a.referrer = account_id_type(id); a.membership_expiration_date = time_point_sec::maximum(); a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; @@ -339,9 +339,9 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.options.issuer_permissions = 0; a.issuer = GRAPHENE_NULL_ACCOUNT; a.options.core_exchange_rate.base.amount = 1; - a.options.core_exchange_rate.base.asset_id = 0; + a.options.core_exchange_rate.base.asset_id = asset_id_type(0); a.options.core_exchange_rate.quote.amount = 1; - a.options.core_exchange_rate.quote.asset_id = 0; + a.options.core_exchange_rate.quote.asset_id = asset_id_type(0); a.dynamic_asset_data_id = dyn_asset.id; }); assert( asset_id_type(core_asset.id) == asset().asset_id ); @@ -364,9 +364,9 @@ void database::init_genesis(const genesis_state_type& genesis_state) a.options.issuer_permissions = 0; a.issuer = GRAPHENE_NULL_ACCOUNT; a.options.core_exchange_rate.base.amount = 1; - a.options.core_exchange_rate.base.asset_id = 0; + a.options.core_exchange_rate.base.asset_id = asset_id_type(0); a.options.core_exchange_rate.quote.amount = 1; - a.options.core_exchange_rate.quote.asset_id = 0; + a.options.core_exchange_rate.quote.asset_id = asset_id_type(0); a.dynamic_asset_data_id = dyn_asset.id; }); FC_ASSERT( asset_obj.get_id() == asset_id_type(id) ); @@ -491,7 +491,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); }); - total_supplies[ 0 ] += collateral_rec.collateral; + total_supplies[ asset_id_type(0) ] += collateral_rec.collateral; total_debts[ new_asset_id ] += collateral_rec.debt; ++collateral_holder_number; } @@ -555,13 +555,13 @@ void database::init_genesis(const genesis_state_type& genesis_state) total_supplies[ asset_id ] += vest.amount; } - if( total_supplies[ 0 ] > 0 ) + if( total_supplies[ asset_id_type(0) ] > 0 ) { adjust_balance(GRAPHENE_COMMITTEE_ACCOUNT, -get_balance(GRAPHENE_COMMITTEE_ACCOUNT,{})); } else { - total_supplies[ 0 ] = GRAPHENE_MAX_SHARE_SUPPLY; + total_supplies[ asset_id_type(0) ] = GRAPHENE_MAX_SHARE_SUPPLY; } const auto& idx = get_index_type().indices().get(); @@ -645,7 +645,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) modify(get_global_properties(), [&](global_property_object& p) { for( uint32_t i = 1; i <= genesis_state.initial_active_witnesses; ++i ) { - p.active_witnesses.insert(i); + p.active_witnesses.insert(witness_id_type(i)); } }); diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index ea127026..ea7e6fa4 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -233,7 +233,12 @@ namespace graphene { namespace chain { indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, ordered_unique< tag, member >, - ordered_non_unique< tag, const_mem_fun > + ordered_unique< tag, + composite_key< asset_object, + const_mem_fun, + member< object, object_id_type, &object::id > + > + > > > asset_object_multi_index_type; typedef generic_index asset_index; diff --git a/libraries/chain/include/graphene/chain/market_object.hpp b/libraries/chain/include/graphene/chain/market_object.hpp index 8bf8fbcb..1bc2fe3e 100644 --- a/libraries/chain/include/graphene/chain/market_object.hpp +++ b/libraries/chain/include/graphene/chain/market_object.hpp @@ -72,9 +72,13 @@ struct by_account; typedef multi_index_container< limit_order_object, indexed_by< - ordered_unique< tag, - member< object, object_id_type, &object::id > >, - ordered_non_unique< tag, member< limit_order_object, time_point_sec, &limit_order_object::expiration> >, + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, + composite_key< limit_order_object, + member< limit_order_object, time_point_sec, &limit_order_object::expiration>, + member< object, object_id_type, &object::id> + > + >, ordered_unique< tag, composite_key< limit_order_object, member< limit_order_object, price, &limit_order_object::sell_price>, @@ -82,7 +86,12 @@ typedef multi_index_container< >, composite_key_compare< std::greater, std::less > >, - ordered_non_unique< tag, member> + ordered_unique< tag, + composite_key< limit_order_object, + member, + member + > + > > > limit_order_multi_index_type; @@ -168,19 +177,22 @@ typedef multi_index_container< force_settlement_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag, - member + ordered_unique< tag, + composite_key< force_settlement_object, + member, + member< object, object_id_type, &object::id > + > >, - ordered_non_unique< tag, + ordered_unique< tag, composite_key< force_settlement_object, const_mem_fun, - member + member, + member< object, object_id_type, &object::id > > > > > force_settlement_object_multi_index_type; - typedef generic_index call_order_index; typedef generic_index force_settlement_index; diff --git a/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp b/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp index a26a73f9..000573bd 100644 --- a/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp +++ b/libraries/chain/include/graphene/chain/withdraw_permission_object.hpp @@ -24,6 +24,7 @@ #pragma once #include #include +#include namespace graphene { namespace chain { @@ -78,9 +79,24 @@ namespace graphene { namespace chain { withdraw_permission_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag, member >, - ordered_non_unique< tag, member >, - ordered_non_unique< tag, member > + ordered_unique< tag, + composite_key< withdraw_permission_object, + member, + member< object, object_id_type, &object::id > + > + >, + ordered_unique< tag, + composite_key< withdraw_permission_object, + member, + member< object, object_id_type, &object::id > + > + >, + ordered_unique< tag, + composite_key< withdraw_permission_object, + member, + member< object, object_id_type, &object::id > + > + > > > withdraw_permission_object_multi_index_type; diff --git a/libraries/chain/protocol/asset_ops.cpp b/libraries/chain/protocol/asset_ops.cpp index 2f666532..1626a2a9 100644 --- a/libraries/chain/protocol/asset_ops.cpp +++ b/libraries/chain/protocol/asset_ops.cpp @@ -158,7 +158,7 @@ void asset_issue_operation::validate()const FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( asset_to_issue.amount.value <= GRAPHENE_MAX_SHARE_SUPPLY ); FC_ASSERT( asset_to_issue.amount.value > 0 ); - FC_ASSERT( asset_to_issue.asset_id != 0 ); + FC_ASSERT( asset_to_issue.asset_id != asset_id_type(0) ); } void asset_fund_fee_pool_operation::validate() const diff --git a/libraries/chain/protocol/fee_schedule.cpp b/libraries/chain/protocol/fee_schedule.cpp index f0d18125..993df085 100644 --- a/libraries/chain/protocol/fee_schedule.cpp +++ b/libraries/chain/protocol/fee_schedule.cpp @@ -130,7 +130,7 @@ namespace graphene { namespace chain { scaled /= GRAPHENE_100_PERCENT; FC_ASSERT( scaled <= GRAPHENE_MAX_SHARE_SUPPLY ); //idump( (base_value)(scaled)(core_exchange_rate) ); - auto result = asset( scaled.to_uint64(), 0 ) * core_exchange_rate; + auto result = asset( scaled.to_uint64(), asset_id_type(0) ) * core_exchange_rate; //FC_ASSERT( result * core_exchange_rate >= asset( scaled.to_uint64()) ); while( result * core_exchange_rate < asset( scaled.to_uint64()) ) diff --git a/libraries/db/include/graphene/db/object_id.hpp b/libraries/db/include/graphene/db/object_id.hpp index e9f40996..02304fc7 100644 --- a/libraries/db/include/graphene/db/object_id.hpp +++ b/libraries/db/include/graphene/db/object_id.hpp @@ -51,12 +51,18 @@ namespace graphene { namespace db { uint16_t space_type()const { return number >> 48; } uint64_t instance()const { return number & GRAPHENE_DB_MAX_INSTANCE_ID; } bool is_null()const { return number == 0; } - operator uint64_t()const { return number; } + explicit operator uint64_t()const { return number; } friend bool operator == ( const object_id_type& a, const object_id_type& b ) { return a.number == b.number; } + + friend bool operator != ( const object_id_type& a, const object_id_type& b ) + { + return a.number != b.number; + } + object_id_type& operator++(int) { ++number; return *this; } object_id_type& operator++() { ++number; return *this; } @@ -68,11 +74,16 @@ namespace graphene { namespace db { } friend size_t hash_value( object_id_type v ) { return std::hash()(v.number); } - friend bool operator < ( const object_id_type& a, const object_id_type& b ) + friend bool operator< ( const object_id_type& a, const object_id_type& b ) { return a.number < b.number; } + friend bool operator> ( const object_id_type& a, const object_id_type& b ) + { + return a.number > b.number; + } + template< typename T > bool is() const { @@ -106,7 +117,7 @@ namespace graphene { namespace db { object_id(){} object_id( unsigned_int i ):instance(i){} - object_id( uint64_t i ):instance(i) + explicit object_id( uint64_t i ):instance(i) { FC_ASSERT( (i >> 48) == 0 ); } @@ -118,11 +129,25 @@ namespace graphene { namespace db { friend object_id operator+(const object_id a, int delta ) { return object_id( uint64_t(a.instance.value+delta) ); } operator object_id_type()const { return object_id_type( SpaceID, TypeID, instance.value ); } - operator uint64_t()const { return object_id_type( *this ).number; } + explicit operator uint64_t()const { return object_id_type( *this ).number; } template const T& operator()(const DB& db)const { return db.get(*this); } + friend bool operator != ( const object_id& a, const object_id& b ) + { + return a.instance != b.instance; + } + friend bool operator != ( const object_id_type& a, const object_id& b ) + { + return a != object_id_type(b); + } + friend bool operator != ( const object_id& a, const object_id_type& b ) + { + return object_id_type(a) != b; + } + + friend bool operator == ( const object_id& a, const object_id& b ) { return a.instance == b.instance; @@ -139,6 +164,10 @@ namespace graphene { namespace db { { return a.instance.value < b.instance.value; } + friend bool operator > ( const object_id& a, const object_id& b ) + { + return a.instance.value > b.instance.value; + } friend size_t hash_value( object_id v ) { return std::hash()(v.instance.value); } unsigned_int instance; diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 8ca7d7b9..50cf634c 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2324,7 +2324,7 @@ public: asset_options opts; opts.flags &= ~(white_list | disable_force_settle | global_settle); opts.issuer_permissions = opts.flags; - opts.core_exchange_rate = price(asset(1), asset(1,1)); + opts.core_exchange_rate = price(asset(1), asset(1,asset_id_type(1))); create_asset(get_account(creator).name, symbol, 2, opts, {}, true); } @@ -2333,7 +2333,7 @@ public: asset_options opts; opts.flags &= ~white_list; opts.issuer_permissions = opts.flags; - opts.core_exchange_rate = price(asset(1), asset(1,1)); + opts.core_exchange_rate = price(asset(1), asset(1,asset_id_type(1))); bitasset_options bopts; create_asset(get_account(creator).name, symbol, 2, opts, bopts, true); } @@ -3644,8 +3644,8 @@ vector wallet_api::get_blind_balances( string key_or_label ) auto pub_key = get_public_key( key_or_label ); auto& to_asset_used_idx = my->_wallet.blind_receipts.get(); - auto start = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,0,false) ); - auto end = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,uint32_t(0xffffffff),true) ); + auto start = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(0),false) ); + auto end = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(uint32_t(0xffffffff)),true) ); while( start != end ) { if( !start->used ) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 7a9e7ce4..846e4ec2 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -439,7 +439,7 @@ const asset_object& database_fixture::create_bitasset( flags |= witness_fed_asset; creator.common_options.issuer_permissions = flags; creator.common_options.flags = flags & ~global_settle; - creator.common_options.core_exchange_rate = price({asset(1,1),asset(1)}); + creator.common_options.core_exchange_rate = price({asset(1,asset_id_type(1)),asset(1)}); creator.bitasset_opts = bitasset_options(); trx.operations.push_back(std::move(creator)); trx.validate(); @@ -466,7 +466,7 @@ const asset_object& database_fixture::create_prediction_market( creator.common_options.flags = flags & ~global_settle; if( issuer == GRAPHENE_WITNESS_ACCOUNT ) creator.common_options.flags |= witness_fed_asset; - creator.common_options.core_exchange_rate = price({asset(1,1),asset(1)}); + creator.common_options.core_exchange_rate = price({asset(1,asset_id_type(1)),asset(1)}); creator.bitasset_opts = bitasset_options(); creator.is_prediction_market = true; trx.operations.push_back(std::move(creator)); @@ -484,7 +484,7 @@ const asset_object& database_fixture::create_user_issued_asset( const string& na creator.symbol = name; creator.common_options.max_supply = 0; creator.precision = 2; - creator.common_options.core_exchange_rate = price({asset(1,1),asset(1)}); + creator.common_options.core_exchange_rate = price({asset(1,asset_id_type(1)),asset(1)}); creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; creator.common_options.flags = charge_market_fee; creator.common_options.issuer_permissions = charge_market_fee; @@ -503,7 +503,7 @@ const asset_object& database_fixture::create_user_issued_asset( const string& na creator.symbol = name; creator.common_options.max_supply = 0; creator.precision = 2; - creator.common_options.core_exchange_rate = price({asset(1,1),asset(1)}); + creator.common_options.core_exchange_rate = price({asset(1,asset_id_type(1)),asset(1)}); creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; creator.common_options.flags = flags; creator.common_options.issuer_permissions = flags; diff --git a/tests/tests/basic_tests.cpp b/tests/tests/basic_tests.cpp index 46249801..2390a7c6 100644 --- a/tests/tests/basic_tests.cpp +++ b/tests/tests/basic_tests.cpp @@ -138,28 +138,33 @@ BOOST_AUTO_TEST_CASE( valid_symbol_test ) BOOST_AUTO_TEST_CASE( price_test ) { - BOOST_CHECK( price::max(0,1) > price::min(0,1) ); - BOOST_CHECK( price::max(1,0) > price::min(1,0) ); - BOOST_CHECK( price::max(0,1) >= price::min(0,1) ); - BOOST_CHECK( price::max(1,0) >= price::min(1,0) ); - BOOST_CHECK( price::max(0,1) >= price::max(0,1) ); - BOOST_CHECK( price::max(1,0) >= price::max(1,0) ); - BOOST_CHECK( price::min(0,1) < price::max(0,1) ); - BOOST_CHECK( price::min(1,0) < price::max(1,0) ); - BOOST_CHECK( price::min(0,1) <= price::max(0,1) ); - BOOST_CHECK( price::min(1,0) <= price::max(1,0) ); - BOOST_CHECK( price::min(0,1) <= price::min(0,1) ); - BOOST_CHECK( price::min(1,0) <= price::min(1,0) ); - BOOST_CHECK( price::min(1,0) != price::max(1,0) ); - BOOST_CHECK( ~price::max(0,1) != price::min(0,1) ); - BOOST_CHECK( ~price::min(0,1) != price::max(0,1) ); - BOOST_CHECK( ~price::max(0,1) == price::min(1,0) ); - BOOST_CHECK( ~price::min(0,1) == price::max(1,0) ); - BOOST_CHECK( ~price::max(0,1) < ~price::min(0,1) ); - BOOST_CHECK( ~price::max(0,1) <= ~price::min(0,1) ); - price a(asset(1), asset(2,1)); - price b(asset(2), asset(2,1)); - price c(asset(1), asset(2,1)); + auto price_max = []( uint32_t a, uint32_t b ) + { return price::max( asset_id_type(a), asset_id_type(b) ); }; + auto price_min = []( uint32_t a, uint32_t b ) + { return price::min( asset_id_type(a), asset_id_type(b) ); }; + + BOOST_CHECK( price_max(0,1) > price_min(0,1) ); + BOOST_CHECK( price_max(1,0) > price_min(1,0) ); + BOOST_CHECK( price_max(0,1) >= price_min(0,1) ); + BOOST_CHECK( price_max(1,0) >= price_min(1,0) ); + BOOST_CHECK( price_max(0,1) >= price_max(0,1) ); + BOOST_CHECK( price_max(1,0) >= price_max(1,0) ); + BOOST_CHECK( price_min(0,1) < price_max(0,1) ); + BOOST_CHECK( price_min(1,0) < price_max(1,0) ); + BOOST_CHECK( price_min(0,1) <= price_max(0,1) ); + BOOST_CHECK( price_min(1,0) <= price_max(1,0) ); + BOOST_CHECK( price_min(0,1) <= price_min(0,1) ); + BOOST_CHECK( price_min(1,0) <= price_min(1,0) ); + BOOST_CHECK( price_min(1,0) != price_max(1,0) ); + BOOST_CHECK( ~price_max(0,1) != price_min(0,1) ); + BOOST_CHECK( ~price_min(0,1) != price_max(0,1) ); + BOOST_CHECK( ~price_max(0,1) == price_min(1,0) ); + BOOST_CHECK( ~price_min(0,1) == price_max(1,0) ); + BOOST_CHECK( ~price_max(0,1) < ~price_min(0,1) ); + BOOST_CHECK( ~price_max(0,1) <= ~price_min(0,1) ); + price a(asset(1), asset(2,asset_id_type(1))); + price b(asset(2), asset(2,asset_id_type(1))); + price c(asset(1), asset(2,asset_id_type(1))); BOOST_CHECK(a < b); BOOST_CHECK(b > a); BOOST_CHECK(a == c); @@ -168,7 +173,7 @@ BOOST_AUTO_TEST_CASE( price_test ) price_feed dummy; dummy.maintenance_collateral_ratio = 1002; dummy.maximum_short_squeeze_ratio = 1234; - dummy.settlement_price = price(asset(1000), asset(2000, 1)); + dummy.settlement_price = price(asset(1000), asset(2000, asset_id_type(1))); price_feed dummy2 = dummy; BOOST_CHECK(dummy == dummy2); } diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 435aac48..7b3867d7 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -48,8 +48,8 @@ BOOST_FIXTURE_TEST_SUITE( operation_tests, database_fixture ) BOOST_AUTO_TEST_CASE( feed_limit_logic_test ) { try { - asset usd(1000,1); - asset core(1000,0); + asset usd(1000,asset_id_type(1)); + asset core(1000,asset_id_type(0)); price_feed feed; feed.settlement_price = usd / core; @@ -695,7 +695,7 @@ BOOST_AUTO_TEST_CASE( create_uia ) creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/ creator.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK; creator.common_options.flags = charge_market_fee; - creator.common_options.core_exchange_rate = price({asset(2),asset(1,1)}); + creator.common_options.core_exchange_rate = price({asset(2),asset(1,asset_id_type(1))}); trx.operations.push_back(std::move(creator)); PUSH_TX( db, trx, ~0 ); diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 7adf3a1c..bddbe5ae 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_create ) REQUIRE_OP_VALIDATION_FAILURE(op, periods_until_expiration, 0); REQUIRE_OP_VALIDATION_FAILURE(op, withdraw_from_account, dan_id); REQUIRE_OP_VALIDATION_FAILURE(op, withdrawal_period_sec, 0); - REQUIRE_THROW_WITH_VALUE(op, withdrawal_limit, asset(10, 10)); + REQUIRE_THROW_WITH_VALUE(op, withdrawal_limit, asset(10, asset_id_type(10))); REQUIRE_THROW_WITH_VALUE(op, authorized_account, account_id_type(1000)); REQUIRE_THROW_WITH_VALUE(op, period_start_time, fc::time_point_sec(10000)); REQUIRE_THROW_WITH_VALUE(op, withdrawal_period_sec, 1); @@ -281,7 +281,7 @@ BOOST_AUTO_TEST_CASE( withdraw_permission_update ) trx.operations.push_back(op); REQUIRE_THROW_WITH_VALUE(op, periods_until_expiration, 0); REQUIRE_THROW_WITH_VALUE(op, withdrawal_period_sec, 0); - REQUIRE_THROW_WITH_VALUE(op, withdrawal_limit, asset(1, 12)); + REQUIRE_THROW_WITH_VALUE(op, withdrawal_limit, asset(1, asset_id_type(12))); REQUIRE_THROW_WITH_VALUE(op, withdrawal_limit, asset(0)); REQUIRE_THROW_WITH_VALUE(op, withdraw_from_account, account_id_type(0)); REQUIRE_THROW_WITH_VALUE(op, authorized_account, account_id_type(0)); diff --git a/tests/tests/uia_tests.cpp b/tests/tests/uia_tests.cpp index 8876f23e..ddb20d2d 100644 --- a/tests/tests/uia_tests.cpp +++ b/tests/tests/uia_tests.cpp @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE( create_advanced_uia ) creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/ creator.common_options.issuer_permissions = charge_market_fee|white_list|override_authority|transfer_restricted|disable_confidential; creator.common_options.flags = charge_market_fee|white_list|override_authority|disable_confidential; - creator.common_options.core_exchange_rate = price({asset(2),asset(1,1)}); + 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()}; trx.operations.push_back(std::move(creator)); PUSH_TX( db, trx, ~0 ); From ead3f951a266c519d90e8920c1bea55338f5ce7b Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Fri, 12 Feb 2016 00:10:52 -0500 Subject: [PATCH 32/43] Remove type_serializer, re-implement minimal functionality needed by cli_wallet #553 --- libraries/db/CMakeLists.txt | 2 +- libraries/db/include/graphene/db/index.hpp | 1 - .../include/graphene/db/type_serializer.hpp | 306 ------------------ libraries/db/type_serializer.cpp | 86 ----- .../include/graphene/wallet/reflect_util.hpp | 18 +- 5 files changed, 18 insertions(+), 395 deletions(-) delete mode 100644 libraries/db/include/graphene/db/type_serializer.hpp delete mode 100644 libraries/db/type_serializer.cpp diff --git a/libraries/db/CMakeLists.txt b/libraries/db/CMakeLists.txt index 82ab91b8..986fe9cb 100644 --- a/libraries/db/CMakeLists.txt +++ b/libraries/db/CMakeLists.txt @@ -1,5 +1,5 @@ file(GLOB HEADERS "include/graphene/db/*.hpp") -add_library( graphene_db undo_database.cpp index.cpp object_database.cpp type_serializer.cpp ${HEADERS} ) +add_library( graphene_db undo_database.cpp index.cpp object_database.cpp ${HEADERS} ) target_link_libraries( graphene_db fc ) target_include_directories( graphene_db PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index 06c9fe61..6c1c975e 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -23,7 +23,6 @@ */ #pragma once #include -#include #include #include #include diff --git a/libraries/db/include/graphene/db/type_serializer.hpp b/libraries/db/include/graphene/db/type_serializer.hpp deleted file mode 100644 index 87dbb968..00000000 --- a/libraries/db/include/graphene/db/type_serializer.hpp +++ /dev/null @@ -1,306 +0,0 @@ -/* - * 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. - */ -#include -#include -#include -#include -#include - -namespace graphene { namespace db { -using std::set; -using std::map; -using fc::time_point_sec; -using fc::time_point; -using std::string; - -std::set& processed_types(); -std::stringstream& current_stream( std::unique_ptr&& s = std::unique_ptr() ); -string remove_tail_if( const string& str, char c, const string& match ); -string remove_namespace_if( const string& str, const string& match ); -string remove_namespace( string str ); - -template -void generate_serializer(); -template -void register_serializer(); - -extern map st; -extern vector> serializers; - -bool register_serializer( const string& name, std::function sr ); - -template struct js_name { static std::string name(){ return remove_namespace(fc::get_typename::name()); }; }; - -template -struct js_name> -{ - static std::string name(){ return "fixed_array "+ fc::to_string(N) + ", " + remove_namespace(fc::get_typename::name()); }; -}; -template struct js_name> { static std::string name(){ return "bytes "+ fc::to_string(N); }; }; -template struct js_name> { static std::string name(){ return "bytes "+ fc::to_string(N); }; }; -template struct js_name< fc::optional > { static std::string name(){ return "optional " + js_name::name(); } }; -template<> struct js_name< object_id_type > { static std::string name(){ return "object_id_type"; } }; -template struct js_name< fc::flat_set > { static std::string name(){ return "set " + js_name::name(); } }; -template struct js_name< std::vector > { static std::string name(){ return "array " + js_name::name(); } }; -template struct js_name< fc::safe > { static std::string name(){ return js_name::name(); } }; - - -template<> struct js_name< std::vector > { static std::string name(){ return "bytes()"; } }; -//template<> struct js_name< op_wrapper > { static std::string name(){ return "operation "; } }; -template<> struct js_name { static std::string name(){ return "bytes 20"; } }; -template<> struct js_name { static std::string name(){ return "bytes 28"; } }; -template<> struct js_name { static std::string name(){ return "varuint32"; } }; -template<> struct js_name { static std::string name(){ return "varint32"; } }; -template<> struct js_name< time_point_sec > { static std::string name(){ return "time_point_sec"; } }; - -template -struct js_name > -{ - static std::string name(){ - return "protocol_id_type \"" + remove_namespace(fc::get_typename::name()) + "\""; - }; -}; - - -template struct js_name< std::set > { static std::string name(){ return "set " + js_name::name(); } }; - -template -struct js_name< std::map > { static std::string name(){ return "map (" + js_name::name() + "), (" + js_name::name() +")"; } }; - -template -struct js_name< fc::flat_map > { static std::string name(){ return "map (" + js_name::name() + "), (" + js_name::name() +")"; } }; - - -template struct js_sv_name; - -template struct js_sv_name -{ static std::string name(){ return "\n " + js_name::name(); } }; - -template -struct js_sv_name { static std::string name(){ return "\n " + js_name::name() +" " + js_sv_name::name(); } }; - - -template -struct js_name< fc::static_variant > -{ - static std::string name( std::string n = ""){ - static const std::string name = n; - if( name == "" ) - return "static_variant [" + js_sv_name::name() + "\n]"; - else return name; - } -}; - - -template::is_defined::value> -struct serializer; - - -struct register_type_visitor -{ - typedef void result_type; - - template - result_type operator()( const Type& op )const { serializer::init(); } -}; - -class register_member_visitor; - -struct serialize_type_visitor -{ - typedef void result_type; - - fc::mutable_variant_object& obj; - int t = 0; - serialize_type_visitor( fc::mutable_variant_object& o, int _t ):obj(o),t(_t){} - - template - result_type operator()( const Type& op )const - { - obj(remove_namespace( fc::get_typename::name()),t); - } -}; - - -class serialize_member_visitor -{ - public: - template - void operator()( const char* name )const - { - current_stream() << " " << name << " : " << fc::get_typename::name() <<"\n"; - } -}; - -template -struct serializer -{ - static_assert( fc::reflector::is_defined::value == false, "invalid template arguments" ); - static void init() - {} - - static void generate() - {} -}; - -template -struct serializer,false> -{ - static void init() { serializer::init(); } - static void generate() {} -}; -template -struct serializer,false> -{ - static void init() { serializer::init(); } - static void generate() {} -}; -/* -template<> -struct serializer,false> -{ - static void init() { } - static void generate() {} -}; -*/ - -template<> -struct serializer -{ - static void init() {} - - static void generate() {} -}; -template<> -struct serializer -{ - static void init() {} - static void generate() {} -}; -#ifdef __APPLE__ -// on mac, size_t is a distinct type from uint64_t or uint32_t and needs a separate specialization -template<> struct serializer { static void init() {} static void generate() {} }; -#endif -template<> struct serializer { static void init() {} static void generate() {} }; -template<> struct serializer { static void init() {} static void generate() {} }; - -template -struct serializer,false> -{ - static void init() { serializer::init(); } - static void generate(){} -}; - -template -struct serializer< graphene::db::object_id ,true> -{ - static void init() {} - static void generate() {} -}; - -template -struct serializer< fc::static_variant, false > -{ - static void init() - { - auto name = js_name>::name(); - if( processed_types().find( name ) == processed_types().end() ) - { - processed_types().insert( name ); - fc::static_variant var; - for( int i = 0; i < var.count(); ++i ) - { - var.set_which(i); - var.visit( register_type_visitor() ); - } - register_serializer( js_name>::name(), [=](){ generate(); } ); - } - } - - static void generate() - { - current_stream() << js_name>::name() << " = static_variant [" + js_sv_name::name() + "\n]\n\n"; - } -}; - -class register_member_visitor -{ - public: - template - void operator()( const char* name )const - { - serializer::init(); - } -}; - -template -struct serializer -{ - static_assert( fc::reflector::is_defined::value == reflected, "invalid template arguments" ); - static void init() - { - auto name = js_name::name(); - if( st.find(name) == st.end() ) - { - fc::reflector::visit( register_member_visitor() ); - register_serializer( name, [=](){ generate(); } ); - } - } - - static void generate() - { - auto name = remove_namespace( js_name::name() ); - if( name == "int64" ) return; - current_stream() << "" << name - << " = new Serializer( \n" - << " \"" + name + "\"\n"; - - fc::reflector::visit( serialize_member_visitor() ); - - current_stream() <<")\n\n"; - } -}; - - -template -std::string get_type_description() -{ - current_stream( std::unique_ptr(new std::stringstream()) ); - processed_types().clear(); - serializer::init(); - for( const auto& gen : serializers ) - gen(); - return current_stream().str(); -} - - -} } // graphene::db - - - - - - - - diff --git a/libraries/db/type_serializer.cpp b/libraries/db/type_serializer.cpp deleted file mode 100644 index 167528d4..00000000 --- a/libraries/db/type_serializer.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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. - */ -#include - -namespace graphene { namespace db { -map st; -vector> serializers; - -std::set& processed_types() -{ - static std::set p; - return p; -} - -std::stringstream& current_stream( std::unique_ptr&& s ) -{ - static std::unique_ptr stream; - if( s ) stream = std::move(s); - FC_ASSERT( stream ); - return *stream; -} - -string remove_tail_if( const string& str, char c, const string& match ) -{ - auto last = str.find_last_of( c ); - if( last != std::string::npos ) - if( str.substr( last + 1 ) == match ) - return str.substr( 0, last ); - return str; -} -string remove_namespace_if( const string& str, const string& match ) -{ - auto last = str.find( match ); - if( last != std::string::npos ) - return str.substr( match.size()+2 ); - return str; -} - -string remove_namespace( string str ) -{ - str = remove_tail_if( str, '_', "operation" ); - str = remove_tail_if( str, '_', "t" ); - str = remove_tail_if( str, '_', "object" ); - str = remove_tail_if( str, '_', "type" ); - str = remove_namespace_if( str, "graphene::chain" ); - str = remove_namespace_if( str, "graphene::db" ); - str = remove_namespace_if( str, "std" ); - str = remove_namespace_if( str, "fc" ); - auto pos = str.find( ":" ); - if( pos != str.npos ) - str.replace( pos, 2, "_" ); - return str; -} -bool register_serializer( const string& name, std::function sr ) -{ - if( st.find(name) == st.end() ) - { - serializers.push_back( sr ); - st[name] = serializers.size() - 1; - return true; - } - return false; -} - -} }// graphene diff --git a/libraries/wallet/include/graphene/wallet/reflect_util.hpp b/libraries/wallet/include/graphene/wallet/reflect_util.hpp index 2d0b77ac..497303c5 100644 --- a/libraries/wallet/include/graphene/wallet/reflect_util.hpp +++ b/libraries/wallet/include/graphene/wallet/reflect_util.hpp @@ -37,6 +37,22 @@ struct static_variant_map namespace impl { +std::string clean_name( const std::string& name ) +{ + std::string result; + const static std::string prefix = "graphene::chain::"; + const static std::string suffix = "_operation"; + // graphene::chain::.*_operation + if( (name.size() >= prefix.size() + suffix.size()) + && (name.substr( 0, prefix.size() ) == prefix) + && (name.substr( name.size()-suffix.size(), suffix.size() ) == suffix ) + ) + return name.substr( prefix.size(), name.size() - prefix.size() - suffix.size() ); + + wlog( "don't know how to clean name: ${name}", ("name", name) ); + return name; +} + struct static_variant_map_visitor { static_variant_map_visitor() {} @@ -47,7 +63,7 @@ struct static_variant_map_visitor result_type operator()( const T& dummy ) { assert( which == m.which_to_name.size() ); - std::string name = js_name::name(); + std::string name = clean_name( fc::get_typename::name() ); m.name_to_which[ name ] = which; m.which_to_name.push_back( name ); } From 2cc0d73609645233554a16792fb70554b15d72ad Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Sun, 14 Feb 2016 17:22:23 -0500 Subject: [PATCH 33/43] check_reflect.py: Detect #575 (*_object using FC_REFLECT rather than FC_REFLECT_DERIVED) --- programs/build_helpers/check_reflect.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/programs/build_helpers/check_reflect.py b/programs/build_helpers/check_reflect.py index d655b846..0f41f355 100755 --- a/programs/build_helpers/check_reflect.py +++ b/programs/build_helpers/check_reflect.py @@ -87,6 +87,8 @@ for root, dirs, files in os.walk("."): cname = m.group(1) members = bubble_list(m.group(2)) name2members_re[cname] = members + if cname.endswith("_object"): + print("FC_REFLECT on {} should be FC_REFLECT_DERIVED".format(cname)) for m in re_reflect_derived.finditer(content): cname = m.group(1) members = bubble_list(m.group(3)) From b1f8c8f291b1fac470e3dc112ee9d9683b7e6b26 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Sun, 14 Feb 2016 17:26:35 -0500 Subject: [PATCH 34/43] buyback_object.hpp: Don't FC_REFLECT buyback_object, FC_REFLECT_DERIVED it instead #538 #575 --- libraries/chain/include/graphene/chain/buyback_object.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/buyback_object.hpp b/libraries/chain/include/graphene/chain/buyback_object.hpp index de84b0e2..79b4886b 100644 --- a/libraries/chain/include/graphene/chain/buyback_object.hpp +++ b/libraries/chain/include/graphene/chain/buyback_object.hpp @@ -64,4 +64,4 @@ typedef generic_index< buyback_object, buyback_multi_index_type > buyback_index; } } // graphene::chain -FC_REFLECT( graphene::chain::buyback_object, (asset_to_buy) ) +FC_REFLECT_DERIVED( graphene::chain::buyback_object, (graphene::db::object), (asset_to_buy) ) From 4e9e3ae054daac7e126d52b89fac1947799dd4b3 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Sun, 14 Feb 2016 20:28:37 -0500 Subject: [PATCH 35/43] Allow asset to update permission flags when no supply exists #572 --- libraries/chain/asset_evaluator.cpp | 9 ++++++--- libraries/chain/hardfork.d/572.hf | 4 ++++ 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 libraries/chain/hardfork.d/572.hf diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 17113d9c..3476d2b1 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -259,9 +259,12 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) } } - // new issuer_permissions must be subset of old issuer permissions - FC_ASSERT(!(o.new_options.issuer_permissions & ~a.options.issuer_permissions), - "Cannot reinstate previously revoked issuer permissions on an asset."); + 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 + FC_ASSERT(!(o.new_options.issuer_permissions & ~a.options.issuer_permissions), + "Cannot reinstate previously revoked issuer permissions on an asset."); + } // changed flags must be subset of old issuer permissions FC_ASSERT(!((o.new_options.flags ^ a.options.flags) & ~a.options.issuer_permissions), diff --git a/libraries/chain/hardfork.d/572.hf b/libraries/chain/hardfork.d/572.hf new file mode 100644 index 00000000..f054225f --- /dev/null +++ b/libraries/chain/hardfork.d/572.hf @@ -0,0 +1,4 @@ +// #572 Allow asset to update permission flags when no supply exists +#ifndef HARDFORK_572_TIME +#define HARDFORK_572_TIME (fc::time_point_sec( 1450378800 )) +#endif From 3fa79740dfea0b573e9ca6d1c4f07b1a83f9ea85 Mon Sep 17 00:00:00 2001 From: abitmore Date: Sat, 13 Feb 2016 17:12:17 +0100 Subject: [PATCH 36/43] Reflect ID of force_settlement_object, fix #575 --- libraries/chain/include/graphene/chain/market_object.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/market_object.hpp b/libraries/chain/include/graphene/chain/market_object.hpp index 1bc2fe3e..c41def13 100644 --- a/libraries/chain/include/graphene/chain/market_object.hpp +++ b/libraries/chain/include/graphene/chain/market_object.hpp @@ -206,4 +206,7 @@ FC_REFLECT_DERIVED( graphene::chain::limit_order_object, FC_REFLECT_DERIVED( graphene::chain::call_order_object, (graphene::db::object), (borrower)(collateral)(debt)(call_price) ) -FC_REFLECT( graphene::chain::force_settlement_object, (owner)(balance)(settlement_date) ) +FC_REFLECT_DERIVED( graphene::chain::force_settlement_object, + (graphene::db::object), + (owner)(balance)(settlement_date) + ) From 89fbb11bb6490f20772218531932b6a8162603ce Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Mon, 15 Feb 2016 12:33:21 -0500 Subject: [PATCH 37/43] Fix #492 - database corruption when closing The database was attempting to pop blocks for which there was no undo history. These changes make it impossible to pop blocks if there isn't a fork db history. --- libraries/chain/db_block.cpp | 5 +++-- libraries/chain/fork_database.cpp | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 0bb841ee..d7c1e214 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -417,9 +417,10 @@ void database::pop_block() 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" ); - pop_undo(); - _block_id_to_block.remove( head_id ); + _fork_db.pop_block(); + _block_id_to_block.remove( head_id ); + pop_undo(); _popped_tx.insert( _popped_tx.begin(), head_block->transactions.begin(), head_block->transactions.end() ); diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index c97b9395..83d4880c 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -38,7 +38,10 @@ void fork_database::reset() void fork_database::pop_block() { - if( _head ) _head = _head->prev.lock(); + FC_ASSERT( _head, "no blocks to pop" ); + auto prev = _head->prev.lock(); + FC_ASSERT( prev, "poping block would leave head block null" ); + _head = prev; } void fork_database::start_block(signed_block b) From 5dd56bd82486d61defcb74d0f2c53b550cb948fe Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 15 Feb 2016 14:35:16 -0500 Subject: [PATCH 38/43] Implement FBA fee routing for STEALTH #563 --- libraries/app/api.cpp | 2 + libraries/app/impacted.cpp | 5 + libraries/chain/CMakeLists.txt | 1 + libraries/chain/account_evaluator.cpp | 18 ++- libraries/chain/confidential_evaluator.cpp | 26 ++++- libraries/chain/db_init.cpp | 34 ++++++ libraries/chain/db_maint.cpp | 92 ++++++++++++++++ libraries/chain/evaluator.cpp | 15 +++ libraries/chain/fba_object.cpp | 103 ++++++++++++++++++ libraries/chain/hardfork.d/563.hf | 4 + .../include/graphene/chain/account_object.hpp | 9 ++ .../graphene/chain/confidential_evaluator.hpp | 6 + .../include/graphene/chain/evaluator.hpp | 5 + .../graphene/chain/fba_accumulator_id.hpp | 41 +++++++ .../include/graphene/chain/fba_object.hpp | 52 +++++++++ .../include/graphene/chain/protocol/fba.hpp | 47 ++++++++ .../graphene/chain/protocol/operations.hpp | 6 +- .../include/graphene/chain/protocol/types.hpp | 9 +- .../include/graphene/chain/vote_count.hpp | 5 + 19 files changed, 472 insertions(+), 8 deletions(-) create mode 100644 libraries/chain/fba_object.cpp create mode 100644 libraries/chain/hardfork.d/563.hf create mode 100644 libraries/chain/include/graphene/chain/fba_accumulator_id.hpp create mode 100644 libraries/chain/include/graphene/chain/fba_object.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/fba.hpp diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index d8793423..35cf3880 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -348,6 +348,8 @@ namespace graphene { namespace app { break; case impl_buyback_object_type: break; + case impl_fba_accumulator_object_type: + break; } } return result; diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index b85d4573..85787423 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -198,6 +198,11 @@ struct get_impacted_account_visitor _impacted.insert( op.account ); } + void operator()( const fba_distribute_operation& op ) + { + _impacted.insert( op.account_id ); + } + }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index d0cbc20b..c38bd80b 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -81,6 +81,7 @@ add_library( graphene_chain account_object.cpp asset_object.cpp + fba_object.cpp proposal_object.cpp vesting_balance_object.cpp diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 03fe26c7..1f29bb0a 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -260,14 +260,28 @@ void_result account_update_evaluator::do_apply( const account_update_operation& database& d = db(); bool sa_before, sa_after; d.modify( *acnt, [&](account_object& a){ - if( o.owner ) a.owner = *o.owner; - if( o.active ) a.active = *o.active; + if( o.owner ) + { + a.owner = *o.owner; + a.top_n_control_flags = 0; + } + if( o.active ) + { + a.active = *o.active; + a.top_n_control_flags = 0; + } if( o.new_options ) a.options = *o.new_options; sa_before = a.has_special_authority(); if( o.extensions.value.owner_special_authority.valid() ) + { a.owner_special_authority = *(o.extensions.value.owner_special_authority); + a.top_n_control_flags = 0; + } if( o.extensions.value.active_special_authority.valid() ) + { a.active_special_authority = *(o.extensions.value.active_special_authority); + a.top_n_control_flags = 0; + } sa_after = a.has_special_authority(); }); diff --git a/libraries/chain/confidential_evaluator.cpp b/libraries/chain/confidential_evaluator.cpp index bcfef1e9..9946b492 100644 --- a/libraries/chain/confidential_evaluator.cpp +++ b/libraries/chain/confidential_evaluator.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include namespace graphene { namespace chain { @@ -67,6 +69,13 @@ void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_opera return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } +void transfer_to_blind_evaluator::pay_fee() +{ + if( db().head_block_time() >= HARDFORK_563_TIME ) + pay_fba_fee( fba_accumulator_id_transfer_to_blind ); + else + generic_evaluator::pay_fee(); +} void_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o ) { try { @@ -104,9 +113,13 @@ void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_o return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } - - - +void transfer_from_blind_evaluator::pay_fee() +{ + if( db().head_block_time() >= HARDFORK_563_TIME ) + pay_fba_fee( fba_accumulator_id_transfer_from_blind ); + else + generic_evaluator::pay_fee(); +} void_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o ) { try { @@ -157,5 +170,12 @@ void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } +void blind_transfer_evaluator::pay_fee() +{ + if( db().head_block_time() >= HARDFORK_563_TIME ) + pay_fba_fee( fba_accumulator_id_blind_transfer ); + else + generic_evaluator::pay_fee(); +} } } // graphene::chain diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 1b9a37c0..302abef4 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -23,6 +23,7 @@ */ #include +#include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -212,6 +214,8 @@ void database::initialize_indexes() add_index< primary_index > >(); add_index< primary_index< special_authority_index > >(); add_index< primary_index< buyback_index > >(); + + add_index< primary_index< simple_index< fba_accumulator_object > > >(); } void database::init_genesis(const genesis_state_type& genesis_state) @@ -664,6 +668,36 @@ void database::init_genesis(const genesis_state_type& genesis_state) wso.current_shuffled_witnesses.push_back( wid ); }); + // Create FBA counters + create([&]( fba_accumulator_object& acc ) + { + FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_transfer_to_blind ) ); + acc.accumulated_fba_fees = 0; +#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET + acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET; +#endif + }); + + create([&]( fba_accumulator_object& acc ) + { + FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_blind_transfer ) ); + acc.accumulated_fba_fees = 0; +#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET + acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET; +#endif + }); + + create([&]( fba_accumulator_object& acc ) + { + FC_ASSERT( acc.id == fba_accumulator_id_type( fba_accumulator_id_transfer_from_blind ) ); + acc.accumulated_fba_fees = 0; +#ifdef GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET + acc.designated_asset = GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET; +#endif + }); + + FC_ASSERT( get_index().get_next_id() == fba_accumulator_id_type( fba_accumulator_id_count ) ); + debug_dump(); _undo_db.enable(); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 9721ae45..9eabb1d9 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -519,11 +521,100 @@ void update_top_n_authorities( database& db ) db.modify( acct, [&]( account_object& a ) { vc.finish( is_owner ? a.owner : a.active ); + if( !vc.is_empty() ) + a.top_n_control_flags |= (is_owner ? account_object::top_n_control_owner : account_object::top_n_control_active); } ); } } ); } +void split_fba_balance( + database& db, + uint64_t fba_id, + uint16_t network_pct, + uint16_t designated_asset_buyback_pct, + uint16_t designated_asset_issuer_pct +) +{ + FC_ASSERT( uint32_t(network_pct) + uint32_t(designated_asset_buyback_pct) + uint32_t(designated_asset_issuer_pct) == GRAPHENE_100_PERCENT ); + const fba_accumulator_object& fba = fba_accumulator_id_type( fba_id )(db); + if( fba.accumulated_fba_fees == 0 ) + return; + + const asset_object& core = asset_id_type(0)(db); + const asset_dynamic_data_object& core_dd = core.dynamic_asset_data_id(db); + + if( !fba.is_configured(db) ) + { + ilog( "${n} core given to network at block ${b} due to non-configured FBA", ("n", fba.accumulated_fba_fees)("b", db.head_block_time()) ); + db.modify( core_dd, [&]( asset_dynamic_data_object& _core_dd ) + { + _core_dd.current_supply -= fba.accumulated_fba_fees; + } ); + db.modify( fba, [&]( fba_accumulator_object& _fba ) + { + _fba.accumulated_fba_fees = 0; + } ); + return; + } + + fc::uint128_t buyback_amount_128 = fba.accumulated_fba_fees.value; + buyback_amount_128 *= designated_asset_buyback_pct; + buyback_amount_128 /= GRAPHENE_100_PERCENT; + share_type buyback_amount = buyback_amount_128.to_uint64(); + + fc::uint128_t issuer_amount_128 = fba.accumulated_fba_fees.value; + issuer_amount_128 *= designated_asset_issuer_pct; + issuer_amount_128 /= GRAPHENE_100_PERCENT; + share_type issuer_amount = issuer_amount_128.to_uint64(); + + // this assert should never fail + FC_ASSERT( buyback_amount + issuer_amount <= fba.accumulated_fba_fees ); + + share_type network_amount = fba.accumulated_fba_fees - (buyback_amount + issuer_amount); + + const asset_object& designated_asset = (*fba.designated_asset)(db); + + if( network_amount != 0 ) + { + db.modify( core_dd, [&]( asset_dynamic_data_object& _core_dd ) + { + _core_dd.current_supply -= network_amount; + } ); + } + + fba_distribute_operation vop; + vop.account_id = *designated_asset.buyback_account; + vop.fba_id = fba.id; + vop.amount = buyback_amount; + if( vop.amount != 0 ) + { + db.adjust_balance( *designated_asset.buyback_account, asset(buyback_amount) ); + db.push_applied_operation(vop); + } + + vop.account_id = designated_asset.issuer; + vop.fba_id = fba.id; + vop.amount = issuer_amount; + if( vop.amount != 0 ) + { + db.adjust_balance( designated_asset.issuer, asset(issuer_amount) ); + db.push_applied_operation(vop); + } + + db.modify( fba, [&]( fba_accumulator_object& _fba ) + { + _fba.accumulated_fba_fees = 0; + } ); +} + +void distribute_fba_balances( database& db ) +{ + split_fba_balance( db, fba_accumulator_id_transfer_to_blind , 20*GRAPHENE_1_PERCENT, 60*GRAPHENE_1_PERCENT, 20*GRAPHENE_1_PERCENT ); + split_fba_balance( db, fba_accumulator_id_blind_transfer , 20*GRAPHENE_1_PERCENT, 60*GRAPHENE_1_PERCENT, 20*GRAPHENE_1_PERCENT ); + split_fba_balance( db, fba_accumulator_id_transfer_from_blind, 20*GRAPHENE_1_PERCENT, 60*GRAPHENE_1_PERCENT, 20*GRAPHENE_1_PERCENT ); +} + void create_buyback_orders( database& db ) { const auto& bbo_idx = db.get_index_type< buyback_index >().indices().get(); @@ -604,6 +695,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g { const auto& gpo = get_global_properties(); + distribute_fba_balances(*this); create_buyback_orders(*this); struct vote_tally_helper { diff --git a/libraries/chain/evaluator.cpp b/libraries/chain/evaluator.cpp index 06e31c21..b27d3960 100644 --- a/libraries/chain/evaluator.cpp +++ b/libraries/chain/evaluator.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -103,4 +104,18 @@ database& generic_evaluator::db()const { return trx_state->db(); } } } FC_CAPTURE_AND_RETHROW() } + void generic_evaluator::pay_fba_fee( uint64_t fba_id ) + { + database& d = db(); + const fba_accumulator_object& fba = d.get< fba_accumulator_object >( fba_accumulator_id_type( fba_id ) ); + if( !fba.is_configured(d) ) + { + generic_evaluator::pay_fee(); + return; + } + d.modify( fba, [&]( fba_accumulator_object& _fba ) + { + _fba.accumulated_fba_fees += core_fee_paid; + } ); + } } } diff --git a/libraries/chain/fba_object.cpp b/libraries/chain/fba_object.cpp new file mode 100644 index 00000000..2febca45 --- /dev/null +++ b/libraries/chain/fba_object.cpp @@ -0,0 +1,103 @@ +/* + * 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. + */ + +#include +#include +#include + +namespace graphene { namespace chain { + +bool fba_accumulator_object::is_configured( const database& db )const +{ + if( !designated_asset.valid() ) + { + ilog( "FBA fee in block ${b} not paid because designated asset was not configured", ("b", db.head_block_num()) ); + return false; + } + const asset_object* dasset = db.find(*designated_asset); + if( dasset == nullptr ) + { + ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset does not exist", ("b", db.head_block_num()) ); + return false; + } + if( dasset->is_market_issued() ) + { + ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: FBA is a BitAsset", ("b", db.head_block_num()) ); + return false; + } + + const uint16_t allowed_flags = charge_market_fee; + + // check enabled issuer_permissions bits is subset of allowed_flags bits + if( (dasset->options.issuer_permissions & allowed_flags) != dasset->options.issuer_permissions ) + { + ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: Disallowed permissions enabled", ("b", db.head_block_num()) ); + return false; + } + + // check enabled issuer_permissions bits is subset of allowed_flags bits + if( (dasset->options.flags & allowed_flags) != dasset->options.flags ) + { + ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: Disallowed flags enabled", ("b", db.head_block_num()) ); + return false; + } + + if( !dasset->buyback_account.valid() ) + { + ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset does not have a buyback account", ("b", db.head_block_num()) ); + return false; + } + const account_object& issuer_acct = dasset->issuer(db); + + if( issuer_acct.owner_special_authority.which() != special_authority::tag< top_holders_special_authority >::value ) + { + ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer has not set owner top_n control", ("b", db.head_block_num()) ); + return false; + } + if( issuer_acct.active_special_authority.which() != special_authority::tag< top_holders_special_authority >::value ) + { + ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer has not set active top_n control", ("b", db.head_block_num()) ); + return false; + } + if( issuer_acct.owner_special_authority.get< top_holders_special_authority >().asset != *designated_asset ) + { + ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer's top_n_control is not set to designated asset", ("b", db.head_block_num()) ); + return false; + } + if( issuer_acct.active_special_authority.get< top_holders_special_authority >().asset != *designated_asset ) + { + ilog( "FBA fee in block ${b} not paid because of FBA misconfiguration: designated asset issuer's top_n_control is not set to designated asset", ("b", db.head_block_num()) ); + return false; + } + + if( issuer_acct.top_n_control_flags != (account_object::top_n_control_owner | account_object::top_n_control_active) ) + { + ilog( "FBA fee in block ${b} not paid because designated asset's top_n control has not yet activated (wait until next maintenance interval)", ("b", db.head_block_num()) ); + return false; + } + + return true; +} + +} } diff --git a/libraries/chain/hardfork.d/563.hf b/libraries/chain/hardfork.d/563.hf new file mode 100644 index 00000000..892214ed --- /dev/null +++ b/libraries/chain/hardfork.d/563.hf @@ -0,0 +1,4 @@ +// #563 Stealth fee routing +#ifndef HARDFORK_563_TIME +#define HARDFORK_563_TIME (fc::time_point_sec( 1455127200 )) +#endif diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index 0af36acc..11f0f586 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -208,6 +208,14 @@ namespace graphene { namespace chain { special_authority owner_special_authority = no_special_authority(); special_authority active_special_authority = no_special_authority(); + /** + * This flag is set when the top_n logic sets both authorities, + * and gets reset when authority or special_authority is set. + */ + uint8_t top_n_control_flags = 0; + static const uint8_t top_n_control_owner = 1; + static const uint8_t top_n_control_active = 2; + /** * This is a set of assets which the account is allowed to have. * This is utilized to restrict buyback accounts to the assets that trade in their markets. @@ -365,6 +373,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_object, (whitelisting_accounts)(blacklisted_accounts) (cashback_vb) (owner_special_authority)(active_special_authority) + (top_n_control_flags) (allowed_assets) ) diff --git a/libraries/chain/include/graphene/chain/confidential_evaluator.hpp b/libraries/chain/include/graphene/chain/confidential_evaluator.hpp index 1f047c09..93e0e78e 100644 --- a/libraries/chain/include/graphene/chain/confidential_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/confidential_evaluator.hpp @@ -37,6 +37,8 @@ class transfer_to_blind_evaluator : public evaluator @@ -46,6 +48,8 @@ class transfer_from_blind_evaluator : public evaluator @@ -55,6 +59,8 @@ class blind_transfer_evaluator : public evaluator void_result do_evaluate( const blind_transfer_operation& o ); void_result do_apply( const blind_transfer_operation& o ) ; + + virtual void pay_fee() override; }; } } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/evaluator.hpp b/libraries/chain/include/graphene/chain/evaluator.hpp index 4271be58..8019e8f6 100644 --- a/libraries/chain/include/graphene/chain/evaluator.hpp +++ b/libraries/chain/include/graphene/chain/evaluator.hpp @@ -138,6 +138,11 @@ namespace graphene { namespace chain { object_id_type get_relative_id( object_id_type rel_id )const; + /** + * pay_fee() for FBA subclass should simply call this method + */ + void pay_fba_fee( uint64_t fba_id ); + asset fee_from_account; share_type core_fee_paid; const account_object* fee_paying_account = nullptr; diff --git a/libraries/chain/include/graphene/chain/fba_accumulator_id.hpp b/libraries/chain/include/graphene/chain/fba_accumulator_id.hpp new file mode 100644 index 00000000..9bc0cf23 --- /dev/null +++ b/libraries/chain/include/graphene/chain/fba_accumulator_id.hpp @@ -0,0 +1,41 @@ +/* + * 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 + +namespace graphene { namespace chain { + +/** + * An object will be created at genesis for each of these FBA accumulators. + */ +enum graphene_fba_accumulator_id_enum +{ + fba_accumulator_id_transfer_to_blind = 0, + fba_accumulator_id_blind_transfer, + fba_accumulator_id_transfer_from_blind, + fba_accumulator_id_count +}; + +} } diff --git a/libraries/chain/include/graphene/chain/fba_object.hpp b/libraries/chain/include/graphene/chain/fba_object.hpp new file mode 100644 index 00000000..aec9e9cd --- /dev/null +++ b/libraries/chain/include/graphene/chain/fba_object.hpp @@ -0,0 +1,52 @@ +/* + * 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 { + +class database; + +/** + * fba_accumulator_object accumulates fees to be paid out via buyback or other FBA mechanism. + */ + +class fba_accumulator_object : public graphene::db::abstract_object< fba_accumulator_object > +{ + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_fba_accumulator_object_type; + + share_type accumulated_fba_fees; + optional< asset_id_type > designated_asset; + + bool is_configured( const database& db )const; +}; + +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::fba_accumulator_object, (graphene::db::object), (accumulated_fba_fees)(designated_asset) ) diff --git a/libraries/chain/include/graphene/chain/protocol/fba.hpp b/libraries/chain/include/graphene/chain/protocol/fba.hpp new file mode 100644 index 00000000..7460ca8d --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/fba.hpp @@ -0,0 +1,47 @@ +/* + * 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 + +namespace graphene { namespace chain { + +struct fba_distribute_operation : public base_operation +{ + struct fee_parameters_type {}; + + asset fee; // always zero + account_id_type account_id; + fba_accumulator_id_type fba_id; + share_type amount; + + account_id_type fee_payer()const { return account_id; } + void validate()const { FC_ASSERT( false ); } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } +}; + +} } + +FC_REFLECT( graphene::chain::fba_distribute_operation::fee_parameters_type, ) + +FC_REFLECT( graphene::chain::fba_distribute_operation, (fee)(account_id)(fba_id)(amount) ) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 66a2ced3..7f2639f1 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -36,7 +38,6 @@ #include #include #include -#include namespace graphene { namespace chain { @@ -89,7 +90,8 @@ namespace graphene { namespace chain { blind_transfer_operation, transfer_from_blind_operation, asset_settle_cancel_operation, // VIRTUAL - asset_claim_fees_operation + asset_claim_fees_operation, + fba_distribute_operation // VIRTUAL > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index fac66d7a..5237fcad 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -154,7 +154,8 @@ namespace graphene { namespace chain { impl_witness_schedule_object_type, impl_budget_record_object_type, impl_special_authority_object_type, - impl_buyback_object_type + impl_buyback_object_type, + impl_fba_accumulator_object_type }; //typedef fc::unsigned_int object_id_type; @@ -205,6 +206,7 @@ namespace graphene { namespace chain { class budget_record_object; class special_authority_object; class buyback_object; + class fba_accumulator_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; @@ -224,6 +226,7 @@ namespace graphene { namespace chain { 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; + typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type; typedef fc::array symbol_type; typedef fc::ripemd160 block_id_type; @@ -355,6 +358,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type, (impl_budget_record_object_type) (impl_special_authority_object_type) (impl_buyback_object_type) + (impl_fba_accumulator_object_type) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) @@ -384,6 +388,9 @@ 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( graphene::chain::void_t, ) FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags, diff --git a/libraries/chain/include/graphene/chain/vote_count.hpp b/libraries/chain/include/graphene/chain/vote_count.hpp index bb23b840..f76a784d 100644 --- a/libraries/chain/include/graphene/chain/vote_count.hpp +++ b/libraries/chain/include/graphene/chain/vote_count.hpp @@ -63,6 +63,11 @@ struct vote_counter out_auth = auth; } + bool is_empty()const + { + return (total_votes == 0); + } + uint64_t last_votes = std::numeric_limits::max(); uint64_t total_votes = 0; int8_t bitshift = -1; From 36a8c3d6f1c35faa736e176650c298126184cfc1 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 15 Feb 2016 14:58:01 -0500 Subject: [PATCH 39/43] Unit test for STEALTH fee routing #563 --- .../chain/include/graphene/chain/config.hpp | 3 + tests/common/database_fixture.cpp | 21 ++ tests/common/database_fixture.hpp | 3 + tests/tests/fee_tests.cpp | 211 ++++++++++++++++++ 4 files changed, 238 insertions(+) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 5f8772d1..74b7d2b5 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -167,3 +167,6 @@ /// Sentinel value used in the scheduler. #define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0)) ///@} + +// hack for unit test +#define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET (asset_id_type(1)) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 7a9e7ce4..44cd64da 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,8 @@ void database_fixture::verify_asset_supplies( const database& db ) } for( const vesting_balance_object& vbo : db.get_index_type< vesting_balance_index >().indices() ) 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; total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget; @@ -1033,6 +1036,24 @@ int64_t database_fixture::get_balance( const account_object& account, const asse return db.get_balance(account.get_id(), a.get_id()).amount.value; } +vector< operation_history_object > database_fixture::get_operation_history( account_id_type account_id )const +{ + vector< operation_history_object > result; + const auto& stats = account_id(db).statistics(db); + if(stats.most_recent_op == account_transaction_history_id_type()) + return result; + + const account_transaction_history_object* node = &stats.most_recent_op(db); + while( true ) + { + result.push_back( node->operation_id(db) ); + if(node->next == account_transaction_history_id_type()) + break; + node = db.find(node->next); + } + return result; +} + namespace test { void set_expiration( const database& db, transaction& tx ) diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 0cb9198d..241fd079 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -28,6 +28,8 @@ #include #include +#include + #include using namespace graphene::db; @@ -278,6 +280,7 @@ 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; + vector< operation_history_object > get_operation_history( account_id_type account_id )const; }; namespace test { diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index 3e5e191a..9d7f0281 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -24,7 +24,12 @@ #include #include + #include + +#include + +#include #include #include @@ -721,4 +726,210 @@ BOOST_AUTO_TEST_CASE( fee_refund_test ) FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( stealth_fba_test ) +{ + try + { + ACTORS( (alice)(bob)(chloe)(dan)(izzy)(philbin)(tom) ); + upgrade_to_lifetime_member(philbin_id); + + generate_blocks( HARDFORK_538_TIME ); + generate_blocks( HARDFORK_555_TIME ); + generate_blocks( HARDFORK_563_TIME ); + generate_blocks( HARDFORK_572_TIME ); + + // Philbin (registrar who registers Rex) + + // Izzy (initial issuer of stealth asset, will later transfer to Tom) + // Alice, Bob, Chloe, Dan (ABCD) + // Rex (recycler -- buyback account for stealth asset) + // Tom (owner of stealth asset who will be set as top_n authority) + + // Izzy creates STEALTH + asset_id_type stealth_id = create_user_issued_asset( "STEALTH", izzy_id(db), + disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee ).id; + + /* + // this is disabled because it doesn't work, our modify() is probably being overwritten by undo + + // + // Init blockchain with stealth ID's + // On a real chain, this would be done with #define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET + // causing the designated_asset fields of these objects to be set at genesis, but for + // this test we modify the db directly. + // + auto set_fba_asset = [&]( uint64_t fba_acc_id, asset_id_type asset_id ) + { + db.modify( fba_accumulator_id_type(fba_acc_id)(db), [&]( fba_accumulator_object& fba ) + { + fba.designated_asset = asset_id; + } ); + }; + + set_fba_asset( fba_accumulator_id_transfer_to_blind , stealth_id ); + set_fba_asset( fba_accumulator_id_blind_transfer , stealth_id ); + set_fba_asset( fba_accumulator_id_transfer_from_blind, stealth_id ); + */ + + // Izzy kills some permission bits (this somehow happened to the real STEALTH in production) + { + asset_update_operation update_op; + update_op.issuer = izzy_id; + update_op.asset_to_update = stealth_id; + asset_options new_options; + new_options = stealth_id(db).options; + new_options.issuer_permissions = charge_market_fee; + new_options.flags = disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee; + // after fixing #579 you should be able to delete the following line + new_options.core_exchange_rate = price( asset( 1, stealth_id ), asset( 1, asset_id_type() ) ); + update_op.new_options = new_options; + signed_transaction tx; + tx.operations.push_back( update_op ); + set_expiration( db, tx ); + sign( tx, izzy_private_key ); + PUSH_TX( db, tx ); + } + + // Izzy transfers issuer duty to Tom + { + asset_update_operation update_op; + update_op.issuer = izzy_id; + update_op.asset_to_update = stealth_id; + update_op.new_issuer = tom_id; + // new_options should be optional, but isn't...the following line should be unnecessary #580 + update_op.new_options = stealth_id(db).options; + signed_transaction tx; + tx.operations.push_back( update_op ); + set_expiration( db, tx ); + sign( tx, izzy_private_key ); + PUSH_TX( db, tx ); + } + + // Tom re-enables the permission bits to clear the flags, then clears them again + // Allowed by #572 when current_supply == 0 + { + asset_update_operation update_op; + update_op.issuer = tom_id; + update_op.asset_to_update = stealth_id; + asset_options new_options; + new_options = stealth_id(db).options; + new_options.issuer_permissions = new_options.flags | charge_market_fee; + update_op.new_options = new_options; + signed_transaction tx; + // enable perms is one op + tx.operations.push_back( update_op ); + + new_options.issuer_permissions = charge_market_fee; + new_options.flags = charge_market_fee; + update_op.new_options = new_options; + // reset wrongly set flags and reset permissions can be done in a single op + tx.operations.push_back( update_op ); + + set_expiration( db, tx ); + sign( tx, tom_private_key ); + PUSH_TX( db, tx ); + } + + // Philbin registers Rex who will be the asset's buyback, including sig from the new issuer (Tom) + account_id_type rex_id; + { + buyback_account_options bbo; + bbo.asset_to_buy = stealth_id; + bbo.asset_to_buy_issuer = tom_id; + bbo.markets.emplace( asset_id_type() ); + account_create_operation create_op = make_account( "rex" ); + create_op.registrar = philbin_id; + create_op.extensions.value.buyback_options = bbo; + create_op.owner = authority::null_authority(); + create_op.active = authority::null_authority(); + + signed_transaction tx; + tx.operations.push_back( create_op ); + set_expiration( db, tx ); + sign( tx, philbin_private_key ); + sign( tx, tom_private_key ); + + processed_transaction ptx = PUSH_TX( db, tx ); + rex_id = ptx.operation_results.back().get< object_id_type >(); + } + + // Tom issues some asset to Alice and Bob + set_expiration( db, trx ); // #11 + issue_uia( alice_id, asset( 1000, stealth_id ) ); + issue_uia( bob_id, asset( 1000, stealth_id ) ); + + // Tom sets his authority to the top_n of the asset + { + top_holders_special_authority top2; + top2.num_top_holders = 2; + top2.asset = stealth_id; + + account_update_operation op; + op.account = tom_id; + op.extensions.value.active_special_authority = top2; + op.extensions.value.owner_special_authority = top2; + + signed_transaction tx; + tx.operations.push_back( op ); + + set_expiration( db, tx ); + sign( tx, tom_private_key ); + + PUSH_TX( db, tx ); + } + + // Wait until the next maintenance interval for top_n to take effect + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + // Do a blind op to add some fees to the pool. + fund( chloe_id(db), asset( 100000, asset_id_type() ) ); + + auto create_transfer_to_blind = [&]( account_id_type account, asset amount, const std::string& key ) -> transfer_to_blind_operation + { + fc::ecc::private_key blind_key = fc::ecc::private_key::regenerate( fc::sha256::hash( key+"-privkey" ) ); + public_key_type blind_pub = blind_key.get_public_key(); + + fc::sha256 secret = fc::sha256::hash( key+"-secret" ); + fc::sha256 nonce = fc::sha256::hash( key+"-nonce" ); + + transfer_to_blind_operation op; + blind_output blind_out; + blind_out.owner = authority( 1, blind_pub, 1 ); + blind_out.commitment = fc::ecc::blind( secret, amount.amount.value ); + blind_out.range_proof = fc::ecc::range_proof_sign( 0, blind_out.commitment, secret, nonce, 0, 0, amount.amount.value ); + + op.amount = amount; + op.from = account; + op.blinding_factor = fc::ecc::blind_sum( {secret}, 1 ); + op.outputs = {blind_out}; + + return op; + }; + + { + transfer_to_blind_operation op = create_transfer_to_blind( chloe_id, asset( 5000, asset_id_type() ), "chloe-key" ); + op.fee = asset( 1000, asset_id_type() ); + + signed_transaction tx; + tx.operations.push_back( op ); + set_expiration( db, tx ); + sign( tx, chloe_private_key ); + + PUSH_TX( db, tx ); + } + + // wait until next maint interval + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + idump( ( get_operation_history( chloe_id ) ) ); + idump( ( get_operation_history( rex_id ) ) ); + idump( ( get_operation_history( tom_id ) ) ); + } + catch( const fc::exception& e ) + { + elog( "caught exception ${e}", ("e", e.to_detail_string()) ); + throw; + } +} + BOOST_AUTO_TEST_SUITE_END() From c63b624caf96e91d5a655954f034c607b2aeb88a Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Mon, 15 Feb 2016 16:01:13 -0500 Subject: [PATCH 40/43] fix build errors and warnings This commit was rebased by theoreticalbts and assigned issue #581 Conflict with 146c0c4d8e24ce20f375b4c978995901464a8dae was resolved, minor improvements. --- .../graphene/chain/confidential_evaluator.hpp | 6 +- .../graphene/chain/market_evaluator.hpp | 6 +- .../db/include/graphene/db/object_id.hpp | 67 ++++--------------- 3 files changed, 20 insertions(+), 59 deletions(-) diff --git a/libraries/chain/include/graphene/chain/confidential_evaluator.hpp b/libraries/chain/include/graphene/chain/confidential_evaluator.hpp index 1f047c09..44d49e81 100644 --- a/libraries/chain/include/graphene/chain/confidential_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/confidential_evaluator.hpp @@ -26,9 +26,9 @@ namespace graphene { namespace chain { -class transfer_to_blind_operation; -class transfer_from_blind_operation; -class blind_transfer_operation; +struct transfer_to_blind_operation; +struct transfer_from_blind_operation; +struct blind_transfer_operation; class transfer_to_blind_evaluator : public evaluator { diff --git a/libraries/chain/include/graphene/chain/market_evaluator.hpp b/libraries/chain/include/graphene/chain/market_evaluator.hpp index 9a9c9ae4..9d653c07 100644 --- a/libraries/chain/include/graphene/chain/market_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/market_evaluator.hpp @@ -31,9 +31,9 @@ namespace graphene { namespace chain { class asset_object; class asset_bitasset_data_object; class call_order_object; - class call_order_update_operation; - class limit_order_cancel_operation; - class limit_order_create_operation; + struct call_order_update_operation; + struct limit_order_cancel_operation; + struct limit_order_create_operation; class limit_order_create_evaluator : public evaluator { diff --git a/libraries/db/include/graphene/db/object_id.hpp b/libraries/db/include/graphene/db/object_id.hpp index 02304fc7..598ff3de 100644 --- a/libraries/db/include/graphene/db/object_id.hpp +++ b/libraries/db/include/graphene/db/object_id.hpp @@ -53,37 +53,22 @@ namespace graphene { namespace db { bool is_null()const { return number == 0; } explicit operator uint64_t()const { return number; } - friend bool operator == ( const object_id_type& a, const object_id_type& b ) - { - return a.number == b.number; - } - - friend bool operator != ( const object_id_type& a, const object_id_type& b ) - { - return a.number != b.number; - } + friend bool operator == ( const object_id_type& a, const object_id_type& b ) { return a.number == b.number; } + friend bool operator != ( const object_id_type& a, const object_id_type& b ) { return a.number != b.number; } + friend bool operator < ( const object_id_type& a, const object_id_type& b ) { return a.number < b.number; } + friend bool operator > ( const object_id_type& a, const object_id_type& b ) { return a.number > b.number; } object_id_type& operator++(int) { ++number; return *this; } object_id_type& operator++() { ++number; return *this; } - friend object_id_type operator+(const object_id_type& a, int delta ) { + friend object_id_type operator+(const object_id_type& a, int delta ) { return object_id_type( a.space(), a.type(), a.instance() + delta ); } - friend object_id_type operator+(const object_id_type& a, int64_t delta ) { + friend object_id_type operator+(const object_id_type& a, int64_t delta ) { return object_id_type( a.space(), a.type(), a.instance() + delta ); } friend size_t hash_value( object_id_type v ) { return std::hash()(v.number); } - friend bool operator< ( const object_id_type& a, const object_id_type& b ) - { - return a.number < b.number; - } - - friend bool operator> ( const object_id_type& a, const object_id_type& b ) - { - return a.number > b.number; - } - template< typename T > bool is() const { @@ -134,40 +119,16 @@ namespace graphene { namespace db { template const T& operator()(const DB& db)const { return db.get(*this); } - friend bool operator != ( const object_id& a, const object_id& b ) - { - return a.instance != b.instance; - } - friend bool operator != ( const object_id_type& a, const object_id& b ) - { - return a != object_id_type(b); - } - friend bool operator != ( const object_id& a, const object_id_type& b ) - { - return object_id_type(a) != b; - } + friend bool operator == ( const object_id& a, const object_id& b ) { return a.instance == b.instance; } + friend bool operator != ( const object_id& a, const object_id& b ) { return a.instance != b.instance; } + friend bool operator == ( const object_id_type& a, const object_id& b ) { return a == object_id_type(b); } + friend bool operator != ( const object_id_type& a, const object_id& b ) { return a != object_id_type(b); } + friend bool operator == ( const object_id& b, const object_id_type& a ) { return a == object_id_type(b); } + friend bool operator != ( const object_id& b, const object_id_type& a ) { return a != object_id_type(b); } + friend bool operator < ( const object_id& a, const object_id& b ) { return a.instance.value < b.instance.value; } + friend bool operator > ( const object_id& a, const object_id& b ) { return a.instance.value > b.instance.value; } - friend bool operator == ( const object_id& a, const object_id& b ) - { - return a.instance == b.instance; - } - friend bool operator == ( const object_id_type& a, const object_id& b ) - { - return a == object_id_type(b); - } - friend bool operator == ( const object_id& b, const object_id_type& a ) - { - return a == object_id_type(b); - } - friend bool operator < ( const object_id& a, const object_id& b ) - { - return a.instance.value < b.instance.value; - } - friend bool operator > ( const object_id& a, const object_id& b ) - { - return a.instance.value > b.instance.value; - } friend size_t hash_value( object_id v ) { return std::hash()(v.instance.value); } unsigned_int instance; From 32f07de4e2f5b9676aaba77dae8de0055577e56b Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 15 Feb 2016 16:56:14 -0500 Subject: [PATCH 41/43] Fix unit test failure due to skip_flags changes caused by #492 fix --- tests/common/database_fixture.cpp | 8 ++++---- tests/common/database_fixture.hpp | 2 +- tests/tests/fee_tests.cpp | 17 +++++++++++++---- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index e42a22b4..67e4dd0d 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -320,20 +320,20 @@ void database_fixture::generate_blocks( uint32_t block_count ) generate_block(); } -void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks) +void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks, uint32_t skip) { if( miss_intermediate_blocks ) { - generate_block(); + generate_block(skip); auto slots_to_miss = db.get_slot_at_time(timestamp); if( slots_to_miss <= 1 ) return; --slots_to_miss; - generate_block(~0, init_account_priv_key, slots_to_miss); + generate_block(skip, init_account_priv_key, slots_to_miss); return; } while( db.head_block_time() < timestamp ) - generate_block(); + generate_block(skip); } account_create_operation database_fixture::make_account( diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 241fd079..ec5e9bd7 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -183,7 +183,7 @@ struct database_fixture { * @brief Generates blocks until the head block time matches or exceeds timestamp * @param timestamp target time to generate blocks until */ - void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true); + void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true, uint32_t skip = ~0); account_create_operation make_account( const std::string& name = "nathan", diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index 9d7f0281..e6fd6748 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -620,14 +620,23 @@ BOOST_AUTO_TEST_CASE( fee_refund_test ) int64_t order_create_fee = 537; int64_t order_cancel_fee = 129; - generate_block(); + uint32_t skip = database::skip_witness_signature + | database::skip_transaction_signatures + | 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 ); for( int i=0; i<2; i++ ) { if( i == 1 ) { - generate_blocks( HARDFORK_445_TIME ); - generate_block(); + generate_blocks( HARDFORK_445_TIME, true, skip ); + generate_block( skip ); } // enable_fees() and change_fees() modifies DB directly, and results will be overwritten by block generation @@ -719,7 +728,7 @@ BOOST_AUTO_TEST_CASE( fee_refund_test ) // but we'll save that for future cleanup // undo above tx's and reset - generate_block(); + generate_block( skip ); db.pop_block(); } } From d280a57af02e092638e25cc5a43280e8f902315f Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 15 Feb 2016 21:29:37 -0500 Subject: [PATCH 42/43] js_operation_serializer: Add missing include #561, broken by #563 --- programs/js_operation_serializer/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 8b9ca362..61c1873e 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include From 1153d77dba6566664ebc1fae9dc411c4ff49fb9f Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Mon, 15 Feb 2016 22:48:47 -0500 Subject: [PATCH 43/43] Update documentation of updating active key to match implementation #556 --- libraries/chain/include/graphene/chain/protocol/account.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index 4e5fdaed..329d03e2 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -138,7 +138,7 @@ namespace graphene { namespace chain { /// New owner authority. If set, this operation requires owner authority to execute. optional owner; - /// New active authority. If set, this operation requires owner authority to execute. + /// New active authority. This can be updated by the current active authority. optional active; /// New account options