Merge remote-tracking branch 'graphene/develop' into bitshares at commit 1153d77dba

Conflicts:
	libraries/chain/include/graphene/chain/config.hpp
This commit is contained in:
theoreticalbts 2016-02-16 11:32:14 -05:00
commit 4f9bf7cc21
78 changed files with 2971 additions and 874 deletions

View file

@ -344,6 +344,12 @@ namespace graphene { namespace app {
break;
case impl_budget_record_object_type:
break;
case impl_special_authority_object_type:
break;
case impl_buyback_object_type:
break;
case impl_fba_accumulator_object_type:
break;
}
}
return result;

View file

@ -375,9 +375,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());
@ -393,8 +411,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...");

View file

@ -960,8 +960,8 @@ vector<call_order_object> database_api_impl::get_margin_positions( const account
{
const auto& idx = _db.get_index_type<call_order_index>();
const auto& aidx = idx.indices().get<by_account>();
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<call_order_object> result;
while( start != end )
{

View file

@ -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<account_id_type>& result )

View file

@ -76,14 +76,19 @@ add_library( graphene_chain
withdraw_permission_evaluator.cpp
worker_evaluator.cpp
confidential_evaluator.cpp
special_authority.cpp
buyback.cpp
account_object.cpp
asset_object.cpp
fba_object.cpp
proposal_object.cpp
vesting_balance_object.cpp
block_database.cpp
is_authorized_asset.cpp
${HEADERS}
"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp"
)

View file

@ -25,10 +25,14 @@
#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>
#include <graphene/chain/internal_exceptions.hpp>
#include <graphene/chain/special_authority.hpp>
#include <graphene/chain/special_authority_object.hpp>
#include <algorithm>
@ -53,6 +57,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 +78,13 @@ 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 );
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;
FC_ASSERT( op.options.num_witness <= chain_params.maximum_witness_count,
@ -102,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)
@ -136,6 +154,16 @@ 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>([&](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( 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 )
@ -159,6 +187,30 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
p.parameters.current_fees->get<account_create_operation>().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;
} );
}
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)) }
@ -166,6 +218,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 +234,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 +257,49 @@ 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){
if( o.owner ) a.owner = *o.owner;
if( o.active ) a.active = *o.active;
database& d = db();
bool sa_before, sa_after;
d.modify( *acnt, [&](account_object& a){
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();
});
if( sa_before & (!sa_after) )
{
const auto& sa_idx = d.get_index_type< special_authority_index >().indices().get<by_account>();
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) ) }

View file

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

View file

@ -28,6 +28,7 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
#include <functional>
@ -163,11 +164,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( 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);
FC_ASSERT( (asset_dyn_data->current_supply + o.asset_to_issue.amount) <= a.options.max_supply );
@ -199,11 +196,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( 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);
FC_ASSERT( (asset_dyn_data->current_supply - o.amount_to_reserve.amount) >= 0 );
@ -270,9 +263,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),

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

@ -26,6 +26,8 @@
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/chain/confidential_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/hardfork.hpp>
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

View file

@ -417,9 +417,10 @@ void database::pop_block()
auto head_id = head_block_id();
optional<signed_block> 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() );

View file

