Merge remote-tracking branch 'graphene/develop' into bitshares at commit 1153d77dba
Conflicts: libraries/chain/include/graphene/chain/config.hpp
This commit is contained in:
commit
4f9bf7cc21
78 changed files with 2971 additions and 874 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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...");
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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) ) }
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
45
libraries/chain/buyback.cpp
Normal file
45
libraries/chain/buyback.cpp
Normal 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) );
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() );
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
} );
|
||||
}
|
||||
} }
|
||||
|
|
|
|||
103
libraries/chain/fba_object.cpp
Normal file
103
libraries/chain/fba_object.cpp
Normal 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;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
4
libraries/chain/hardfork.d/516.hf
Normal file
4
libraries/chain/hardfork.d/516.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #516 Special authorities
|
||||
#ifndef HARDFORK_516_TIME
|
||||
#define HARDFORK_516_TIME (fc::time_point_sec( 1455127200 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/533.hf
Normal file
4
libraries/chain/hardfork.d/533.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #533 Improve vote counting implementation
|
||||
#ifndef HARDFORK_533_TIME
|
||||
#define HARDFORK_533_TIME (fc::time_point_sec( 1455127200 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/538.hf
Normal file
4
libraries/chain/hardfork.d/538.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #538 Buyback accounts
|
||||
#ifndef HARDFORK_538_TIME
|
||||
#define HARDFORK_538_TIME (fc::time_point_sec( 1455127200 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/555.hf
Normal file
4
libraries/chain/hardfork.d/555.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #555 Buyback accounts
|
||||
#ifndef HARDFORK_555_TIME
|
||||
#define HARDFORK_555_TIME (fc::time_point_sec( 1455127200 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/563.hf
Normal file
4
libraries/chain/hardfork.d/563.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #563 Stealth fee routing
|
||||
#ifndef HARDFORK_563_TIME
|
||||
#define HARDFORK_563_TIME (fc::time_point_sec( 1455127200 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/572.hf
Normal file
4
libraries/chain/hardfork.d/572.hf
Normal 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
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
34
libraries/chain/include/graphene/chain/buyback.hpp
Normal file
34
libraries/chain/include/graphene/chain/buyback.hpp
Normal 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
|
||||
67
libraries/chain/include/graphene/chain/buyback_object.hpp
Normal file
67
libraries/chain/include/graphene/chain/buyback_object.hpp
Normal 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) )
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
///@{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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" )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
||||
} }
|
||||
52
libraries/chain/include/graphene/chain/fba_object.hpp
Normal file
52
libraries/chain/include/graphene/chain/fba_object.hpp
Normal 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) )
|
||||
|
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
@ -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>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
52
libraries/chain/include/graphene/chain/protocol/buyback.hpp
Normal file
52
libraries/chain/include/graphene/chain/protocol/buyback.hpp
Normal 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) );
|
||||
200
libraries/chain/include/graphene/chain/protocol/ext.hpp
Normal file
200
libraries/chain/include/graphene/chain/protocol/ext.hpp
Normal 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
|
||||
47
libraries/chain/include/graphene/chain/protocol/fba.hpp
Normal file
47
libraries/chain/include/graphene/chain/protocol/fba.hpp
Normal 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) )
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
34
libraries/chain/include/graphene/chain/special_authority.hpp
Normal file
34
libraries/chain/include/graphene/chain/special_authority.hpp
Normal 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
|
||||
|
|
@ -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)
|
||||
)
|
||||
77
libraries/chain/include/graphene/chain/vote_count.hpp
Normal file
77
libraries/chain/include/graphene/chain/vote_count.hpp
Normal 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
|
||||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
70
libraries/chain/is_authorized_asset.cpp
Normal file
70
libraries/chain/is_authorized_asset.cpp
Normal 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
|
||||
|
|
@ -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) );
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()) )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
70
libraries/chain/special_authority.cpp
Normal file
70
libraries/chain/special_authority.cpp
Normal 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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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) ) }
|
||||
|
|
|
|||
|
|
@ -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" )
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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} )
|
||||
|
||||
|
|
|
|||
134
programs/build_helpers/check_reflect.py
Executable file
134
programs/build_helpers/check_reflect.py
Executable 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)
|
||||
214
programs/build_helpers/member_enumerator.cpp
Normal file
214
programs/build_helpers/member_enumerator.cpp
Normal 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;
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue