Implement buyback accounts #538

This commit is contained in:
theoreticalbts 2016-02-11 04:59:28 -05:00
parent bd22592d19
commit c33fe35e4e
21 changed files with 373 additions and 4 deletions

View file

@ -346,6 +346,8 @@ namespace graphene { namespace app {
break;
case impl_special_authority_object_type:
break;
case impl_buyback_object_type:
break;
}
}
return result;

View file

@ -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

View file

@ -25,6 +25,8 @@
#include <fc/smart_ref_impl.hpp>
#include <graphene/chain/account_evaluator.hpp>
#include <graphene/chain/buyback.hpp>
#include <graphene/chain/buyback_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
@ -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)) }

View file

@ -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 <graphene/chain/protocol/buyback.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
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) );
}
} }

View file

@ -29,6 +29,7 @@
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/block_summary_object.hpp>
#include <graphene/chain/budget_record_object.hpp>
#include <graphene/chain/buyback_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/confidential_object.hpp>
@ -210,6 +211,7 @@ void database::initialize_indexes()
add_index< primary_index<simple_index<witness_schedule_object > > >();
add_index< primary_index<simple_index<budget_record_object > > >();
add_index< primary_index< special_authority_index > >();
add_index< primary_index< buyback_index > >();
}
void database::init_genesis(const genesis_state_type& genesis_state)

View file

@ -33,9 +33,11 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/budget_record_object.hpp>
#include <graphene/chain/buyback_object.hpp>
#include <graphene/chain/chain_property_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/special_authority_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/vote_count.hpp>
@ -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<by_id>();
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;

View file

@ -0,0 +1,4 @@
// #538 Buyback accounts
#ifndef HARDFORK_538_TIME
#define HARDFORK_538_TIME (fc::time_point_sec( 1455127200 ))
#endif

View file

@ -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<asset_id_type> > 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,

View file

@ -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<asset_bitasset_data_id_type> bitasset_data_id;
optional<account_id_type> 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)
)

View file

@ -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 <graphene/chain/protocol/buyback.hpp>
namespace graphene { namespace chain {
class database;
void evaluate_buyback_account_options( const database& db, const buyback_account_options& auth );
} } // graphene::chain

View file

@ -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 <graphene/chain/protocol/types.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
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<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_asset>, 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) )

View file

@ -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

View file

@ -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
///@{

View file

@ -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" )

View file

@ -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;

View file

@ -23,6 +23,7 @@
*/
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/buyback.hpp>
#include <graphene/chain/protocol/ext.hpp>
#include <graphene/chain/protocol/special_authority.hpp>
#include <graphene/chain/protocol/types.hpp>
@ -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<account_id_type>& 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)

View file

@ -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_id_type,weight_type> account_auths;
flat_map<public_key_type,weight_type> key_auths;

View file

@ -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 <graphene/chain/protocol/types.hpp>
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) );

View file

@ -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<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> 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 )

View file

@ -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() )

View file

@ -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 );
}
}
}