@ -23,19 +23,23 @@
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#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>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/special_authority_object.hpp>
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/withdraw_permission_object.hpp>
@ -208,6 +212,10 @@ void database::initialize_indexes()
add_index< primary_index<simple_index<chain_property_object > > >();
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 > >();
add_index< primary_index< simple_index< fba_accumulator_object > > >();
}
void database::init_genesis(const genesis_state_type& genesis_state)
@ -314,7 +322,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
a.statistics = create<account_statistics_object>([&](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;
@ -337,9 +345,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 );
@ -362,9 +370,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) );
@ -489,7 +497,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;
}
@ -554,13 +562,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<asset_index>().indices().get<by_symbol>();
@ -644,8 +652,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.witness_accounts.insert(get(witness_id_type(i)).witness_account);
p.active_witnesses.insert(witness_id_type(i));
}
});
@ -661,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>([&]( 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>([&]( 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>([&]( 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<fba_accumulator_object>().get_next_id() == fba_accumulator_id_type( fba_accumulator_id_count ) );
debug_dump();
_undo_db.enable();

View file

@ -28,14 +28,21 @@
#include <fc/uint128.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/hardfork.hpp>
#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/fba_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>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/worker_object.hpp>
@ -183,32 +190,43 @@ void database::update_active_witnesses()
}
// Update witness authority
modify( get(GRAPHENE_WITNESS_ACCOUNT), [&]( account_object& a ) {
uint64_t total_votes = 0;
map<account_id_type, uint64_t> weights;
a.active.weight_threshold = 0;
a.active.clear();
for( const witness_object& wit : wits )
modify( get(GRAPHENE_WITNESS_ACCOUNT), [&]( account_object& a )
{
if( head_block_time() < HARDFORK_533_TIME )
{
weights.emplace(wit.witness_account, _vote_tally_buffer[wit.vote_id]);
total_votes += _vote_tally_buffer[wit.vote_id];
}
uint64_t total_votes = 0;
map<account_id_type, uint64_t> weights;
a.active.weight_threshold = 0;
a.active.clear();
// 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 )
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;
}
a.active.weight_threshold /= 2;
a.active.weight_threshold += 1;
}
else
{
// 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;
vote_counter vc;
for( const witness_object& wit : wits )
vc.add( wit.witness_account, _vote_tally_buffer[wit.vote_id] );
vc.finish( a.active );
}
a.active.weight_threshold /= 2;
a.active.weight_threshold += 1;
});
} );
modify(gpo, [&]( global_property_object& gp ){
gp.active_witnesses.clear();
@ -218,13 +236,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() }
@ -256,32 +267,43 @@ void database::update_active_committee_members()
// Update committee authorities
if( !committee_members.empty() )
{
modify(get(GRAPHENE_COMMITTEE_ACCOUNT), [&](account_object& a) {
uint64_t total_votes = 0;
map<account_id_type, uint64_t> weights;
a.active.weight_threshold = 0;
a.active.clear();
for( const committee_member_object& del : committee_members )
modify(get(GRAPHENE_COMMITTEE_ACCOUNT), [&](account_object& a)
{
if( head_block_time() < HARDFORK_533_TIME )
{
weights.emplace(del.committee_member_account, _vote_tally_buffer[del.vote_id]);
total_votes += _vote_tally_buffer[del.vote_id];
}
uint64_t total_votes = 0;
map<account_id_type, uint64_t> weights;
a.active.weight_threshold = 0;
a.active.clear();
// 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 )
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;
}
a.active.weight_threshold /= 2;
a.active.weight_threshold += 1;
}
else
{
// 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;
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 );
}
a.active.weight_threshold /= 2;
a.active.weight_threshold += 1;
});
} );
modify(get(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT), [&](account_object& a) {
a.active = get(GRAPHENE_COMMITTEE_ACCOUNT).active;
});
@ -441,10 +463,234 @@ 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<by_id>();
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 );
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<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();
distribute_fba_balances(*this);
create_buyback_orders(*this);
struct vote_tally_helper {
database& d;
const global_property_object& props;
@ -521,7 +767,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<uint64_t>& target): target(target){}
@ -533,6 +782,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();

View file

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

View file

@ -413,9 +413,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;
});
}
}
}
} FC_CAPTURE_AND_RETHROW() }

View file

@ -25,10 +25,12 @@
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
@ -61,7 +63,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) );
}
@ -102,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;
} );
}
} }

View file

@ -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 <graphene/chain/database.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/fba_object.hpp>
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;
}
} }

View file

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

View file

@ -0,0 +1,4 @@
// #516 Special authorities
#ifndef HARDFORK_516_TIME
#define HARDFORK_516_TIME (fc::time_point_sec( 1455127200 ))
#endif

View file

@ -0,0 +1,4 @@
// #533 Improve vote counting implementation
#ifndef HARDFORK_533_TIME
#define HARDFORK_533_TIME (fc::time_point_sec( 1455127200 ))
#endif

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

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

View file

@ -0,0 +1,4 @@
// #563 Stealth fee routing
#ifndef HARDFORK_563_TIME
#define HARDFORK_563_TIME (fc::time_point_sec( 1455127200 ))
#endif

View file

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

View file

