Merge remote-tracking branch 'graphene/develop' into bitshares

This commit is contained in:
theoreticalbts 2016-03-16 01:09:52 -04:00
commit 4dbfe53f1f
38 changed files with 1624 additions and 188 deletions

View file

@ -11,7 +11,8 @@ add_library( graphene_app
${EGENESIS_HEADERS}
)
target_link_libraries( graphene_app graphene_market_history graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities )
# need to link graphene_debug_witness because plugins aren't sufficiently isolated #246
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness )
target_include_directories( graphene_app
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )

View file

@ -95,6 +95,12 @@ namespace graphene { namespace app {
{
_crypto_api = std::make_shared< crypto_api >();
}
else if( api_name == "debug_api" )
{
// can only enable this API if the plugin was loaded
if( _app.get_plugin( "debug_witness" ) )
_debug_api = std::make_shared< graphene::debug_witness::debug_api >( std::ref(_app) );
}
return;
}
@ -211,6 +217,12 @@ namespace graphene { namespace app {
return *_crypto_api;
}
fc::api<graphene::debug_witness::debug_api> login_api::debug() const
{
FC_ASSERT(_debug_api);
return *_debug_api;
}
vector<account_id_type> get_relevant_accounts( const object* obj )
{
vector<account_id_type> result;
@ -380,25 +392,58 @@ namespace graphene { namespace app {
return result;
}
vector<operation_history_object> history_api::get_account_history(account_id_type account, operation_history_id_type stop, unsigned limit, operation_history_id_type start) const
vector<operation_history_object> history_api::get_account_history( account_id_type account,
operation_history_id_type stop,
unsigned limit,
operation_history_id_type start ) const
{
FC_ASSERT(_app.chain_database());
FC_ASSERT( _app.chain_database() );
const auto& db = *_app.chain_database();
FC_ASSERT( limit <= 100 );
vector<operation_history_object> result;
const auto& stats = account(db).statistics(db);
if( stats.most_recent_op == account_transaction_history_id_type() ) return result;
const account_transaction_history_object* node = &stats.most_recent_op(db);
if( start == operation_history_id_type() )
start = node->operation_id;
while(node && node->operation_id.instance.value > stop.instance.value && result.size() < limit)
{
if( node->operation_id.instance.value <= start.instance.value )
result.push_back( node->operation_id(db) );
if( node->next == account_transaction_history_id_type() )
node = nullptr;
else node = &node->next(db);
}
return result;
}
vector<operation_history_object> history_api::get_relative_account_history( account_id_type account,
uint32_t stop,
unsigned limit,
uint32_t start) const
{
FC_ASSERT( _app.chain_database() );
const auto& db = *_app.chain_database();
FC_ASSERT(limit <= 100);
vector<operation_history_object> result;
const auto& stats = account(db).statistics(db);
if(stats.most_recent_op == account_transaction_history_id_type()) return result;
const account_transaction_history_object* node = &stats.most_recent_op(db);
if(start == operation_history_id_type())
start = node->id;
while(node && node->operation_id.instance.value > stop.instance.value && result.size() < limit)
if( start == 0 )
start = account(db).statistics(db).total_ops;
else start = min( account(db).statistics(db).total_ops, start );
const auto& hist_idx = db.get_index_type<account_transaction_history_index>();
const auto& by_seq_idx = hist_idx.indices().get<by_seq>();
auto itr = by_seq_idx.upper_bound( boost::make_tuple( account, start ) );
auto itr_stop = by_seq_idx.lower_bound( boost::make_tuple( account, stop ) );
--itr;
while ( itr != itr_stop && result.size() < limit )
{
if(node->id.instance() <= start.instance.value)
result.push_back(node->operation_id(db));
if(node->next == account_transaction_history_id_type())
node = nullptr;
else node = db.find(node->next);
result.push_back( itr->operation_id(db) );
--itr;
}
return result;
}

View file

@ -225,7 +225,7 @@ namespace detail {
if( !_options->count("rpc-endpoint") )
return;
bool enable_deflate_compression = _options->count("disable-permessage-deflate") == 0;
bool enable_deflate_compression = _options->count("enable-permessage-deflate") != 0;
_websocket_server = std::make_shared<fc::http::websocket_server>(enable_deflate_compression);
@ -254,7 +254,7 @@ namespace detail {
}
string password = _options->count("server-pem-password") ? _options->at("server-pem-password").as<string>() : "";
bool enable_deflate_compression = _options->count("disable-permessage-deflate") == 0;
bool enable_deflate_compression = _options->count("enable-permessage-deflate") != 0;
_websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>( _options->at("server-pem").as<string>(), password, enable_deflate_compression );
_websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){
@ -949,8 +949,8 @@ void application::set_program_options(boost::program_options::options_descriptio
("checkpoint,c", bpo::value<vector<string>>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
("rpc-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8090"), "Endpoint for websocket RPC to listen on")
("rpc-tls-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8089"), "Endpoint for TLS websocket RPC to listen on")
("disable-permessage-deflate", "Disable support for per-message deflate compression in the websocket servers "
"(--rpc-endpoint and --rpc-tls-endpoint), enabled by default")
("enable-permessage-deflate", "Enable support for per-message deflate compression in the websocket servers "
"(--rpc-endpoint and --rpc-tls-endpoint), disabled by default")
("server-pem,p", bpo::value<string>()->implicit_value("server.pem"), "The TLS certificate file for this server")
("server-pem-password,P", bpo::value<string>()->implicit_value(""), "Password for this certificate")
("genesis-json", bpo::value<boost::filesystem::path>(), "File to read Genesis State from")

View file

@ -31,6 +31,8 @@
#include <fc/crypto/hex.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/rational.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <cctype>
@ -1025,56 +1027,37 @@ market_ticker database_api_impl::get_ticker( const string& base, const string& q
try {
if( base_id > quote_id ) std::swap(base_id, quote_id);
const auto& bidx = _db.get_index_type<bucket_index>();
const auto& by_key_idx = bidx.indices().get<by_key>();
uint32_t bucket_size = 86400;
uint32_t day = 86400;
auto now = fc::time_point_sec( fc::time_point::now() );
auto itr = by_key_idx.lower_bound( bucket_key( base_id, quote_id, bucket_size,
now - bucket_size ) );
auto orders = get_order_book( base, quote, 1 );
auto trades = get_trade_history( base, quote, now, fc::time_point_sec( now.sec_since_epoch() - day ), 100 );
if( itr != by_key_idx.end() && itr->key.base == base_id && itr->key.quote == quote_id && itr->key.seconds == bucket_size )
result.latest = trades[0].price;
for ( market_trade t: trades )
{
auto trades = get_trade_history( base, quote, now, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
result.base_volume += t.value;
result.quote_volume += t.amount;
}
if (assets[0]->id == base_id)
{
result.latest = trades[0].price;
result.percent_change = ( result.latest / ( price_to_real( itr->open_quote, assets[1]->precision ) / price_to_real( itr->open_base, assets[0]->precision ) ) - 1 ) * 100;
//result.lowest_ask = price_to_real( itr->low_quote, assets[1]->precision ) / price_to_real( itr->low_base, assets[0]->precision );
//result.highest_bid = price_to_real( itr->high_quote, assets[1]->precision ) / price_to_real( itr->high_base, assets[0]->precision );
result.lowest_ask = orders.asks[0].first;
result.highest_bid = orders.bids[0].first;
}
else
{
result.latest = trades[0].price;
result.percent_change = ( result.latest / ( price_to_real( itr->open_base, assets[1]->precision ) / price_to_real( itr->open_quote, assets[0]->precision ) ) - 1) * 100;
//result.lowest_ask = price_to_real( itr->low_base, assets[1]->precision ) / price_to_real( itr->low_quote, assets[0]->precision );
//result.highest_bid = price_to_real( itr->high_base, assets[1]->precision ) / price_to_real( itr->high_quote, assets[0]->precision );
result.lowest_ask = orders.bids[0].first;
result.highest_bid = orders.asks[0].first;
}
while (trades.size() == 100)
{
trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - day ), 100 );
for ( market_trade t: trades )
{
result.base_volume += t.amount;
result.quote_volume += t.value;
result.base_volume += t.value;
result.quote_volume += t.amount;
}
}
while (trades.size() == 100)
{
for ( market_trade t: trades )
{
result.base_volume += t.amount;
result.quote_volume += t.value;
}
trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
}
trades = get_trade_history( base, quote, trades.back().date, fc::time_point_sec(), 1 );
result.percent_change = trades.size() > 0 ? ( ( result.latest / trades.back().price ) - 1 ) * 100 : 0;
//if (assets[0]->id == base_id)
{
result.lowest_ask = orders.asks[0].price;
result.highest_bid = orders.bids[0].price;
}
return result;
@ -1111,19 +1094,19 @@ market_volume database_api_impl::get_24_volume( const string& base, const string
for ( market_trade t: trades )
{
result.base_volume += t.amount;
result.quote_volume += t.value;
result.base_volume += t.value;
result.quote_volume += t.amount;
}
while (trades.size() == 100)
{
trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
for ( market_trade t: trades )
{
result.base_volume += t.amount;
result.quote_volume += t.value;
result.base_volume += t.value;
result.quote_volume += t.amount;
}
trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
}
return result;
@ -1137,6 +1120,7 @@ order_book database_api::get_order_book( const string& base, const string& quote
order_book database_api_impl::get_order_book( const string& base, const string& quote, unsigned limit )const
{
using boost::multiprecision::uint128_t;
FC_ASSERT( limit <= 50 );
order_book result;
@ -1156,21 +1140,28 @@ order_book database_api_impl::get_order_book( const string& base, const string&
auto price_to_real = [&]( const price& p )
{
if( p.base.asset_id == base_id )
return asset_to_real( p.quote, assets[1]->precision ) / asset_to_real( p.base, assets[0]->precision );
return asset_to_real( p.base, assets[0]->precision ) / asset_to_real( p.quote, assets[1]->precision );
else
return asset_to_real( p.base, assets[1]->precision ) / asset_to_real( p.quote, assets[0]->precision );
return asset_to_real( p.quote, assets[0]->precision ) / asset_to_real( p.base, assets[1]->precision );
};
for( const auto& o : orders ) {
for( const auto& o : orders )
{
if( o.sell_price.base.asset_id == base_id )
{
result.asks.push_back( std::make_pair( price_to_real(o.sell_price),
asset_to_real(o.sell_price.base, assets[0]->precision)) );
order ord;
ord.price = price_to_real( o.sell_price );
ord.quote = asset_to_real( share_type( ( uint128_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ), assets[1]->precision );
ord.base = asset_to_real( o.for_sale, assets[0]->precision );
result.bids.push_back( ord );
}
else
{
result.bids.push_back( std::make_pair( price_to_real(o.sell_price),
asset_to_real(o.sell_price.quote, assets[0]->precision ) ) );
order ord;
ord.price = price_to_real( o.sell_price );
ord.quote = asset_to_real( o.for_sale, assets[1]->precision );
ord.base = asset_to_real( share_type( ( uint64_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ), assets[0]->precision );
result.asks.push_back( ord );
}
}
@ -1225,13 +1216,13 @@ vector<market_trade> database_api_impl::get_trade_history( const string& base,
if( assets[0]->id == itr->op.receives.asset_id )
{
trade.amount = price_to_real( itr->op.receives.amount, assets[0]->precision );
trade.value = price_to_real( itr->op.pays.amount, assets[1]->precision );
trade.amount = price_to_real( itr->op.pays.amount, assets[1]->precision );
trade.value = price_to_real( itr->op.receives.amount, assets[0]->precision );
}
else
{
trade.amount = price_to_real( itr->op.pays.amount, assets[0]->precision );
trade.value = price_to_real( itr->op.receives.amount, assets[1]->precision );
trade.amount = price_to_real( itr->op.receives.amount, assets[1]->precision );
trade.value = price_to_real( itr->op.pays.amount, assets[0]->precision );
}
trade.date = itr->time;

View file

@ -30,6 +30,8 @@
#include <graphene/market_history/market_history_plugin.hpp>
#include <graphene/debug_witness/debug_api.hpp>
#include <graphene/net/node.hpp>
#include <fc/api.hpp>
@ -91,6 +93,22 @@ namespace graphene { namespace app {
operation_history_id_type stop = operation_history_id_type(),
unsigned limit = 100,
operation_history_id_type start = operation_history_id_type())const;
/**
* @breif Get operations relevant to the specified account referenced
* by an event numbering specific to the account. The current number of operations
* for the account can be found in the account statistics (or use 0 for start).
* @param account The account whose history should be queried
* @param stop Sequence number of earliest operation. 0 is default and will
* query 'limit' number of operations.
* @param limit Maximum number of operations to retrieve (must not exceed 100)
* @param start Sequence number of the most recent operation to retrieve.
* 0 is default, which will start querying from the most recent operation.
* @return A list of operations performed by account, ordered from most recent to oldest.
*/
vector<operation_history_object> get_relative_account_history( account_id_type account,
uint32_t stop = 0,
unsigned limit = 100,
uint32_t start = 0) const;
vector<order_history_object> get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const;
vector<bucket_object> get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds,
@ -265,6 +283,8 @@ namespace graphene { namespace app {
fc::api<network_node_api> network_node()const;
/// @brief Retrieve the cryptography API
fc::api<crypto_api> crypto()const;
/// @brief Retrieve the debug API (if available)
fc::api<graphene::debug_witness::debug_api> debug()const;
private:
/// @brief Called to enable an API, not reflected.
@ -276,6 +296,7 @@ namespace graphene { namespace app {
optional< fc::api<network_node_api> > _network_node_api;
optional< fc::api<history_api> > _history_api;
optional< fc::api<crypto_api> > _crypto_api;
optional< fc::api<graphene::debug_witness::debug_api> > _debug_api;
};
}} // graphene::app
@ -291,6 +312,7 @@ FC_REFLECT( graphene::app::verify_range_proof_rewind_result,
FC_API(graphene::app::history_api,
(get_account_history)
(get_relative_account_history)
(get_fill_order_history)
(get_market_history)
(get_market_history_buckets)
@ -326,4 +348,5 @@ FC_API(graphene::app::login_api,
(history)
(network_node)
(crypto)
(debug)
)

View file

@ -64,40 +64,47 @@ using namespace std;
class database_api_impl;
struct order
{
double price;
double quote;
double base;
};
struct order_book
{
string base;
string quote;
vector< pair<double,double> > bids;
vector< pair<double,double> > asks;
string base;
string quote;
vector< order > bids;
vector< order > asks;
};
struct market_ticker
{
string base;
string quote;
double latest;
double lowest_ask;
double highest_bid;
double percent_change;
double base_volume;
double quote_volume;
string base;
string quote;
double latest;
double lowest_ask;
double highest_bid;
double percent_change;
double base_volume;
double quote_volume;
};
struct market_volume
{
string base;
string quote;
double base_volume;
double quote_volume;
string base;
string quote;
double base_volume;
double quote_volume;
};
struct market_trade
{
fc::time_point_sec date;
double price;
double amount;
double value;
fc::time_point_sec date;
double price;
double amount;
double value;
};
/**
@ -116,7 +123,7 @@ class database_api
/////////////
// Objects //
/////////////
/**
* @brief Get the objects corresponding to the provided IDs
* @param ids IDs of the objects to retrieve
@ -202,7 +209,7 @@ class database_api
//////////
// Keys //
//////////
vector<vector<account_id_type>> get_key_references( vector<public_key_type> key )const;
//////////////
@ -465,9 +472,9 @@ class database_api
*/
map<string, committee_member_id_type> lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const;
/// WORKERS
/// WORKERS
/**
* Return the worker objects associated with this account.
*/
@ -515,7 +522,7 @@ class database_api
bool verify_authority( const signed_transaction& trx )const;
/**
* @return true if the signers have enough authority to authorize an account
* @return true if the signers have enough authority to authorize an account
*/
bool verify_account_authority( const string& name_or_id, const flat_set<public_key_type>& signers )const;
@ -553,6 +560,8 @@ class database_api
};
} }
FC_REFLECT( graphene::app::order, (price)(quote)(base) );
FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) );
FC_REFLECT( graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) );
FC_REFLECT( graphene::app::market_volume, (base)(quote)(base_volume)(quote_volume) );

View file

@ -33,6 +33,7 @@
#include <graphene/chain/internal_exceptions.hpp>
#include <graphene/chain/special_authority.hpp>
#include <graphene/chain/special_authority_object.hpp>
#include <graphene/chain/worker_object.hpp>
#include <algorithm>
@ -54,6 +55,43 @@ void verify_authority_accounts( const database& db, const authority& a )
}
}
void verify_account_votes( const database& db, const account_options& options )
{
// ensure account's votes satisfy requirements
// NB only the part of vote checking that requires chain state is here,
// the rest occurs in account_options::validate()
const auto& gpo = db.get_global_properties();
const auto& chain_params = gpo.parameters;
FC_ASSERT( options.num_witness <= chain_params.maximum_witness_count,
"Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) );
FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count,
"Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) );
uint32_t max_vote_id = gpo.next_available_vote_id;
bool has_worker_votes = false;
for( auto id : options.votes )
{
FC_ASSERT( id < max_vote_id );
has_worker_votes |= (id.type() == vote_id_type::worker);
}
if( has_worker_votes && (db.head_block_time() >= HARDFORK_607_TIME) )
{
const auto& against_worker_idx = db.get_index_type<worker_index>().indices().get<by_vote_against>();
for( auto id : options.votes )
{
if( id.type() == vote_id_type::worker )
{
FC_ASSERT( against_worker_idx.find( id ) == against_worker_idx.end() );
}
}
}
}
void_result account_create_evaluator::do_evaluate( const account_create_operation& op )
{ try {
database& d = db();
@ -62,14 +100,18 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
FC_ASSERT( !op.extensions.value.owner_special_authority.valid() );
FC_ASSERT( !op.extensions.value.active_special_authority.valid() );
}
if( d.head_block_time() < HARDFORK_599_TIME )
{
FC_ASSERT( !op.extensions.value.null_ext.valid() );
FC_ASSERT( !op.extensions.value.owner_special_authority.valid() );
FC_ASSERT( !op.extensions.value.active_special_authority.valid() );
FC_ASSERT( !op.extensions.value.buyback_options.valid() );
}
FC_ASSERT( d.find_object(op.options.voting_account), "Invalid proxy account specified." );
FC_ASSERT( fee_paying_account->is_lifetime_member(), "Only Lifetime members may register an account." );
FC_ASSERT( op.referrer(d).is_member(d.head_block_time()), "The referrer must be either a lifetime or annual subscriber." );
const auto& global_props = d.get_global_properties();
const auto& chain_params = global_props.parameters;
try
{
verify_authority_accounts( d, op.owner );
@ -84,27 +126,7 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
evaluate_special_authority( d, *op.extensions.value.active_special_authority );
if( op.extensions.value.buyback_options.valid() )
evaluate_buyback_account_options( d, *op.extensions.value.buyback_options );
uint32_t max_vote_id = global_props.next_available_vote_id;
FC_ASSERT( op.options.num_witness <= chain_params.maximum_witness_count,
"Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) );
FC_ASSERT( op.options.num_committee <= chain_params.maximum_committee_count,
"Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) );
safe<uint32_t> counts[vote_id_type::VOTE_TYPE_COUNT];
for( auto id : op.options.votes )
{
FC_ASSERT( id < max_vote_id );
counts[id.type()]++;
}
FC_ASSERT(counts[vote_id_type::witness] <= op.options.num_witness,
"",
("count", counts[vote_id_type::witness])("num", op.options.num_witness));
FC_ASSERT(counts[vote_id_type::committee] <= op.options.num_committee,
"",
("count", counts[vote_id_type::committee])("num", op.options.num_committee));
verify_account_votes( d, op.options );
auto& acnt_indx = d.get_index_type<account_index>();
if( op.name.size() )
@ -223,8 +245,12 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
FC_ASSERT( !o.extensions.value.owner_special_authority.valid() );
FC_ASSERT( !o.extensions.value.active_special_authority.valid() );
}
const auto& chain_params = d.get_global_properties().parameters;
if( d.head_block_time() < HARDFORK_599_TIME )
{
FC_ASSERT( !o.extensions.value.null_ext.valid() );
FC_ASSERT( !o.extensions.value.owner_special_authority.valid() );
FC_ASSERT( !o.extensions.value.active_special_authority.valid() );
}
try
{
@ -241,16 +267,8 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
acnt = &o.account(d);
if( o.new_options )
{
FC_ASSERT( o.new_options->num_witness <= chain_params.maximum_witness_count );
FC_ASSERT( o.new_options->num_committee <= chain_params.maximum_committee_count );
uint32_t max_vote_id = d.get_global_properties().next_available_vote_id;
for( auto id : o.new_options->votes )
{
FC_ASSERT( id < max_vote_id );
}
}
if( o.new_options.valid() )
verify_account_votes( d, *o.new_options );
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
@ -371,11 +389,13 @@ void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator:
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - a.network_fee_percentage;
} else if( a.is_annual_member(d.head_block_time()) ) {
// Renew an annual subscription that's still in effect.
FC_ASSERT( d.head_block_time() <= HARDFORK_613_TIME );
FC_ASSERT(a.membership_expiration_date - d.head_block_time() < fc::days(3650),
"May not extend annual membership more than a decade into the future.");
a.membership_expiration_date += fc::days(365);
} else {
// Upgrade from basic account.
FC_ASSERT( d.head_block_time() <= HARDFORK_613_TIME );
a.statistics(d).process_fees(a, d);
assert(a.is_basic_account(d.head_block_time()));
a.referrer = a.get_id();

View file

@ -36,6 +36,8 @@
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/evaluator.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace chain {
bool database::is_known_block( const block_id_type& id )const
@ -532,6 +534,8 @@ void database::_apply_block( const signed_block& next_block )
// to be called for header validation?
update_maintenance_flag( maint_needed );
update_witness_schedule();
if( !_node_property_object.debug_updates.empty() )
apply_debug_updates();
// notify observers that the block has been applied
applied_block( next_block ); //emit

View file

@ -95,4 +95,107 @@ void database::debug_dump()
*/
}
void debug_apply_update( database& db, const fc::variant_object& vo )
{
static const uint8_t
db_action_nil = 0,
db_action_create = 1,
db_action_write = 2,
db_action_update = 3,
db_action_delete = 4;
// "_action" : "create" object must not exist, unspecified fields take defaults
// "_action" : "write" object may exist, is replaced entirely, unspecified fields take defaults
// "_action" : "update" object must exist, unspecified fields don't change
// "_action" : "delete" object must exist, will be deleted
// if _action is unspecified:
// - delete if object contains only ID field
// - otherwise, write
object_id_type oid;
uint8_t action = db_action_nil;
auto it_id = vo.find("id");
FC_ASSERT( it_id != vo.end() );
from_variant( it_id->value(), oid );
action = ( vo.size() == 1 ) ? db_action_delete : db_action_write;
from_variant( vo["id"], oid );
if( vo.size() == 1 )
action = db_action_delete;
auto it_action = vo.find("_action" );
if( it_action != vo.end() )
{
const std::string& str_action = it_action->value().get_string();
if( str_action == "create" )
action = db_action_create;
else if( str_action == "write" )
action = db_action_write;
else if( str_action == "update" )
action = db_action_update;
else if( str_action == "delete" )
action = db_action_delete;
}
auto& idx = db.get_index( oid );
switch( action )
{
case db_action_create:
/*
idx.create( [&]( object& obj )
{
idx.object_from_variant( vo, obj );
} );
*/
FC_ASSERT( false );
break;
case db_action_write:
db.modify( db.get_object( oid ), [&]( object& obj )
{
idx.object_default( obj );
idx.object_from_variant( vo, obj );
} );
break;
case db_action_update:
db.modify( db.get_object( oid ), [&]( object& obj )
{
idx.object_from_variant( vo, obj );
} );
break;
case db_action_delete:
db.remove( db.get_object( oid ) );
break;
default:
FC_ASSERT( false );
}
}
void database::apply_debug_updates()
{
block_id_type head_id = head_block_id();
auto it = _node_property_object.debug_updates.find( head_id );
if( it == _node_property_object.debug_updates.end() )
return;
for( const fc::variant_object& update : it->second )
debug_apply_update( *this, update );
}
void database::debug_update( const fc::variant_object& update )
{
block_id_type head_id = head_block_id();
auto it = _node_property_object.debug_updates.find( head_id );
if( it == _node_property_object.debug_updates.end() )
it = _node_property_object.debug_updates.emplace( head_id, std::vector< fc::variant_object >() ).first;
it->second.emplace_back( update );
optional<signed_block> head_block = fetch_block_by_id( head_id );
FC_ASSERT( head_block.valid() );
// What the last block does has been changed by adding to node_property_object, so we have to re-apply it
pop_block();
push_block( *head_block );
}
} }

View file

@ -102,11 +102,12 @@ void database::update_worker_votes()
{
auto& idx = get_index_type<worker_index>();
auto itr = idx.indices().get<by_account>().begin();
bool allow_negative_votes = (head_block_time() < HARDFORK_607_TIME);
while( itr != idx.indices().get<by_account>().end() )
{
modify( *itr, [&]( worker_object& obj ){
obj.total_votes_for = _vote_tally_buffer[obj.vote_for];
obj.total_votes_against = _vote_tally_buffer[obj.vote_against];
obj.total_votes_against = allow_negative_votes ? _vote_tally_buffer[obj.vote_against] : 0;
});
++itr;
}
@ -684,6 +685,37 @@ void create_buyback_orders( database& db )
return;
}
void deprecate_annual_members( database& db )
{
const auto& account_idx = db.get_index_type<account_index>().indices().get<by_id>();
fc::time_point_sec now = db.head_block_time();
for( const account_object& acct : account_idx )
{
try
{
transaction_evaluation_state upgrade_context(&db);
upgrade_context.skip_fee_schedule_check = true;
if( acct.is_annual_member( now ) )
{
account_upgrade_operation upgrade_vop;
upgrade_vop.fee = asset( 0, asset_id_type() );
upgrade_vop.account_to_upgrade = acct.id;
upgrade_vop.upgrade_to_lifetime_member = true;
db.apply_operation( upgrade_context, upgrade_vop );
}
}
catch( const fc::exception& e )
{
// we can in fact get here, e.g. if asset issuer of buy/sell asset blacklists/whitelists the buyback account
wlog( "Skipping annual member deprecate processing for account ${a} (${an}) at block ${n}; exception was ${e}",
("a", acct.id)("an", acct.name)("n", db.head_block_num())("e", e.to_detail_string()) );
continue;
}
}
return;
}
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
{
const auto& gpo = get_global_properties();
@ -830,7 +862,12 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
}
}
modify(get_dynamic_global_properties(), [next_maintenance_time](dynamic_global_property_object& d) {
const dynamic_global_property_object& dgpo = get_dynamic_global_properties();
if( (dgpo.next_maintenance_time < HARDFORK_613_TIME) && (next_maintenance_time >= HARDFORK_613_TIME) )
deprecate_annual_members(*this);
modify(dgpo, [next_maintenance_time](dynamic_global_property_object& d) {
d.next_maintenance_time = next_maintenance_time;
d.accounts_registered_this_interval = 0;
});

View file

@ -27,6 +27,7 @@
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/transaction_object.hpp>
@ -434,7 +435,12 @@ void database::update_expired_feeds()
assert( a.is_market_issued() );
const asset_bitasset_data_object& b = a.bitasset_data(*this);
if( b.feed_is_expired(head_block_time()) )
bool feed_is_expired;
if( head_block_time() < HARDFORK_615_TIME )
feed_is_expired = b.feed_is_expired_before_hardfork_615( head_block_time() );
else
feed_is_expired = b.feed_is_expired( head_block_time() );
if( feed_is_expired )
{
modify(b, [this](asset_bitasset_data_object& a) {
a.update_median_feeds(head_block_time());

View file

@ -0,0 +1,4 @@
// #599 Unpacking of extension is incorrect
#ifndef HARDFORK_599_TIME
#define HARDFORK_599_TIME (fc::time_point_sec( 1458061200 ))
#endif

View file

@ -0,0 +1,4 @@
// #607 Disable negative voting on workers
#ifndef HARDFORK_607_TIME
#define HARDFORK_607_TIME (fc::time_point_sec( 1458061200 ))
#endif

View file

@ -0,0 +1,4 @@
// #613 Deprecate annual membership
#ifndef HARDFORK_613_TIME
#define HARDFORK_613_TIME (fc::time_point_sec( 1458061200 ))
#endif

View file

@ -0,0 +1,4 @@
// #615 Fix price feed expiration check, so websocket server will never spam too much data
#ifndef HARDFORK_615_TIME
#define HARDFORK_615_TIME (fc::time_point_sec( 1457550000 ))
#endif

View file

@ -50,6 +50,7 @@ namespace graphene { namespace chain {
* 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;
uint32_t total_ops = 0;
/**
* When calculating votes it is necessary to know how much is stored in orders (and thus unavailable for
@ -385,6 +386,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_statistics_object,
(graphene::chain::object),
(owner)
(most_recent_op)
(total_ops)
(total_core_in_orders)
(lifetime_fees_paid)
(pending_fees)(pending_vested_fees)

View file

@ -211,6 +211,8 @@ namespace graphene { namespace chain {
time_point_sec feed_expiration_time()const
{ return current_feed_publication_time + options.feed_lifetime_sec; }
bool feed_is_expired_before_hardfork_615(time_point_sec current_time)const
{ return feed_expiration_time() >= current_time; }
bool feed_is_expired(time_point_sec current_time)const
{ return feed_expiration_time() >= current_time; }
void update_median_feeds(time_point_sec current_time);

View file

@ -322,6 +322,8 @@ namespace graphene { namespace chain {
//////////////////// db_debug.cpp ////////////////////
void debug_dump();
void apply_debug_updates();
void debug_update( const fc::variant_object& update );
//////////////////// db_market.cpp ////////////////////

View file

@ -43,5 +43,6 @@ namespace graphene { namespace chain {
~node_property_object(){}
uint32_t skip_flags = 0;
std::map< block_id_type, std::vector< fc::variant_object > > debug_updates;
};
} } // graphene::chain

View file

@ -24,6 +24,7 @@
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/db/object.hpp>
#include <boost/multi_index/composite_key.hpp>
namespace graphene { namespace chain {
@ -89,13 +90,44 @@ namespace graphene { namespace chain {
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_account_transaction_history_object_type;
account_id_type account; /// the account this operation applies to
operation_history_id_type operation_id;
uint32_t sequence = 0; /// the operation position within the given account
account_transaction_history_id_type next;
//std::pair<account_id_type,operation_history_id_type> account_op()const { return std::tie( account, operation_id ); }
//std::pair<account_id_type,uint32_t> account_seq()const { return std::tie( account, sequence ); }
};
struct by_id;
struct by_seq;
struct by_op;
typedef multi_index_container<
account_transaction_history_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_seq>,
composite_key< account_transaction_history_object,
member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>,
member< account_transaction_history_object, uint32_t, &account_transaction_history_object::sequence>
>
>,
ordered_unique< tag<by_op>,
composite_key< account_transaction_history_object,
member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>,
member< account_transaction_history_object, operation_history_id_type, &account_transaction_history_object::operation_id>
>
>
>
> account_transaction_history_multi_index_type;
typedef generic_index<account_transaction_history_object, account_transaction_history_multi_index_type> account_transaction_history_index;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::operation_history_object, (graphene::chain::object),
(op)(result)(block_num)(trx_in_block)(op_in_trx)(virtual_op) )
FC_REFLECT_DERIVED( graphene::chain::account_transaction_history_object, (graphene::chain::object),
(operation_id)(next) )
(account)(operation_id)(sequence)(next) )

View file

@ -130,6 +130,8 @@ namespace graphene { namespace db {
virtual fc::uint128 hash()const = 0;
virtual void add_observer( const shared_ptr<index_observer>& ) = 0;
virtual void object_from_variant( const fc::variant& var, object& obj )const = 0;
virtual void object_default( object& obj )const = 0;
};
class secondary_index
@ -298,6 +300,24 @@ namespace graphene { namespace db {
_observers.emplace_back( o );
}
virtual void object_from_variant( const fc::variant& var, object& obj )const override
{
object_id_type id = obj.id;
object_type* result = dynamic_cast<object_type*>( &obj );
FC_ASSERT( result != nullptr );
fc::from_variant( var, *result );
obj.id = id;
}
virtual void object_default( object& obj )const override
{
object_id_type id = obj.id;
object_type* result = dynamic_cast<object_type*>( &obj );
FC_ASSERT( result != nullptr );
(*result) = object_type();
obj.id = id;
}
private:
object_id_type _next_id;
};

@ -1 +1 @@
Subproject commit 21045dde5faa8fcf5f43b97c85f9df210317633b
Subproject commit 622ff58039f2388433272a44fe416f5b8025589a

View file

@ -2,3 +2,4 @@ add_subdirectory( witness )
add_subdirectory( account_history )
add_subdirectory( market_history )
add_subdirectory( delayed_node )
add_subdirectory( debug_witness )

View file

@ -119,10 +119,13 @@ void account_history_plugin_impl::update_account_histories( const signed_block&
const auto& stats_obj = account_id(db).statistics(db);
const auto& ath = db.create<account_transaction_history_object>( [&]( account_transaction_history_object& obj ){
obj.operation_id = oho.id;
obj.account = account_id;
obj.sequence = stats_obj.total_ops+1;
obj.next = stats_obj.most_recent_op;
});
db.modify( stats_obj, [&]( account_statistics_object& obj ){
obj.most_recent_op = ath.id;
obj.total_ops = ath.sequence;
});
}
}
@ -148,6 +151,11 @@ void account_history_plugin_impl::update_account_histories( const signed_block&
}
} // end namespace detail
account_history_plugin::account_history_plugin() :
my( new detail::account_history_plugin_impl(*this) )
{
@ -177,7 +185,7 @@ void account_history_plugin::plugin_initialize(const boost::program_options::var
{
database().applied_block.connect( [&]( const signed_block& b){ my->update_account_histories(b); } );
database().add_index< primary_index< simple_index< operation_history_object > > >();
database().add_index< primary_index< simple_index< account_transaction_history_object > > >();
database().add_index< primary_index< account_transaction_history_index > >();
LOAD_VALUE_SET(options, "tracked-accounts", my->_tracked_accounts, graphene::chain::account_id_type);
}

View file

@ -26,10 +26,15 @@
#include <graphene/app/plugin.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <fc/thread/future.hpp>
namespace graphene { namespace account_history {
using namespace chain;
using namespace chain;
//using namespace graphene::db;
//using boost::multi_index_container;
//using namespace boost::multi_index;
//
// Plugins should #define their SPACE_ID's so plugins with
@ -78,3 +83,27 @@ class account_history_plugin : public graphene::app::plugin
} } //graphene::account_history
/*struct by_id;
struct by_seq;
struct by_op;
typedef boost::multi_index_container<
graphene::chain::account_transaction_history_object,
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
boost::multi_index::ordered_unique< tag<by_seq>,
composite_key< account_transaction_history_object,
member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>,
member< account_transaction_history_object, uint32_t, &account_transaction_history_object::sequence>
>
>,
boost::multi_index::ordered_unique< tag<by_op>,
composite_key< account_transaction_history_object,
member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>,
member< account_transaction_history_object, operation_history_id_type, &account_transaction_history_object::operation_id>
>
>
>
> account_transaction_history_multi_index_type;
typedef graphene::account_history::generic_index<graphene::chain::account_transaction_history_object, account_transaction_history_multi_index_type> account_transaction_history_index;
*/

View file

@ -0,0 +1,18 @@
file(GLOB HEADERS "include/graphene/debug_witness/*.hpp")
add_library( graphene_debug_witness
debug_api.cpp
debug_witness.cpp
)
target_link_libraries( graphene_debug_witness graphene_chain graphene_app )
target_include_directories( graphene_debug_witness
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
install( TARGETS
graphene_debug_witness
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View file

@ -0,0 +1,157 @@
#include <fc/filesystem.hpp>
#include <fc/optional.hpp>
#include <fc/variant_object.hpp>
#include <graphene/app/application.hpp>
#include <graphene/chain/block_database.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <graphene/debug_witness/debug_api.hpp>
#include <graphene/debug_witness/debug_witness.hpp>
namespace graphene { namespace debug_witness {
namespace detail {
class debug_api_impl
{
public:
debug_api_impl( graphene::app::application& _app );
void debug_push_blocks( const std::string& src_filename, uint32_t count );
void debug_generate_blocks( const std::string& debug_key, uint32_t count );
void debug_update_object( const fc::variant_object& update );
//void debug_save_db( std::string db_path );
void debug_stream_json_objects( const std::string& filename );
void debug_stream_json_objects_flush();
std::shared_ptr< graphene::debug_witness_plugin::debug_witness_plugin > get_plugin();
graphene::app::application& app;
};
debug_api_impl::debug_api_impl( graphene::app::application& _app ) : app( _app )
{}
void debug_api_impl::debug_push_blocks( const std::string& src_filename, uint32_t count )
{
if( count == 0 )
return;
std::shared_ptr< graphene::chain::database > db = app.chain_database();
fc::path src_path = fc::path( src_filename );
if( fc::is_directory( src_path ) )
{
ilog( "Loading ${n} from block_database ${fn}", ("n", count)("fn", src_filename) );
graphene::chain::block_database bdb;
bdb.open( src_path );
uint32_t first_block = db->head_block_num()+1;
for( uint32_t i=0; i<count; i++ )
{
fc::optional< graphene::chain::signed_block > block = bdb.fetch_by_number( first_block+i );
if( !block.valid() )
{
wlog( "Block database ${fn} only contained ${i} of ${n} requested blocks", ("i", i)("n", count)("fn", src_filename) );
return;
}
try
{
db->push_block( *block );
}
catch( const fc::exception& e )
{
elog( "Got exception pushing block ${bn} : ${bid} (${i} of ${n})", ("bn", block->block_num())("bid", block->id())("i", i)("n", count) );
elog( "Exception backtrace: ${bt}", ("bt", e.to_detail_string()) );
}
}
ilog( "Completed loading block_database successfully" );
return;
}
}
void debug_api_impl::debug_generate_blocks( const std::string& debug_key, uint32_t count )
{
if( count == 0 )
return;
fc::optional<fc::ecc::private_key> debug_private_key = graphene::utilities::wif_to_key( debug_key );
FC_ASSERT( debug_private_key.valid() );
graphene::chain::public_key_type debug_public_key = debug_private_key->get_public_key();
std::shared_ptr< graphene::chain::database > db = app.chain_database();
for( uint32_t i=0; i<count; i++ )
{
graphene::chain::witness_id_type scheduled_witness = db->get_scheduled_witness( 1 );
fc::time_point_sec scheduled_time = db->get_slot_time( 1 );
graphene::chain::public_key_type scheduled_key = scheduled_witness( *db ).signing_key;
if( scheduled_key != debug_public_key )
{
ilog( "Modified key for witness ${w}", ("w", scheduled_witness) );
fc::mutable_variant_object update;
update("_action", "update")("id", scheduled_witness)("signing_key", debug_public_key);
db->debug_update( update );
}
db->generate_block( scheduled_time, scheduled_witness, *debug_private_key, graphene::chain::database::skip_nothing );
}
}
void debug_api_impl::debug_update_object( const fc::variant_object& update )
{
std::shared_ptr< graphene::chain::database > db = app.chain_database();
db->debug_update( update );
}
std::shared_ptr< graphene::debug_witness_plugin::debug_witness_plugin > debug_api_impl::get_plugin()
{
return app.get_plugin< graphene::debug_witness_plugin::debug_witness_plugin >( "debug_witness" );
}
void debug_api_impl::debug_stream_json_objects( const std::string& filename )
{
get_plugin()->set_json_object_stream( filename );
}
void debug_api_impl::debug_stream_json_objects_flush()
{
get_plugin()->flush_json_object_stream();
}
} // detail
debug_api::debug_api( graphene::app::application& app )
{
my = std::make_shared< detail::debug_api_impl >(app);
}
void debug_api::debug_push_blocks( std::string source_filename, uint32_t count )
{
my->debug_push_blocks( source_filename, count );
}
void debug_api::debug_generate_blocks( std::string debug_key, uint32_t count )
{
my->debug_generate_blocks( debug_key, count );
}
void debug_api::debug_update_object( fc::variant_object update )
{
my->debug_update_object( update );
}
void debug_api::debug_stream_json_objects( std::string filename )
{
my->debug_stream_json_objects( filename );
}
void debug_api::debug_stream_json_objects_flush()
{
my->debug_stream_json_objects_flush();
}
} } // graphene::debug_witness

View file

@ -0,0 +1,173 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/debug_witness/debug_witness.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/time/time.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <fc/smart_ref_impl.hpp>
#include <fc/thread/thread.hpp>
#include <iostream>
using namespace graphene::debug_witness_plugin;
using std::string;
using std::vector;
namespace bpo = boost::program_options;
debug_witness_plugin::~debug_witness_plugin() {}
void debug_witness_plugin::plugin_set_program_options(
boost::program_options::options_description& command_line_options,
boost::program_options::options_description& config_file_options)
{
auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan")));
command_line_options.add_options()
("private-key", bpo::value<vector<string>>()->composing()->multitoken()->
DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))),
"Tuple of [PublicKey, WIF private key] (may specify multiple times)");
config_file_options.add(command_line_options);
}
std::string debug_witness_plugin::plugin_name()const
{
return "debug_witness";
}
void debug_witness_plugin::plugin_initialize(const boost::program_options::variables_map& options)
{ try {
ilog("debug_witness plugin: plugin_initialize() begin");
_options = &options;
if( options.count("private-key") )
{
const std::vector<std::string> key_id_to_wif_pair_strings = options["private-key"].as<std::vector<std::string>>();
for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings)
{
auto key_id_to_wif_pair = graphene::app::dejsonify<std::pair<chain::public_key_type, std::string> >(key_id_to_wif_pair_string);
idump((key_id_to_wif_pair));
fc::optional<fc::ecc::private_key> private_key = graphene::utilities::wif_to_key(key_id_to_wif_pair.second);
if (!private_key)
{
// the key isn't in WIF format; see if they are still passing the old native private key format. This is
// just here to ease the transition, can be removed soon
try
{
private_key = fc::variant(key_id_to_wif_pair.second).as<fc::ecc::private_key>();
}
catch (const fc::exception&)
{
FC_THROW("Invalid WIF-format private key ${key_string}", ("key_string", key_id_to_wif_pair.second));
}
}
_private_keys[key_id_to_wif_pair.first] = *private_key;
}
}
ilog("debug_witness plugin: plugin_initialize() end");
} FC_LOG_AND_RETHROW() }
void debug_witness_plugin::plugin_startup()
{
ilog("debug_witness_plugin::plugin_startup() begin");
chain::database& db = database();
// connect needed signals
_applied_block_conn = db.applied_block.connect([this](const graphene::chain::signed_block& b){ on_applied_block(b); });
_changed_objects_conn = db.changed_objects.connect([this](const std::vector<graphene::db::object_id_type>& ids){ on_changed_objects(ids); });
_removed_objects_conn = db.removed_objects.connect([this](const std::vector<const graphene::db::object*>& objs){ on_removed_objects(objs); });
return;
}
void debug_witness_plugin::on_changed_objects( const std::vector<graphene::db::object_id_type>& ids )
{
if( _json_object_stream && (ids.size() > 0) )
{
const chain::database& db = database();
for( const graphene::db::object_id_type& oid : ids )
{
const graphene::db::object* obj = db.find_object( oid );
if( obj == nullptr )
{
(*_json_object_stream) << "{\"id\":" << fc::json::to_string( oid ) << "}\n";
}
else
{
(*_json_object_stream) << fc::json::to_string( obj->to_variant() ) << '\n';
}
}
}
}
void debug_witness_plugin::on_removed_objects( const std::vector<const graphene::db::object*> objs )
{
/*
if( _json_object_stream )
{
for( const graphene::db::object* obj : objs )
{
(*_json_object_stream) << "{\"id\":" << fc::json::to_string( obj->id ) << "}\n";
}
}
*/
}
void debug_witness_plugin::on_applied_block( const graphene::chain::signed_block& b )
{
if( _json_object_stream )
{
(*_json_object_stream) << "{\"bn\":" << fc::to_string( b.block_num() ) << "}\n";
}
}
void debug_witness_plugin::set_json_object_stream( const std::string& filename )
{
if( _json_object_stream )
{
_json_object_stream->close();
_json_object_stream.reset();
}
_json_object_stream = std::make_shared< std::ofstream >( filename );
}
void debug_witness_plugin::flush_json_object_stream()
{
if( _json_object_stream )
_json_object_stream->flush();
}
void debug_witness_plugin::plugin_shutdown()
{
if( _json_object_stream )
{
_json_object_stream->close();
_json_object_stream.reset();
}
return;
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <memory>
#include <string>
#include <fc/api.hpp>
#include <fc/variant_object.hpp>
namespace graphene { namespace app {
class application;
} }
namespace graphene { namespace debug_witness {
namespace detail {
class debug_api_impl;
}
class debug_api
{
public:
debug_api( graphene::app::application& app );
/**
* Push blocks from existing database.
*/
void debug_push_blocks( std::string src_filename, uint32_t count );
/**
* Generate blocks locally.
*/
void debug_generate_blocks( std::string debug_key, uint32_t count );
/**
* Directly manipulate database objects (will undo and re-apply last block with new changes post-applied).
*/
void debug_update_object( fc::variant_object update );
/**
* Start a node with given initial path.
*/
// not implemented
//void start_node( std::string name, std::string initial_db_path );
/**
* Save the database to disk.
*/
// not implemented
//void save_db( std::string db_path );
/**
* Stream objects to file. (Hint: Create with mkfifo and pipe it to a script)
*/
void debug_stream_json_objects( std::string filename );
/**
* Flush streaming file.
*/
void debug_stream_json_objects_flush();
std::shared_ptr< detail::debug_api_impl > my;
};
} }
FC_API(graphene::debug_witness::debug_api,
(debug_push_blocks)
(debug_generate_blocks)
(debug_update_object)
(debug_stream_json_objects)
(debug_stream_json_objects_flush)
)

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/app/plugin.hpp>
#include <graphene/chain/database.hpp>
#include <fc/thread/future.hpp>
namespace graphene { namespace debug_witness_plugin {
class debug_witness_plugin : public graphene::app::plugin {
public:
~debug_witness_plugin();
std::string plugin_name()const override;
virtual void plugin_set_program_options(
boost::program_options::options_description &command_line_options,
boost::program_options::options_description &config_file_options
) override;
virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;
virtual void plugin_startup() override;
virtual void plugin_shutdown() override;
void set_json_object_stream( const std::string& filename );
void flush_json_object_stream();
private:
void on_changed_objects( const std::vector<graphene::db::object_id_type>& ids );
void on_removed_objects( const std::vector<const graphene::db::object*> objs );
void on_applied_block( const graphene::chain::signed_block& b );
boost::program_options::variables_map _options;
std::map<chain::public_key_type, fc::ecc::private_key> _private_keys;
std::shared_ptr< std::ofstream > _json_object_stream;
boost::signals2::scoped_connection _applied_block_conn;
boost::signals2::scoped_connection _changed_objects_conn;
boost::signals2::scoped_connection _removed_objects_conn;
};
} } //graphene::debug_witness_plugin

View file

@ -849,6 +849,51 @@ class wallet_api
uint32_t timeout_sec = 0,
bool fill_or_kill = false,
bool broadcast = false);
/** Place a limit order attempting to sell one asset for another.
*
* This API call abstracts away some of the details of the sell_asset call to be more
* user friendly. All orders placed with sell never timeout and will not be killed if they
* cannot be filled immediately. If you wish for one of these parameters to be different,
* then sell_asset should be used instead.
*
* @param seller_account the account providing the asset being sold, and which will
* receive the processed of the sale.
* @param base The name or id of the asset to sell.
* @param quote The name or id of the asset to recieve.
* @param rate The rate in base:quote at which you want to sell.
* @param amount The amount of base you want to sell.
* @param broadcast true to broadcast the transaction on the network.
* @returns The signed transaction selling the funds.
*/
signed_transaction sell( string seller_account,
string base,
string quote,
double rate,
double amount,
bool broadcast );
/** Place a limit order attempting to buy one asset with another.
*
* This API call abstracts away some of the details of the sell_asset call to be more
* user friendly. All orders placed with buy never timeout and will not be killed if they
* cannot be filled immediately. If you wish for one of these parameters to be different,
* then sell_asset should be used instead.
*
* @param buyer_account The account buying the asset for another asset.
* @param base The name or id of the asset to buy.
* @param quote The name or id of the assest being offered as payment.
* @param rate The rate in base:quote at which you want to buy.
* @param amount the amount of base you want to buy.
* @param broadcast true to broadcast the transaction on the network.
* @param The signed transaction selling the funds.
*/
signed_transaction buy( string buyer_account,
string base,
string quote,
double rate,
double amount,
bool broadcast );
/** Borrow an asset or update the debt/collateral ratio for the loan.
*
@ -1401,9 +1446,16 @@ class wallet_api
const approval_delta& delta,
bool broadcast /* = false */
);
order_book get_order_book( const string& base, const string& quote, unsigned limit = 50);
void dbg_make_uia(string creator, string symbol);
void dbg_make_mia(string creator, string symbol);
void dbg_push_blocks( std::string src_filename, uint32_t count );
void dbg_generate_blocks( std::string debug_wif_key, uint32_t count );
void dbg_stream_json_objects( const std::string& filename );
void dbg_update_object( fc::variant_object update );
void flood_network(string prefix, uint32_t number_of_transactions);
void network_add_nodes( const vector<string>& nodes );
@ -1517,6 +1569,8 @@ FC_API( graphene::wallet::wallet_api,
(upgrade_account)
(create_account_with_brain_key)
(sell_asset)
(sell)
(buy)
(borrow_asset)
(cancel_order)
(transfer)
@ -1574,6 +1628,10 @@ FC_API( graphene::wallet::wallet_api,
(approve_proposal)
(dbg_make_uia)
(dbg_make_mia)
(dbg_push_blocks)
(dbg_generate_blocks)
(dbg_stream_json_objects)
(dbg_update_object)
(flood_network)
(network_add_nodes)
(network_get_connected_peers)
@ -1589,4 +1647,5 @@ FC_API( graphene::wallet::wallet_api,
(blind_transfer)
(blind_history)
(receive_blind_transfer)
(get_order_book)
)

View file

@ -69,6 +69,7 @@
#include <graphene/wallet/wallet.hpp>
#include <graphene/wallet/api_documentation.hpp>
#include <graphene/wallet/reflect_util.hpp>
#include <graphene/debug_witness/debug_api.hpp>
#include <fc/smart_ref_impl.hpp>
#ifndef WIN32
@ -280,7 +281,7 @@ private:
auto iter = _wallet.pending_witness_registrations.find(witness_name);
FC_ASSERT(iter != _wallet.pending_witness_registrations.end());
std::string wif_key = iter->second;
// get the list key id this key is registered with in the chain
fc::optional<fc::ecc::private_key> witness_private_key = wif_to_key(wif_key);
FC_ASSERT(witness_private_key);
@ -316,12 +317,12 @@ private:
if( optional_account )
claim_registered_account(*optional_account);
}
if (!_wallet.pending_witness_registrations.empty())
{
// make a vector of the owner accounts for witnesses pending registration
std::vector<string> pending_witness_names = boost::copy_range<std::vector<string> >(boost::adaptors::keys(_wallet.pending_witness_registrations));
// look up the owners on the blockchain
std::vector<fc::optional<graphene::chain::account_object>> owner_account_objects = _remote_db->lookup_account_names(pending_witness_names);
@ -365,9 +366,9 @@ private:
// if the user executes the same command twice in quick succession,
// we might generate the same transaction id, and cause the second
// transaction to be rejected. This can be avoided by altering the
// transaction to be rejected. This can be avoided by altering the
// second transaction slightly (bumping up the expiration time by
// a second). Keep track of recent transaction ids we've generated
// a second). Keep track of recent transaction ids we've generated
// so we can know if we need to do this
struct recently_generated_transaction_record
{
@ -377,7 +378,7 @@ private:
struct timestamp_index{};
typedef boost::multi_index_container<recently_generated_transaction_record,
boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::member<recently_generated_transaction_record,
graphene::chain::transaction_id_type,
graphene::chain::transaction_id_type,
&recently_generated_transaction_record::transaction_id>,
std::hash<graphene::chain::transaction_id_type> >,
boost::multi_index::ordered_non_unique<boost::multi_index::tag<timestamp_index>,
@ -686,7 +687,7 @@ public:
if (!optional_private_key)
FC_THROW("Invalid private key");
graphene::chain::public_key_type wif_pub_key = optional_private_key->get_public_key();
account_object account = get_account( account_name_or_id );
// make a list of all current public keys for the named account
@ -1042,7 +1043,7 @@ public:
{ try {
int active_key_index = find_first_unused_derived_key_index(owner_privkey);
fc::ecc::private_key active_privkey = derive_private_key( key_to_wif(owner_privkey), active_key_index);
int memo_key_index = find_first_unused_derived_key_index(active_privkey);
fc::ecc::private_key memo_privkey = derive_private_key( key_to_wif(active_privkey), memo_key_index);
@ -1350,7 +1351,7 @@ public:
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) }
signed_transaction create_committee_member(string owner_account, string url,
signed_transaction create_committee_member(string owner_account, string url,
bool broadcast /* = false */)
{ try {
@ -1370,7 +1371,7 @@ public:
witness_object get_witness(string owner_account)
{
try
try
{
fc::optional<witness_id_type> witness_id = maybe_id<witness_id_type>(owner_account);
if (witness_id)
@ -1405,7 +1406,7 @@ public:
committee_member_object get_committee_member(string owner_account)
{
try
try
{
fc::optional<committee_member_id_type> committee_member_id = maybe_id<committee_member_id_type>(owner_account);
if (committee_member_id)
@ -1460,7 +1461,7 @@ public:
tx.operations.push_back( witness_create_op );
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
tx.validate();
_wallet.pending_witness_registrations[owner_account] = key_to_wif(witness_private_key);
return sign_transaction( tx, broadcast );
@ -1774,7 +1775,7 @@ public:
if (account_object_to_modify.options.num_witness == desired_number_of_witnesses &&
account_object_to_modify.options.num_committee == desired_number_of_committee_members)
FC_THROW("Account ${account} is already voting for ${witnesses} witnesses and ${committee_members} committee_members",
FC_THROW("Account ${account} is already voting for ${witnesses} witnesses and ${committee_members} committee_members",
("account", account_to_modify)("witnesses", desired_number_of_witnesses)("committee_members",desired_number_of_witnesses));
account_object_to_modify.options.num_witness = desired_number_of_witnesses;
account_object_to_modify.options.num_committee = desired_number_of_committee_members;
@ -2161,11 +2162,95 @@ public:
for( auto& r : records )
{
asset_object as = get_asset( r.amount.asset_id );
ss << fc::get_approximate_relative_time_string( r.date )
ss << fc::get_approximate_relative_time_string( r.date )
<< " " << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n";
}
return ss.str();
};
m["get_order_book"] = [this](variant result, const fc::variants& a)
{
auto orders = result.as<order_book>();
auto bids = orders.bids;
auto asks = orders.asks;
std::stringstream ss;
std::stringstream sum_stream;
sum_stream << "Sum(" << orders.base << ')';
double bid_sum = 0;
double ask_sum = 0;
const int spacing = 20;
auto prettify_num = [&]( double n )
{
//ss << n;
if (abs( round( n ) - n ) < 0.00000000001 )
{
//ss << setiosflags( !ios::fixed ) << (int) n; // doesn't compile on Linux with gcc
ss << (int) n;
}
else if (n - floor(n) < 0.000001)
{
ss << setiosflags( ios::fixed ) << setprecision(10) << n;
}
else
{
ss << setiosflags( ios::fixed ) << setprecision(6) << n;
}
};
ss << setprecision( 8 ) << setiosflags( ios::fixed ) << setiosflags( ios::left );
ss << ' ' << setw( (spacing * 4) + 6 ) << "BUY ORDERS" << "SELL ORDERS\n"
<< ' ' << setw( spacing + 1 ) << "Price" << setw( spacing ) << orders.quote << ' ' << setw( spacing )
<< orders.base << ' ' << setw( spacing ) << sum_stream.str()
<< " " << setw( spacing + 1 ) << "Price" << setw( spacing ) << orders.quote << ' ' << setw( spacing )
<< orders.base << ' ' << setw( spacing ) << sum_stream.str()
<< "\n====================================================================================="
<< "|=====================================================================================\n";
for (int i = 0; i < bids.size() || i < asks.size() ; i++)
{
if ( i < bids.size() )
{
bid_sum += bids[i].base;
ss << ' ' << setw( spacing );
prettify_num( bids[i].price );
ss << ' ' << setw( spacing );
prettify_num( bids[i].quote );
ss << ' ' << setw( spacing );
prettify_num( bids[i].base );
ss << ' ' << setw( spacing );
prettify_num( bid_sum );
ss << ' ';
}
else
{
ss << setw( (spacing * 4) + 5 ) << ' ';
}
ss << '|';
if ( i < asks.size() )
{
ask_sum += asks[i].base;
ss << ' ' << setw( spacing );
prettify_num( asks[i].price );
ss << ' ' << setw( spacing );
prettify_num( asks[i].quote );
ss << ' ' << setw( spacing );
prettify_num( asks[i].base );
ss << ' ' << setw( spacing );
prettify_num( ask_sum );
}
ss << '\n';
}
ss << endl
<< "Buy Total: " << bid_sum << ' ' << orders.base << endl
<< "Sell Total: " << ask_sum << ' ' << orders.base << endl;
return ss.str();
};
return m;
}
@ -2338,6 +2423,34 @@ public:
create_asset(get_account(creator).name, symbol, 2, opts, bopts, true);
}
void dbg_push_blocks( const std::string& src_filename, uint32_t count )
{
use_debug_api();
(*_remote_debug)->debug_push_blocks( src_filename, count );
(*_remote_debug)->debug_stream_json_objects_flush();
}
void dbg_generate_blocks( const std::string& debug_wif_key, uint32_t count )
{
use_debug_api();
(*_remote_debug)->debug_generate_blocks( debug_wif_key, count );
(*_remote_debug)->debug_stream_json_objects_flush();
}
void dbg_stream_json_objects( const std::string& filename )
{
use_debug_api();
(*_remote_debug)->debug_stream_json_objects( filename );
(*_remote_debug)->debug_stream_json_objects_flush();
}
void dbg_update_object( const fc::variant_object& update )
{
use_debug_api();
(*_remote_debug)->debug_update_object( update );
(*_remote_debug)->debug_stream_json_objects_flush();
}
void use_network_node_api()
{
if( _remote_net_node )
@ -2356,6 +2469,26 @@ public:
}
}
void use_debug_api()
{
if( _remote_debug )
return;
try
{
_remote_debug = _remote_api->debug();
}
catch( const fc::exception& e )
{
std::cerr << "\nCouldn't get debug node API. You probably are not configured\n"
"to access the debug API on the node you are connecting to.\n"
"\n"
"To fix this problem:\n"
"- Please ensure you are running debug_node, not witness_node.\n"
"- Please follow the instructions in README.md to set up an apiaccess file.\n"
"\n";
}
}
void network_add_nodes( const vector<string>& nodes )
{
use_network_node_api();
@ -2392,7 +2525,7 @@ public:
dbg_make_uia(master.name, "SHILL");
} catch(...) {/* Ignore; the asset probably already exists.*/}
fc::time_point start = fc::time_point::now();
fc::time_point start = fc::time_point::now();
for( int i = 0; i < number_of_accounts; ++i )
{
std::ostringstream brain_key;
@ -2449,6 +2582,7 @@ public:
fc::api<network_broadcast_api> _remote_net_broadcast;
fc::api<history_api> _remote_hist;
optional< fc::api<network_node_api> > _remote_net_node;
optional< fc::api<graphene::debug_witness::debug_api> > _remote_debug;
flat_map<string, operation> _prototype_ops;
@ -2474,7 +2608,7 @@ std::string operation_printer::operator()(const T& op)const
//op.get_balance_delta( acc, result );
auto a = wallet.get_asset( op.fee.asset_id );
auto payer = wallet.get_account( op.fee_payer() );
string op_name = fc::get_typename<T>::name();
if( op_name.find_last_of(':') != string::npos )
op_name.erase(0, op_name.find_last_of(':')+1);
@ -2494,7 +2628,7 @@ std::string operation_printer::operator()(const transfer_from_blind_operation& o
auto a = wallet.get_asset( op.fee.asset_id );
auto receiver = wallet.get_account( op.to );
out << receiver.name
out << receiver.name
<< " received " << a.amount_to_pretty_string( op.amount ) << " from blinded balance";
return "";
}
@ -2504,7 +2638,7 @@ std::string operation_printer::operator()(const transfer_to_blind_operation& op)
auto a = wallet.get_asset( op.amount.asset_id );
auto sender = wallet.get_account( op.from );
out << sender.name
out << sender.name
<< " sent " << a.amount_to_pretty_string( op.amount ) << " to " << op.outputs.size() << " blinded balance" << (op.outputs.size()>1?"s":"")
<< " fee: " << fa.amount_to_pretty_string( op.fee );
return "";
@ -2641,7 +2775,7 @@ vector<operation_detail> wallet_api::get_account_history(string name, int limit)
while( limit > 0 )
{
operation_history_id_type start;
if( result.size() )
if( result.size() )
{
start = result.back().op.id;
start = start + 1;
@ -3077,7 +3211,7 @@ signed_transaction wallet_api::whitelist_account(string authorizing_account,
return my->whitelist_account(authorizing_account, account_to_list, new_listing_status, broadcast);
}
signed_transaction wallet_api::create_committee_member(string owner_account, string url,
signed_transaction wallet_api::create_committee_member(string owner_account, string url,
bool broadcast /* = false */)
{
return my->create_committee_member(owner_account, url, broadcast);
@ -3183,7 +3317,7 @@ signed_transaction wallet_api::set_desired_witness_and_committee_member_count(st
uint16_t desired_number_of_committee_members,
bool broadcast /* = false */)
{
return my->set_desired_witness_and_committee_member_count(account_to_modify, desired_number_of_witnesses,
return my->set_desired_witness_and_committee_member_count(account_to_modify, desired_number_of_witnesses,
desired_number_of_committee_members, broadcast);
}
@ -3214,6 +3348,26 @@ void wallet_api::dbg_make_mia(string creator, string symbol)
my->dbg_make_mia(creator, symbol);
}
void wallet_api::dbg_push_blocks( std::string src_filename, uint32_t count )
{
my->dbg_push_blocks( src_filename, count );
}
void wallet_api::dbg_generate_blocks( std::string debug_wif_key, uint32_t count )
{
my->dbg_generate_blocks( debug_wif_key, count );
}
void wallet_api::dbg_stream_json_objects( const std::string& filename )
{
my->dbg_stream_json_objects( filename );
}
void wallet_api::dbg_update_object( fc::variant_object update )
{
my->dbg_update_object( update );
}
void wallet_api::network_add_nodes( const vector<string>& nodes )
{
my->network_add_nodes( nodes );
@ -3522,7 +3676,7 @@ vector< signed_transaction > wallet_api_impl::import_balance( string name_or_id,
if( broadcast )
_remote_net_broadcast->broadcast_transaction(signed_tx);
}
return result;
} FC_CAPTURE_AND_RETHROW( (name_or_id) ) }
@ -3552,6 +3706,28 @@ signed_transaction wallet_api::sell_asset(string seller_account,
symbol_to_receive, expiration, fill_or_kill, broadcast);
}
signed_transaction wallet_api::sell( string seller_account,
string base,
string quote,
double rate,
double amount,
bool broadcast )
{
return my->sell_asset( seller_account, std::to_string( amount ), base,
std::to_string( rate * amount ), quote, 0, false, broadcast );
}
signed_transaction wallet_api::buy( string buyer_account,
string base,
string quote,
double rate,
double amount,
bool broadcast )
{
return my->sell_asset( buyer_account, std::to_string( rate * amount ), quote,
std::to_string( amount ), base, 0, false, broadcast );
}
signed_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell,
string asset_symbol, string amount_of_collateral, bool broadcast)
{
@ -3675,7 +3851,7 @@ vector<asset> wallet_api::get_blind_balances( string key_or_label )
}
blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_key_or_label,
string to_account_id_or_name,
string to_account_id_or_name,
string amount_in,
string symbol,
bool broadcast )
@ -3694,7 +3870,7 @@ blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_ke
auto conf = blind_transfer_help( from_blind_account_key_or_label,
from_blind_account_key_or_label,
from_blind_account_key_or_label,
blind_in, symbol, false, true/*to_temp*/ );
FC_ASSERT( conf.outputs.size() > 0 );
@ -3709,24 +3885,24 @@ blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_ke
conf.trx.operations.push_back(from_blind);
ilog( "about to validate" );
conf.trx.validate();
if( broadcast && conf.outputs.size() == 2 ) {
// Save the change
blind_confirmation::output conf_output;
blind_confirmation::output change_output = conf.outputs[0];
// The wallet must have a private key for confirmation.to, this is used to decrypt the memo
public_key_type from_key = get_public_key(from_blind_account_key_or_label);
conf_output.confirmation.to = from_key;
conf_output.confirmation.one_time_key = change_output.confirmation.one_time_key;
conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo;
conf_output.confirmation_receipt = conf_output.confirmation;
//try {
//try {
receive_blind_transfer( conf_output.confirmation_receipt, from_blind_account_key_or_label, "@"+to_account.name );
//} catch ( ... ){}
}
ilog( "about to broadcast" );
conf.trx = sign_transaction( conf.trx, broadcast );
@ -3747,7 +3923,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,
string symbol,
bool broadcast,
bool to_temp )
{
{
blind_confirmation confirm;
try {
@ -3791,7 +3967,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,
blinding_factors.push_back( start->data.blinding_factor );
total_amount += start->amount;
if( total_amount >= amount + blind_tr.fee )
if( total_amount >= amount + blind_tr.fee )
break;
}
++start;
@ -3859,7 +4035,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,
conf_output.decrypted_memo.amount = change;
conf_output.decrypted_memo.blinding_factor = change_blind_factor;
conf_output.decrypted_memo.commitment = change_out.commitment;
conf_output.decrypted_memo.check = from_secret._hash[0];
conf_output.decrypted_memo.check = from_secret._hash[0];
conf_output.confirmation.one_time_key = one_time_key.get_public_key();
conf_output.confirmation.to = from_key;
conf_output.confirmation.encrypted_memo = fc::aes_encrypt( from_secret, fc::raw::pack( conf_output.decrypted_memo ) );
@ -3877,7 +4053,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,
conf_output.decrypted_memo.amount = amount;
conf_output.decrypted_memo.blinding_factor = blind_factor;
conf_output.decrypted_memo.commitment = to_out.commitment;
conf_output.decrypted_memo.check = secret._hash[0];
conf_output.decrypted_memo.check = secret._hash[0];
conf_output.confirmation.one_time_key = one_time_key.get_public_key();
conf_output.confirmation.to = to_key;
conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) );
@ -3887,9 +4063,9 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,
confirm.outputs.push_back( conf_output );
/** commitments must be in sorted order */
std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(),
std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(),
[&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } );
std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(),
std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(),
[&]( const blind_input& a, const blind_input& b ){ return a.commitment < b.commitment; } );
confirm.trx.operations.emplace_back( std::move(blind_tr) );
@ -3914,10 +4090,10 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,
* Transfers a public balance from @from to one or more blinded balances using a
* stealth transfer.
*/
blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name,
blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name,
string asset_symbol,
/** map from key or label to amount */
vector<pair<string, string>> to_amounts,
vector<pair<string, string>> to_amounts,
bool broadcast )
{ try {
FC_ASSERT( !is_locked() );
@ -3949,7 +4125,7 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name
auto amount = asset_obj->amount_from_string(item.second);
total_amount += amount;
fc::ecc::public_key to_pub_key = to_key;
blind_output out;
@ -3965,7 +4141,7 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name
conf_output.decrypted_memo.amount = amount;
conf_output.decrypted_memo.blinding_factor = blind_factor;
conf_output.decrypted_memo.commitment = out.commitment;
conf_output.decrypted_memo.check = secret._hash[0];
conf_output.decrypted_memo.check = secret._hash[0];
conf_output.confirmation.one_time_key = one_time_key.get_public_key();
conf_output.confirmation.to = to_key;
conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) );
@ -3979,7 +4155,7 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name
bop.blinding_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() );
/** commitments must be in sorted order */
std::sort( bop.outputs.begin(), bop.outputs.end(),
std::sort( bop.outputs.begin(), bop.outputs.end(),
[&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } );
confirm.trx.operations.push_back( bop );
@ -4025,7 +4201,7 @@ blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, s
result.to_key = *conf.to;
result.to_label = get_key_label( result.to_key );
if( memo.from )
if( memo.from )
{
result.from_key = *memo.from;
result.from_label = get_key_label( result.from_key );
@ -4045,7 +4221,7 @@ blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, s
// confirm the amount matches the commitment (verify the blinding factor)
auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value );
FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) );
blind_balance bal;
bal.amount = memo.amount;
bal.to = *conf.to;
@ -4063,7 +4239,7 @@ blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, s
auto child_key_itr = owner.key_auths.find( child_pubkey );
if( child_key_itr != owner.key_auths.end() )
my->_keys[child_key_itr->first] = key_to_wif( child_priv_key );
// my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal );
result.date = fc::time_point::now();
@ -4080,7 +4256,7 @@ vector<blind_receipt> wallet_api::blind_history( string key_or_account )
vector<blind_receipt> result;
auto pub_key = get_public_key( key_or_account );
if( pub_key == public_key_type() )
if( pub_key == public_key_type() )
return vector<blind_receipt>();
for( auto& r : my->_wallet.blind_receipts )
@ -4092,6 +4268,11 @@ vector<blind_receipt> wallet_api::blind_history( string key_or_account )
return result;
}
order_book wallet_api::get_order_book( const string& base, const string& quote, unsigned limit )
{
return( my->_remote_db->get_order_book( base, quote, limit ) );
}
signed_block_with_info::signed_block_with_info( const signed_block& block )
: signed_block( block )
{

View file

@ -2,6 +2,7 @@ add_subdirectory( build_helpers )
add_subdirectory( cli_wallet )
add_subdirectory( genesis_util )
add_subdirectory( witness_node )
add_subdirectory( debug_node )
add_subdirectory( delayed_node )
add_subdirectory( js_operation_serializer )
add_subdirectory( size_checker )

View file

@ -0,0 +1,21 @@
add_executable( debug_node main.cpp )
if( UNIX AND NOT APPLE )
set(rt_library rt )
endif()
find_package( Gperftools QUIET )
if( GPERFTOOLS_FOUND )
message( STATUS "Found gperftools; compiling debug_node with TCMalloc")
list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc )
endif()
target_link_libraries( debug_node
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_debug_witness graphene_chain graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
install( TARGETS
debug_node
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View file

@ -0,0 +1,307 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/app/application.hpp>
#include <graphene/debug_witness/debug_witness.hpp>
#include <graphene/account_history/account_history_plugin.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <fc/exception/exception.hpp>
#include <fc/thread/thread.hpp>
#include <fc/interprocess/signals.hpp>
#include <fc/log/console_appender.hpp>
#include <fc/log/file_appender.hpp>
#include <fc/log/logger.hpp>
#include <fc/log/logger_config.hpp>
#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <iostream>
#include <fstream>
#ifdef WIN32
# include <signal.h>
#else
# include <csignal>
#endif
using namespace graphene;
namespace bpo = boost::program_options;
void write_default_logging_config_to_stream(std::ostream& out);
fc::optional<fc::logging_config> load_logging_config_from_ini_file(const fc::path& config_ini_filename);
int main(int argc, char** argv) {
app::application* node = new app::application();
fc::oexception unhandled_exception;
try {
bpo::options_description app_options("Graphene Witness Node");
bpo::options_description cfg_options("Graphene Witness Node");
app_options.add_options()
("help,h", "Print this help message and exit.")
("data-dir,d", bpo::value<boost::filesystem::path>()->default_value("witness_node_data_dir"), "Directory containing databases, configuration file, etc.")
;
bpo::variables_map options;
auto witness_plug = node->register_plugin<debug_witness_plugin::debug_witness_plugin>();
auto history_plug = node->register_plugin<account_history::account_history_plugin>();
auto market_history_plug = node->register_plugin<market_history::market_history_plugin>();
try
{
bpo::options_description cli, cfg;
node->set_program_options(cli, cfg);
app_options.add(cli);
cfg_options.add(cfg);
bpo::store(bpo::parse_command_line(argc, argv, app_options), options);
}
catch (const boost::program_options::error& e)
{
std::cerr << "Error parsing command line: " << e.what() << "\n";
return 1;
}
if( options.count("help") )
{
std::cout << app_options << "\n";
return 0;
}
fc::path data_dir;
if( options.count("data-dir") )
{
data_dir = options["data-dir"].as<boost::filesystem::path>();
if( data_dir.is_relative() )
data_dir = fc::current_path() / data_dir;
}
fc::path config_ini_path = data_dir / "config.ini";
if( fc::exists(config_ini_path) )
{
// get the basic options
bpo::store(bpo::parse_config_file<char>(config_ini_path.preferred_string().c_str(), cfg_options, true), options);
// try to get logging options from the config file.
try
{
fc::optional<fc::logging_config> logging_config = load_logging_config_from_ini_file(config_ini_path);
if (logging_config)
fc::configure_logging(*logging_config);
}
catch (const fc::exception&)
{
wlog("Error parsing logging config from config file ${config}, using default config", ("config", config_ini_path.preferred_string()));
}
}
else
{
ilog("Writing new config file at ${path}", ("path", config_ini_path));
if( !fc::exists(data_dir) )
fc::create_directories(data_dir);
std::ofstream out_cfg(config_ini_path.preferred_string());
for( const boost::shared_ptr<bpo::option_description> od : cfg_options.options() )
{
if( !od->description().empty() )
out_cfg << "# " << od->description() << "\n";
boost::any store;
if( !od->semantic()->apply_default(store) )
out_cfg << "# " << od->long_name() << " = \n";
else {
auto example = od->format_parameter();
if( example.empty() )
// This is a boolean switch
out_cfg << od->long_name() << " = " << "false\n";
else {
// The string is formatted "arg (=<interesting part>)"
example.erase(0, 6);
example.erase(example.length()-1);
out_cfg << od->long_name() << " = " << example << "\n";
}
}
out_cfg << "\n";
}
write_default_logging_config_to_stream(out_cfg);
out_cfg.close();
// read the default logging config we just wrote out to the file and start using it
fc::optional<fc::logging_config> logging_config = load_logging_config_from_ini_file(config_ini_path);
if (logging_config)
fc::configure_logging(*logging_config);
}
bpo::notify(options);
node->initialize(data_dir, options);
node->initialize_plugins( options );
node->startup();
node->startup_plugins();
fc::promise<int>::ptr exit_promise = new fc::promise<int>("UNIX Signal Handler");
fc::set_signal_handler([&exit_promise](int signal) {
elog( "Caught SIGINT attempting to exit cleanly" );
exit_promise->set_value(signal);
}, SIGINT);
fc::set_signal_handler([&exit_promise](int signal) {
elog( "Caught SIGTERM attempting to exit cleanly" );
exit_promise->set_value(signal);
}, SIGTERM);
ilog("Started witness node on a chain with ${h} blocks.", ("h", node->chain_database()->head_block_num()));
ilog("Chain ID is ${id}", ("id", node->chain_database()->get_chain_id()) );
int signal = exit_promise->wait();
ilog("Exiting from signal ${n}", ("n", signal));
node->shutdown_plugins();
node->shutdown();
delete node;
return 0;
} catch( const fc::exception& e ) {
// deleting the node can yield, so do this outside the exception handler
unhandled_exception = e;
}
if (unhandled_exception)
{
elog("Exiting with error:\n${e}", ("e", unhandled_exception->to_detail_string()));
node->shutdown();
delete node;
return 1;
}
}
// logging config is too complicated to be parsed by boost::program_options,
// so we do it by hand
//
// Currently, you can only specify the filenames and logging levels, which
// are all most users would want to change. At a later time, options can
// be added to control rotation intervals, compression, and other seldom-
// used features
void write_default_logging_config_to_stream(std::ostream& out)
{
out << "# declare an appender named \"stderr\" that writes messages to the console\n"
"[log.console_appender.stderr]\n"
"stream=std_error\n\n"
"# declare an appender named \"p2p\" that writes messages to p2p.log\n"
"[log.file_appender.p2p]\n"
"filename=logs/p2p/p2p.log\n"
"# filename can be absolute or relative to this config file\n\n"
"# route any messages logged to the default logger to the \"stderr\" logger we\n"
"# declared above, if they are info level are higher\n"
"[logger.default]\n"
"level=info\n"
"appenders=stderr\n\n"
"# route messages sent to the \"p2p\" logger to the p2p appender declared above\n"
"[logger.p2p]\n"
"level=debug\n"
"appenders=p2p\n\n";
}
fc::optional<fc::logging_config> load_logging_config_from_ini_file(const fc::path& config_ini_filename)
{
try
{
fc::logging_config logging_config;
bool found_logging_config = false;
boost::property_tree::ptree config_ini_tree;
boost::property_tree::ini_parser::read_ini(config_ini_filename.preferred_string().c_str(), config_ini_tree);
for (const auto& section : config_ini_tree)
{
const std::string& section_name = section.first;
const boost::property_tree::ptree& section_tree = section.second;
const std::string console_appender_section_prefix = "log.console_appender.";
const std::string file_appender_section_prefix = "log.file_appender.";
const std::string logger_section_prefix = "logger.";
if (boost::starts_with(section_name, console_appender_section_prefix))
{
std::string console_appender_name = section_name.substr(console_appender_section_prefix.length());
std::string stream_name = section_tree.get<std::string>("stream");
// construct a default console appender config here
// stdout/stderr will be taken from ini file, everything else hard-coded here
fc::console_appender::config console_appender_config;
console_appender_config.level_colors.emplace_back(
fc::console_appender::level_color(fc::log_level::debug,
fc::console_appender::color::green));
console_appender_config.level_colors.emplace_back(
fc::console_appender::level_color(fc::log_level::warn,
fc::console_appender::color::brown));
console_appender_config.level_colors.emplace_back(
fc::console_appender::level_color(fc::log_level::error,
fc::console_appender::color::cyan));
console_appender_config.stream = fc::variant(stream_name).as<fc::console_appender::stream::type>();
logging_config.appenders.push_back(fc::appender_config(console_appender_name, "console", fc::variant(console_appender_config)));
found_logging_config = true;
}
else if (boost::starts_with(section_name, file_appender_section_prefix))
{
std::string file_appender_name = section_name.substr(file_appender_section_prefix.length());
fc::path file_name = section_tree.get<std::string>("filename");
if (file_name.is_relative())
file_name = fc::absolute(config_ini_filename).parent_path() / file_name;
// construct a default file appender config here
// filename will be taken from ini file, everything else hard-coded here
fc::file_appender::config file_appender_config;
file_appender_config.filename = file_name;
file_appender_config.flush = true;
file_appender_config.rotate = true;
file_appender_config.rotation_interval = fc::hours(1);
file_appender_config.rotation_limit = fc::days(1);
logging_config.appenders.push_back(fc::appender_config(file_appender_name, "file", fc::variant(file_appender_config)));
found_logging_config = true;
}
else if (boost::starts_with(section_name, logger_section_prefix))
{
std::string logger_name = section_name.substr(logger_section_prefix.length());
std::string level_string = section_tree.get<std::string>("level");
std::string appenders_string = section_tree.get<std::string>("appenders");
fc::logger_config logger_config(logger_name);
logger_config.level = fc::variant(level_string).as<fc::log_level>();
boost::split(logger_config.appenders, appenders_string,
boost::is_any_of(" ,"),
boost::token_compress_on);
logging_config.loggers.push_back(logger_config);
found_logging_config = true;
}
}
if (found_logging_config)
return logging_config;
else
return fc::optional<fc::logging_config>();
}
FC_RETHROW_EXCEPTIONS(warn, "")
}

View file

@ -9,8 +9,9 @@ if( GPERFTOOLS_FOUND )
list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc )
endif()
# We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246
target_link_libraries( witness_node
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
install( TARGETS
witness_node

View file

@ -747,6 +747,7 @@ BOOST_AUTO_TEST_CASE( stealth_fba_test )
generate_blocks( HARDFORK_555_TIME );
generate_blocks( HARDFORK_563_TIME );
generate_blocks( HARDFORK_572_TIME );
generate_blocks( HARDFORK_599_TIME );
// Philbin (registrar who registers Rex)

View file

@ -1338,6 +1338,7 @@ BOOST_AUTO_TEST_CASE( top_n_special )
ACTORS( (alice)(bob)(chloe)(dan)(izzy)(stan) );
generate_blocks( HARDFORK_516_TIME );
generate_blocks( HARDFORK_599_TIME );
try
{
@ -1490,6 +1491,7 @@ BOOST_AUTO_TEST_CASE( buyback )
generate_blocks( HARDFORK_538_TIME );
generate_blocks( HARDFORK_555_TIME );
generate_blocks( HARDFORK_599_TIME );
try
{