From c33fe35e4eca74b90045a191bc5925987b1dc987 Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Thu, 11 Feb 2016 04:59:28 -0500 Subject: [PATCH] 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 ); + } + } }