@ -204,6 +204,31 @@ namespace graphene { namespace chain {
* Vesting balance which receives cashback_reward deposits.
*/
optional<vesting_balance_id_type> cashback_vb;
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.
* 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)
|| (active_special_authority.which() != special_authority::tag< no_special_authority >::value);
}
template<typename DB>
const vesting_balance_object& cashback_balance(const DB& db)const
{
@ -233,12 +258,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; }
};
@ -351,8 +370,12 @@ 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)
(cashback_vb) )
(whitelisted_accounts)(blacklisted_accounts)
(cashback_vb)
(owner_special_authority)(active_special_authority)
(top_n_control_flags)
(allowed_assets)
)
FC_REFLECT_DERIVED( graphene::chain::account_balance_object,
(graphene::db::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
@ -233,7 +235,12 @@ namespace graphene { namespace chain {
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_symbol>, member<asset_object, string, &asset_object::symbol> >,
ordered_non_unique< tag<by_type>, const_mem_fun<asset_object, bool, &asset_object::is_market_issued> >
ordered_unique< tag<by_type>,
composite_key< asset_object,
const_mem_fun<asset_object, bool, &asset_object::is_market_issued>,
member< object, object_id_type, &object::id >
>
>
>
> asset_object_multi_index_type;
typedef generic_index<asset_object, asset_object_multi_index_type> asset_index;
@ -261,4 +268,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_DERIVED( graphene::chain::buyback_object, (graphene::db::object), (asset_to_buy) )

View file

@ -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<transfer_to_blind_evaluator>
{
@ -37,6 +37,8 @@ class transfer_to_blind_evaluator : public evaluator<transfer_to_blind_evaluator
void_result do_evaluate( const transfer_to_blind_operation& o );
void_result do_apply( const transfer_to_blind_operation& o ) ;
virtual void pay_fee() override;
};
class transfer_from_blind_evaluator : public evaluator<transfer_from_blind_evaluator>
@ -46,6 +48,8 @@ class transfer_from_blind_evaluator : public evaluator<transfer_from_blind_evalu
void_result do_evaluate( const transfer_from_blind_operation& o );
void_result do_apply( const transfer_from_blind_operation& o ) ;
virtual void pay_fee() override;
};
class blind_transfer_evaluator : public evaluator<blind_transfer_evaluator>
@ -55,6 +59,8 @@ class blind_transfer_evaluator : public evaluator<blind_transfer_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

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
@ -166,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))

View file

@ -273,14 +273,6 @@ namespace graphene { namespace chain {
operation::tag<typename EvaluatorType::operation_type>::value].reset( new op_evaluator_impl<EvaluatorType>() );
}
template<typename EvaluatorType>
void register_evaluation_observer( evaluation_observer& observer )
{
unique_ptr<op_evaluator>& op_eval = _operation_evaluators[operation::tag<typename EvaluatorType::operation_type>::value];
op_eval->eval_observers.push_back( &observer );
return;
}
//////////////////// db_balance.cpp ////////////////////
/**
@ -370,7 +362,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 );
@ -412,12 +404,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

@ -33,47 +33,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:
@ -138,6 +97,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;
@ -152,8 +116,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<evaluation_observer*> eval_observers;
};
template<typename T>
@ -162,57 +124,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<fc::exception> 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);
}
};

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

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

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>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
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) )

View file

@ -52,7 +52,6 @@ namespace graphene { namespace chain {
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
// n.b. witness scheduling is done by witness_schedule object
flat_set<account_id_type> witness_accounts; // updated once per maintenance interval
};
/**

View file

@ -0,0 +1,55 @@
/*
* 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;
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.
*/
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;
bool slow_check = detail::_is_authorized_asset( d, acct, asset_obj );
return slow_check;
}
} }

View file

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

View file

@ -72,9 +72,13 @@ struct by_account;
typedef multi_index_container<
limit_order_object,
indexed_by<
ordered_unique< tag<by_id>,
member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_expiration>, member< limit_order_object, time_point_sec, &limit_order_object::expiration> >,
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_expiration>,
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<by_price>,
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<price>, std::less<object_id_type> >
>,
ordered_non_unique< tag<by_account>, member<limit_order_object, account_id_type, &limit_order_object::seller>>
ordered_unique< tag<by_account>,
composite_key< limit_order_object,
member<limit_order_object, account_id_type, &limit_order_object::seller>,
member<object, object_id_type, &object::id>
>
>
>
> limit_order_multi_index_type;
@ -168,19 +177,22 @@ typedef multi_index_container<
force_settlement_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_account>,
member<force_settlement_object, account_id_type, &force_settlement_object::owner>
ordered_unique< tag<by_account>,
composite_key< force_settlement_object,
member<force_settlement_object, account_id_type, &force_settlement_object::owner>,
member< object, object_id_type, &object::id >
>
>,
ordered_non_unique< tag<by_expiration>,
ordered_unique< tag<by_expiration>,
composite_key< force_settlement_object,
const_mem_fun<force_settlement_object, asset_id_type, &force_settlement_object::settlement_asset_id>,
member<force_settlement_object, time_point_sec, &force_settlement_object::settlement_date>
member<force_settlement_object, time_point_sec, &force_settlement_object::settlement_date>,
member< object, object_id_type, &object::id >
>
>
>
> force_settlement_object_multi_index_type;
typedef generic_index<call_order_object, call_order_multi_index_type> call_order_index;
typedef generic_index<force_settlement_object, force_settlement_object_multi_index_type> force_settlement_index;
@ -194,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)
)

View file

@ -23,15 +23,19 @@
*/
#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>
#include <graphene/chain/protocol/vote.hpp>
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 +65,16 @@ 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;
optional< buyback_account_options > buyback_options;
};
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,11 +95,19 @@ 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;
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 );
}
};
/**
@ -98,8 +119,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;
};
@ -109,22 +138,25 @@ namespace graphene { namespace chain {
/// New owner authority. If set, this operation requires owner authority to execute.
optional<authority> 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<authority> active;
/// New account options
optional<account_options> 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<account_id_type>& a )const
{ if( owner ) a.insert( account ); }
{ if( is_owner_update() ) a.insert( account ); }
void get_required_active_authorities( flat_set<account_id_type>& a )const
{ if( !owner ) a.insert( account ); }
{ if( !is_owner_update() ) a.insert( account ); }
};
/**
@ -236,16 +268,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)(buyback_options) )
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))

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

@ -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 <fc/io/varint.hpp>
#include <fc/reflect/reflect.hpp>
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<typename Member, class Class, Member (Class::*member)>
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<typename Member, class Class, Member (Class::*member)>
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<T>& value )
{
graphene_extension_pack_count_visitor<T> count_vtor( value.value );
fc::reflector<T>::visit( count_vtor );
fc::raw::pack( stream, unsigned_int( count_vtor.count ) );
graphene_extension_pack_read_visitor<Stream,T> read_vtor( stream, value.value );
fc::reflector<T>::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<T>& value )
{
graphene_extension_unpack_visitor<Stream, T> vtor( s, value.value );
fc::reflector<T>::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<typename Member, class Class, Member (Class::*member)>
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<T>& value )
{
graphene_extension_from_variant_visitor<T> vtor( var.get_object(), value.value );
fc::reflector<T>::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<typename Member, class Class, Member (Class::*member)>
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<T>& value, fc::variant& var )
{
graphene_extension_to_variant_visitor<T> vtor( value.value );
fc::reflector<T>::visit( vtor );
var = vtor.mvo;
}
} // fc

View file

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

View file

@ -29,6 +29,8 @@
#include <graphene/chain/protocol/balance.hpp>
#include <graphene/chain/protocol/custom.hpp>
#include <graphene/chain/protocol/committee_member.hpp>
#include <graphene/chain/protocol/confidential.hpp>
#include <graphene/chain/protocol/fba.hpp>
#include <graphene/chain/protocol/market.hpp>
#include <graphene/chain/protocol/proposal.hpp>
#include <graphene/chain/protocol/transfer.hpp>
@ -36,7 +38,6 @@
#include <graphene/chain/protocol/withdraw_permission.hpp>
#include <graphene/chain/protocol/witness.hpp>
#include <graphene/chain/protocol/worker.hpp>
#include <graphene/chain/protocol/confidential.hpp>
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

View file

@ -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 <graphene/chain/protocol/types.hpp>
#include <fc/reflect/reflect.hpp>
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 )

View file

@ -152,7 +152,10 @@ 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,
impl_buyback_object_type,
impl_fba_accumulator_object_type
};
//typedef fc::unsigned_int object_id_type;
@ -201,6 +204,9 @@ namespace graphene { namespace chain {
class chain_property_object;
class witness_schedule_object;
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;
@ -218,6 +224,9 @@ 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 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<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
typedef fc::ripemd160 block_id_type;
@ -347,6 +356,9 @@ 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)
(impl_buyback_object_type)
(impl_fba_accumulator_object_type)
)
FC_REFLECT_TYPENAME( graphene::chain::share_type )
@ -375,6 +387,10 @@ FC_REFLECT_TYPENAME( graphene::chain::transaction_obj_id_type )
FC_REFLECT_TYPENAME( graphene::chain::block_summary_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_transaction_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::budget_record_id_type )
FC_REFLECT_TYPENAME( graphene::chain::special_authority_id_type )
FC_REFLECT_TYPENAME( graphene::chain::buyback_id_type )
FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type )
FC_REFLECT( graphene::chain::void_t, )
FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags,

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

View file

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

View file

@ -0,0 +1,77 @@
/*
* 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/authority.hpp>
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 )
{
if( votes == 0 )
return;
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<weight_type>::max() );
total_votes += scaled_votes;
assert( total_votes <= std::numeric_limits<uint32_t>::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<uint32_t>::max() );
uint32_t weight = uint32_t( total_votes );
weight = (weight >> 1)+1;
auth.weight_threshold = weight;
out_auth = auth;
}
bool is_empty()const
{
return (total_votes == 0);
}
uint64_t last_votes = std::numeric_limits<uint64_t>::max();
uint64_t total_votes = 0;
int8_t bitshift = -1;
authority auth;
};
} } // graphene::chain

View file

@ -24,6 +24,7 @@
#pragma once
#include <graphene/chain/protocol/authority.hpp>
#include <graphene/db/generic_index.hpp>
#include <boost/multi_index/composite_key.hpp>
namespace graphene { namespace chain {
@ -78,9 +79,24 @@ namespace graphene { namespace chain {
withdraw_permission_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_non_unique< tag<by_from>, member<withdraw_permission_object, account_id_type, &withdraw_permission_object::withdraw_from_account> >,
ordered_non_unique< tag<by_authorized>, member<withdraw_permission_object, account_id_type, &withdraw_permission_object::authorized_account> >,
ordered_non_unique< tag<by_expiration>, member<withdraw_permission_object, time_point_sec, &withdraw_permission_object::expiration> >
ordered_unique< tag<by_from>,
composite_key< withdraw_permission_object,
member<withdraw_permission_object, account_id_type, &withdraw_permission_object::withdraw_from_account>,
member< object, object_id_type, &object::id >
>
>,
ordered_unique< tag<by_authorized>,
composite_key< withdraw_permission_object,
member<withdraw_permission_object, account_id_type, &withdraw_permission_object::authorized_account>,
member< object, object_id_type, &object::id >
>
>,
ordered_unique< tag<by_expiration>,
composite_key< withdraw_permission_object,
member<withdraw_permission_object, time_point_sec, &withdraw_permission_object::expiration>,
member< object, object_id_type, &object::id >
>
>
>
> withdraw_permission_object_multi_index_type;
@ -96,4 +112,5 @@ FC_REFLECT_DERIVED( graphene::chain::withdraw_permission_object, (graphene::db::
(withdrawal_period_sec)
(period_start_time)
(expiration)
(claimed_this_period)
)

View file

@ -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 <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene { namespace chain {
namespace detail {
bool _is_authorized_asset(
const database& d,
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() )
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;
}
} // detail
} } // graphene::chain

View file

@ -23,11 +23,14 @@
*/
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
#include <graphene/chain/protocol/market.hpp>
@ -49,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( _seller->is_authorized_asset( *_sell_asset, d ) );
if( _receive_asset->options.flags & white_list ) FC_ASSERT( _seller->is_authorized_asset( *_receive_asset, d ) );
}
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",
("balance",d.get_balance(*_seller,*_sell_asset))("amount_to_sell",op.amount_to_sell) );

