Merge branch 'github_master'
This commit is contained in:
commit
9261314997
52 changed files with 1153 additions and 253 deletions
|
|
@ -260,7 +260,7 @@ namespace graphene { namespace app {
|
|||
{
|
||||
case impl_global_property_object_type:{
|
||||
} case impl_dynamic_global_property_object_type:{
|
||||
} case impl_index_meta_object_type:{
|
||||
} case impl_reserved0_object_type:{
|
||||
} case impl_asset_dynamic_data_type:{
|
||||
} case impl_asset_bitasset_data_type:{
|
||||
break;
|
||||
|
|
@ -293,6 +293,7 @@ namespace graphene { namespace app {
|
|||
} case impl_account_transaction_history_object_type:{
|
||||
} case impl_chain_property_object_type: {
|
||||
} case impl_witness_schedule_object_type: {
|
||||
} case impl_budget_record_object_type: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ namespace detail {
|
|||
reset_p2p_node(_data_dir);
|
||||
reset_websocket_server();
|
||||
reset_websocket_tls_server();
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
optional< api_access_info > get_api_access_info(const string& username)const
|
||||
{
|
||||
|
|
@ -859,7 +859,15 @@ void application::initialize(const fc::path& data_dir, const boost::program_opti
|
|||
|
||||
void application::startup()
|
||||
{
|
||||
try {
|
||||
my->startup();
|
||||
} catch ( const fc::exception& e ) {
|
||||
elog( "${e}", ("e",e.to_detail_string()) );
|
||||
throw;
|
||||
} catch ( ... ) {
|
||||
elog( "unexpected exception" );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<abstract_plugin> application::get_plugin(const string& name) const
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
std::string get_transaction_hex(const signed_transaction& trx)const;
|
||||
set<public_key_type> get_required_signatures( const signed_transaction& trx, const flat_set<public_key_type>& available_keys )const;
|
||||
set<public_key_type> get_potential_signatures( const signed_transaction& trx )const;
|
||||
set<address> get_potential_address_signatures( const signed_transaction& trx )const;
|
||||
bool verify_authority( const signed_transaction& trx )const;
|
||||
bool verify_account_authority( const string& name_or_id, const flat_set<public_key_type>& signers )const;
|
||||
processed_transaction validate_transaction( const signed_transaction& trx )const;
|
||||
|
|
@ -1213,6 +1214,10 @@ set<public_key_type> database_api::get_potential_signatures( const signed_transa
|
|||
{
|
||||
return my->get_potential_signatures( trx );
|
||||
}
|
||||
set<address> database_api::get_potential_address_signatures( const signed_transaction& trx )const
|
||||
{
|
||||
return my->get_potential_address_signatures( trx );
|
||||
}
|
||||
|
||||
set<public_key_type> database_api_impl::get_potential_signatures( const signed_transaction& trx )const
|
||||
{
|
||||
|
|
@ -1242,6 +1247,31 @@ set<public_key_type> database_api_impl::get_potential_signatures( const signed_t
|
|||
return result;
|
||||
}
|
||||
|
||||
set<address> database_api_impl::get_potential_address_signatures( const signed_transaction& trx )const
|
||||
{
|
||||
set<address> result;
|
||||
trx.get_required_signatures(
|
||||
_db.get_chain_id(),
|
||||
flat_set<public_key_type>(),
|
||||
[&]( account_id_type id )
|
||||
{
|
||||
const auto& auth = id(_db).active;
|
||||
for( const auto& k : auth.get_addresses() )
|
||||
result.insert(k);
|
||||
return &auth;
|
||||
},
|
||||
[&]( account_id_type id )
|
||||
{
|
||||
const auto& auth = id(_db).owner;
|
||||
for( const auto& k : auth.get_addresses() )
|
||||
result.insert(k);
|
||||
return &auth;
|
||||
},
|
||||
_db.get_global_properties().parameters.max_authority_depth
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool database_api::verify_authority( const signed_transaction& trx )const
|
||||
{
|
||||
return my->verify_authority( trx );
|
||||
|
|
|
|||
|
|
@ -416,6 +416,7 @@ class database_api
|
|||
* to get the minimum subset.
|
||||
*/
|
||||
set<public_key_type> get_potential_signatures( const signed_transaction& trx )const;
|
||||
set<address> get_potential_address_signatures( const signed_transaction& trx )const;
|
||||
|
||||
/**
|
||||
* @return true of the @ref trx has all of the required signatures, otherwise throws an exception
|
||||
|
|
@ -536,6 +537,7 @@ FC_API(graphene::app::database_api,
|
|||
(get_transaction_hex)
|
||||
(get_required_signatures)
|
||||
(get_potential_signatures)
|
||||
(get_potential_address_signatures)
|
||||
(verify_authority)
|
||||
(verify_account_authority)
|
||||
(validate_transaction)
|
||||
|
|
|
|||
|
|
@ -180,12 +180,26 @@ void_result account_whitelist_evaluator::do_apply(const account_whitelist_operat
|
|||
a.whitelisting_accounts.insert(o.authorizing_account);
|
||||
else
|
||||
a.whitelisting_accounts.erase(o.authorizing_account);
|
||||
|
||||
if( o.new_listing & o.black_listed )
|
||||
a.blacklisting_accounts.insert(o.authorizing_account);
|
||||
else
|
||||
a.blacklisting_accounts.erase(o.authorizing_account);
|
||||
});
|
||||
|
||||
/** for tracking purposes only, this state is not needed to evaluate */
|
||||
d.modify( o.authorizing_account(d), [&]( account_object& a ) {
|
||||
if( o.new_listing & o.white_listed )
|
||||
a.whitelisted_accounts.insert( o.account_to_list );
|
||||
else
|
||||
a.whitelisted_accounts.erase( o.account_to_list );
|
||||
|
||||
if( o.new_listing & o.black_listed )
|
||||
a.blacklisted_accounts.insert( o.account_to_list );
|
||||
else
|
||||
a.blacklisted_accounts.erase( o.account_to_list );
|
||||
});
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ processed_transaction database::validate_transaction( const signed_transaction&
|
|||
}
|
||||
|
||||
processed_transaction database::push_proposal(const proposal_object& proposal)
|
||||
{
|
||||
{ try {
|
||||
transaction_evaluation_state eval_state(this);
|
||||
eval_state._is_proposed_trx = true;
|
||||
|
||||
|
|
@ -253,15 +253,20 @@ processed_transaction database::push_proposal(const proposal_object& proposal)
|
|||
processed_transaction ptrx(proposal.proposed_transaction);
|
||||
eval_state._trx = &ptrx;
|
||||
|
||||
auto session = _undo_db.start_undo_session();
|
||||
for( auto& op : proposal.proposed_transaction.operations )
|
||||
eval_state.operation_results.emplace_back(apply_operation(eval_state, op));
|
||||
remove(proposal);
|
||||
session.merge();
|
||||
try {
|
||||
auto session = _undo_db.start_undo_session(true);
|
||||
for( auto& op : proposal.proposed_transaction.operations )
|
||||
eval_state.operation_results.emplace_back(apply_operation(eval_state, op));
|
||||
remove(proposal);
|
||||
session.merge();
|
||||
} catch ( const fc::exception& e ) {
|
||||
elog( "e", ("e",e.to_detail_string() ) );
|
||||
throw;
|
||||
}
|
||||
|
||||
ptrx.operation_results = std::move(eval_state.operation_results);
|
||||
return ptrx;
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (proposal) ) }
|
||||
|
||||
signed_block database::generate_block(
|
||||
fc::time_point_sec when,
|
||||
|
|
@ -273,7 +278,7 @@ signed_block database::generate_block(
|
|||
signed_block result;
|
||||
detail::with_skip_flags( *this, skip, [&]()
|
||||
{
|
||||
result = _generate_block( when, witness_id, block_signing_private_key, true );
|
||||
result = _generate_block( when, witness_id, block_signing_private_key );
|
||||
} );
|
||||
return result;
|
||||
}
|
||||
|
|
@ -281,8 +286,7 @@ signed_block database::generate_block(
|
|||
signed_block database::_generate_block(
|
||||
fc::time_point_sec when,
|
||||
witness_id_type witness_id,
|
||||
const fc::ecc::private_key& block_signing_private_key,
|
||||
bool retry_on_failure
|
||||
const fc::ecc::private_key& block_signing_private_key
|
||||
)
|
||||
{
|
||||
try {
|
||||
|
|
@ -353,8 +357,15 @@ signed_block database::_generate_block(
|
|||
{
|
||||
wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
|
||||
}
|
||||
|
||||
_pending_tx_session.reset();
|
||||
|
||||
// We have temporarily broken the invariant that
|
||||
// _pending_tx_session is the result of applying _pending_tx, as
|
||||
// _pending_tx now consists of the set of postponed transactions.
|
||||
// However, the push_block() call below will re-create the
|
||||
// _pending_tx_session.
|
||||
|
||||
pending_block.previous = head_block_id();
|
||||
pending_block.timestamp = when;
|
||||
pending_block.transaction_merkle_root = pending_block.calculate_merkle_root();
|
||||
|
|
@ -363,6 +374,7 @@ signed_block database::_generate_block(
|
|||
if( !(skip & skip_witness_signature) )
|
||||
pending_block.sign( block_signing_private_key );
|
||||
|
||||
// TODO: Move this to _push_block() so session is restored.
|
||||
FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size );
|
||||
|
||||
push_block( pending_block, skip );
|
||||
|
|
@ -467,6 +479,7 @@ void database::_apply_block( const signed_block& next_block )
|
|||
|
||||
update_global_dynamic_data(next_block);
|
||||
update_signing_witness(signing_witness, next_block);
|
||||
update_last_irreversible_block();
|
||||
|
||||
// Are we at the maintenance interval?
|
||||
if( maint_needed )
|
||||
|
|
@ -643,4 +656,9 @@ void database::add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts
|
|||
_checkpoints[i.first] = i.second;
|
||||
}
|
||||
|
||||
bool database::before_last_checkpoint()const
|
||||
{
|
||||
return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num());
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -44,24 +44,24 @@ void database::debug_dump()
|
|||
|
||||
for( const account_balance_object& a : balance_index )
|
||||
{
|
||||
idump(("balance")(a));
|
||||
// idump(("balance")(a));
|
||||
total_balances[a.asset_type] += a.balance;
|
||||
}
|
||||
for( const account_statistics_object& s : statistics_index )
|
||||
{
|
||||
idump(("statistics")(s));
|
||||
// idump(("statistics")(s));
|
||||
reported_core_in_orders += s.total_core_in_orders;
|
||||
}
|
||||
for( const limit_order_object& o : db.get_index_type<limit_order_index>().indices() )
|
||||
{
|
||||
idump(("limit_order")(o));
|
||||
// idump(("limit_order")(o));
|
||||
auto for_sale = o.amount_for_sale();
|
||||
if( for_sale.asset_id == asset_id_type() ) core_in_orders += for_sale.amount;
|
||||
total_balances[for_sale.asset_id] += for_sale.amount;
|
||||
}
|
||||
for( const call_order_object& o : db.get_index_type<call_order_index>().indices() )
|
||||
{
|
||||
idump(("call_order")(o));
|
||||
// idump(("call_order")(o));
|
||||
auto col = o.get_collateral();
|
||||
if( col.asset_id == asset_id_type() ) core_in_orders += col.amount;
|
||||
total_balances[col.asset_id] += col.amount;
|
||||
|
|
@ -71,17 +71,23 @@ void database::debug_dump()
|
|||
{
|
||||
total_balances[asset_obj.id] += asset_obj.dynamic_asset_data_id(db).accumulated_fees;
|
||||
total_balances[asset_id_type()] += asset_obj.dynamic_asset_data_id(db).fee_pool;
|
||||
// edump((total_balances[asset_obj.id])(asset_obj.dynamic_asset_data_id(db).current_supply ) );
|
||||
}
|
||||
|
||||
if( total_balances[asset_id_type()].value != core_asset_data.current_supply.value )
|
||||
{
|
||||
edump( (total_balances[asset_id_type()].value)(core_asset_data.current_supply.value ));
|
||||
}
|
||||
|
||||
edump((core_in_orders)(reported_core_in_orders));
|
||||
|
||||
/*
|
||||
const auto& vbidx = db.get_index_type<simple_index<vesting_balance_object>>();
|
||||
for( const auto& s : vbidx )
|
||||
{
|
||||
idump(("vesting_balance")(s));
|
||||
// idump(("vesting_balance")(s));
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#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/chain_property_object.hpp>
|
||||
#include <graphene/chain/committee_member_object.hpp>
|
||||
#include <graphene/chain/global_property_object.hpp>
|
||||
|
|
@ -194,6 +195,7 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<flat_index< block_summary_object >> >();
|
||||
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 > > >();
|
||||
}
|
||||
|
||||
void database::init_genesis(const genesis_state_type& genesis_state)
|
||||
|
|
@ -458,6 +460,10 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
cop.active = cop.owner;
|
||||
account_id_type owner_account_id = apply_operation(genesis_eval_state, cop).get<object_id_type>();
|
||||
|
||||
modify( owner_account_id(*this).statistics(*this), [&]( account_statistics_object& o ) {
|
||||
o.total_core_in_orders = collateral_rec.collateral;
|
||||
});
|
||||
|
||||
create<call_order_object>([&](call_order_object& c) {
|
||||
c.borrower = owner_account_id;
|
||||
c.collateral = collateral_rec.collateral;
|
||||
|
|
@ -609,6 +615,8 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
wso.current_shuffled_witnesses.push_back( wid );
|
||||
});
|
||||
|
||||
debug_dump();
|
||||
|
||||
_undo_db.enable();
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Cryptonomex, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
||||
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted until September 8, 2015, provided that the following conditions are met:
|
||||
*
|
||||
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
||||
*
|
||||
* 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 <boost/multiprecision/integer.hpp>
|
||||
|
|
@ -143,19 +130,32 @@ void database::pay_workers( share_type& budget )
|
|||
void database::update_active_witnesses()
|
||||
{ try {
|
||||
assert( _witness_count_histogram_buffer.size() > 0 );
|
||||
share_type stake_target = _total_voting_stake / 2;
|
||||
share_type stake_tally = _witness_count_histogram_buffer[0];
|
||||
share_type stake_target = (_total_voting_stake-_witness_count_histogram_buffer[0]) / 2;
|
||||
|
||||
/// accounts that vote for 0 or 1 witness do not get to express an opinion on
|
||||
/// the number of witnesses to have (they abstain and are non-voting accounts)
|
||||
|
||||
share_type stake_tally = 0;
|
||||
|
||||
size_t witness_count = 0;
|
||||
if( stake_target > 0 )
|
||||
{
|
||||
while( (witness_count < _witness_count_histogram_buffer.size() - 1)
|
||||
&& (stake_tally <= stake_target) )
|
||||
{
|
||||
stake_tally += _witness_count_histogram_buffer[++witness_count];
|
||||
}
|
||||
}
|
||||
|
||||
const chain_property_object& cpo = get_chain_properties();
|
||||
auto wits = sort_votable_objects<witness_index>(std::max(witness_count*2+1, (size_t)cpo.immutable_parameters.min_witness_count));
|
||||
|
||||
edump((wits.size())(witness_count*2+1));
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
|
||||
for( const witness_object& wit : wits )
|
||||
const auto& all_witnesses = get_index_type<witness_index>().indices();
|
||||
|
||||
for( const witness_object& wit : all_witnesses )
|
||||
{
|
||||
modify( wit, [&]( witness_object& obj ){
|
||||
obj.total_votes = _vote_tally_buffer[wit.vote_id];
|
||||
|
|
@ -212,8 +212,11 @@ void database::update_active_witnesses()
|
|||
void database::update_active_committee_members()
|
||||
{ try {
|
||||
assert( _committee_count_histogram_buffer.size() > 0 );
|
||||
uint64_t stake_target = _total_voting_stake / 2;
|
||||
uint64_t stake_tally = _committee_count_histogram_buffer[0];
|
||||
share_type stake_target = (_total_voting_stake-_witness_count_histogram_buffer[0]) / 2;
|
||||
|
||||
/// accounts that vote for 0 or 1 witness do not get to express an opinion on
|
||||
/// the number of witnesses to have (they abstain and are non-voting accounts)
|
||||
uint64_t stake_tally = 0; // _committee_count_histogram_buffer[0];
|
||||
size_t committee_member_count = 0;
|
||||
if( stake_target > 0 )
|
||||
while( (committee_member_count < _committee_count_histogram_buffer.size() - 1)
|
||||
|
|
@ -271,17 +274,25 @@ void database::update_active_committee_members()
|
|||
});
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
share_type database::get_max_budget( fc::time_point_sec now )const
|
||||
void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const
|
||||
{
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
const asset_object& core = asset_id_type(0)(*this);
|
||||
const asset_dynamic_data_object& core_dd = core.dynamic_asset_data_id(*this);
|
||||
|
||||
rec.from_initial_reserve = core.reserved(*this);
|
||||
rec.from_accumulated_fees = core_dd.accumulated_fees;
|
||||
rec.from_unused_witness_budget = dpo.witness_budget;
|
||||
|
||||
if( (dpo.last_budget_time == fc::time_point_sec())
|
||||
|| (now <= dpo.last_budget_time) )
|
||||
return share_type(0);
|
||||
{
|
||||
rec.time_since_last_budget = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t dt = (now - dpo.last_budget_time).to_seconds();
|
||||
rec.time_since_last_budget = uint64_t( dt );
|
||||
|
||||
// We'll consider accumulated_fees to be reserved at the BEGINNING
|
||||
// of the maintenance interval. However, for speed we only
|
||||
|
|
@ -289,7 +300,7 @@ share_type database::get_max_budget( fc::time_point_sec now )const
|
|||
// end of the maintenance interval. Thus the accumulated_fees
|
||||
// are available for the budget at this point, but not included
|
||||
// in core.reserved().
|
||||
share_type reserve = core.reserved(*this) + core_dd.accumulated_fees;
|
||||
share_type reserve = rec.from_initial_reserve + core_dd.accumulated_fees;
|
||||
// Similarly, we consider leftover witness_budget to be burned
|
||||
// at the BEGINNING of the maintenance interval.
|
||||
reserve += dpo.witness_budget;
|
||||
|
|
@ -304,11 +315,11 @@ share_type database::get_max_budget( fc::time_point_sec now )const
|
|||
budget_u128 >>= GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS;
|
||||
share_type budget;
|
||||
if( budget_u128 < reserve.value )
|
||||
budget = share_type(budget_u128.to_uint64());
|
||||
rec.total_budget = share_type(budget_u128.to_uint64());
|
||||
else
|
||||
budget = reserve;
|
||||
rec.total_budget = reserve;
|
||||
|
||||
return budget;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -342,10 +353,14 @@ void database::process_budget()
|
|||
// blocks_to_maint > 0 because time_to_maint > 0,
|
||||
// which means numerator is at least equal to block_interval
|
||||
|
||||
share_type available_funds = get_max_budget(now);
|
||||
budget_record rec;
|
||||
initialize_budget_record( now, rec );
|
||||
share_type available_funds = rec.total_budget;
|
||||
|
||||
share_type witness_budget = gpo.parameters.witness_pay_per_block.value * blocks_to_maint;
|
||||
rec.requested_witness_budget = witness_budget;
|
||||
witness_budget = std::min(witness_budget, available_funds);
|
||||
rec.witness_budget = witness_budget;
|
||||
available_funds -= witness_budget;
|
||||
|
||||
fc::uint128_t worker_budget_u128 = gpo.parameters.worker_budget_per_day.value;
|
||||
|
|
@ -357,24 +372,34 @@ void database::process_budget()
|
|||
worker_budget = available_funds;
|
||||
else
|
||||
worker_budget = worker_budget_u128.to_uint64();
|
||||
rec.worker_budget = worker_budget;
|
||||
available_funds -= worker_budget;
|
||||
|
||||
share_type leftover_worker_funds = worker_budget;
|
||||
pay_workers(leftover_worker_funds);
|
||||
rec.leftover_worker_funds = leftover_worker_funds;
|
||||
available_funds += leftover_worker_funds;
|
||||
|
||||
share_type unused_prev_witness_budget = dpo.witness_budget;
|
||||
rec.supply_delta = rec.witness_budget
|
||||
+ rec.worker_budget
|
||||
- rec.leftover_worker_funds
|
||||
- rec.from_accumulated_fees
|
||||
- rec.from_unused_witness_budget;
|
||||
|
||||
modify(core, [&]( asset_dynamic_data_object& _core )
|
||||
{
|
||||
_core.current_supply = (_core.current_supply
|
||||
+ witness_budget
|
||||
_core.current_supply = (_core.current_supply + rec.supply_delta );
|
||||
|
||||
assert( rec.supply_delta ==
|
||||
witness_budget
|
||||
+ worker_budget
|
||||
- leftover_worker_funds
|
||||
- _core.accumulated_fees
|
||||
- unused_prev_witness_budget
|
||||
- dpo.witness_budget
|
||||
);
|
||||
_core.accumulated_fees = 0;
|
||||
});
|
||||
|
||||
modify(dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
// Since initial witness_budget was rolled into
|
||||
|
|
@ -384,6 +409,12 @@ void database::process_budget()
|
|||
_dpo.last_budget_time = now;
|
||||
});
|
||||
|
||||
create< budget_record_object >( [&]( budget_record_object& _rec )
|
||||
{
|
||||
_rec.time = head_block_time();
|
||||
_rec.record = rec;
|
||||
});
|
||||
|
||||
// available_funds is money we could spend, but don't want to.
|
||||
// we simply let it evaporate back into the reserve.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@
|
|||
#include <graphene/chain/operation_history_object.hpp>
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
#include <fc/io/fstream.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
|
|
@ -105,6 +108,31 @@ void database::open(
|
|||
{
|
||||
try
|
||||
{
|
||||
auto is_new = [&]() -> bool
|
||||
{
|
||||
// directory doesn't exist
|
||||
if( !fc::exists( data_dir ) )
|
||||
return true;
|
||||
// if directory exists but is empty, return true; else false.
|
||||
return ( fc::directory_iterator( data_dir ) == fc::directory_iterator() );
|
||||
};
|
||||
|
||||
auto is_outdated = [&]() -> bool
|
||||
{
|
||||
if( !fc::exists( data_dir / "db_version" ) )
|
||||
return true;
|
||||
std::string version_str;
|
||||
fc::read_file_contents( data_dir / "db_version", version_str );
|
||||
return (version_str != GRAPHENE_CURRENT_DB_VERSION);
|
||||
};
|
||||
|
||||
if( (!is_new()) && is_outdated() )
|
||||
{
|
||||
ilog( "Old database version detected, reindex is required" );
|
||||
wipe( data_dir, false );
|
||||
fc::remove_all( data_dir / "db_version" );
|
||||
}
|
||||
|
||||
object_database::open(data_dir);
|
||||
|
||||
_block_id_to_block.open(data_dir / "database" / "block_num_to_block");
|
||||
|
|
@ -122,9 +150,22 @@ void database::open(
|
|||
FC_ASSERT( head_block_num() == 0, "last block ID does not match current chain state" );
|
||||
}
|
||||
}
|
||||
|
||||
// doing this down here helps ensure that DB will be wiped
|
||||
// if any of the above steps were interrupted on a previous run
|
||||
if( !fc::exists( data_dir / "db_version" ) )
|
||||
{
|
||||
std::ofstream db_version(
|
||||
(data_dir / "db_version").generic_string().c_str(),
|
||||
std::ios::out | std::ios::binary | std::ios::trunc );
|
||||
std::string version_string = GRAPHENE_CURRENT_DB_VERSION;
|
||||
db_version.write( version_string.c_str(), version_string.size() );
|
||||
db_version.close();
|
||||
}
|
||||
|
||||
//idump((head_block_id())(head_block_num()));
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW( (data_dir) )
|
||||
FC_CAPTURE_LOG_AND_RETHROW( (data_dir) )
|
||||
}
|
||||
|
||||
void database::close(uint32_t blocks_to_rewind)
|
||||
|
|
@ -155,6 +196,11 @@ void database::close(uint32_t blocks_to_rewind)
|
|||
{
|
||||
}
|
||||
|
||||
// Since pop_block() will move tx's in the popped blocks into pending,
|
||||
// we have to clear_pending() after we're done popping to get a clean
|
||||
// DB state (issue #336).
|
||||
clear_pending();
|
||||
|
||||
object_database::flush();
|
||||
object_database::close();
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,9 @@ void database::cancel_order( const limit_order_object& order, bool create_virtua
|
|||
|
||||
modify( order.seller(*this).statistics(*this),[&]( account_statistics_object& obj ){
|
||||
if( refunded.asset_id == asset_id_type() )
|
||||
{
|
||||
obj.total_core_in_orders -= refunded.amount;
|
||||
}
|
||||
});
|
||||
adjust_balance(order.seller, refunded);
|
||||
|
||||
|
|
@ -220,19 +222,30 @@ int database::match( const limit_order_object& bid, const limit_order_object& as
|
|||
}
|
||||
|
||||
|
||||
asset database::match( const call_order_object& call, const force_settlement_object& settle, const price& match_price,
|
||||
asset max_settlement )
|
||||
{
|
||||
assert(call.get_debt().asset_id == settle.balance.asset_id );
|
||||
assert(call.debt > 0 && call.collateral > 0 && settle.balance.amount > 0);
|
||||
asset database::match( const call_order_object& call,
|
||||
const force_settlement_object& settle,
|
||||
const price& match_price,
|
||||
asset max_settlement )
|
||||
{ try {
|
||||
FC_ASSERT(call.get_debt().asset_id == settle.balance.asset_id );
|
||||
FC_ASSERT(call.debt > 0 && call.collateral > 0 && settle.balance.amount > 0);
|
||||
|
||||
auto settle_for_sale = std::min(settle.balance, max_settlement);
|
||||
auto call_debt = call.get_debt();
|
||||
|
||||
asset call_receives = std::min(settle_for_sale, call_debt),
|
||||
call_pays = call_receives * match_price,
|
||||
settle_pays = call_receives,
|
||||
settle_receives = call_pays;
|
||||
asset call_receives = std::min(settle_for_sale, call_debt);
|
||||
asset call_pays = call_receives * match_price;
|
||||
asset settle_pays = call_receives;
|
||||
asset settle_receives = call_pays;
|
||||
|
||||
/**
|
||||
* If the least collateralized call position lacks sufficient
|
||||
* collateral to cover at the match price then this indicates a black
|
||||
* swan event according to the price feed, but only the market
|
||||
* can trigger a black swan. So now we must cancel the forced settlement
|
||||
* object.
|
||||
*/
|
||||
GRAPHENE_ASSERT( call_pays < call.get_collateral(), black_swan_exception, "" );
|
||||
|
||||
assert( settle_pays == settle_for_sale || call_receives == call.get_debt() );
|
||||
|
||||
|
|
@ -240,12 +253,12 @@ asset database::match( const call_order_object& call, const force_settlement_obj
|
|||
fill_order(settle, settle_pays, settle_receives);
|
||||
|
||||
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 )
|
||||
{
|
||||
assert( order.amount_for_sale().asset_id == pays.asset_id );
|
||||
assert( pays.asset_id != receives.asset_id );
|
||||
{ try {
|
||||
FC_ASSERT( order.amount_for_sale().asset_id == pays.asset_id );
|
||||
FC_ASSERT( pays.asset_id != receives.asset_id );
|
||||
|
||||
const account_object& seller = order.seller(*this);
|
||||
const asset_object& recv_asset = receives.asset_id(*this);
|
||||
|
|
@ -279,15 +292,15 @@ bool database::fill_order( const limit_order_object& order, const asset& pays, c
|
|||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (order)(pays)(receives) ) }
|
||||
|
||||
|
||||
bool database::fill_order( const call_order_object& order, const asset& pays, const asset& receives )
|
||||
{ try {
|
||||
//idump((pays)(receives)(order));
|
||||
assert( order.get_debt().asset_id == receives.asset_id );
|
||||
assert( order.get_collateral().asset_id == pays.asset_id );
|
||||
assert( order.get_collateral() >= pays );
|
||||
FC_ASSERT( order.get_debt().asset_id == receives.asset_id );
|
||||
FC_ASSERT( order.get_collateral().asset_id == pays.asset_id );
|
||||
FC_ASSERT( order.get_collateral() >= pays );
|
||||
|
||||
optional<asset> collateral_freed;
|
||||
modify( order, [&]( call_order_object& o ){
|
||||
|
|
@ -315,11 +328,13 @@ bool database::fill_order( const call_order_object& order, const asset& pays, co
|
|||
const account_statistics_object& borrower_statistics = borrower.statistics(*this);
|
||||
if( collateral_freed )
|
||||
adjust_balance(borrower.get_id(), *collateral_freed);
|
||||
|
||||
modify( borrower_statistics, [&]( account_statistics_object& b ){
|
||||
if( collateral_freed && collateral_freed->amount > 0 )
|
||||
b.total_core_in_orders -= collateral_freed->amount;
|
||||
if( pays.asset_id == asset_id_type() )
|
||||
b.total_core_in_orders -= pays.amount;
|
||||
|
||||
assert( b.total_core_in_orders >= 0 );
|
||||
});
|
||||
}
|
||||
|
|
@ -374,6 +389,10 @@ bool database::fill_order(const force_settlement_object& settle, const asset& pa
|
|||
bool database::check_call_orders(const asset_object& mia, bool enable_black_swan)
|
||||
{ try {
|
||||
if( !mia.is_market_issued() ) return false;
|
||||
|
||||
if( check_for_blackswan( mia, enable_black_swan ) )
|
||||
return false;
|
||||
|
||||
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
|
||||
if( bitasset.is_prediction_market ) return false;
|
||||
if( bitasset.current_feed.settlement_price.is_null() ) return false;
|
||||
|
|
@ -395,15 +414,23 @@ bool database::check_call_orders(const asset_object& mia, bool enable_black_swan
|
|||
auto limit_end = limit_price_index.upper_bound( min_price );
|
||||
|
||||
if( limit_itr == limit_end ) {
|
||||
/*
|
||||
if( head_block_num() > 300000 )
|
||||
ilog( "no orders below between: ${p} and: ${m}",
|
||||
("p", bitasset.current_feed.max_short_squeeze_price())
|
||||
("m", max_price) );
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) );
|
||||
auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) );
|
||||
auto call_min = price::min( bitasset.options.short_backing_asset, mia.id );
|
||||
auto call_max = price::max( bitasset.options.short_backing_asset, mia.id );
|
||||
auto call_itr = call_price_index.lower_bound( call_min );
|
||||
auto call_end = call_price_index.upper_bound( call_max );
|
||||
|
||||
bool filled_limit = false;
|
||||
|
||||
while( call_itr != call_end )
|
||||
while( !check_for_blackswan( mia, enable_black_swan ) && call_itr != call_end )
|
||||
{
|
||||
bool filled_call = false;
|
||||
price match_price;
|
||||
|
|
@ -419,17 +446,16 @@ bool database::check_call_orders(const asset_object& mia, bool enable_black_swan
|
|||
match_price.validate();
|
||||
|
||||
if( match_price > ~call_itr->call_price )
|
||||
{
|
||||
return filled_limit;
|
||||
}
|
||||
|
||||
auto usd_to_buy = call_itr->get_debt();
|
||||
|
||||
if( usd_to_buy * match_price > call_itr->get_collateral() )
|
||||
{
|
||||
elog( "black swan detected" );
|
||||
edump((enable_black_swan));
|
||||
FC_ASSERT( enable_black_swan );
|
||||
//globally_settle_asset(mia, call_itr->get_debt() / call_itr->get_collateral());
|
||||
globally_settle_asset(mia, bitasset.current_feed.settlement_price );// call_itr->get_debt() / call_itr->get_collateral());
|
||||
globally_settle_asset(mia, bitasset.current_feed.settlement_price );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -458,6 +484,7 @@ bool database::check_call_orders(const asset_object& mia, bool enable_black_swan
|
|||
|
||||
auto old_limit_itr = filled_limit ? limit_itr++ : limit_itr;
|
||||
fill_order(*old_limit_itr, order_pays, order_receives);
|
||||
|
||||
} // whlie call_itr != call_end
|
||||
|
||||
return filled_limit;
|
||||
|
|
@ -468,7 +495,9 @@ void database::pay_order( const account_object& receiver, const asset& receives,
|
|||
const auto& balances = receiver.statistics(*this);
|
||||
modify( balances, [&]( account_statistics_object& b ){
|
||||
if( pays.asset_id == asset_id_type() )
|
||||
{
|
||||
b.total_core_in_orders -= pays.amount;
|
||||
}
|
||||
});
|
||||
adjust_balance(receiver.get_id(), receives);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ void database::update_global_dynamic_data( const signed_block& b )
|
|||
for( uint32_t i = 0; i < missed_blocks; ++i ) {
|
||||
const auto& witness_missed = get_scheduled_witness( i+1 )(*this);
|
||||
if( witness_missed.id != b.witness ) {
|
||||
const auto& witness_account = witness_missed.witness_account(*this);
|
||||
/*
|
||||
const auto& witness_account = witness_missed.witness_account(*this);
|
||||
if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) )
|
||||
wlog( "Witness ${name} missed block ${n} around ${t}", ("name",witness_account.name)("n",b.block_num())("t",b.timestamp) );
|
||||
*/
|
||||
|
|
@ -80,14 +80,15 @@ void database::update_global_dynamic_data( const signed_block& b )
|
|||
|
||||
if( !(get_node_properties().skip_flags & skip_undo_history_check) )
|
||||
{
|
||||
GRAPHENE_ASSERT( _dgp.recently_missed_count < GRAPHENE_MAX_UNDO_HISTORY, undo_database_exception,
|
||||
GRAPHENE_ASSERT( _dgp.head_block_number - _dgp.last_irreversible_block_num < GRAPHENE_MAX_UNDO_HISTORY, undo_database_exception,
|
||||
"The database does not have enough undo history to support a blockchain with so many missed blocks. "
|
||||
"Please add a checkpoint if you would like to continue applying blocks beyond this point.",
|
||||
("last_irreversible_block_num",_dgp.last_irreversible_block_num)("head", _dgp.head_block_number)
|
||||
("recently_missed",_dgp.recently_missed_count)("max_undo",GRAPHENE_MAX_UNDO_HISTORY) );
|
||||
}
|
||||
|
||||
_undo_db.set_max_size( _dgp.recently_missed_count + GRAPHENE_MIN_UNDO_HISTORY );
|
||||
_fork_db.set_max_size( _dgp.recently_missed_count + GRAPHENE_MIN_UNDO_HISTORY );
|
||||
_undo_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + GRAPHENE_MIN_UNDO_HISTORY );
|
||||
_fork_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + GRAPHENE_MIN_UNDO_HISTORY );
|
||||
}
|
||||
|
||||
void database::update_signing_witness(const witness_object& signing_witness, const signed_block& new_block)
|
||||
|
|
@ -108,9 +109,45 @@ void database::update_signing_witness(const witness_object& signing_witness, con
|
|||
modify( signing_witness, [&]( witness_object& _wit )
|
||||
{
|
||||
_wit.last_aslot = new_block_aslot;
|
||||
_wit.last_confirmed_block_num = new_block.block_num();
|
||||
} );
|
||||
}
|
||||
|
||||
void database::update_last_irreversible_block()
|
||||
{
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
|
||||
vector< const witness_object* > wit_objs;
|
||||
wit_objs.reserve( gpo.active_witnesses.size() );
|
||||
for( const witness_id_type& wid : gpo.active_witnesses )
|
||||
wit_objs.push_back( &(wid(*this)) );
|
||||
|
||||
static_assert( GRAPHENE_IRREVERSIBLE_THRESHOLD > 0, "irreversible threshold must be nonzero" );
|
||||
|
||||
// 1 1 1 2 2 2 2 2 2 2 -> 2 .7*10 = 7
|
||||
// 1 1 1 1 1 1 1 2 2 2 -> 1
|
||||
// 3 3 3 3 3 3 3 3 3 3 -> 3
|
||||
|
||||
size_t offset = ((GRAPHENE_100_PERCENT - GRAPHENE_IRREVERSIBLE_THRESHOLD) * wit_objs.size() / GRAPHENE_100_PERCENT);
|
||||
|
||||
std::nth_element( wit_objs.begin(), wit_objs.begin() + offset, wit_objs.end(),
|
||||
[]( const witness_object* a, const witness_object* b )
|
||||
{
|
||||
return a->last_confirmed_block_num < b->last_confirmed_block_num;
|
||||
} );
|
||||
|
||||
uint32_t new_last_irreversible_block_num = wit_objs[offset]->last_confirmed_block_num;
|
||||
|
||||
if( new_last_irreversible_block_num > dpo.last_irreversible_block_num )
|
||||
{
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.last_irreversible_block_num = new_last_irreversible_block_num;
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
void database::clear_expired_transactions()
|
||||
{
|
||||
//Look for expired transactions in the deduplication list, and remove them.
|
||||
|
|
@ -143,6 +180,72 @@ void database::clear_expired_proposals()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* let HB = the highest bid for the collateral (aka who will pay the most DEBT for the least collateral)
|
||||
* let SP = current median feed's Settlement Price
|
||||
* let LC = the least collateralized call order's swan price (debt/collateral)
|
||||
*
|
||||
* If there is no valid price feed or no bids then there is no black swan.
|
||||
*
|
||||
* A black swan occurs if MAX(HB,SP) <= LC
|
||||
*/
|
||||
bool database::check_for_blackswan( const asset_object& mia, bool enable_black_swan )
|
||||
{
|
||||
if( !mia.is_market_issued() ) return false;
|
||||
|
||||
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
|
||||
if( bitasset.has_settlement() ) return true; // already force settled
|
||||
auto settle_price = bitasset.current_feed.settlement_price;
|
||||
if( settle_price.is_null() ) return false; // no feed
|
||||
|
||||
const call_order_index& call_index = get_index_type<call_order_index>();
|
||||
const auto& call_price_index = call_index.indices().get<by_price>();
|
||||
|
||||
const limit_order_index& limit_index = get_index_type<limit_order_index>();
|
||||
const auto& limit_price_index = limit_index.indices().get<by_price>();
|
||||
|
||||
// looking for limit orders selling the most USD for the least CORE
|
||||
auto highest_possible_bid = price::max( mia.id, bitasset.options.short_backing_asset );
|
||||
// stop when limit orders are selling too little USD for too much CORE
|
||||
auto lowest_possible_bid = price::min( mia.id, bitasset.options.short_backing_asset );
|
||||
|
||||
assert( highest_possible_bid.base.asset_id == lowest_possible_bid.base.asset_id );
|
||||
// NOTE limit_price_index is sorted from greatest to least
|
||||
auto limit_itr = limit_price_index.lower_bound( highest_possible_bid );
|
||||
auto limit_end = limit_price_index.upper_bound( lowest_possible_bid );
|
||||
|
||||
auto call_min = price::min( bitasset.options.short_backing_asset, mia.id );
|
||||
auto call_max = price::max( bitasset.options.short_backing_asset, mia.id );
|
||||
auto call_itr = call_price_index.lower_bound( call_min );
|
||||
auto call_end = call_price_index.upper_bound( call_max );
|
||||
|
||||
if( call_itr == call_end ) return false; // no call orders
|
||||
|
||||
price highest = settle_price;
|
||||
if( limit_itr != limit_end ) {
|
||||
assert( settle_price.base.asset_id == limit_itr->sell_price.base.asset_id );
|
||||
highest = std::max( limit_itr->sell_price, settle_price );
|
||||
}
|
||||
|
||||
auto least_collateral = call_itr->collateralization();
|
||||
if( ~least_collateral >= highest )
|
||||
{
|
||||
elog( "Black Swan detected: \n"
|
||||
" Least collateralized call: ${lc} ${~lc}\n"
|
||||
// " Highest Bid: ${hb} ${~hb}\n"
|
||||
" Settle Price: ${sp} ${~sp}\n"
|
||||
" Max: ${h} ${~h}\n",
|
||||
("lc",least_collateral.to_real())("~lc",(~least_collateral).to_real())
|
||||
// ("hb",limit_itr->sell_price.to_real())("~hb",(~limit_itr->sell_price).to_real())
|
||||
("sp",settle_price.to_real())("~sp",(~settle_price).to_real())
|
||||
("h",highest.to_real())("~h",(~highest).to_real()) );
|
||||
FC_ASSERT( enable_black_swan, "Black swan was detected during a margin update which is not allowed to trigger a blackswan" );
|
||||
globally_settle_asset(mia, ~least_collateral );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void database::clear_expired_orders()
|
||||
{
|
||||
detail::with_skip_flags( *this,
|
||||
|
|
@ -187,6 +290,13 @@ void database::clear_expired_orders()
|
|||
const asset_object& mia_object = get(current_asset);
|
||||
const asset_bitasset_data_object mia = mia_object.bitasset_data(*this);
|
||||
|
||||
if( mia.has_settlement() )
|
||||
{
|
||||
ilog( "Canceling a force settlement because of black swan" );
|
||||
cancel_order( order );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Has this order not reached its settlement date?
|
||||
if( order.settlement_date > head_block_time() )
|
||||
{
|
||||
|
|
@ -234,7 +344,15 @@ void database::clear_expired_orders()
|
|||
// There should always be a call order, since asset exists!
|
||||
assert(itr != call_index.end() && itr->debt_type() == mia_object.get_id());
|
||||
asset max_settlement = max_settlement_volume - settled;
|
||||
settled += match(*itr, order, settlement_price, max_settlement);
|
||||
|
||||
try {
|
||||
settled += match(*itr, order, settlement_price, max_settlement);
|
||||
}
|
||||
catch ( const black_swan_exception& e ) {
|
||||
wlog( "black swan detected: ${e}", ("e", e.to_detail_string() ) );
|
||||
cancel_order( order );
|
||||
break;
|
||||
}
|
||||
}
|
||||
modify(mia, [settled](asset_bitasset_data_object& b) {
|
||||
b.force_settled_volume = settled.amount;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ fc::variant_object get_config()
|
|||
result[ "GRAPHENE_MAX_ACCOUNT_NAME_LENGTH" ] = GRAPHENE_MAX_ACCOUNT_NAME_LENGTH;
|
||||
result[ "GRAPHENE_MIN_ASSET_SYMBOL_LENGTH" ] = GRAPHENE_MIN_ASSET_SYMBOL_LENGTH;
|
||||
result[ "GRAPHENE_MAX_ASSET_SYMBOL_LENGTH" ] = GRAPHENE_MAX_ASSET_SYMBOL_LENGTH;
|
||||
result[ "GRAPHENE_MAX_ASSET_NAME_LENGTH" ] = GRAPHENE_MAX_ASSET_NAME_LENGTH;
|
||||
result[ "GRAPHENE_MAX_SHARE_SUPPLY" ] = GRAPHENE_MAX_SHARE_SUPPLY;
|
||||
result[ "GRAPHENE_MAX_PAY_RATE" ] = GRAPHENE_MAX_PAY_RATE;
|
||||
result[ "GRAPHENE_MAX_SIG_CHECK_DEPTH" ] = GRAPHENE_MAX_SIG_CHECK_DEPTH;
|
||||
|
|
|
|||
|
|
@ -41,11 +41,7 @@ namespace graphene { namespace chain {
|
|||
account_id_type owner;
|
||||
|
||||
/**
|
||||
* Keep the most recent operation as a root pointer to a linked list of the transaction history. This field is
|
||||
* not required by core validation and could in theory be made an annotation on the account object, but
|
||||
* because transaction history is so common and this object is already cached in the undo buffer (because it
|
||||
* likely affected the balances of this account) it is convienent to simply track this data here. Account
|
||||
* balance objects don't currenty inherit from annotated object.
|
||||
* Keep the most recent operation as a root pointer to a linked list of the transaction history.
|
||||
*/
|
||||
account_transaction_history_id_type most_recent_op;
|
||||
|
||||
|
|
@ -110,7 +106,7 @@ namespace graphene { namespace chain {
|
|||
* Accounts are the primary unit of authority on the graphene system. Users must have an account in order to use
|
||||
* assets, trade in the markets, vote for committee_members, etc.
|
||||
*/
|
||||
class account_object : public graphene::db::annotated_object<account_object>
|
||||
class account_object : public graphene::db::abstract_object<account_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
|
|
@ -134,7 +130,7 @@ namespace graphene { namespace chain {
|
|||
account_id_type lifetime_referrer;
|
||||
|
||||
/// Percentage of fee which should go to network.
|
||||
uint16_t network_fee_percentage;
|
||||
uint16_t network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
|
||||
/// Percentage of fee which should go to lifetime referrer.
|
||||
uint16_t lifetime_referrer_fee_percentage = 0;
|
||||
/// Percentage of referral rewards (leftover fee after paying network and lifetime referrer) which should go
|
||||
|
|
@ -170,6 +166,21 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
flat_set<account_id_type> whitelisting_accounts;
|
||||
|
||||
/**
|
||||
* Optionally track all of the accounts this account has whitelisted or blacklisted, these should
|
||||
* be made Immutable so that when the account object is cloned no deep copy is required. This state is
|
||||
* tracked for GUI display purposes.
|
||||
*
|
||||
* TODO: move white list tracking to its own multi-index container rather than having 4 fields on an
|
||||
* account. This will scale better because under the current design if you whitelist 2000 accounts,
|
||||
* then every time someone fetches this account object they will get the full list of 2000 accounts.
|
||||
*/
|
||||
///@{
|
||||
set<account_id_type> whitelisted_accounts;
|
||||
set<account_id_type> blacklisted_accounts;
|
||||
///@}
|
||||
|
||||
|
||||
/**
|
||||
* This is a set of all accounts which have 'blacklisted' this account. Blacklisting is only used in core
|
||||
* validation for the purpose of forbidding accounts from holding and transacting in whitelisted assets. This
|
||||
|
|
@ -220,20 +231,6 @@ namespace graphene { namespace chain {
|
|||
account_id_type get_id()const { return id; }
|
||||
};
|
||||
|
||||
/**
|
||||
* This object is attached as the meta annotation on the account object, this information is not relevant to
|
||||
* validation.
|
||||
*/
|
||||
class meta_account_object : public graphene::db::abstract_object<meta_account_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = meta_account_object_type;
|
||||
|
||||
public_key_type memo_key;
|
||||
committee_member_id_type committee_member_id; // optional
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This secondary index will allow a reverse lookup of all accounts that a particular key or account
|
||||
* is an potential signing authority.
|
||||
|
|
@ -264,6 +261,7 @@ namespace graphene { namespace chain {
|
|||
set<address> before_address_members;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief This secondary index will allow a reverse lookup of all accounts that have been referred by
|
||||
* a particular account.
|
||||
|
|
@ -326,21 +324,19 @@ namespace graphene { namespace chain {
|
|||
}}
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::account_object,
|
||||
(graphene::db::annotated_object<graphene::chain::account_object>),
|
||||
(graphene::db::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) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::account_balance_object,
|
||||
(graphene::db::object),
|
||||
(owner)(asset_type)(balance) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::meta_account_object,
|
||||
(graphene::db::object),
|
||||
(memo_key)(committee_member_id) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (graphene::chain::object),
|
||||
FC_REFLECT_DERIVED( graphene::chain::account_statistics_object,
|
||||
(graphene::chain::object),
|
||||
(owner)
|
||||
(most_recent_op)
|
||||
(total_core_in_orders)
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ namespace graphene { namespace chain {
|
|||
* All assets have a globally unique symbol name that controls how they are traded and an issuer who
|
||||
* has authority over the parameters of the asset.
|
||||
*/
|
||||
class asset_object : public graphene::db::annotated_object<asset_object>
|
||||
class asset_object : public graphene::db::abstract_object<asset_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
|
|
@ -249,8 +249,7 @@ FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db::
|
|||
(settlement_fund)
|
||||
)
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::asset_object,
|
||||
(graphene::db::annotated_object<graphene::chain::asset_object>),
|
||||
FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object),
|
||||
(symbol)
|
||||
(precision)
|
||||
(issuer)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Cryptonomex, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
||||
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted until September 8, 2015, provided that the following conditions are met:
|
||||
*
|
||||
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct budget_record
|
||||
{
|
||||
uint64_t time_since_last_budget = 0;
|
||||
|
||||
// sources of budget
|
||||
share_type from_initial_reserve = 0;
|
||||
share_type from_accumulated_fees = 0;
|
||||
share_type from_unused_witness_budget = 0;
|
||||
|
||||
// witness budget requested by the committee
|
||||
share_type requested_witness_budget = 0;
|
||||
|
||||
// funds that can be released from reserve at maximum rate
|
||||
share_type total_budget = 0;
|
||||
|
||||
// sinks of budget, should sum up to total_budget
|
||||
share_type witness_budget = 0;
|
||||
share_type worker_budget = 0;
|
||||
|
||||
// unused budget
|
||||
share_type leftover_worker_funds = 0;
|
||||
|
||||
// change in supply due to budget operations
|
||||
share_type supply_delta = 0;
|
||||
};
|
||||
|
||||
class budget_record_object;
|
||||
|
||||
class budget_record_object : public graphene::db::abstract_object<budget_record_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_budget_record_object_type;
|
||||
|
||||
fc::time_point_sec time;
|
||||
budget_record record;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT(
|
||||
graphene::chain::budget_record,
|
||||
(time_since_last_budget)
|
||||
(from_initial_reserve)
|
||||
(from_accumulated_fees)
|
||||
(from_unused_witness_budget)
|
||||
(requested_witness_budget)
|
||||
(total_budget)
|
||||
(witness_budget)
|
||||
(worker_budget)
|
||||
(leftover_worker_funds)
|
||||
(supply_delta)
|
||||
)
|
||||
|
||||
FC_REFLECT_DERIVED(
|
||||
graphene::chain::budget_record_object,
|
||||
(graphene::db::object),
|
||||
(time)
|
||||
(record)
|
||||
)
|
||||
|
|
@ -26,8 +26,6 @@
|
|||
#define GRAPHENE_MIN_ASSET_SYMBOL_LENGTH 3
|
||||
#define GRAPHENE_MAX_ASSET_SYMBOL_LENGTH 16
|
||||
|
||||
#define GRAPHENE_MAX_ASSET_NAME_LENGTH 127
|
||||
|
||||
#define GRAPHENE_MAX_SHARE_SUPPLY int64_t(1000000000000000ll)
|
||||
#define GRAPHENE_MAX_PAY_RATE 10000 /* 100% */
|
||||
#define GRAPHENE_MAX_SIG_CHECK_DEPTH 2
|
||||
|
|
@ -137,6 +135,10 @@
|
|||
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4
|
||||
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3
|
||||
|
||||
#define GRAPHENE_CURRENT_DB_VERSION "test5b"
|
||||
|
||||
#define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT)
|
||||
|
||||
/**
|
||||
* Reserved Account IDs with special meaning
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ namespace graphene { namespace chain {
|
|||
using graphene::db::abstract_object;
|
||||
using graphene::db::object;
|
||||
|
||||
struct budget_record;
|
||||
|
||||
/**
|
||||
* @class database
|
||||
* @brief tracks the blockchain state in an extensible manner
|
||||
|
|
@ -122,6 +124,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
void add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts );
|
||||
const flat_map<uint32_t,block_id_type> get_checkpoints()const { return _checkpoints; }
|
||||
bool before_last_checkpoint()const;
|
||||
|
||||
bool push_block( const signed_block& b, uint32_t skip = skip_nothing );
|
||||
processed_transaction push_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing );
|
||||
|
|
@ -140,8 +143,7 @@ namespace graphene { namespace chain {
|
|||
signed_block _generate_block(
|
||||
const fc::time_point_sec when,
|
||||
witness_id_type witness_id,
|
||||
const fc::ecc::private_key& block_signing_private_key,
|
||||
bool retry_on_failure
|
||||
const fc::ecc::private_key& block_signing_private_key
|
||||
);
|
||||
|
||||
void pop_block();
|
||||
|
|
@ -420,19 +422,21 @@ namespace graphene { namespace chain {
|
|||
//////////////////// db_update.cpp ////////////////////
|
||||
void update_global_dynamic_data( const signed_block& b );
|
||||
void update_signing_witness(const witness_object& signing_witness, const signed_block& new_block);
|
||||
void update_last_irreversible_block();
|
||||
void clear_expired_transactions();
|
||||
void clear_expired_proposals();
|
||||
void clear_expired_orders();
|
||||
void update_expired_feeds();
|
||||
void update_maintenance_flag( bool new_maintenance_flag );
|
||||
void update_withdraw_permissions();
|
||||
bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true );
|
||||
|
||||
///Steps performed only at maintenance intervals
|
||||
///@{
|
||||
|
||||
//////////////////// db_maint.cpp ////////////////////
|
||||
|
||||
share_type get_max_budget( fc::time_point_sec now )const;
|
||||
void initialize_budget_record( fc::time_point_sec now, budget_record& rec )const;
|
||||
void process_budget();
|
||||
void pay_workers( share_type& budget );
|
||||
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ struct skip_flags_restorer
|
|||
}
|
||||
|
||||
node_property_object& _npo;
|
||||
uint32_t _old_skip_flags;
|
||||
uint32_t _old_skip_flags; // initialized in ctor
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -69,7 +69,8 @@ namespace graphene { namespace chain {
|
|||
FC_DECLARE_DERIVED_EXCEPTION( operation_evaluate_exception, graphene::chain::chain_exception, 3050000, "operation evaluation exception" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( utility_exception, graphene::chain::chain_exception, 3060000, "utility method exception" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( undo_database_exception, graphene::chain::chain_exception, 3070000, "undo database exception" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, graphene::chain::chain_exception, 3080000, "unlinkable block" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, graphene::chain::chain_exception, 3080000, "unlinkable block" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( black_swan_exception, graphene::chain::chain_exception, 3090000, "black swan" )
|
||||
|
||||
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_active_auth, graphene::chain::transaction_exception, 3030001, "missing required active authority" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_owner_auth, graphene::chain::transaction_exception, 3030002, "missing required owner authority" )
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace graphene { namespace chain {
|
|||
block_id_type previous_id()const { return data.previous; }
|
||||
|
||||
weak_ptr< fork_item > prev;
|
||||
uint32_t num;
|
||||
uint32_t num; // initialized in ctor
|
||||
/**
|
||||
* Used to flag a block as invalid and prevent other blocks from
|
||||
* building on top of it.
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ struct genesis_state_type {
|
|||
string issuer_name;
|
||||
|
||||
string description;
|
||||
uint8_t precision;
|
||||
uint8_t precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
|
||||
|
||||
share_type max_supply;
|
||||
share_type accumulated_fees;
|
||||
|
|
@ -84,7 +84,7 @@ struct genesis_state_type {
|
|||
vector<initial_asset_type> initial_assets;
|
||||
vector<initial_balance_type> initial_balances;
|
||||
vector<initial_vesting_balance_type> initial_vesting_balances;
|
||||
uint64_t initial_active_witnesses;
|
||||
uint64_t initial_active_witnesses = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT;
|
||||
vector<initial_witness_type> initial_witness_candidates;
|
||||
vector<initial_committee_member_type> initial_committee_candidates;
|
||||
vector<initial_worker_type> initial_worker_candidates;
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
uint32_t dynamic_flags = 0;
|
||||
|
||||
uint32_t last_irreversible_block_num = 0;
|
||||
|
||||
enum dynamic_flag_bits
|
||||
{
|
||||
/**
|
||||
|
|
@ -129,6 +131,7 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::
|
|||
(current_aslot)
|
||||
(recent_slots_filled)
|
||||
(dynamic_flags)
|
||||
(last_irreversible_block_num)
|
||||
)
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::global_property_object, (graphene::db::object),
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ namespace graphene { namespace chain {
|
|||
* On the @ref settlement_date the @ref balance will be converted to the collateral asset
|
||||
* and paid to @ref owner and then this object will be deleted.
|
||||
*/
|
||||
class force_settlement_object : public graphene::db::annotated_object<force_settlement_object>
|
||||
class force_settlement_object : public abstract_object<force_settlement_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
|
|
|
|||
|
|
@ -33,9 +33,9 @@ namespace graphene { namespace chain {
|
|||
class node_property_object
|
||||
{
|
||||
public:
|
||||
node_property_object() : skip_flags(0) {}
|
||||
node_property_object(){}
|
||||
~node_property_object(){}
|
||||
|
||||
uint32_t skip_flags;
|
||||
uint32_t skip_flags = 0;
|
||||
};
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ namespace graphene { namespace chain {
|
|||
account_id_type account_to_list;
|
||||
/// The new white and blacklist status of account_to_list, as determined by authorizing_account
|
||||
/// This is a bitfield using values defined in the account_listing enum
|
||||
uint8_t new_listing;
|
||||
uint8_t new_listing = no_listing;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return authorizing_account; }
|
||||
|
|
|
|||
|
|
@ -83,6 +83,15 @@ namespace graphene { namespace chain {
|
|||
result.push_back(k.first);
|
||||
return result;
|
||||
}
|
||||
vector<address> get_addresses() const
|
||||
{
|
||||
vector<address> result;
|
||||
result.reserve( address_auths.size() );
|
||||
for( const auto& k : address_auths )
|
||||
result.push_back(k.first);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
friend bool operator == ( const authority& a, const authority& b )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace graphene { namespace chain {
|
|||
asset fee;
|
||||
account_id_type payer;
|
||||
flat_set<account_id_type> required_auths;
|
||||
uint16_t id;
|
||||
uint16_t id = 0;
|
||||
vector<char> data;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace graphene { namespace chain {
|
|||
* be unique with high probability as long as the generating host has a high-resolution clock OR a strong source
|
||||
* of entropy for generating private keys.
|
||||
*/
|
||||
uint64_t nonce;
|
||||
uint64_t nonce = 0;
|
||||
/**
|
||||
* This field contains the AES encrypted packed @ref memo_message
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ namespace graphene { namespace chain {
|
|||
{
|
||||
impl_global_property_object_type,
|
||||
impl_dynamic_global_property_object_type,
|
||||
impl_index_meta_object_type,
|
||||
impl_reserved0_object_type, // formerly index_meta_object_type, TODO: delete me
|
||||
impl_asset_dynamic_data_type,
|
||||
impl_asset_bitasset_data_type,
|
||||
impl_account_balance_object_type,
|
||||
|
|
@ -142,13 +142,8 @@ namespace graphene { namespace chain {
|
|||
impl_account_transaction_history_object_type,
|
||||
impl_blinded_balance_object_type,
|
||||
impl_chain_property_object_type,
|
||||
impl_witness_schedule_object_type
|
||||
};
|
||||
|
||||
enum meta_info_object_type
|
||||
{
|
||||
meta_asset_object_type,
|
||||
meta_account_object_type
|
||||
impl_witness_schedule_object_type,
|
||||
impl_budget_record_object_type
|
||||
};
|
||||
|
||||
//typedef fc::unsigned_int object_id_type;
|
||||
|
|
@ -186,7 +181,6 @@ namespace graphene { namespace chain {
|
|||
// implementation types
|
||||
class global_property_object;
|
||||
class dynamic_global_property_object;
|
||||
class index_meta_object;
|
||||
class asset_dynamic_data_object;
|
||||
class asset_bitasset_data_object;
|
||||
class account_balance_object;
|
||||
|
|
@ -196,6 +190,7 @@ namespace graphene { namespace chain {
|
|||
class account_transaction_history_object;
|
||||
class chain_property_object;
|
||||
class witness_schedule_object;
|
||||
class budget_record_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;
|
||||
|
|
@ -211,6 +206,7 @@ namespace graphene { namespace chain {
|
|||
account_transaction_history_object> account_transaction_history_id_type;
|
||||
typedef object_id< implementation_ids, impl_chain_property_object_type, chain_property_object> chain_property_id_type;
|
||||
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 fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
|
||||
typedef fc::ripemd160 block_id_type;
|
||||
|
|
@ -225,8 +221,8 @@ namespace graphene { namespace chain {
|
|||
{
|
||||
struct binary_key
|
||||
{
|
||||
binary_key():check(0){}
|
||||
uint32_t check;
|
||||
binary_key() {}
|
||||
uint32_t check = 0;
|
||||
fc::ecc::public_key_data data;
|
||||
};
|
||||
fc::ecc::public_key_data key_data;
|
||||
|
|
@ -277,7 +273,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||
(impl_global_property_object_type)
|
||||
(impl_dynamic_global_property_object_type)
|
||||
(impl_index_meta_object_type)
|
||||
(impl_reserved0_object_type)
|
||||
(impl_asset_dynamic_data_type)
|
||||
(impl_asset_bitasset_data_type)
|
||||
(impl_account_balance_object_type)
|
||||
|
|
@ -288,10 +284,9 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
|||
(impl_blinded_balance_object_type)
|
||||
(impl_chain_property_object_type)
|
||||
(impl_witness_schedule_object_type)
|
||||
(impl_budget_record_object_type)
|
||||
)
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::meta_info_object_type, (meta_account_object_type)(meta_asset_object_type) )
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::share_type )
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::account_id_type )
|
||||
|
|
@ -316,6 +311,7 @@ FC_REFLECT_TYPENAME( graphene::chain::account_statistics_id_type )
|
|||
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( graphene::chain::void_t, )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags, (charge_market_fee)(white_list)(transfer_restricted)(override_authority)(disable_force_settle)(global_settle)(disable_confidential) )
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ namespace graphene { namespace chain {
|
|||
/// The maximum amount authorized_account is allowed to withdraw in a given withdrawal period
|
||||
asset withdrawal_limit;
|
||||
/// Length of the withdrawal period in seconds
|
||||
uint32_t withdrawal_period_sec;
|
||||
uint32_t withdrawal_period_sec = 0;
|
||||
/// The number of withdrawal periods this permission is valid for
|
||||
uint32_t periods_until_expiration;
|
||||
uint32_t periods_until_expiration = 0;
|
||||
/// Time at which the first withdrawal period begins; must be in the future
|
||||
time_point_sec period_start_time;
|
||||
|
||||
|
|
@ -70,11 +70,11 @@ namespace graphene { namespace chain {
|
|||
/// New maximum amount the withdrawer is allowed to charge per withdrawal period
|
||||
asset withdrawal_limit;
|
||||
/// New length of the period between withdrawals
|
||||
uint32_t withdrawal_period_sec;
|
||||
uint32_t withdrawal_period_sec = 0;
|
||||
/// New beginning of the next withdrawal period; must be in the future
|
||||
time_point_sec period_start_time;
|
||||
/// The new number of withdrawal periods for which this permission will be valid
|
||||
uint32_t periods_until_expiration;
|
||||
uint32_t periods_until_expiration = 0;
|
||||
|
||||
account_id_type fee_payer()const { return withdraw_from_account; }
|
||||
void validate()const;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ namespace graphene { namespace chain {
|
|||
vote_id_type vote_id;
|
||||
uint64_t total_votes = 0;
|
||||
string url;
|
||||
int64_t total_missed = 0;
|
||||
int64_t total_missed = 0;
|
||||
uint32_t last_confirmed_block_num = 0;
|
||||
|
||||
witness_object() : vote_id(vote_id_type::witness) {}
|
||||
};
|
||||
|
|
@ -72,4 +73,5 @@ FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object),
|
|||
(total_votes)
|
||||
(url)
|
||||
(total_missed)
|
||||
(last_confirmed_block_num)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -183,6 +183,8 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
|||
call.debt = o.delta_debt.amount;
|
||||
call.call_price = price::call_price(o.delta_debt, o.delta_collateral,
|
||||
_bitasset_data->current_feed.maintenance_collateral_ratio);
|
||||
|
||||
auto swan_price = call.get_debt()/ call.get_collateral();
|
||||
});
|
||||
}
|
||||
else
|
||||
|
|
@ -194,6 +196,7 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
|||
call.debt += o.delta_debt.amount;
|
||||
if( call.debt > 0 )
|
||||
{
|
||||
auto swan_price = call.get_debt()/ call.get_collateral();
|
||||
call.call_price = price::call_price(call.get_debt(), call.get_collateral(),
|
||||
_bitasset_data->current_feed.maintenance_collateral_ratio);
|
||||
}
|
||||
|
|
@ -229,6 +232,7 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
|||
}
|
||||
else
|
||||
{
|
||||
//edump( (~call_obj->call_price) ("<")( _bitasset_data->current_feed.settlement_price) );
|
||||
// We didn't fill any call orders. This may be because we
|
||||
// aren't in margin call territory, or it may be because there
|
||||
// were no matching orders. In the latter case, we throw.
|
||||
|
|
|
|||
|
|
@ -104,7 +104,13 @@ namespace graphene { namespace chain {
|
|||
auto scaled = fc::uint128(base_value) * scale;
|
||||
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;
|
||||
//FC_ASSERT( result * core_exchange_rate >= asset( scaled.to_uint64()) );
|
||||
|
||||
while( result * core_exchange_rate < asset( scaled.to_uint64()) )
|
||||
result.amount++;
|
||||
|
||||
FC_ASSERT( result.amount <= GRAPHENE_MAX_SHARE_SUPPLY );
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
proposal_create_operation proposal_create_operation::committee_proposal(const chain_parameters& global_params, fc::time_point_sec head_block_time )
|
||||
{
|
||||
// TODO move this method to unit tests as it is not useful
|
||||
proposal_create_operation op;
|
||||
op.expiration_time = head_block_time + global_params.maximum_proposal_lifetime;
|
||||
op.review_period_seconds = global_params.committee_proposal_review_period;
|
||||
|
|
|
|||
|
|
@ -114,6 +114,42 @@ struct sign_state
|
|||
return itr->second = true;
|
||||
}
|
||||
|
||||
optional<map<address,public_key_type>> available_address_sigs;
|
||||
optional<map<address,public_key_type>> provided_address_sigs;
|
||||
|
||||
bool signed_by( const address& a ) {
|
||||
if( !available_address_sigs ) {
|
||||
available_address_sigs = std::map<address,public_key_type>();
|
||||
provided_address_sigs = std::map<address,public_key_type>();
|
||||
for( auto& item : available_keys ) {
|
||||
(*available_address_sigs)[ address(pts_address(item, false, 56) ) ] = item;
|
||||
(*available_address_sigs)[ address(pts_address(item, true, 56) ) ] = item;
|
||||
(*available_address_sigs)[ address(pts_address(item, false, 0) ) ] = item;
|
||||
(*available_address_sigs)[ address(pts_address(item, true, 0) ) ] = item;
|
||||
(*available_address_sigs)[ address(item) ] = item;
|
||||
}
|
||||
for( auto& item : provided_signatures ) {
|
||||
(*provided_address_sigs)[ address(pts_address(item.first, false, 56) ) ] = item.first;
|
||||
(*provided_address_sigs)[ address(pts_address(item.first, true, 56) ) ] = item.first;
|
||||
(*provided_address_sigs)[ address(pts_address(item.first, false, 0) ) ] = item.first;
|
||||
(*provided_address_sigs)[ address(pts_address(item.first, true, 0) ) ] = item.first;
|
||||
(*provided_address_sigs)[ address(item.first) ] = item.first;
|
||||
}
|
||||
}
|
||||
auto itr = provided_address_sigs->find(a);
|
||||
if( itr == provided_address_sigs->end() )
|
||||
{
|
||||
auto aitr = available_address_sigs->find(a);
|
||||
if( aitr != available_address_sigs->end() ) {
|
||||
auto pk = available_keys.find(aitr->second);
|
||||
if( pk != available_keys.end() )
|
||||
return provided_signatures[aitr->second] = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return provided_signatures[itr->second] = true;
|
||||
}
|
||||
|
||||
bool check_authority( account_id_type id )
|
||||
{
|
||||
if( approved_by.find(id) != approved_by.end() ) return true;
|
||||
|
|
@ -138,6 +174,14 @@ struct sign_state
|
|||
return true;
|
||||
}
|
||||
|
||||
for( const auto& k : auth.address_auths )
|
||||
if( signed_by( k.first ) )
|
||||
{
|
||||
total_weight += k.second;
|
||||
if( total_weight >= auth.weight_threshold )
|
||||
return true;
|
||||
}
|
||||
|
||||
for( const auto& a : auth.account_auths )
|
||||
{
|
||||
if( approved_by.find(a.first) == approved_by.end() )
|
||||
|
|
|
|||
|
|
@ -67,6 +67,19 @@ namespace graphene { namespace db {
|
|||
return a.number < b.number;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
bool is() const
|
||||
{
|
||||
return (number >> 48) == ((T::space_id << 8) | (T::type_id));
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
T as() const
|
||||
{
|
||||
FC_ASSERT( is<T>() );
|
||||
return T( *this );
|
||||
}
|
||||
|
||||
uint64_t number;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -62,32 +62,34 @@ namespace graphene { namespace db {
|
|||
elog( "${e}", ("e",e.to_detail_string() ) );
|
||||
throw; // maybe crash..
|
||||
}
|
||||
if( _disable_on_exit ) _db.disable();
|
||||
}
|
||||
void commit() { _apply_undo = false; _db.commit(); }
|
||||
void undo() { if( _apply_undo ) _db.undo(); _apply_undo = false; }
|
||||
void merge() { if( _apply_undo ) _db.merge(); _apply_undo = false; }
|
||||
|
||||
session& operator = ( session&& mv )
|
||||
{
|
||||
{ try {
|
||||
if( this == &mv ) return *this;
|
||||
if( _apply_undo ) _db.undo();
|
||||
_apply_undo = mv._apply_undo;
|
||||
mv._apply_undo = false;
|
||||
return *this;
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
private:
|
||||
friend class undo_database;
|
||||
session(undo_database& db): _db(db) {}
|
||||
session(undo_database& db, bool disable_on_exit = false): _db(db),_disable_on_exit(disable_on_exit) {}
|
||||
undo_database& _db;
|
||||
bool _apply_undo = true;
|
||||
bool _disable_on_exit = false;
|
||||
};
|
||||
|
||||
void disable();
|
||||
void enable();
|
||||
bool enabled()const { return !_disabled; }
|
||||
|
||||
session start_undo_session();
|
||||
session start_undo_session( bool force_enable = false );
|
||||
/**
|
||||
* This should be called just after an object is created
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ const object& object_database::get_object( object_id_type id )const
|
|||
const index& object_database::get_index(uint8_t space_id, uint8_t type_id)const
|
||||
{
|
||||
FC_ASSERT( _index.size() > space_id, "", ("space_id",space_id)("type_id",type_id)("index.size",_index.size()) );
|
||||
assert( _index[space_id].size() > type_id ); //, "", ("space_id",space_id)("type_id",type_id)("index[space_id].size",_index[space_id].size()) );
|
||||
FC_ASSERT( _index[space_id].size() > type_id, "", ("space_id",space_id)("type_id",type_id)("index[space_id].size",_index[space_id].size()) );
|
||||
const auto& tmp = _index[space_id][type_id];
|
||||
FC_ASSERT( tmp );
|
||||
|
|
|
|||
|
|
@ -24,16 +24,19 @@ namespace graphene { namespace db {
|
|||
void undo_database::enable() { _disabled = false; }
|
||||
void undo_database::disable() { _disabled = true; }
|
||||
|
||||
undo_database::session undo_database::start_undo_session()
|
||||
undo_database::session undo_database::start_undo_session( bool force_enable )
|
||||
{
|
||||
if( _disabled ) return session(*this);
|
||||
if( _disabled && !force_enable ) return session(*this);
|
||||
bool disable_on_exit = _disabled && force_enable;
|
||||
if( force_enable )
|
||||
_disabled = false;
|
||||
|
||||
while( size() > max_size() )
|
||||
_stack.pop_front();
|
||||
|
||||
_stack.emplace_back();
|
||||
++_active_sessions;
|
||||
return session(*this);
|
||||
return session(*this, disable_on_exit );
|
||||
}
|
||||
void undo_database::on_create( const object& obj )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,8 +56,11 @@
|
|||
* our peers and save a copy in a cache were we will find it if
|
||||
* a peer requests it. We expire out old items out of the cache
|
||||
* after this number of blocks go by.
|
||||
*
|
||||
* Recently lowered from 30 to match the default expiration time
|
||||
* the web wallet imposes on transactions.
|
||||
*/
|
||||
#define GRAPHENE_NET_MESSAGE_CACHE_DURATION_IN_BLOCKS 30
|
||||
#define GRAPHENE_NET_MESSAGE_CACHE_DURATION_IN_BLOCKS 5
|
||||
|
||||
/**
|
||||
* We prevent a peer from offering us a list of blocks which, if we fetched them
|
||||
|
|
@ -68,11 +71,22 @@
|
|||
*/
|
||||
#define GRAPHENE_NET_FUTURE_SYNC_BLOCKS_GRACE_PERIOD_SEC (60 * 60)
|
||||
|
||||
#define GRAPHENE_NET_INSUFFICIENT_RELAY_FEE_PENALTY_SEC 15
|
||||
|
||||
#define GRAPHENE_NET_MAX_INVENTORY_SIZE_IN_MINUTES 2
|
||||
|
||||
#define GRAPHENE_NET_MAX_BLOCKS_PER_PEER_DURING_SYNCING 100
|
||||
#define GRAPHENE_NET_MAX_BLOCKS_PER_PEER_DURING_SYNCING 200
|
||||
|
||||
/**
|
||||
* During normal operation, how many items will be fetched from each
|
||||
* peer at a time. This will only come into play when the network
|
||||
* is being flooded -- typically transactions will be fetched as soon
|
||||
* as we find out about them, so only one item will be requested
|
||||
* at a time.
|
||||
*
|
||||
* No tests have been done to find the optimal value for this
|
||||
* parameter, so consider increasing or decreasing it if performance
|
||||
* during flooding is lacking.
|
||||
*/
|
||||
#define GRAPHENE_NET_MAX_ITEMS_PER_PEER_DURING_NORMAL_OPERATION 1
|
||||
|
||||
/**
|
||||
* Instead of fetching all item IDs from a peer, then fetching all blocks
|
||||
|
|
|
|||
|
|
@ -1108,39 +1108,81 @@ namespace graphene { namespace net { namespace detail {
|
|||
|
||||
fc::time_point next_peer_unblocked_time = fc::time_point::maximum();
|
||||
|
||||
std::forward_list<std::pair<peer_connection_ptr, item_id> > fetch_messages_to_send;
|
||||
std::vector<fc::future<void> > write_ops;
|
||||
for (auto iter = _items_to_fetch.begin(); iter != _items_to_fetch.end();)
|
||||
// we need to construct a list of items to request from each peer first,
|
||||
// then send the messages (in two steps, to avoid yielding while iterating)
|
||||
// we want to evenly distribute our requests among our peers.
|
||||
struct requested_item_count_index {};
|
||||
struct peer_and_items_to_fetch
|
||||
{
|
||||
peer_connection_ptr peer;
|
||||
std::vector<item_id> item_ids;
|
||||
peer_and_items_to_fetch(const peer_connection_ptr& peer) : peer(peer) {}
|
||||
bool operator<(const peer_and_items_to_fetch& rhs) const { return peer < rhs.peer; }
|
||||
size_t number_of_items() const { return item_ids.size(); }
|
||||
};
|
||||
typedef boost::multi_index_container<peer_and_items_to_fetch,
|
||||
boost::multi_index::indexed_by<boost::multi_index::ordered_unique<boost::multi_index::member<peer_and_items_to_fetch, peer_connection_ptr, &peer_and_items_to_fetch::peer> >,
|
||||
boost::multi_index::ordered_non_unique<boost::multi_index::tag<requested_item_count_index>,
|
||||
boost::multi_index::const_mem_fun<peer_and_items_to_fetch, size_t, &peer_and_items_to_fetch::number_of_items> > > > fetch_messages_to_send_set;
|
||||
fetch_messages_to_send_set items_by_peer;
|
||||
|
||||
// initialize the fetch_messages_to_send with an empty set of items for all idle peers
|
||||
for (const peer_connection_ptr& peer : _active_connections)
|
||||
if (peer->idle())
|
||||
items_by_peer.insert(peer_and_items_to_fetch(peer));
|
||||
|
||||
// now loop over all items we want to fetch
|
||||
for (auto item_iter = _items_to_fetch.begin(); item_iter != _items_to_fetch.end();)
|
||||
{
|
||||
// and find a peer that has it, we'll use the one who has the least requests going to it to load balance
|
||||
bool item_fetched = false;
|
||||
for (const peer_connection_ptr& peer : _active_connections)
|
||||
for (auto peer_iter = items_by_peer.get<requested_item_count_index>().begin(); peer_iter != items_by_peer.get<requested_item_count_index>().end(); ++peer_iter)
|
||||
{
|
||||
if (peer->idle() &&
|
||||
peer->inventory_peer_advertised_to_us.find(iter->item) != peer->inventory_peer_advertised_to_us.end())
|
||||
const peer_connection_ptr& peer = peer_iter->peer;
|
||||
// if they have the item and we haven't already decided to ask them for too many other items
|
||||
if (peer_iter->item_ids.size() < GRAPHENE_NET_MAX_ITEMS_PER_PEER_DURING_NORMAL_OPERATION &&
|
||||
peer->inventory_peer_advertised_to_us.find(item_iter->item) != peer->inventory_peer_advertised_to_us.end())
|
||||
{
|
||||
if (peer->is_transaction_fetching_inhibited() && iter->item.item_type == graphene::net::trx_message_type)
|
||||
if (item_iter->item.item_type == graphene::net::trx_message_type && peer->is_transaction_fetching_inhibited())
|
||||
next_peer_unblocked_time = std::min(peer->transaction_fetching_inhibited_until, next_peer_unblocked_time);
|
||||
else
|
||||
{
|
||||
dlog("requesting item ${hash} from peer ${endpoint}",
|
||||
("hash", iter->item.item_hash)("endpoint", peer->get_remote_endpoint()));
|
||||
peer->items_requested_from_peer.insert(peer_connection::item_to_time_map_type::value_type(iter->item, fc::time_point::now()));
|
||||
item_id item_id_to_fetch = iter->item;
|
||||
iter = _items_to_fetch.erase(iter);
|
||||
//dlog("requesting item ${hash} from peer ${endpoint}",
|
||||
// ("hash", iter->item.item_hash)("endpoint", peer->get_remote_endpoint()));
|
||||
item_id item_id_to_fetch = item_iter->item;
|
||||
peer->items_requested_from_peer.insert(peer_connection::item_to_time_map_type::value_type(item_id_to_fetch, fc::time_point::now()));
|
||||
item_iter = _items_to_fetch.erase(item_iter);
|
||||
item_fetched = true;
|
||||
fetch_messages_to_send.emplace_front(std::make_pair(peer, item_id_to_fetch));
|
||||
items_by_peer.get<requested_item_count_index>().modify(peer_iter, [&item_id_to_fetch](peer_and_items_to_fetch& peer_and_items) {
|
||||
peer_and_items.item_ids.push_back(item_id_to_fetch);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!item_fetched)
|
||||
++iter;
|
||||
++item_iter;
|
||||
}
|
||||
|
||||
for (const auto& peer_and_item : fetch_messages_to_send)
|
||||
peer_and_item.first->send_message(fetch_items_message(peer_and_item.second.item_type,
|
||||
std::vector<item_hash_t>{peer_and_item.second.item_hash}));
|
||||
fetch_messages_to_send.clear();
|
||||
// we've figured out which peer will be providing each item, now send the messages.
|
||||
for (const peer_and_items_to_fetch& peer_and_items : items_by_peer)
|
||||
{
|
||||
// the item lists are heterogenous and
|
||||
// the fetch_items_message can only deal with one item type at a time.
|
||||
std::map<uint32_t, std::vector<item_hash_t> > items_to_fetch_by_type;
|
||||
for (const item_id& item : peer_and_items.item_ids)
|
||||
items_to_fetch_by_type[item.item_type].push_back(item.item_hash);
|
||||
for (auto& items_by_type : items_to_fetch_by_type)
|
||||
{
|
||||
dlog("requesting ${count} items of type ${type} from peer ${endpoint}: ${hashes}",
|
||||
("count", items_by_type.second.size())("type", (uint32_t)items_by_type.first)
|
||||
("endpoint", peer_and_items.peer->get_remote_endpoint())
|
||||
("hashes", items_by_type.second));
|
||||
peer_and_items.peer->send_message(fetch_items_message(items_by_type.first,
|
||||
items_by_type.second));
|
||||
}
|
||||
}
|
||||
items_by_peer.clear();
|
||||
|
||||
if (!_items_to_fetch_updated)
|
||||
{
|
||||
|
|
@ -3742,14 +3784,6 @@ namespace graphene { namespace net { namespace detail {
|
|||
_delegate->handle_message( message_to_process );
|
||||
message_validated_time = fc::time_point::now();
|
||||
}
|
||||
catch ( const insufficient_relay_fee& )
|
||||
{
|
||||
// flooding control. The message was valid but we can't handle it now.
|
||||
assert(message_to_process.msg_type == graphene::net::trx_message_type); // we only support throttling transactions.
|
||||
if (message_to_process.msg_type == graphene::net::trx_message_type)
|
||||
originating_peer->transaction_fetching_inhibited_until = fc::time_point::now() + fc::seconds(GRAPHENE_NET_INSUFFICIENT_RELAY_FEE_PENALTY_SEC);
|
||||
return;
|
||||
}
|
||||
catch ( const fc::canceled_exception& )
|
||||
{
|
||||
throw;
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
*/
|
||||
|
||||
#include <graphene/delayed_node/delayed_node_plugin.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/worker_evaluator.hpp>
|
||||
#include <graphene/app/api.hpp>
|
||||
|
||||
#include <fc/network/http/websocket.hpp>
|
||||
|
|
@ -33,12 +33,12 @@ namespace bpo = boost::program_options;
|
|||
namespace detail {
|
||||
struct delayed_node_plugin_impl {
|
||||
std::string remote_endpoint;
|
||||
int delay_blocks;
|
||||
fc::http::websocket_client client;
|
||||
std::shared_ptr<fc::rpc::websocket_api_connection> client_connection;
|
||||
fc::api<graphene::app::database_api> database_api;
|
||||
boost::signals2::scoped_connection client_connection_closed;
|
||||
bool currently_fetching = false;
|
||||
graphene::chain::block_id_type last_received_remote_head;
|
||||
graphene::chain::block_id_type last_processed_remote_head;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -49,12 +49,12 @@ delayed_node_plugin::delayed_node_plugin()
|
|||
delayed_node_plugin::~delayed_node_plugin()
|
||||
{}
|
||||
|
||||
void delayed_node_plugin::plugin_set_program_options(bpo::options_description&, bpo::options_description& cfg)
|
||||
void delayed_node_plugin::plugin_set_program_options(bpo::options_description& cli, bpo::options_description& cfg)
|
||||
{
|
||||
cfg.add_options()
|
||||
cli.add_options()
|
||||
("trusted-node", boost::program_options::value<std::string>()->required(), "RPC endpoint of a trusted validating node (required)")
|
||||
("delay-block-count", boost::program_options::value<int>()->required(), "Number of blocks to delay before advancing chain state (required)")
|
||||
;
|
||||
cfg.add(cli);
|
||||
}
|
||||
|
||||
void delayed_node_plugin::connect()
|
||||
|
|
@ -69,58 +69,79 @@ void delayed_node_plugin::connect()
|
|||
void delayed_node_plugin::plugin_initialize(const boost::program_options::variables_map& options)
|
||||
{
|
||||
my->remote_endpoint = "ws://" + options.at("trusted-node").as<std::string>();
|
||||
my->delay_blocks = options.at("delay-block-count").as<int>();
|
||||
}
|
||||
|
||||
void delayed_node_plugin::sync_with_trusted_node(uint32_t remote_head_block_num)
|
||||
void delayed_node_plugin::sync_with_trusted_node()
|
||||
{
|
||||
struct raii {
|
||||
bool* target;
|
||||
~raii() {
|
||||
*target = false;
|
||||
auto& db = database();
|
||||
uint32_t synced_blocks = 0;
|
||||
uint32_t pass_count = 0;
|
||||
while( true )
|
||||
{
|
||||
graphene::chain::dynamic_global_property_object remote_dpo = my->database_api->get_dynamic_global_properties();
|
||||
if( remote_dpo.last_irreversible_block_num <= db.head_block_num() )
|
||||
{
|
||||
if( remote_dpo.last_irreversible_block_num < db.head_block_num() )
|
||||
{
|
||||
wlog( "Trusted node seems to be behind delayed node" );
|
||||
}
|
||||
if( synced_blocks > 1 )
|
||||
{
|
||||
ilog( "Delayed node finished syncing ${n} blocks in ${k} passes", ("n", synced_blocks)("k", pass_count) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
pass_count++;
|
||||
while( remote_dpo.last_irreversible_block_num > db.head_block_num() )
|
||||
{
|
||||
fc::optional<graphene::chain::signed_block> block = my->database_api->get_block( db.head_block_num()+1 );
|
||||
FC_ASSERT(block, "Trusted node claims it has blocks it doesn't actually have.");
|
||||
ilog("Pushing block #${n}", ("n", block->block_num()));
|
||||
db.push_block(*block);
|
||||
synced_blocks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (my->currently_fetching) return;
|
||||
raii releaser{&my->currently_fetching};
|
||||
my->currently_fetching = true;
|
||||
void delayed_node_plugin::mainloop()
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
try
|
||||
{
|
||||
fc::usleep( fc::microseconds( 296645 ) ); // wake up a little over 3Hz
|
||||
|
||||
auto head_block = database().head_block_num();
|
||||
while (remote_head_block_num - head_block > my->delay_blocks) {
|
||||
fc::optional<graphene::chain::signed_block> block = my->database_api->get_block(++head_block);
|
||||
FC_ASSERT(block, "Trusted node claims it has blocks it doesn't actually have.");
|
||||
ilog("Pushing block #${n}", ("n", block->block_num()));
|
||||
database().push_block(*block);
|
||||
if( my->last_received_remote_head == my->last_processed_remote_head )
|
||||
continue;
|
||||
|
||||
sync_with_trusted_node();
|
||||
my->last_processed_remote_head = my->last_received_remote_head;
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
elog("Error during connection: ${e}", ("e", e.to_detail_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void delayed_node_plugin::plugin_startup()
|
||||
{
|
||||
try {
|
||||
fc::async([this]()
|
||||
{
|
||||
mainloop();
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
connect();
|
||||
|
||||
my->database_api->set_subscribe_callback([this] (const fc::variant& v) {
|
||||
auto& updates = v.get_array();
|
||||
for( const auto& v : updates )
|
||||
{
|
||||
if( v.is_object() )
|
||||
{
|
||||
auto& obj = v.get_object();
|
||||
if( obj["id"].as<graphene::chain::object_id_type>() == graphene::chain::dynamic_global_property_id_type() )
|
||||
{
|
||||
auto props = v.as<graphene::chain::dynamic_global_property_object>();
|
||||
sync_with_trusted_node(props.head_block_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
||||
// Go ahead and get in sync now, before subscribing
|
||||
chain::dynamic_global_property_object props = my->database_api->get_dynamic_global_properties();
|
||||
sync_with_trusted_node(props.head_block_number);
|
||||
|
||||
my->database_api->set_block_applied_callback([this]( const fc::variant& block_id )
|
||||
{
|
||||
fc::from_variant( block_id, my->last_received_remote_head );
|
||||
} );
|
||||
return;
|
||||
} catch (const fc::exception& e) {
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
elog("Error during connection: ${e}", ("e", e.to_detail_string()));
|
||||
}
|
||||
fc::async([this]{connection_failed();});
|
||||
|
|
@ -129,7 +150,7 @@ void delayed_node_plugin::plugin_startup()
|
|||
void delayed_node_plugin::connection_failed()
|
||||
{
|
||||
elog("Connection to trusted node failed; retrying in 5 seconds...");
|
||||
fc::schedule([this]{plugin_startup();}, fc::time_point::now() + fc::seconds(5));
|
||||
fc::schedule([this]{connect();}, fc::time_point::now() + fc::seconds(5));
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -34,11 +34,12 @@ public:
|
|||
boost::program_options::options_description& cfg) override;
|
||||
virtual void plugin_initialize(const boost::program_options::variables_map& options) override;
|
||||
virtual void plugin_startup() override;
|
||||
void mainloop();
|
||||
|
||||
protected:
|
||||
void connection_failed();
|
||||
void connect();
|
||||
void sync_with_trusted_node(uint32_t remote_head_block_num);
|
||||
void sync_with_trusted_node();
|
||||
};
|
||||
|
||||
} } //graphene::account_history
|
||||
|
|
|
|||
|
|
@ -107,7 +107,8 @@ struct operation_process_fill_order
|
|||
auto itr = by_key_idx.find( key );
|
||||
if( itr == by_key_idx.end() )
|
||||
{ // create new bucket
|
||||
const auto& obj = db.create<bucket_object>( [&]( bucket_object& b ){
|
||||
/* const auto& obj = */
|
||||
db.create<bucket_object>( [&]( bucket_object& b ){
|
||||
b.key = key;
|
||||
b.quote_volume += trade_price.quote.amount;
|
||||
b.base_volume += trade_price.base.amount;
|
||||
|
|
@ -120,7 +121,7 @@ struct operation_process_fill_order
|
|||
b.low_base = b.close_base;
|
||||
b.low_quote = b.close_quote;
|
||||
});
|
||||
wlog( " creating bucket ${b}", ("b",obj) );
|
||||
//wlog( " creating bucket ${b}", ("b",obj) );
|
||||
}
|
||||
else
|
||||
{ // update existing bucket
|
||||
|
|
|
|||
80
libraries/wallet/include/graphene/wallet/reflect_util.hpp
Normal file
80
libraries/wallet/include/graphene/wallet/reflect_util.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Cryptonomex, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
||||
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted until September 8, 2015, provided that the following conditions are met:
|
||||
*
|
||||
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// This file contains various reflection methods that are used to
|
||||
// support the wallet, e.g. allow specifying operations by name
|
||||
// instead of ID.
|
||||
|
||||
namespace graphene { namespace wallet {
|
||||
|
||||
struct static_variant_map
|
||||
{
|
||||
flat_map< string, int > name_to_which;
|
||||
vector< string > which_to_name;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct static_variant_map_visitor
|
||||
{
|
||||
static_variant_map_visitor() {}
|
||||
|
||||
typedef void result_type;
|
||||
|
||||
template< typename T >
|
||||
result_type operator()( const T& dummy )
|
||||
{
|
||||
assert( which == m.which_to_name.size() );
|
||||
std::string name = js_name<T>::name();
|
||||
m.name_to_which[ name ] = which;
|
||||
m.which_to_name.push_back( name );
|
||||
}
|
||||
|
||||
static_variant_map m;
|
||||
int which;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template< typename T >
|
||||
T from_which_variant( int which, const variant& v )
|
||||
{
|
||||
// Parse a variant for a known which()
|
||||
T result;
|
||||
result.set_which( which );
|
||||
from_variant( v, result );
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static_variant_map create_static_variant_map()
|
||||
{
|
||||
T dummy;
|
||||
int n = dummy.count();
|
||||
impl::static_variant_map_visitor vtor;
|
||||
for( int i=0; i<n; i++ )
|
||||
{
|
||||
dummy.set_which(i);
|
||||
vtor.which = i;
|
||||
dummy.visit( vtor );
|
||||
}
|
||||
return vtor.m;
|
||||
}
|
||||
|
||||
} } // namespace graphene::wallet
|
||||
|
|
@ -1269,22 +1269,29 @@ class wallet_api
|
|||
* desired.
|
||||
*
|
||||
* @param proposing_account The account paying the fee to propose the tx
|
||||
* @param expiration_time Timestamp specifying when the proposal will either take effect or expire.
|
||||
* @param changed_values The values to change; all other chain parameters are filled in with default values
|
||||
* @param broadcast true if you wish to broadcast the transaction
|
||||
* @return the signed version of the transaction
|
||||
*/
|
||||
signed_transaction propose_parameter_change(
|
||||
const string& proposing_account,
|
||||
fc::time_point_sec expiration_time,
|
||||
const variant_object& changed_values,
|
||||
bool broadcast = false);
|
||||
|
||||
/** Propose a fee change.
|
||||
*
|
||||
* Not implemented.
|
||||
*
|
||||
* @param proposing_account The account paying the fee to propose the tx
|
||||
* @param expiration_time Timestamp specifying when the proposal will either take effect or expire.
|
||||
* @param changed_values Map of operation type to new fee. Operations may be specified by name or ID.
|
||||
* The "scale" key changes the scale. All other operations will maintain current values.
|
||||
* @param broadcast true if you wish to broadcast the transaction
|
||||
* @return the signed version of the transaction
|
||||
*/
|
||||
signed_transaction propose_fee_change(
|
||||
const string& proposing_account,
|
||||
fc::time_point_sec expiration_time,
|
||||
const variant_object& changed_values,
|
||||
bool broadcast = false);
|
||||
|
||||
|
|
|
|||
|
|
@ -51,10 +51,12 @@
|
|||
|
||||
#include <graphene/app/api.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
#include <graphene/utilities/words.hpp>
|
||||
#include <graphene/wallet/wallet.hpp>
|
||||
#include <graphene/wallet/api_documentation.hpp>
|
||||
#include <graphene/wallet/reflect_util.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
#ifndef WIN32
|
||||
|
|
@ -1965,6 +1967,7 @@ public:
|
|||
|
||||
signed_transaction propose_parameter_change(
|
||||
const string& proposing_account,
|
||||
fc::time_point_sec expiration_time,
|
||||
const variant_object& changed_values,
|
||||
bool broadcast = false)
|
||||
{
|
||||
|
|
@ -1979,8 +1982,10 @@ public:
|
|||
committee_member_update_global_parameters_operation update_op;
|
||||
update_op.new_parameters = new_params;
|
||||
|
||||
proposal_create_operation prop_op = proposal_create_operation::committee_proposal(
|
||||
current_params, get_dynamic_global_properties().time );
|
||||
proposal_create_operation prop_op;
|
||||
|
||||
prop_op.expiration_time = expiration_time;
|
||||
prop_op.review_period_seconds = current_params.committee_proposal_review_period;
|
||||
prop_op.fee_paying_account = get_account(proposing_account).id;
|
||||
|
||||
prop_op.proposed_ops.emplace_back( update_op );
|
||||
|
|
@ -1996,10 +2001,84 @@ public:
|
|||
|
||||
signed_transaction propose_fee_change(
|
||||
const string& proposing_account,
|
||||
const variant_object& changed_values,
|
||||
fc::time_point_sec expiration_time,
|
||||
const variant_object& changed_fees,
|
||||
bool broadcast = false)
|
||||
{
|
||||
FC_ASSERT( false, "not implemented" );
|
||||
const chain_parameters& current_params = get_global_properties().parameters;
|
||||
const fee_schedule_type& current_fees = *(current_params.current_fees);
|
||||
|
||||
flat_map< int, fee_parameters > fee_map;
|
||||
fee_map.reserve( current_fees.parameters.size() );
|
||||
for( const fee_parameters& op_fee : current_fees.parameters )
|
||||
fee_map[ op_fee.which() ] = op_fee;
|
||||
uint32_t scale = current_fees.scale;
|
||||
|
||||
for( const auto& item : changed_fees )
|
||||
{
|
||||
const string& key = item.key();
|
||||
if( key == "scale" )
|
||||
{
|
||||
int64_t _scale = item.value().as_int64();
|
||||
FC_ASSERT( _scale >= 0 );
|
||||
FC_ASSERT( _scale <= std::numeric_limits<uint32_t>::max() );
|
||||
scale = uint32_t( _scale );
|
||||
continue;
|
||||
}
|
||||
// is key a number?
|
||||
auto is_numeric = [&]() -> bool
|
||||
{
|
||||
size_t n = key.size();
|
||||
for( size_t i=0; i<n; i++ )
|
||||
{
|
||||
if( !isdigit( key[i] ) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
int which;
|
||||
if( is_numeric() )
|
||||
which = std::stoi( key );
|
||||
else
|
||||
{
|
||||
const auto& n2w = _operation_which_map.name_to_which;
|
||||
auto it = n2w.find( key );
|
||||
FC_ASSERT( it != n2w.end(), "unknown operation" );
|
||||
which = it->second;
|
||||
}
|
||||
|
||||
fee_parameters fp = from_which_variant< fee_parameters >( which, item.value() );
|
||||
fee_map[ which ] = fp;
|
||||
}
|
||||
|
||||
fee_schedule_type new_fees;
|
||||
|
||||
for( const std::pair< int, fee_parameters >& item : fee_map )
|
||||
new_fees.parameters.insert( item.second );
|
||||
new_fees.scale = scale;
|
||||
|
||||
chain_parameters new_params = current_params;
|
||||
new_params.current_fees = new_fees;
|
||||
|
||||
committee_member_update_global_parameters_operation update_op;
|
||||
update_op.new_parameters = new_params;
|
||||
|
||||
proposal_create_operation prop_op;
|
||||
|
||||
prop_op.expiration_time = expiration_time;
|
||||
prop_op.review_period_seconds = current_params.committee_proposal_review_period;
|
||||
prop_op.fee_paying_account = get_account(proposing_account).id;
|
||||
|
||||
prop_op.proposed_ops.emplace_back( update_op );
|
||||
current_params.current_fees->set_fee( prop_op.proposed_ops.back().op );
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back(prop_op);
|
||||
set_operation_fees(tx, current_params.current_fees);
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction(tx, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction approve_proposal(
|
||||
|
|
@ -2168,6 +2247,8 @@ public:
|
|||
|
||||
flat_map<string, operation> _prototype_ops;
|
||||
|
||||
static_variant_map _operation_which_map = create_static_variant_map< operation >();
|
||||
|
||||
#ifdef __unix__
|
||||
mode_t _old_umask;
|
||||
#endif
|
||||
|
|
@ -2863,20 +2944,22 @@ void wallet_api::flood_network(string prefix, uint32_t number_of_transactions)
|
|||
|
||||
signed_transaction wallet_api::propose_parameter_change(
|
||||
const string& proposing_account,
|
||||
fc::time_point_sec expiration_time,
|
||||
const variant_object& changed_values,
|
||||
bool broadcast /* = false */
|
||||
)
|
||||
{
|
||||
return my->propose_parameter_change( proposing_account, changed_values, broadcast );
|
||||
return my->propose_parameter_change( proposing_account, expiration_time, changed_values, broadcast );
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::propose_fee_change(
|
||||
const string& proposing_account,
|
||||
const variant_object& changed_values,
|
||||
fc::time_point_sec expiration_time,
|
||||
const variant_object& changed_fees,
|
||||
bool broadcast /* = false */
|
||||
)
|
||||
{
|
||||
return my->propose_fee_change( proposing_account, changed_values, broadcast );
|
||||
return my->propose_fee_change( proposing_account, expiration_time, changed_fees, broadcast );
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::approve_proposal(
|
||||
|
|
|
|||
71
programs/genesis_util/remove.py
Executable file
71
programs/genesis_util/remove.py
Executable file
|
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
def dump_json(obj, out, pretty):
|
||||
if pretty:
|
||||
json.dump(obj, out, indent=2, sort_keys=True)
|
||||
else:
|
||||
json.dump(obj, out, separators=(",", ":"), sort_keys=True)
|
||||
return
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Remove entities from snapshot")
|
||||
parser.add_argument("-o", "--output", metavar="OUT", default="-", help="output filename (default: stdout)")
|
||||
parser.add_argument("-i", "--input", metavar="IN", default="-", help="input filename (default: stdin)")
|
||||
parser.add_argument("-a", "--asset", metavar="ASSETS", nargs="+", help="list of asset(s) to delete")
|
||||
parser.add_argument("-p", "--pretty", action="store_true", default=False, help="pretty print output")
|
||||
opts = parser.parse_args()
|
||||
|
||||
if opts.input == "-":
|
||||
genesis = json.load(sys.stdin)
|
||||
else:
|
||||
with open(opts.input, "r") as f:
|
||||
genesis = json.load(f)
|
||||
|
||||
if opts.asset is None:
|
||||
opts.asset = []
|
||||
rm_asset_set = set(opts.asset)
|
||||
|
||||
removed_asset_entries = {aname : 0 for aname in opts.asset}
|
||||
new_initial_assets = []
|
||||
for asset in genesis["initial_assets"]:
|
||||
symbol = asset["symbol"]
|
||||
if symbol not in rm_asset_set:
|
||||
new_initial_assets.append(asset)
|
||||
else:
|
||||
removed_asset_entries[symbol] += 1
|
||||
genesis["initial_assets"] = new_initial_assets
|
||||
|
||||
removed_balance_entries = {aname : [] for aname in opts.asset}
|
||||
new_initial_balances = []
|
||||
for balance in genesis["initial_balances"]:
|
||||
symbol = balance["asset_symbol"]
|
||||
if symbol not in rm_asset_set:
|
||||
new_initial_balances.append(balance)
|
||||
else:
|
||||
removed_balance_entries[symbol].append(balance)
|
||||
genesis["initial_balances"] = new_initial_balances
|
||||
# TODO: Remove from initial_vesting_balances
|
||||
|
||||
for aname in opts.asset:
|
||||
sys.stderr.write(
|
||||
"Asset {sym} removed {acount} initial_assets, {bcount} initial_balances totaling {btotal}\n".format(
|
||||
sym=aname,
|
||||
acount=removed_asset_entries[aname],
|
||||
bcount=len(removed_balance_entries[aname]),
|
||||
btotal=sum(int(e["amount"]) for e in removed_balance_entries[aname]),
|
||||
))
|
||||
|
||||
if opts.output == "-":
|
||||
dump_json( genesis, sys.stdout, opts.pretty )
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
with open(opts.output, "w") as f:
|
||||
dump_json( genesis, f, opts.pretty )
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -68,12 +68,6 @@ database_fixture::database_fixture()
|
|||
|
||||
boost::program_options::variables_map options;
|
||||
|
||||
// app.initialize();
|
||||
ahplugin->plugin_set_app(&app);
|
||||
ahplugin->plugin_initialize(options);
|
||||
mhplugin->plugin_set_app(&app);
|
||||
mhplugin->plugin_initialize(options);
|
||||
|
||||
genesis_state.initial_timestamp = time_point_sec( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
|
||||
|
||||
genesis_state.initial_active_witnesses = 10;
|
||||
|
|
@ -88,7 +82,14 @@ database_fixture::database_fixture()
|
|||
genesis_state.initial_witness_candidates.push_back({name, init_account_priv_key.get_public_key()});
|
||||
}
|
||||
genesis_state.initial_parameters.current_fees->zero_all_fees();
|
||||
db.init_genesis(genesis_state);
|
||||
open_database();
|
||||
|
||||
// app.initialize();
|
||||
ahplugin->plugin_set_app(&app);
|
||||
ahplugin->plugin_initialize(options);
|
||||
mhplugin->plugin_set_app(&app);
|
||||
mhplugin->plugin_initialize(options);
|
||||
|
||||
ahplugin->plugin_startup();
|
||||
mhplugin->plugin_startup();
|
||||
|
||||
|
|
@ -292,8 +293,6 @@ void database_fixture::open_database()
|
|||
|
||||
signed_block database_fixture::generate_block(uint32_t skip, const fc::ecc::private_key& key, int miss_blocks)
|
||||
{
|
||||
open_database();
|
||||
|
||||
skip |= database::skip_undo_history_check;
|
||||
// skip == ~0 will skip checks specified in database::validation_steps
|
||||
auto block = db.generate_block(db.get_slot_time(miss_blocks + 1),
|
||||
|
|
|
|||
|
|
@ -1035,7 +1035,7 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture )
|
|||
{
|
||||
ACTORS( (alice)(bob) );
|
||||
|
||||
auto generate_block = [&]( database& d, uint32_t skip = database::skip_nothing ) -> signed_block
|
||||
auto generate_block = [&]( database& d, uint32_t skip ) -> signed_block
|
||||
{
|
||||
return d.generate_block(d.get_slot_time(1), d.get_scheduled_witness(1), init_account_priv_key, skip);
|
||||
};
|
||||
|
|
@ -1058,7 +1058,7 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture )
|
|||
BOOST_CHECK( db2.get( alice_id ).name == "alice" );
|
||||
BOOST_CHECK( db2.get( bob_id ).name == "bob" );
|
||||
|
||||
db2.push_block(generate_block(db));
|
||||
db2.push_block(generate_block(db, database::skip_nothing));
|
||||
transfer( account_id_type(), alice_id, asset( 1000 ) );
|
||||
transfer( account_id_type(), bob_id, asset( 1000 ) );
|
||||
// need to skip authority check here as well for same reason as above
|
||||
|
|
@ -1073,7 +1073,7 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture )
|
|||
{
|
||||
for( int i=0; i<n; i++ )
|
||||
{
|
||||
signed_block b = generate_block(db2);
|
||||
signed_block b = generate_block(db2, database::skip_nothing);
|
||||
PUSH_BLOCK( db, b );
|
||||
}
|
||||
};
|
||||
|
|
@ -1112,7 +1112,7 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture )
|
|||
BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 1000);
|
||||
|
||||
// generate a block with db and ensure we don't somehow apply it
|
||||
PUSH_BLOCK(db2, generate_block(db));
|
||||
PUSH_BLOCK(db2, generate_block(db, database::skip_nothing));
|
||||
BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 1000);
|
||||
BOOST_CHECK_EQUAL(db.get_balance( bob_id, asset_id_type()).amount.value, 1000);
|
||||
|
||||
|
|
@ -1133,7 +1133,7 @@ BOOST_FIXTURE_TEST_CASE( transaction_invalidated_in_cache, database_fixture )
|
|||
signed_transaction tx_b = generate_xfer_tx( alice_id, bob_id, 2000, 10 );
|
||||
signed_transaction tx_c = generate_xfer_tx( alice_id, bob_id, 500, 10 );
|
||||
|
||||
generate_block( db );
|
||||
generate_block( db, database::skip_nothing );
|
||||
|
||||
PUSH_TX( db, tx_a );
|
||||
BOOST_CHECK_EQUAL(db.get_balance(alice_id, asset_id_type()).amount.value, 2000);
|
||||
|
|
|
|||
|
|
@ -262,6 +262,117 @@ BOOST_AUTO_TEST_CASE( black_swan )
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Black swan occurs when price feed falls, triggered by settlement
|
||||
* order.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE( black_swan_issue_346 )
|
||||
{ try {
|
||||
ACTORS((buyer)(seller)(borrower)(borrower2)(settler)(feeder));
|
||||
|
||||
const asset_object& core = asset_id_type()(db);
|
||||
|
||||
int trial = 0;
|
||||
const int64_t init_balance(1000000);
|
||||
|
||||
vector< const account_object* > actors{ &buyer, &seller, &borrower, &borrower2, &settler, &feeder };
|
||||
|
||||
auto top_up = [&]()
|
||||
{
|
||||
for( const account_object* actor : actors )
|
||||
{
|
||||
int64_t bal = get_balance( *actor, core );
|
||||
if( bal < init_balance )
|
||||
transfer( committee_account, actor->id, asset(init_balance - bal) );
|
||||
else if( bal > init_balance )
|
||||
transfer( actor->id, committee_account, asset(bal - init_balance) );
|
||||
}
|
||||
};
|
||||
|
||||
auto setup_asset = [&]() -> const asset_object&
|
||||
{
|
||||
const asset_object& bitusd = create_bitasset("BITUSD"+fc::to_string(trial), feeder_id);
|
||||
update_feed_producers( bitusd, {feeder.id} );
|
||||
BOOST_CHECK( !bitusd.bitasset_data(db).has_settlement() );
|
||||
trial++;
|
||||
return bitusd;
|
||||
};
|
||||
|
||||
/*
|
||||
* GRAPHENE_COLLATERAL_RATIO_DENOM
|
||||
uint16_t maintenance_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO;
|
||||
uint16_t maximum_short_squeeze_ratio = GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO;
|
||||
*/
|
||||
|
||||
// situations to test:
|
||||
// 1. minus short squeeze protection would be black swan, otherwise no
|
||||
// 2. issue 346 (price feed drops followed by force settle, drop should trigger BS)
|
||||
// 3. feed price < D/C of least collateralized short < call price < highest bid
|
||||
|
||||
auto set_price = [&](
|
||||
const asset_object& bitusd,
|
||||
const price& settlement_price
|
||||
)
|
||||
{
|
||||
price_feed feed;
|
||||
feed.settlement_price = settlement_price;
|
||||
wdump( (feed.max_short_squeeze_price()) );
|
||||
publish_feed( bitusd, feeder, feed );
|
||||
};
|
||||
|
||||
auto wait_for_settlement = [&]()
|
||||
{
|
||||
const auto& idx = db.get_index_type<force_settlement_index>().indices().get<by_expiration>();
|
||||
const auto& itr = idx.rbegin();
|
||||
if( itr == idx.rend() )
|
||||
return;
|
||||
generate_blocks( itr->settlement_date );
|
||||
BOOST_CHECK( !idx.empty() );
|
||||
generate_block();
|
||||
BOOST_CHECK( idx.empty() );
|
||||
};
|
||||
|
||||
{
|
||||
const asset_object& bitusd = setup_asset();
|
||||
top_up();
|
||||
set_price( bitusd, bitusd.amount(1) / core.amount(5) ); // $0.20
|
||||
borrow(borrower, bitusd.amount(100), asset(1000)); // 2x collat
|
||||
transfer( borrower, settler, bitusd.amount(100) );
|
||||
|
||||
// drop to $0.02 and settle
|
||||
BOOST_CHECK( !bitusd.bitasset_data(db).has_settlement() );
|
||||
set_price( bitusd, bitusd.amount(1) / core.amount(50) ); // $0.02
|
||||
BOOST_CHECK( bitusd.bitasset_data(db).has_settlement() );
|
||||
GRAPHENE_REQUIRE_THROW( borrow( borrower2, bitusd.amount(100), asset(10000) ), fc::exception );
|
||||
force_settle( settler, bitusd.amount(100) );
|
||||
|
||||
// wait for forced settlement to execute
|
||||
// this would throw on Sep.18 testnet, see #346
|
||||
wait_for_settlement();
|
||||
}
|
||||
|
||||
// issue 350
|
||||
{
|
||||
// ok, new asset
|
||||
const asset_object& bitusd = setup_asset();
|
||||
top_up();
|
||||
set_price( bitusd, bitusd.amount(40) / core.amount(1000) ); // $0.04
|
||||
borrow( borrower, bitusd.amount(100), asset(5000) ); // 2x collat
|
||||
transfer( borrower, seller, bitusd.amount(100) );
|
||||
limit_order_id_type oid_019 = create_sell_order( seller, bitusd.amount(39), core.amount(2000) )->id; // this order is at $0.019, we should not be able to match against it
|
||||
limit_order_id_type oid_020 = create_sell_order( seller, bitusd.amount(40), core.amount(2000) )->id; // this order is at $0.020, we should be able to match against it
|
||||
set_price( bitusd, bitusd.amount(21) / core.amount(1000) ); // $0.021
|
||||
BOOST_CHECK( !bitusd.bitasset_data(db).has_settlement() );
|
||||
BOOST_CHECK( db.find_object( oid_019 ) != nullptr );
|
||||
BOOST_CHECK( db.find_object( oid_020 ) == nullptr );
|
||||
}
|
||||
|
||||
} catch( const fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( prediction_market )
|
||||
{ try {
|
||||
ACTORS((judge)(dan)(nathan));
|
||||
|
|
|
|||
Loading…
Reference in a new issue