View file

@ -203,6 +203,23 @@ 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 );
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 );
}
}
}
@ -221,7 +238,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 );
@ -237,9 +264,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 )

View file

@ -161,7 +161,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

View file

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

View file

@ -38,29 +38,6 @@ void balance_claim_operation::validate()const
FC_ASSERT( balance_owner_key != public_key_type() );
}
struct required_auth_visitor
{
typedef void result_type;
vector<authority>& result;
required_auth_visitor( vector<authority>& r ):result(r){}
/** for most operations this is a no-op */
template<typename T>
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<authority>& result )
{
op.visit( required_auth_visitor( result ) );
}
/**
* @brief Used to validate operations in a polymorphic manner
*/

View file

@ -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 <graphene/chain/protocol/special_authority.hpp>
#include <graphene/chain/database.hpp>
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

View file

@ -25,6 +25,7 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
namespace graphene { namespace chain {
void_result transfer_evaluator::do_evaluate( const transfer_operation& op )
@ -35,34 +36,23 @@ 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 {
if( asset_type.options.flags & white_list )
{
GRAPHENE_ASSERT(
from_account.is_authorized_asset( asset_type, d ),
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 ),
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( from_account.is_authorized_asset( asset_type, d ) );
}
// the above becomes no-op after hardfork because this check will then be performed in evaluator
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( asset_type.is_transfer_restricted() )
{
@ -108,18 +98,13 @@ 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);
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

View file

@ -27,6 +27,7 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
namespace graphene { namespace chain {
@ -69,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( to.is_authorized_asset( _asset, d ) );
FC_ASSERT( from.is_authorized_asset( _asset, d ) );
}
}
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 ) );
}
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) ) }

View file

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

View file

@ -23,7 +23,6 @@
*/
#pragma once
#include <graphene/db/object.hpp>
#include <graphene/db/type_serializer.hpp>
#include <fc/interprocess/file_mapping.hpp>
#include <fc/io/raw.hpp>
#include <fc/io/json.hpp>

View file

@ -51,28 +51,24 @@ 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; }
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<uint64_t>()(v.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 +102,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,27 +114,21 @@ 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<typename DB>
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& 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 == 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 size_t hash_value( object_id v ) { return std::hash<uint64_t>()(v.instance.value); }
unsigned_int instance;

View file

@ -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 <fc/reflect/variant.hpp>
#include <fc/crypto/ripemd160.hpp>
#include <fc/crypto/sha224.hpp>
#include <sstream>
#include <set>
namespace graphene { namespace db {
using std::set;
using std::map;
using fc::time_point_sec;
using fc::time_point;
using std::string;
std::set<std::string>& processed_types();
std::stringstream& current_stream( std::unique_ptr<std::stringstream>&& s = std::unique_ptr<std::stringstream>() );
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<typename T>
void generate_serializer();
template<typename T>
void register_serializer();
extern map<string, size_t > st;
extern vector<std::function<void()>> serializers;
bool register_serializer( const string& name, std::function<void()> sr );
template<typename T> struct js_name { static std::string name(){ return remove_namespace(fc::get_typename<T>::name()); }; };
template<typename T, size_t N>
struct js_name<fc::array<T,N>>
{
static std::string name(){ return "fixed_array "+ fc::to_string(N) + ", " + remove_namespace(fc::get_typename<T>::name()); };
};
template<size_t N> struct js_name<fc::array<char,N>> { static std::string name(){ return "bytes "+ fc::to_string(N); }; };
template<size_t N> struct js_name<fc::array<uint8_t,N>> { static std::string name(){ return "bytes "+ fc::to_string(N); }; };
template<typename T> struct js_name< fc::optional<T> > { static std::string name(){ return "optional " + js_name<T>::name(); } };
template<> struct js_name< object_id_type > { static std::string name(){ return "object_id_type"; } };
template<typename T> struct js_name< fc::flat_set<T> > { static std::string name(){ return "set " + js_name<T>::name(); } };
template<typename T> struct js_name< std::vector<T> > { static std::string name(){ return "array " + js_name<T>::name(); } };
template<typename T> struct js_name< fc::safe<T> > { static std::string name(){ return js_name<T>::name(); } };
template<> struct js_name< std::vector<char> > { static std::string name(){ return "bytes()"; } };
//template<> struct js_name< op_wrapper > { static std::string name(){ return "operation "; } };
template<> struct js_name<fc::uint160> { static std::string name(){ return "bytes 20"; } };
template<> struct js_name<fc::sha224> { static std::string name(){ return "bytes 28"; } };
template<> struct js_name<fc::unsigned_int> { static std::string name(){ return "varuint32"; } };
template<> struct js_name<fc::signed_int> { static std::string name(){ return "varint32"; } };
template<> struct js_name< time_point_sec > { static std::string name(){ return "time_point_sec"; } };
template<uint8_t S, uint8_t T, typename O>
struct js_name<graphene::db::object_id<S,T,O> >
{
static std::string name(){
return "protocol_id_type \"" + remove_namespace(fc::get_typename<O>::name()) + "\"";
};
};
template<typename T> struct js_name< std::set<T> > { static std::string name(){ return "set " + js_name<T>::name(); } };
template<typename K, typename V>
struct js_name< std::map<K,V> > { static std::string name(){ return "map (" + js_name<K>::name() + "), (" + js_name<V>::name() +")"; } };
template<typename K, typename V>
struct js_name< fc::flat_map<K,V> > { static std::string name(){ return "map (" + js_name<K>::name() + "), (" + js_name<V>::name() +")"; } };
template<typename... T> struct js_sv_name;
template<typename A> struct js_sv_name<A>
{ static std::string name(){ return "\n " + js_name<A>::name(); } };
template<typename A, typename... T>
struct js_sv_name<A,T...> { static std::string name(){ return "\n " + js_name<A>::name() +" " + js_sv_name<T...>::name(); } };
template<typename... T>
struct js_name< fc::static_variant<T...> >
{
static std::string name( std::string n = ""){
static const std::string name = n;
if( name == "" )
return "static_variant [" + js_sv_name<T...>::name() + "\n]";
else return name;
}
};
template<typename T, bool reflected = fc::reflector<T>::is_defined::value>
struct serializer;
struct register_type_visitor
{
typedef void result_type;
template<typename Type>
result_type operator()( const Type& op )const { serializer<Type>::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<typename Type>
result_type operator()( const Type& op )const
{
obj(remove_namespace( fc::get_typename<Type>::name()),t);
}
};
class serialize_member_visitor
{
public:
template<typename Member, class Class, Member (Class::*member)>
void operator()( const char* name )const
{
current_stream() << " " << name << " : " << fc::get_typename<Member>::name() <<"\n";
}
};
template<typename T>
struct serializer<T,false>
{
static_assert( fc::reflector<T>::is_defined::value == false, "invalid template arguments" );
static void init()
{}
static void generate()
{}
};
template<typename T, size_t N>
struct serializer<fc::array<T,N>,false>
{
static void init() { serializer<T>::init(); }
static void generate() {}
};
template<typename T>
struct serializer<std::vector<T>,false>
{
static void init() { serializer<T>::init(); }
static void generate() {}
};
/*
template<>
struct serializer<std::vector<operation>,false>
{
static void init() { }
static void generate() {}
};
*/
template<>
struct serializer<object_id_type,true>
{
static void init() {}
static void generate() {}
};
template<>
struct serializer<uint64_t,false>
{
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<size_t,false> { static void init() {} static void generate() {} };
#endif
template<> struct serializer<int64_t,false> { static void init() {} static void generate() {} };
template<> struct serializer<int64_t,true> { static void init() {} static void generate() {} };
template<typename T>
struct serializer<fc::optional<T>,false>
{
static void init() { serializer<T>::init(); }
static void generate(){}
};
template<uint8_t SpaceID, uint8_t TypeID, typename T>
struct serializer< graphene::db::object_id<SpaceID,TypeID,T> ,true>
{
static void init() {}
static void generate() {}
};
template<typename... T>
struct serializer< fc::static_variant<T...>, false >
{
static void init()
{
auto name = js_name<fc::static_variant<T...>>::name();
if( processed_types().find( name ) == processed_types().end() )
{
processed_types().insert( name );
fc::static_variant<T...> var;
for( int i = 0; i < var.count(); ++i )
{
var.set_which(i);
var.visit( register_type_visitor() );
}
register_serializer( js_name<fc::static_variant<T...>>::name(), [=](){ generate(); } );
}
}
static void generate()
{
current_stream() << js_name<fc::static_variant<T...>>::name() << " = static_variant [" + js_sv_name<T...>::name() + "\n]\n\n";
}
};
class register_member_visitor
{
public:
template<typename Member, class Class, Member (Class::*member)>
void operator()( const char* name )const
{
serializer<Member>::init();
}
};
template<typename T, bool reflected>
struct serializer
{
static_assert( fc::reflector<T>::is_defined::value == reflected, "invalid template arguments" );
static void init()
{
auto name = js_name<T>::name();
if( st.find(name) == st.end() )
{
fc::reflector<T>::visit( register_member_visitor() );
register_serializer( name, [=](){ generate(); } );
}
}
static void generate()
{
auto name = remove_namespace( js_name<T>::name() );
if( name == "int64" ) return;
current_stream() << "" << name
<< " = new Serializer( \n"
<< " \"" + name + "\"\n";
fc::reflector<T>::visit( serialize_member_visitor() );
current_stream() <<")\n\n";
}
};
template<typename T>
std::string get_type_description()
{
current_stream( std::unique_ptr<std::stringstream>(new std::stringstream()) );
processed_types().clear();
serializer<T>::init();
for( const auto& gen : serializers )
gen();
return current_stream().str();
}
} } // graphene::db

View file

@ -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 <graphene/db/index.hpp>
namespace graphene { namespace db {
map<string, size_t > st;
vector<std::function<void()>> serializers;
std::set<std::string>& processed_types()
{
static std::set<std::string> p;
return p;
}
std::stringstream& current_stream( std::unique_ptr<std::stringstream>&& s )
{
static std::unique_ptr<std::stringstream> 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<void()> sr )
{
if( st.find(name) == st.end() )
{
serializers.push_back( sr );
st[name] = serializers.size() - 1;
return true;
}
return false;
}
} }// graphene

View file

@ -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<T>::name();
std::string name = clean_name( fc::get_typename<T>::name() );
m.name_to_which[ name ] = which;
m.which_to_name.push_back( name );
}

View file

@ -1985,8 +1985,8 @@ public:
limit_order_cancel_operation op;
op.fee_paying_account = get_object<limit_order_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);
@ -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);
}
@ -3446,7 +3446,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;
}
}
@ -3634,8 +3644,8 @@ vector<asset> 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<by_to_asset_used>();
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 )
@ -3679,7 +3689,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 );
@ -3693,6 +3703,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 );
//} catch ( ... ){}
}
ilog( "about to broadcast" );
conf.trx = sign_transaction( conf.trx, broadcast );
@ -3989,7 +4017,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<stealth_confirmation::memo_data>( plain_memo );
result.to_key = *conf.to;
result.to_label = get_key_label( result.to_key );
if( memo.from )
@ -4013,9 +4040,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 ) );
auto 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;
@ -4025,21 +4049,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();

View file

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

View file

@ -0,0 +1,134 @@
#!/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
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))
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)

View file

@ -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 <graphene/chain/protocol/protocol.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/withdraw_permission_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <fc/smart_ref_impl.hpp>
#include <iostream>
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<typename Member, class Class, Member (Class::*member)>
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<typename T>
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<T...> dummy2;
static_variant_visitor vtor( this );
for( int w=0; w<dummy2.count(); w++ )
{
dummy2.set_which(w);
dummy2.visit( vtor );
}
}
template<typename IsReflected=fc::false_type>
struct if_reflected
{
template< typename T >
static void process_class( class_processor* proc, const T* dummy )
{
std::string tname = fc::get_typename<T>::name();
std::cerr << "skipping non-reflected class " << tname << std::endl;
}
};
template<>
struct if_reflected<fc::true_type>
{
template< typename T >
static void process_class( class_processor* proc, const T* dummy )
{
std::string tname = fc::get_typename<T>::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<T> vtor( proc );
fc::reflector<T>::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<T>::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<signed_block>(result);
//graphene::member_enumerator::process_class<transfer_operation>(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;
}

View file

@ -23,14 +23,20 @@
*/
#include <graphene/chain/protocol/protocol.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/withdraw_permission_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/confidential_object.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/withdraw_permission_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/worker_object.hpp>
#include <fc/smart_ref_impl.hpp>
#include <iostream>

View file

@ -32,6 +32,7 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
@ -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;
@ -317,18 +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();
auto slots_to_miss = db.get_slot_at_time(timestamp) - 1;
if( slots_to_miss <= 0 ) return;
generate_block(~0, init_account_priv_key, slots_to_miss);
generate_block(skip);
auto slots_to_miss = db.get_slot_at_time(timestamp);
if( slots_to_miss <= 1 )
return;
--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(
@ -437,7 +442,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();
@ -464,7 +469,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));
@ -482,7 +487,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;
@ -501,7 +506,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;
@ -1031,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 )

View file

@ -28,6 +28,8 @@
#include <fc/io/json.hpp>
#include <fc/smart_ref_impl.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <iostream>
using namespace graphene::db;
@ -181,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",
@ -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 {

View file

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

View file

@ -24,7 +24,12 @@
#include <fc/smart_ref_impl.hpp>
#include <fc/uint128.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
@ -615,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
@ -714,11 +728,217 @@ 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();
}
}
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()

View file

@ -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 );
@ -1152,8 +1152,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<account_id_type> active_witnesses(global_props.witness_accounts.begin(),
global_props.witness_accounts.end());
vector<account_id_type> 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;

View file

@ -26,6 +26,7 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/account_object.hpp>
@ -73,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);
@ -280,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));
@ -1332,4 +1333,291 @@ 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_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()

View file

@ -27,6 +27,7 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
@ -53,7 +54,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 );
@ -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;