Merge branch 'develop' into feature/GRPH-88

This commit is contained in:
Alfredo Garcia 2019-09-12 16:07:36 -03:00 committed by GitHub
commit 755383c121
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1327 additions and 257 deletions

View file

@ -1,36 +1,28 @@
stages:
- pull
- build
- test
before_script:
- cd /var/www/Projects/595.peerplays/blockchain
pulljob:
stage: pull
script:
- git pull origin master
only:
- master
tags:
- pp-dev
buildjob:
build:
stage: build
script:
- git submodule update --init --recursive
- cmake .
- make
only:
- master
tags:
- pp-dev
testjob:
- make -j$(nproc)
artifacts:
untracked: true
paths:
- libraries/
- programs/
- tests/
tags:
- builder
test:
stage: test
dependencies:
- build
script:
- ./tests/betting_test
- ./tests/chain_test
- ./tests/tournament_test
only:
- master
tags:
- pp-dev
tags:
- builder

View file

@ -135,7 +135,7 @@ else( WIN32 ) # Apple AND Linux
endif( APPLE )
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-class-memaccess -Wno-parentheses -Wno-terminate -Wno-invalid-offsetof" )
elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" )

View file

@ -550,26 +550,32 @@ namespace graphene { namespace app {
unsigned limit,
operation_history_id_type 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->operation_id;
FC_ASSERT( _app.chain_database() );
const auto& db = *_app.chain_database();
FC_ASSERT( limit <= 100 );
vector<operation_history_object> result;
try {
const account_transaction_history_object& node = account(db).statistics(db).most_recent_op(db);
if(start == operation_history_id_type() || start.instance.value > node.operation_id.instance.value)
start = node.operation_id;
} catch(...) { return result; }
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);
}
const auto& hist_idx = db.get_index_type<account_transaction_history_index>();
const auto& by_op_idx = hist_idx.indices().get<by_op>();
auto index_start = by_op_idx.begin();
auto itr = by_op_idx.lower_bound(boost::make_tuple(account, start));
return result;
while(itr != index_start && itr->account == account && itr->operation_id.instance.value > stop.instance.value && result.size() < limit)
{
if(itr->operation_id.instance.value <= start.instance.value)
result.push_back(itr->operation_id(db));
--itr;
}
if(stop.instance.value == 0 && result.size() < limit && itr->account == account) {
result.push_back(itr->operation_id(db));
}
return result;
}
vector<operation_history_object> history_api::get_account_history_operations( account_id_type account,
@ -594,11 +600,16 @@ namespace graphene { namespace app {
if(node->operation_id(db).op.which() == operation_id)
result.push_back( node->operation_id(db) );
}
}
if( node->next == account_transaction_history_id_type() )
node = nullptr;
else node = &node->next(db);
}
if( stop.instance.value == 0 && result.size() < limit ) {
auto head = db.find(account_transaction_history_id_type());
if (head != nullptr && head->account == account && head->operation_id(db).op.which() == operation_id)
result.push_back(head->operation_id(db));
}
return result;
}

View file

@ -226,7 +226,7 @@ namespace detail {
void new_connection( const fc::http::websocket_connection_ptr& c )
{
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c);
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(c);
auto login = std::make_shared<graphene::app::login_api>( std::ref(*_self) );
login->enable_api("database_api");

View file

@ -554,7 +554,7 @@ vector<vector<account_id_type>> database_api_impl::get_key_references( vector<pu
auto itr = refs.account_to_address_memberships.find(a);
if( itr != refs.account_to_address_memberships.end() )
{
result.reserve( itr->second.size() );
result.reserve( result.size() + itr->second.size() );
for( auto item : itr->second )
{
wdump((a)(item)(item(_db).name));
@ -565,7 +565,7 @@ vector<vector<account_id_type>> database_api_impl::get_key_references( vector<pu
if( itr != refs.account_to_key_memberships.end() )
{
result.reserve( itr->second.size() );
result.reserve( result.size() + itr->second.size() );
for( auto item : itr->second ) result.push_back(item);
}
final_result.emplace_back( std::move(result) );

View file

@ -612,7 +612,8 @@ void database::_apply_block( const signed_block& next_block )
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
update_witness_schedule(next_block);
update_global_dynamic_data(next_block);
const uint32_t missed = update_witness_missed_blocks( next_block );
update_global_dynamic_data( next_block, missed );
update_signing_witness(signing_witness, next_block);
update_last_irreversible_block();

View file

@ -43,43 +43,13 @@
namespace graphene { namespace chain {
void database::update_global_dynamic_data( const signed_block& b )
void database::update_global_dynamic_data( const signed_block& b, const uint32_t missed_blocks )
{
const dynamic_global_property_object& _dgp = dynamic_global_property_id_type(0)(*this);
const global_property_object& gpo = get_global_properties();
uint32_t missed_blocks = get_slot_at_time( b.timestamp );
//#define DIRTY_TRICK // problem with missed_blocks can occur when "maintenance_interval" set to few minutes
#ifdef DIRTY_TRICK
if (missed_blocks != 0) {
#else
assert( missed_blocks != 0 );
#endif
// bad if-condition, this code needs to execute for both shuffled and rng algorithms
// if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
// {
missed_blocks--;
for( uint32_t i = 0; i < missed_blocks; ++i ) {
const auto& witness_missed = get_scheduled_witness( i+1 )(*this);
if( witness_missed.id != b.witness ) {
/*
const auto& witness_account = witness_missed.witness_account(*this);
if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) )
wlog( "Witness ${name} missed block ${n} around ${t}", ("name",witness_account.name)("n",b.block_num())("t",b.timestamp) );
*/
modify( witness_missed, [&]( witness_object& w ) {
w.total_missed++;
});
}
}
// }
#ifdef DIRTY_TRICK
}
#endif
// dynamic global properties updating
modify( _dgp, [&]( dynamic_global_property_object& dgp ){
modify( _dgp, [&b,this,missed_blocks]( dynamic_global_property_object& dgp ){
secret_hash_type::encoder enc;
fc::raw::pack( enc, dgp.random );
fc::raw::pack( enc, b.previous_secret );
@ -87,9 +57,10 @@ void database::update_global_dynamic_data( const signed_block& b )
_random_number_generator = fc::hash_ctr_rng<secret_hash_type, 20>(dgp.random.data());
if( BOOST_UNLIKELY( b.block_num() == 1 ) )
const uint32_t block_num = b.block_num();
if( BOOST_UNLIKELY( block_num == 1 ) )
dgp.recently_missed_count = 0;
else if( _checkpoints.size() && _checkpoints.rbegin()->first >= b.block_num() )
else if( _checkpoints.size() && _checkpoints.rbegin()->first >= block_num )
dgp.recently_missed_count = 0;
else if( missed_blocks )
dgp.recently_missed_count += GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT*missed_blocks;
@ -98,7 +69,7 @@ void database::update_global_dynamic_data( const signed_block& b )
else if( dgp.recently_missed_count > 0 )
dgp.recently_missed_count--;
dgp.head_block_number = b.block_num();
dgp.head_block_number = block_num;
dgp.head_block_id = b.id();
dgp.time = b.timestamp;
dgp.current_witness = b.witness;

View file

@ -226,6 +226,22 @@ void database::update_witness_schedule(const signed_block& next_block)
idump( ( double(total_time/1000000.0)/calls) );
}
uint32_t database::update_witness_missed_blocks( const signed_block& b )
{
uint32_t missed_blocks = get_slot_at_time( b.timestamp );
FC_ASSERT( missed_blocks != 0, "Trying to push double-produced block onto current block?!" );
missed_blocks--;
const auto& witnesses = witness_schedule_id_type()(*this).current_shuffled_witnesses;
if( missed_blocks < witnesses.size() )
for( uint32_t i = 0; i < missed_blocks; ++i ) {
const auto& witness_missed = get_scheduled_witness( i+1 )(*this);
modify( witness_missed, []( witness_object& w ) {
w.total_missed++;
});
}
return missed_blocks;
}
uint32_t database::witness_participation_rate()const
{
const global_property_object& gpo = get_global_properties();

View file

@ -488,8 +488,11 @@ namespace graphene { namespace chain {
const witness_object& _validate_block_header( const signed_block& next_block )const;
void create_block_summary(const signed_block& next_block);
//////////////////// db_witness_schedule.cpp ////////////////////
uint32_t update_witness_missed_blocks( const signed_block& b );
//////////////////// db_update.cpp ////////////////////
void update_global_dynamic_data( const signed_block& b );
void update_global_dynamic_data( const signed_block& b, const uint32_t missed_blocks );
void update_signing_witness(const witness_object& signing_witness, const signed_block& new_block);
void update_last_irreversible_block();
void clear_expired_transactions();

View file

@ -51,8 +51,9 @@ class proposal_object : public abstract_object<proposal_object>
flat_set<account_id_type> available_owner_approvals;
flat_set<public_key_type> available_key_approvals;
account_id_type proposer;
std::string fail_reason;
bool is_authorized_to_execute(database& db)const;
bool is_authorized_to_execute(database& db) const;
};
/**

View file

@ -33,9 +33,6 @@
#include <algorithm>
#include <boost/multi_index/composite_key.hpp>
#define offset_d(i,f) (long(&(i)->f) - long(i))
#define offset_s(t,f) offset_d((t*)1000, f)
namespace graphene { namespace chain {
using namespace graphene::db;
@ -191,7 +188,7 @@ namespace graphene { namespace chain {
member_offset<vesting_balance_object, asset_id_type, (size_t) (offsetof(vesting_balance_object,balance) + offsetof(asset,asset_id))>,
member_offset<vesting_balance_object, share_type, (size_t) (offsetof(vesting_balance_object,balance) + offsetof(asset,amount))>
//member<vesting_balance_object, account_id_type, &vesting_balance_object::owner>
//member_offset<vesting_balance_object, account_id_type, (size_t) (offset_s(vesting_balance_object,owner))>
//member_offset<vesting_balance_object, account_id_type, (size_t) (offsetof(vesting_balance_object,owner))>
>,
composite_key_compare<
std::less< asset_id_type >,

View file

@ -244,20 +244,6 @@ void_result proposal_update_evaluator::do_evaluate(const proposal_update_operati
"", ("id", id)("available", _proposal->available_owner_approvals) );
}
/* All authority checks happen outside of evaluators
if( (d.get_node_properties().skip_flags & database::skip_authority_check) == 0 )
{
for( const auto& id : o.key_approvals_to_add )
{
FC_ASSERT( trx_state->signed_by(id) );
}
for( const auto& id : o.key_approvals_to_remove )
{
FC_ASSERT( trx_state->signed_by(id) );
}
}
*/
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
@ -293,6 +279,9 @@ void_result proposal_update_evaluator::do_apply(const proposal_update_operation&
try {
_processed_transaction = d.push_proposal(*_proposal);
} catch(fc::exception& e) {
d.modify(*_proposal, [&e](proposal_object& p) {
p.fail_reason = e.to_string(fc::log_level(fc::log_level::all));
});
wlog("Proposed transaction ${id} failed to apply once approved with exception:\n----\n${reason}\n----\nWill try again when it expires.",
("id", o.proposal)("reason", e.to_detail_string()));
_proposal_failed = true;

View file

@ -43,14 +43,11 @@ bool proposal_object::is_authorized_to_execute(database& db) const
}
catch ( const fc::exception& e )
{
//idump((available_active_approvals));
//wlog((e.to_detail_string()));
return false;
}
return true;
}
void required_approval_index::object_inserted( const object& obj )
{
assert( dynamic_cast<const proposal_object*>(&obj) );

View file

@ -234,14 +234,12 @@ namespace graphene { namespace db {
fc::raw::unpack(ds, _next_id);
fc::raw::unpack(ds, open_ver);
FC_ASSERT( open_ver == get_object_version(), "Incompatible Version, the serialization of objects in this index has changed" );
try {
vector<char> tmp;
while( true )
{
fc::raw::unpack( ds, tmp );
load( tmp );
}
} catch ( const fc::exception& ){}
vector<char> tmp;
while( ds.remaining() > 0 )
{
fc::raw::unpack( ds, tmp );
load( tmp );
}
}
virtual void save( const path& db ) override

@ -1 +1 @@
Subproject commit 94b046dce6bb86fd22abd1831fc9056103f4aa5d
Subproject commit 443f858d9b4733bb6d894da9315ce00ac3246065

View file

@ -81,11 +81,23 @@ void account_history_plugin_impl::update_account_histories( const signed_block&
{
graphene::chain::database& db = database();
vector<optional< operation_history_object > >& hist = db.get_applied_operations();
bool is_first = true;
auto skip_oho_id = [&is_first,&db,this]() {
if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo
{
db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) );
is_first = false;
}
else
_oho_index->use_next_id();
};
for( optional< operation_history_object >& o_op : hist )
{
optional<operation_history_object> oho;
auto create_oho = [&]() {
is_first = false;
operation_history_object result = db.create<operation_history_object>( [&]( operation_history_object& h )
{
if( o_op.valid() )
@ -99,7 +111,7 @@ void account_history_plugin_impl::update_account_histories( const signed_block&
{
// Note: the 2nd and 3rd checks above are for better performance, when the db is not clean,
// they will break consistency of account_stats.total_ops and removed_ops and most_recent_op
_oho_index->use_next_id();
skip_oho_id();
continue;
}
else if( !_partial_operations )
@ -179,7 +191,7 @@ void account_history_plugin_impl::update_account_histories( const signed_block&
}
}
if (_partial_operations && ! oho.valid())
_oho_index->use_next_id();
skip_oho_id();
}
}

View file

@ -65,7 +65,7 @@ void delayed_node_plugin::plugin_set_program_options(bpo::options_description& c
void delayed_node_plugin::connect()
{
my->client_connection = std::make_shared<fc::rpc::websocket_api_connection>(*my->client.connect(my->remote_endpoint));
my->client_connection = std::make_shared<fc::rpc::websocket_api_connection>(my->client.connect(my->remote_endpoint));
my->database_api = my->client_connection->get_remote_api<graphene::app::database_api>(0);
my->client_connection_closed = my->client_connection->closed.connect([this] {
connection_failed();

View file

@ -718,8 +718,6 @@ public:
}
account_object get_account(account_id_type id) const
{
if( _wallet.my_accounts.get<by_id>().count(id) )
return *_wallet.my_accounts.get<by_id>().find(id);
auto rec = _remote_db->get_accounts({id}).front();
FC_ASSERT(rec);
return *rec;
@ -733,19 +731,6 @@ public:
// It's an ID
return get_account(*id);
} else {
// It's a name
if( _wallet.my_accounts.get<by_name>().count(account_name_or_id) )
{
auto local_account = *_wallet.my_accounts.get<by_name>().find(account_name_or_id);
auto blockchain_account = _remote_db->lookup_account_names({account_name_or_id}).front();
FC_ASSERT( blockchain_account );
if (local_account.id != blockchain_account->id)
elog("my account id ${id} different from blockchain id ${id2}", ("id", local_account.id)("id2", blockchain_account->id));
if (local_account.name != blockchain_account->name)
elog("my account name ${id} different from blockchain name ${id2}", ("id", local_account.name)("id2", blockchain_account->name));
return *_wallet.my_accounts.get<by_name>().find(account_name_or_id);
}
auto rec = _remote_db->lookup_account_names({account_name_or_id}).front();
FC_ASSERT( rec && rec->name == account_name_or_id );
return *rec;
@ -2206,77 +2191,15 @@ public:
signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false)
{
flat_set<account_id_type> req_active_approvals;
flat_set<account_id_type> req_owner_approvals;
vector<authority> other_auths;
tx.get_required_authorities( req_active_approvals, req_owner_approvals, other_auths );
for( const auto& auth : other_auths )
for( const auto& a : auth.account_auths )
req_active_approvals.insert(a.first);
// std::merge lets us de-duplicate account_id's that occur in both
// sets, and dump them into a vector (as required by remote_db api)
// at the same time
vector<account_id_type> v_approving_account_ids;
std::merge(req_active_approvals.begin(), req_active_approvals.end(),
req_owner_approvals.begin() , req_owner_approvals.end(),
std::back_inserter(v_approving_account_ids));
/// TODO: fetch the accounts specified via other_auths as well.
vector< optional<account_object> > approving_account_objects =
_remote_db->get_accounts( v_approving_account_ids );
/// TODO: recursively check one layer deeper in the authority tree for keys
FC_ASSERT( approving_account_objects.size() == v_approving_account_ids.size() );
flat_map<account_id_type, account_object*> approving_account_lut;
size_t i = 0;
for( optional<account_object>& approving_acct : approving_account_objects )
{
if( !approving_acct.valid() )
{
wlog( "operation_get_required_auths said approval of non-existing account ${id} was needed",
("id", v_approving_account_ids[i]) );
i++;
continue;
}
approving_account_lut[ approving_acct->id ] = &(*approving_acct);
i++;
}
flat_set<public_key_type> approving_key_set;
for( account_id_type& acct_id : req_active_approvals )
{
const auto it = approving_account_lut.find( acct_id );
if( it == approving_account_lut.end() )
continue;
const account_object* acct = it->second;
vector<public_key_type> v_approving_keys = acct->active.get_keys();
for( const public_key_type& approving_key : v_approving_keys )
approving_key_set.insert( approving_key );
}
for( account_id_type& acct_id : req_owner_approvals )
{
const auto it = approving_account_lut.find( acct_id );
if( it == approving_account_lut.end() )
continue;
const account_object* acct = it->second;
vector<public_key_type> v_approving_keys = acct->owner.get_keys();
for( const public_key_type& approving_key : v_approving_keys )
approving_key_set.insert( approving_key );
}
for( const authority& a : other_auths )
{
for( const auto& k : a.key_auths )
approving_key_set.insert( k.first );
}
set<public_key_type> pks = _remote_db->get_potential_signatures(tx);
flat_set<public_key_type> owned_keys;
owned_keys.reserve(pks.size());
std::copy_if(pks.begin(), pks.end(), std::inserter(owned_keys, owned_keys.end()),
[this](const public_key_type &pk) { return _keys.find(pk) != _keys.end(); });
set<public_key_type> approving_key_set = _remote_db->get_required_signatures(tx, owned_keys);
auto dyn_props = get_dynamic_global_properties();
tx.set_reference_block( dyn_props.head_block_id );
tx.set_reference_block(dyn_props.head_block_id);
// first, some bookkeeping, expire old items from _recently_generated_transactions
// since transactions include the head block id, we just need the index for keeping transactions unique
@ -2290,23 +2213,11 @@ public:
uint32_t expiration_time_offset = 0;
for (;;)
{
tx.set_expiration( dyn_props.time + fc::seconds(30 + expiration_time_offset) );
tx.set_expiration(dyn_props.time + fc::seconds(30 + expiration_time_offset));
tx.signatures.clear();
for( public_key_type& key : approving_key_set )
{
auto it = _keys.find(key);
if( it != _keys.end() )
{
fc::optional<fc::ecc::private_key> privkey = wif_to_key( it->second );
FC_ASSERT( privkey.valid(), "Malformed private key in _keys" );
tx.sign( *privkey, _chain_id );
}
/// TODO: if transaction has enough signatures to be "valid" don't add any more,
/// there are cases where the wallet may have more keys than strictly necessary and
/// the transaction will be rejected if the transaction validates without requiring
/// all signatures provided
}
for (const public_key_type &key : approving_key_set)
tx.sign(get_private_key(key), _chain_id);
graphene::chain::transaction_id_type this_transaction_id = tx.id();
auto iter = _recently_generated_transactions.find(this_transaction_id);
@ -2328,11 +2239,11 @@ public:
{
try
{
_remote_net_broadcast->broadcast_transaction( tx );
_remote_net_broadcast->broadcast_transaction(tx);
}
catch (const fc::exception& e)
{
elog("Caught exception while broadcasting tx ${id}: ${e}", ("id", tx.id().str())("e", e.to_detail_string()) );
elog("Caught exception while broadcasting tx ${id}: ${e}", ("id", tx.id().str())("e", e.to_detail_string()));
throw;
}
}

View file

@ -175,7 +175,7 @@ int main( int argc, char** argv )
fc::http::websocket_client client;
idump((wdata.ws_server));
auto con = client.connect( wdata.ws_server );
auto apic = std::make_shared<fc::rpc::websocket_api_connection>(*con);
auto apic = std::make_shared<fc::rpc::websocket_api_connection>(con);
auto remote_api = apic->get_remote_api< login_api >(1);
edump((wdata.ws_user)(wdata.ws_password) );
@ -215,7 +215,7 @@ int main( int argc, char** argv )
_websocket_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){
std::cout << "here... \n";
wlog("." );
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c);
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(c);
wsc->register_api(wapi);
c->set_session_data( wsc );
});
@ -232,7 +232,7 @@ int main( int argc, char** argv )
if( options.count("rpc-tls-endpoint") )
{
_websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c);
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(c);
wsc->register_api(wapi);
c->set_session_data( wsc );
});

View file

@ -41,4 +41,14 @@ file(GLOB RANDOM_SOURCES "random/*.cpp")
add_executable( random_test ${RANDOM_SOURCES} ${COMMON_SOURCES} )
target_link_libraries( random_test graphene_chain graphene_app graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
file(GLOB CLI_SOURCES "cli/*.cpp")
add_executable( cli_test ${CLI_SOURCES} )
if(WIN32)
list(APPEND PLATFORM_SPECIFIC_LIBS ws2_32)
endif()
target_link_libraries( cli_test graphene_chain graphene_app graphene_witness graphene_wallet graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
if(MSVC)
set_source_files_properties( cli/main.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
endif(MSVC)
add_subdirectory( generate_empty_blocks )

440
tests/cli/main.cpp Normal file
View file

@ -0,0 +1,440 @@
/*
* Copyright (c) 2019 PBSA, 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/app/plugin.hpp>
#include <graphene/app/api.hpp>
#include <graphene/utilities/tempdir.hpp>
#include <graphene/bookie/bookie_plugin.hpp>
#include <graphene/egenesis/egenesis.hpp>
#include <graphene/wallet/wallet.hpp>
#include <fc/thread/thread.hpp>
#include <fc/network/http/websocket.hpp>
#include <fc/rpc/websocket_api.hpp>
#include <fc/rpc/cli.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/aes.hpp>
#include <fc/smart_ref_impl.hpp>
#ifdef _WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#include <winsock2.h>
#include <WS2tcpip.h>
#else
#include <sys/socket.h>
#include <netinet/ip.h>
#include <sys/types.h>
#endif
#include <thread>
#include <boost/filesystem/path.hpp>
#define BOOST_TEST_MODULE Test Application
#include <boost/test/included/unit_test.hpp>
/*****
* Global Initialization for Windows
* ( sets up Winsock stuf )
*/
#ifdef _WIN32
int sockInit(void)
{
WSADATA wsa_data;
return WSAStartup(MAKEWORD(1,1), &wsa_data);
}
int sockQuit(void)
{
return WSACleanup();
}
#endif
/*********************
* Helper Methods
*********************/
#include "../common/genesis_file_util.hpp"
#define INVOKE(test) ((struct test*)this)->test_method();
//////
/// @brief attempt to find an available port on localhost
/// @returns an available port number, or -1 on error
/////
int get_available_port()
{
struct sockaddr_in sin;
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd == -1)
return -1;
sin.sin_family = AF_INET;
sin.sin_port = 0;
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (::bind(socket_fd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)) == -1)
return -1;
socklen_t len = sizeof(sin);
if (getsockname(socket_fd, (struct sockaddr *)&sin, &len) == -1)
return -1;
#ifdef _WIN32
closesocket(socket_fd);
#else
close(socket_fd);
#endif
return ntohs(sin.sin_port);
}
///////////
/// @brief Start the application
/// @param app_dir the temporary directory to use
/// @param server_port_number to be filled with the rpc endpoint port number
/// @returns the application object
//////////
std::shared_ptr<graphene::app::application> start_application(fc::temp_directory& app_dir, int& server_port_number) {
std::shared_ptr<graphene::app::application> app1(new graphene::app::application{});
app1->register_plugin< graphene::bookie::bookie_plugin>();
app1->startup_plugins();
boost::program_options::variables_map cfg;
#ifdef _WIN32
sockInit();
#endif
server_port_number = get_available_port();
cfg.emplace(
"rpc-endpoint",
boost::program_options::variable_value(string("127.0.0.1:" + std::to_string(server_port_number)), false)
);
cfg.emplace("genesis-json", boost::program_options::variable_value(create_genesis_file(app_dir), false));
cfg.emplace("seed-nodes", boost::program_options::variable_value(string("[]"), false));
app1->initialize(app_dir.path(), cfg);
app1->initialize_plugins(cfg);
app1->startup_plugins();
app1->startup();
fc::usleep(fc::milliseconds(500));
return app1;
}
///////////
/// Send a block to the db
/// @param app the application
/// @param returned_block the signed block
/// @returns true on success
///////////
bool generate_block(std::shared_ptr<graphene::app::application> app, graphene::chain::signed_block& returned_block)
{
try {
fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
auto db = app->chain_database();
returned_block = db->generate_block( db->get_slot_time(1),
db->get_scheduled_witness(1),
committee_key,
database::skip_nothing );
return true;
} catch (exception &e) {
return false;
}
}
bool generate_block(std::shared_ptr<graphene::app::application> app)
{
graphene::chain::signed_block returned_block;
return generate_block(app, returned_block);
}
///////////
/// @brief Skip intermediate blocks, and generate a maintenance block
/// @param app the application
/// @returns true on success
///////////
bool generate_maintenance_block(std::shared_ptr<graphene::app::application> app) {
try {
fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
uint32_t skip = ~0;
auto db = app->chain_database();
auto maint_time = db->get_dynamic_global_properties().next_maintenance_time;
auto slots_to_miss = db->get_slot_at_time(maint_time);
db->generate_block(db->get_slot_time(slots_to_miss),
db->get_scheduled_witness(slots_to_miss),
committee_key,
skip);
return true;
} catch (exception& e)
{
return false;
}
}
///////////
/// @brief a class to make connecting to the application server easier
///////////
class client_connection
{
public:
/////////
// constructor
/////////
client_connection(
std::shared_ptr<graphene::app::application> app,
const fc::temp_directory& data_dir,
const int server_port_number
)
{
wallet_data.chain_id = app->chain_database()->get_chain_id();
wallet_data.ws_server = "ws://127.0.0.1:" + std::to_string(server_port_number);
wallet_data.ws_user = "";
wallet_data.ws_password = "";
websocket_connection = websocket_client.connect( wallet_data.ws_server );
api_connection = std::make_shared<fc::rpc::websocket_api_connection>(websocket_connection);
remote_login_api = api_connection->get_remote_api< graphene::app::login_api >(1);
BOOST_CHECK(remote_login_api->login( wallet_data.ws_user, wallet_data.ws_password ) );
wallet_api_ptr = std::make_shared<graphene::wallet::wallet_api>(wallet_data, remote_login_api);
wallet_filename = data_dir.path().generic_string() + "/wallet.json";
wallet_api_ptr->set_wallet_filename(wallet_filename);
wallet_api = fc::api<graphene::wallet::wallet_api>(wallet_api_ptr);
wallet_cli = std::make_shared<fc::rpc::cli>();
for( auto& name_formatter : wallet_api_ptr->get_result_formatters() )
wallet_cli->format_result( name_formatter.first, name_formatter.second );
boost::signals2::scoped_connection closed_connection(websocket_connection->closed.connect([=]{
cerr << "Server has disconnected us.\n";
wallet_cli->stop();
}));
(void)(closed_connection);
}
~client_connection()
{
// wait for everything to finish up
fc::usleep(fc::milliseconds(500));
}
public:
fc::http::websocket_client websocket_client;
graphene::wallet::wallet_data wallet_data;
fc::http::websocket_connection_ptr websocket_connection;
std::shared_ptr<fc::rpc::websocket_api_connection> api_connection;
fc::api<login_api> remote_login_api;
std::shared_ptr<graphene::wallet::wallet_api> wallet_api_ptr;
fc::api<graphene::wallet::wallet_api> wallet_api;
std::shared_ptr<fc::rpc::cli> wallet_cli;
std::string wallet_filename;
};
///////////////////////////////
// Cli Wallet Fixture
///////////////////////////////
struct cli_fixture
{
class dummy
{
public:
~dummy()
{
// wait for everything to finish up
fc::usleep(fc::milliseconds(500));
}
};
dummy dmy;
int server_port_number;
fc::temp_directory app_dir;
std::shared_ptr<graphene::app::application> app1;
client_connection con;
std::vector<std::string> nathan_keys;
cli_fixture() :
server_port_number(0),
app_dir( graphene::utilities::temp_directory_path() ),
app1( start_application(app_dir, server_port_number) ),
con( app1, app_dir, server_port_number ),
nathan_keys( {"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"} )
{
BOOST_TEST_MESSAGE("Setup cli_wallet::boost_fixture_test_case");
using namespace graphene::chain;
using namespace graphene::app;
try
{
BOOST_TEST_MESSAGE("Setting wallet password");
con.wallet_api_ptr->set_password("supersecret");
con.wallet_api_ptr->unlock("supersecret");
// import Nathan account
BOOST_TEST_MESSAGE("Importing nathan key");
BOOST_CHECK_EQUAL(nathan_keys[0], "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3");
BOOST_CHECK(con.wallet_api_ptr->import_key("nathan", nathan_keys[0]));
} catch( fc::exception& e ) {
edump((e.to_detail_string()));
throw;
}
}
~cli_fixture()
{
BOOST_TEST_MESSAGE("Cleanup cli_wallet::boost_fixture_test_case");
// wait for everything to finish up
fc::usleep(fc::seconds(1));
app1->shutdown();
#ifdef _WIN32
sockQuit();
#endif
}
};
///////////////////////////////
// Tests
///////////////////////////////
////////////////
// Start a server and connect using the same calls as the CLI
////////////////
BOOST_FIXTURE_TEST_CASE( cli_connect, cli_fixture )
{
BOOST_TEST_MESSAGE("Testing wallet connection.");
}
BOOST_FIXTURE_TEST_CASE( upgrade_nathan_account, cli_fixture )
{
try
{
BOOST_TEST_MESSAGE("Upgrade Nathan's account");
account_object nathan_acct_before_upgrade, nathan_acct_after_upgrade;
std::vector<signed_transaction> import_txs;
signed_transaction upgrade_tx;
BOOST_TEST_MESSAGE("Importing nathan's balance");
import_txs = con.wallet_api_ptr->import_balance("nathan", nathan_keys, true);
nathan_acct_before_upgrade = con.wallet_api_ptr->get_account("nathan");
BOOST_CHECK(generate_block(app1));
// upgrade nathan
BOOST_TEST_MESSAGE("Upgrading Nathan to LTM");
upgrade_tx = con.wallet_api_ptr->upgrade_account("nathan", true);
nathan_acct_after_upgrade = con.wallet_api_ptr->get_account("nathan");
// verify that the upgrade was successful
BOOST_CHECK_PREDICATE(
std::not_equal_to<uint32_t>(),
(nathan_acct_before_upgrade.membership_expiration_date.sec_since_epoch())
(nathan_acct_after_upgrade.membership_expiration_date.sec_since_epoch())
);
BOOST_CHECK(nathan_acct_after_upgrade.is_lifetime_member());
} catch( fc::exception& e ) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_FIXTURE_TEST_CASE( create_new_account, cli_fixture )
{
try
{
INVOKE(upgrade_nathan_account);
// create a new account
graphene::wallet::brain_key_info bki = con.wallet_api_ptr->suggest_brain_key();
BOOST_CHECK(!bki.brain_priv_key.empty());
signed_transaction create_acct_tx = con.wallet_api_ptr->create_account_with_brain_key(
bki.brain_priv_key, "jmjatlanta", "nathan", "nathan", true
);
// save the private key for this new account in the wallet file
BOOST_CHECK(con.wallet_api_ptr->import_key("jmjatlanta", bki.wif_priv_key));
con.wallet_api_ptr->save_wallet_file(con.wallet_filename);
// attempt to give jmjatlanta some CORE
BOOST_TEST_MESSAGE("Transferring CORE from Nathan to jmjatlanta");
signed_transaction transfer_tx = con.wallet_api_ptr->transfer(
"nathan", "jmjatlanta", "10000", "1.3.0", "Here are some CORE token for your new account", true
);
} catch( fc::exception& e ) {
edump((e.to_detail_string()));
throw;
}
}
///////////////////////
// Start a server and connect using the same calls as the CLI
// Vote for two witnesses, and make sure they both stay there
// after a maintenance block
///////////////////////
BOOST_FIXTURE_TEST_CASE( cli_vote_for_2_witnesses, cli_fixture )
{
try
{
BOOST_TEST_MESSAGE("Cli Vote Test for 2 Witnesses");
INVOKE(upgrade_nathan_account); // just to fund nathan
// get the details for init1
witness_object init1_obj = con.wallet_api_ptr->get_witness("init1");
int init1_start_votes = init1_obj.total_votes;
// Vote for a witness
signed_transaction vote_witness1_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init1", true, true);
// generate a block to get things started
BOOST_CHECK(generate_block(app1));
// wait for a maintenance interval
BOOST_CHECK(generate_maintenance_block(app1));
// Verify that the vote is there
init1_obj = con.wallet_api_ptr->get_witness("init1");
witness_object init2_obj = con.wallet_api_ptr->get_witness("init2");
int init1_middle_votes = init1_obj.total_votes;
BOOST_CHECK(init1_middle_votes > init1_start_votes);
// Vote for a 2nd witness
int init2_start_votes = init2_obj.total_votes;
signed_transaction vote_witness2_tx = con.wallet_api_ptr->vote_for_witness("nathan", "init2", true, true);
// send another block to trigger maintenance interval
BOOST_CHECK(generate_maintenance_block(app1));
// Verify that both the first vote and the 2nd are there
init2_obj = con.wallet_api_ptr->get_witness("init2");
init1_obj = con.wallet_api_ptr->get_witness("init1");
int init2_middle_votes = init2_obj.total_votes;
BOOST_CHECK(init2_middle_votes > init2_start_votes);
int init1_last_votes = init1_obj.total_votes;
BOOST_CHECK(init1_last_votes > init1_start_votes);
} catch( fc::exception& e ) {
edump((e.to_detail_string()));
throw;
}
}

View file

@ -109,6 +109,24 @@ database_fixture::database_fixture()
genesis_state.initial_parameters.current_fees->zero_all_fees();
open_database();
// add account tracking for ahplugin for special test case with track-account enabled
if( !options.count("track-account") && boost::unit_test::framework::current_test_case().p_name.value == "track_account") {
std::vector<std::string> track_account;
std::string track = "\"1.2.18\"";
track_account.push_back(track);
options.insert(std::make_pair("track-account", boost::program_options::variable_value(track_account, false)));
options.insert(std::make_pair("partial-operations", boost::program_options::variable_value(true, false)));
}
// account tracking 2 accounts
if( !options.count("track-account") && boost::unit_test::framework::current_test_case().p_name.value == "track_account2") {
std::vector<std::string> track_account;
std::string track = "\"1.2.0\"";
track_account.push_back(track);
track = "\"1.2.17\"";
track_account.push_back(track);
options.insert(std::make_pair("track-account", boost::program_options::variable_value(track_account, false)));
}
// app.initialize();
ahplugin->plugin_set_app(&app);
ahplugin->plugin_initialize(options);

View file

@ -59,7 +59,7 @@ BOOST_AUTO_TEST_CASE( simple_single_signature )
sign(trx, nathan_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 500);
BOOST_CHECK_EQUAL(get_balance(nathan, core), static_cast<int64_t>(old_balance - 500));
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
@ -97,25 +97,25 @@ BOOST_AUTO_TEST_CASE( any_two_of_three )
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
sign(trx, nathan_key2);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 500);
BOOST_CHECK_EQUAL(get_balance(nathan, core), static_cast<int64_t>(old_balance - 500));
trx.signatures.clear();
sign(trx, nathan_key2);
sign(trx, nathan_key3);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 1000);
BOOST_CHECK_EQUAL(get_balance(nathan, core), static_cast<int64_t>(old_balance - 1000));
trx.signatures.clear();
sign(trx, nathan_key1);
sign(trx, nathan_key3);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 1500);
BOOST_CHECK_EQUAL(get_balance(nathan, core), static_cast<int64_t>(old_balance - 1500));
trx.signatures.clear();
//sign(trx, fc::ecc::private_key::generate());
sign(trx,nathan_key3);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 1500);
BOOST_CHECK_EQUAL(get_balance(nathan, core), static_cast<int64_t>(old_balance - 1500));
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
@ -165,7 +165,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts )
BOOST_TEST_MESSAGE( "Attempting to transfer with parent1 and parent2 signature, should succeed" );
sign(trx,parent1_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 500);
BOOST_CHECK_EQUAL(get_balance(child, core), static_cast<int64_t>(old_balance - 500));
trx.operations.clear();
trx.signatures.clear();
@ -180,7 +180,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts )
sign(trx,parent1_key);
sign(trx,parent2_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_REQUIRE_EQUAL(child.active.num_auths(), 3);
BOOST_REQUIRE_EQUAL(child.active.num_auths(), 3u);
trx.operations.clear();
trx.signatures.clear();
}
@ -203,13 +203,13 @@ BOOST_AUTO_TEST_CASE( recursive_accounts )
BOOST_TEST_MESSAGE( "Attempting transfer both parents, should succeed" );
sign(trx, parent1_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 1000);
BOOST_CHECK_EQUAL(get_balance(child, core), static_cast<int64_t>(old_balance - 1000));
trx.signatures.clear();
BOOST_TEST_MESSAGE( "Attempting transfer with just child key, should succeed" );
sign(trx, child_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 1500);
BOOST_CHECK_EQUAL(get_balance(child, core), static_cast<int64_t>(old_balance - 1500));
trx.operations.clear();
trx.signatures.clear();
@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts )
BOOST_TEST_MESSAGE( "Attempt to transfer using parent2_key and grandparent_key" );
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 2000);
BOOST_CHECK_EQUAL(get_balance(child, core), static_cast<int64_t>(old_balance - 2000));
trx.clear();
BOOST_TEST_MESSAGE( "Update grandparent account authority to be committee account" );
@ -268,7 +268,7 @@ BOOST_AUTO_TEST_CASE( recursive_accounts )
trx.signatures.clear();
sign(trx, child_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 2500);
BOOST_CHECK_EQUAL(get_balance(child, core), static_cast<int64_t>(old_balance - 2500));
trx.operations.clear();
trx.signatures.clear();
@ -329,17 +329,17 @@ BOOST_AUTO_TEST_CASE( proposed_single_account )
vector<authority> other;
flat_set<account_id_type> active_set, owner_set;
operation_get_required_authorities(op,active_set,owner_set,other);
BOOST_CHECK_EQUAL(active_set.size(), 1);
BOOST_CHECK_EQUAL(owner_set.size(), 0);
BOOST_CHECK_EQUAL(other.size(), 0);
BOOST_CHECK_EQUAL(active_set.size(), 1lu);
BOOST_CHECK_EQUAL(owner_set.size(), 0lu);
BOOST_CHECK_EQUAL(other.size(), 0lu);
BOOST_CHECK(*active_set.begin() == moneyman.get_id());
active_set.clear();
other.clear();
operation_get_required_authorities(op.proposed_ops.front().op,active_set,owner_set,other);
BOOST_CHECK_EQUAL(active_set.size(), 1);
BOOST_CHECK_EQUAL(owner_set.size(), 0);
BOOST_CHECK_EQUAL(other.size(), 0);
BOOST_CHECK_EQUAL(active_set.size(), 1lu);
BOOST_CHECK_EQUAL(owner_set.size(), 0lu);
BOOST_CHECK_EQUAL(other.size(), 0lu);
BOOST_CHECK(*active_set.begin() == nathan.id);
}
@ -349,10 +349,10 @@ BOOST_AUTO_TEST_CASE( proposed_single_account )
sign( trx, init_account_priv_key );
const proposal_object& proposal = db.get<proposal_object>(PUSH_TX( db, trx ).operation_results.front().get<object_id_type>());
BOOST_CHECK_EQUAL(proposal.required_active_approvals.size(), 1);
BOOST_CHECK_EQUAL(proposal.available_active_approvals.size(), 0);
BOOST_CHECK_EQUAL(proposal.required_owner_approvals.size(), 0);
BOOST_CHECK_EQUAL(proposal.available_owner_approvals.size(), 0);
BOOST_CHECK_EQUAL(proposal.required_active_approvals.size(), 1lu);
BOOST_CHECK_EQUAL(proposal.available_active_approvals.size(), 0lu);
BOOST_CHECK_EQUAL(proposal.required_owner_approvals.size(), 0lu);
BOOST_CHECK_EQUAL(proposal.available_owner_approvals.size(), 0lu);
BOOST_CHECK(*proposal.required_active_approvals.begin() == nathan.id);
proposal_update_operation pup;
@ -389,6 +389,49 @@ BOOST_AUTO_TEST_CASE( proposed_single_account )
}
}
BOOST_AUTO_TEST_CASE( proposal_failure )
{
try
{
ACTORS( (bob) (alice) );
fund( bob, asset(1000000) );
fund( alice, asset(1000000) );
// create proposal that will eventually fail due to lack of funds
transfer_operation top;
top.to = alice_id;
top.from = bob_id;
top.amount = asset(2000000);
proposal_create_operation pop;
pop.proposed_ops.push_back( { top } );
pop.expiration_time = db.head_block_time() + fc::days(1);
pop.fee_paying_account = bob_id;
trx.operations.push_back( pop );
trx.signatures.clear();
sign( trx, bob_private_key );
processed_transaction processed = PUSH_TX( db, trx );
proposal_object prop = db.get<proposal_object>(processed.operation_results.front().get<object_id_type>());
trx.clear();
generate_block();
// add signature
proposal_update_operation up_op;
up_op.proposal = prop.id;
up_op.fee_paying_account = bob_id;
up_op.active_approvals_to_add.emplace( bob_id );
trx.operations.push_back( up_op );
sign( trx, bob_private_key );
PUSH_TX( db, trx );
trx.clear();
// check fail reason
const proposal_object& result = db.get<proposal_object>(prop.id);
BOOST_CHECK(!result.fail_reason.empty());
BOOST_CHECK_EQUAL( result.fail_reason.substr(0, 16), "Assert Exception");
}
FC_LOG_AND_RETHROW()
}
/// Verify that committee authority cannot be invoked in a normal transaction
BOOST_AUTO_TEST_CASE( committee_authority )
{ try {
@ -696,7 +739,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_delete, database_fixture )
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_active_approvals.size(), 1);
BOOST_CHECK_EQUAL(prop.available_active_approvals.size(), 1lu);
std::swap(uop.active_approvals_to_add, uop.active_approvals_to_remove);
trx.operations.push_back(uop);
@ -704,7 +747,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_delete, database_fixture )
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_active_approvals.size(), 0);
BOOST_CHECK_EQUAL(prop.available_active_approvals.size(), 0lu);
}
{
@ -758,8 +801,8 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_delete, database_fixture )
}
const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();
BOOST_CHECK_EQUAL(prop.required_active_approvals.size(), 1);
BOOST_CHECK_EQUAL(prop.required_owner_approvals.size(), 1);
BOOST_CHECK_EQUAL(prop.required_active_approvals.size(), 1lu);
BOOST_CHECK_EQUAL(prop.required_owner_approvals.size(), 1lu);
BOOST_CHECK(!prop.is_authorized_to_execute(db));
{
@ -772,7 +815,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_delete, database_fixture )
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_owner_approvals.size(), 1);
BOOST_CHECK_EQUAL(prop.available_owner_approvals.size(), 1lu);
std::swap(uop.owner_approvals_to_add, uop.owner_approvals_to_remove);
trx.operations.push_back(uop);
@ -780,7 +823,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_delete, database_fixture )
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_owner_approvals.size(), 0);
BOOST_CHECK_EQUAL(prop.available_owner_approvals.size(), 0lu);
}
{
@ -835,8 +878,8 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_complete, database_fixture )
}
const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();
BOOST_CHECK_EQUAL(prop.required_active_approvals.size(), 1);
BOOST_CHECK_EQUAL(prop.required_owner_approvals.size(), 1);
BOOST_CHECK_EQUAL(prop.required_active_approvals.size(), 1lu);
BOOST_CHECK_EQUAL(prop.required_owner_approvals.size(), 1lu);
BOOST_CHECK(!prop.is_authorized_to_execute(db));
{
@ -852,7 +895,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_complete, database_fixture )
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 1);
BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 1lu);
std::swap(uop.key_approvals_to_add, uop.key_approvals_to_remove);
trx.operations.push_back(uop);
@ -862,7 +905,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_complete, database_fixture )
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 0);
BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 0lu);
std::swap(uop.key_approvals_to_add, uop.key_approvals_to_remove);
trx.operations.push_back(uop);
@ -872,7 +915,7 @@ BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_complete, database_fixture )
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 1);
BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 1lu);
uop.key_approvals_to_add.clear();
uop.owner_approvals_to_add.insert(nathan.get_id());

View file

@ -33,6 +33,7 @@
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/utilities/tempdir.hpp>
@ -1288,18 +1289,50 @@ BOOST_AUTO_TEST_CASE( genesis_reserve_ids )
}
}
BOOST_FIXTURE_TEST_CASE( miss_some_blocks, database_fixture )
{ try {
std::vector<witness_id_type> witnesses = witness_schedule_id_type()(db).current_shuffled_witnesses;
BOOST_CHECK_EQUAL( 10, witnesses.size() );
// database_fixture constructor calls generate_block once, signed by witnesses[0]
generate_block(); // witnesses[1]
generate_block(); // witnesses[2]
for( const auto& id : witnesses )
BOOST_CHECK_EQUAL( 0, id(db).total_missed );
// generate_blocks generates another block *now* (witnesses[3])
// and one at now+10 blocks (witnesses[12%10])
generate_blocks( db.head_block_time() + db.get_global_properties().parameters.block_interval * 10, true );
// i. e. 8 blocks are missed in between by witness[4..11%10]
for( uint32_t i = 0; i < witnesses.size(); i++ )
BOOST_CHECK_EQUAL( (i+7) % 10 < 2 ? 0 : 1, witnesses[i](db).total_missed );
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( miss_many_blocks, database_fixture )
{
try
{
auto get_misses = []( database& db ) {
std::map< witness_id_type, uint32_t > misses;
for( const auto& witness_id : witness_schedule_id_type()(db).current_shuffled_witnesses )
misses[witness_id] = witness_id(db).total_missed;
return misses;
};
generate_block();
generate_block();
generate_block();
auto missed_before = get_misses( db );
// miss 10 maintenance intervals
generate_blocks( db.get_dynamic_global_properties().next_maintenance_time + db.get_global_properties().parameters.maintenance_interval * 10, true );
generate_block();
generate_block();
generate_block();
auto missed_after = get_misses( db );
BOOST_CHECK_EQUAL( missed_before.size(), missed_after.size() );
for( const auto& miss : missed_before )
{
const auto& after = missed_after.find( miss.first );
BOOST_REQUIRE( after != missed_after.end() );
BOOST_CHECK_EQUAL( miss.second, after->second );
}
}
catch (fc::exception& e)
{

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2017 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
@ -34,6 +34,8 @@
using namespace graphene::chain;
BOOST_FIXTURE_TEST_SUITE( database_tests, database_fixture )
BOOST_AUTO_TEST_CASE( undo_test )
{
try {
@ -59,3 +61,34 @@ BOOST_AUTO_TEST_CASE( undo_test )
throw;
}
}
BOOST_AUTO_TEST_CASE( flat_index_test )
{
ACTORS((sam));
const auto& bitusd = create_bitasset("USDBIT", sam.id);
update_feed_producers(bitusd, {sam.id});
price_feed current_feed;
current_feed.settlement_price = bitusd.amount(100) / asset(100);
publish_feed(bitusd, sam, current_feed);
FC_ASSERT( bitusd.bitasset_data_id->instance == 0 );
FC_ASSERT( !(*bitusd.bitasset_data_id)(db).current_feed.settlement_price.is_null() );
try {
auto ses = db._undo_db.start_undo_session();
const auto& obj1 = db.create<asset_bitasset_data_object>( [&]( asset_bitasset_data_object& obj ){
obj.settlement_fund = 17;
});
FC_ASSERT( obj1.settlement_fund == 17 );
throw std::string("Expected");
// With flat_index, obj1 will not really be removed from the index
} catch ( const std::string& e )
{ // ignore
}
// force maintenance
const auto& dynamic_global_props = db.get<dynamic_global_property_object>(dynamic_global_property_id_type());
generate_blocks(dynamic_global_props.next_maintenance_time, true);
FC_ASSERT( !(*bitusd.bitasset_data_id)(db).current_feed.settlement_price.is_null() );
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -0,0 +1,594 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2019 PBSA, 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 <boost/test/unit_test.hpp>
#include <graphene/app/database_api.hpp>
#include <graphene/app/api.hpp>
#include <graphene/chain/account_object.hpp>
#include "../common/database_fixture.hpp"
#include <fc/smart_ref_impl.hpp>
#include <fc/crypto/digest.hpp>
using namespace graphene::app;
using namespace graphene::chain;
using namespace graphene::chain::test;
BOOST_FIXTURE_TEST_SUITE(account_history_tests, database_fixture)
BOOST_AUTO_TEST_CASE(get_account_history) {
try {
graphene::app::history_api hist_api(app);
//account_id_type() do 3 ops
create_bitasset("USD", account_id_type());
auto dan_acc = create_account("dan");
auto bob_acc = create_account("bob");
generate_block();
fc::usleep(fc::milliseconds(2000));
int asset_create_op_id = operation::tag<asset_create_operation>::value;
int account_create_op_id = operation::tag<account_create_operation>::value;
//account_id_type() did 3 ops and includes id0
vector<operation_history_object> histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 100, operation_history_id_type());
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);
BOOST_CHECK_EQUAL(histories[2].op.which(), asset_create_op_id);
// 1 account_create op larger than id1
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 100, operation_history_id_type());
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK(histories[0].id.instance() != 0);
BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);
// Limit 2 returns 2 result
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 2, operation_history_id_type());
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK(histories[1].id.instance() != 0);
BOOST_CHECK_EQUAL(histories[1].op.which(), account_create_op_id);
// bob has 1 op
histories = hist_api.get_account_history(bob_acc.get_id(), operation_history_id_type(), 100, operation_history_id_type());
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(zero_id_object) {
try {
graphene::app::history_api hist_api(app);
// no history at all in the chain
vector<operation_history_object> histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 4, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 0u);
create_bitasset("USD", account_id_type()); // create op 0
generate_block();
fc::usleep(fc::milliseconds(2000));
// what if the account only has one history entry and it is 0?
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type());
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(get_account_history_additional) {
try {
graphene::app::history_api hist_api(app);
// A = account_id_type() with records { 5, 3, 1, 0 }, and
// B = dan with records { 6, 4, 2, 1 }
// account_id_type() and dan share operation id 1(account create) - share can be also in id 0
// no history at all in the chain
vector<operation_history_object> histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 4, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 0u);
create_bitasset("USD", account_id_type()); // create op 0
generate_block();
// what if the account only has one history entry and it is 0?
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type());
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);
const account_object& dan = create_account("dan"); // create op 1
create_bitasset("CNY", dan.id); // create op 2
create_bitasset("BTC", account_id_type()); // create op 3
create_bitasset("XMR", dan.id); // create op 4
create_bitasset("EUR", account_id_type()); // create op 5
create_bitasset("OIL", dan.id); // create op 6
generate_block();
// f(A, 0, 4, 9) = { 5, 3, 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(9));
BOOST_CHECK_EQUAL(histories.size(), 4u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);
// f(A, 0, 4, 6) = { 5, 3, 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(6));
BOOST_CHECK_EQUAL(histories.size(), 4u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);
// f(A, 0, 4, 5) = { 5, 3, 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(5));
BOOST_CHECK_EQUAL(histories.size(), 4u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);
// f(A, 0, 4, 4) = { 3, 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(4));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);
// f(A, 0, 4, 3) = { 3, 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(3));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);
// f(A, 0, 4, 2) = { 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(2));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);
// f(A, 0, 4, 1) = { 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type(1));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);
// f(A, 0, 4, 0) = { 5, 3, 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 4, operation_history_id_type());
BOOST_CHECK_EQUAL(histories.size(), 4u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);
// f(A, 1, 5, 9) = { 5, 3 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(9));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
// f(A, 1, 5, 6) = { 5, 3 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(6));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
// f(A, 1, 5, 5) = { 5, 3 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(5));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
// f(A, 1, 5, 4) = { 3 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(4));
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
// f(A, 1, 5, 3) = { 3 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(3));
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
// f(A, 1, 5, 2) = { }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(2));
BOOST_CHECK_EQUAL(histories.size(), 0u);
// f(A, 1, 5, 1) = { }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(1));
BOOST_CHECK_EQUAL(histories.size(), 0u);
// f(A, 1, 5, 0) = { 5, 3 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 5, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
// f(A, 0, 3, 9) = { 5, 3, 1 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(9));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
// f(A, 0, 3, 6) = { 5, 3, 1 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(6));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
// f(A, 0, 3, 5) = { 5, 3, 1 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(5));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
// f(A, 0, 3, 4) = { 3, 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(4));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);
// f(A, 0, 3, 3) = { 3, 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(3));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 0u);
// f(A, 0, 3, 2) = { 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(2));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);
// f(A, 0, 3, 1) = { 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type(1));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);
// f(A, 0, 3, 0) = { 5, 3, 1 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(), 3, operation_history_id_type());
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
// f(B, 0, 4, 9) = { 6, 4, 2, 1 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(9));
BOOST_CHECK_EQUAL(histories.size(), 4u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u);
BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);
// f(B, 0, 4, 6) = { 6, 4, 2, 1 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(6));
BOOST_CHECK_EQUAL(histories.size(), 4u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u);
BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);
// f(B, 0, 4, 5) = { 4, 2, 1 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(5));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
// f(B, 0, 4, 4) = { 4, 2, 1 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(4));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
// f(B, 0, 4, 3) = { 2, 1 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(3));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);
// f(B, 0, 4, 2) = { 2, 1 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(2));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 2u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 1u);
// f(B, 0, 4, 1) = { 1 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type(1));
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 1u);
// f(B, 0, 4, 0) = { 6, 4, 2, 1 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(), 4, operation_history_id_type());
BOOST_CHECK_EQUAL(histories.size(), 4u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 2u);
BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);
// f(B, 2, 4, 9) = { 6, 4 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(9));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
// f(B, 2, 4, 6) = { 6, 4 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(6));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
// f(B, 2, 4, 5) = { 4 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(5));
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
// f(B, 2, 4, 4) = { 4 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(4));
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
// f(B, 2, 4, 3) = { }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(3));
BOOST_CHECK_EQUAL(histories.size(), 0u);
// f(B, 2, 4, 2) = { }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(2));
BOOST_CHECK_EQUAL(histories.size(), 0u);
// f(B, 2, 4, 1) = { }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(1));
BOOST_CHECK_EQUAL(histories.size(), 0u);
// f(B, 2, 4, 0) = { 6, 4 }
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(2), 4, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
// 0 limits
histories = hist_api.get_account_history(dan.get_id(), operation_history_id_type(0), 0, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 0u);
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(3), 0, operation_history_id_type(9));
BOOST_CHECK_EQUAL(histories.size(), 0u);
// create a new account C = alice { 7 }
auto alice = create_account("alice");
generate_block();
// f(C, 0, 4, 10) = { 7 }
histories = hist_api.get_account_history(alice.get_id(), operation_history_id_type(0), 4, operation_history_id_type(10));
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u);
// f(C, 8, 4, 10) = { }
histories = hist_api.get_account_history(alice.get_id(), operation_history_id_type(8), 4, operation_history_id_type(10));
BOOST_CHECK_EQUAL(histories.size(), 0u);
// f(A, 0, 10, 0) = { 7, 5, 3, 1, 0 }
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 5u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 7u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 5u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[3].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[4].id.instance(), 0u);
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(track_account) {
try {
graphene::app::history_api hist_api(app);
// account_id_type() is not tracked
// account_id_type() creates alice(not tracked account)
const account_object& alice = create_account("alice");
auto alice_id = alice.id;
//account_id_type() creates some ops
create_bitasset("CNY", account_id_type());
create_bitasset("USD", account_id_type());
// account_id_type() creates dan(account tracked)
const account_object& dan = create_account("dan");
auto dan_id = dan.id;
// dan makes 1 op
create_bitasset("EUR", dan_id);
generate_block( ~database::skip_fork_db );
// anything against account_id_type() should be {}
vector<operation_history_object> histories =
hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 0u);
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 0u);
histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(1), 1, operation_history_id_type(2));
BOOST_CHECK_EQUAL(histories.size(), 0u);
// anything against alice should be {}
histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 0u);
histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 0u);
histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(2));
BOOST_CHECK_EQUAL(histories.size(), 0u);
// dan should have history
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 3u);
// create more ops, starting with an untracked account
create_bitasset( "BTC", account_id_type() );
create_bitasset( "GBP", dan_id );
generate_block( ~database::skip_fork_db );
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);
db.pop_block();
// Try again, should result in same object IDs
create_bitasset( "BTC", account_id_type() );
create_bitasset( "GBP", dan_id );
generate_block();
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 3u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 6u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 4u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 3u);
} catch (fc::exception &e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(track_account2) {
try {
graphene::app::history_api hist_api(app);
// account_id_type() is tracked
// account_id_type() creates alice(tracked account)
const account_object& alice = create_account("alice");
auto alice_id = alice.id;
//account_id_type() creates some ops
create_bitasset("CNY", account_id_type());
create_bitasset("USD", account_id_type());
// alice makes 1 op
create_bitasset("EUR", alice_id);
// account_id_type() creates dan(account not tracked)
const account_object& dan = create_account("dan");
auto dan_id = dan.id;
generate_block();
// all account_id_type() should have 4 ops {4,2,1,0}
vector<operation_history_object> histories = hist_api.get_account_history(account_id_type(), operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 4u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 4u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 2u);
BOOST_CHECK_EQUAL(histories[2].id.instance(), 1u);
BOOST_CHECK_EQUAL(histories[3].id.instance(), 0u);
// all alice account should have 2 ops {3, 0}
histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
BOOST_CHECK_EQUAL(histories[1].id.instance(), 0u);
// alice first op should be {0}
histories = hist_api.get_account_history(alice_id, operation_history_id_type(0), 1, operation_history_id_type(1));
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);
// alice second op should be {3}
histories = hist_api.get_account_history(alice_id, operation_history_id_type(1), 1, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 3u);
// anything against dan should be {}
histories = hist_api.get_account_history(dan_id, operation_history_id_type(0), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 0u);
histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 10, operation_history_id_type(0));
BOOST_CHECK_EQUAL(histories.size(), 0u);
histories = hist_api.get_account_history(dan_id, operation_history_id_type(1), 1, operation_history_id_type(2));
BOOST_CHECK_EQUAL(histories.size(), 0u);
} catch (fc::exception &e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE(get_account_history_operations) {
try {
graphene::app::history_api hist_api(app);
//account_id_type() do 3 ops
create_bitasset("CNY", account_id_type());
create_account("sam");
create_account("alice");
generate_block();
fc::usleep(fc::milliseconds(2000));
int asset_create_op_id = operation::tag<asset_create_operation>::value;
int account_create_op_id = operation::tag<account_create_operation>::value;
//account_id_type() did 1 asset_create op
vector<operation_history_object> histories = hist_api.get_account_history_operations(account_id_type(), asset_create_op_id, operation_history_id_type(), operation_history_id_type(), 100);
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].id.instance(), 0u);
BOOST_CHECK_EQUAL(histories[0].op.which(), asset_create_op_id);
//account_id_type() did 2 account_create ops
histories = hist_api.get_account_history_operations(account_id_type(), account_create_op_id, operation_history_id_type(), operation_history_id_type(), 100);
BOOST_CHECK_EQUAL(histories.size(), 2u);
BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);
// No asset_create op larger than id1
histories = hist_api.get_account_history_operations(account_id_type(), asset_create_op_id, operation_history_id_type(), operation_history_id_type(1), 100);
BOOST_CHECK_EQUAL(histories.size(), 0u);
// Limit 1 returns 1 result
histories = hist_api.get_account_history_operations(account_id_type(), account_create_op_id, operation_history_id_type(),operation_history_id_type(), 1);
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);
// alice has 1 op
histories = hist_api.get_account_history_operations(get_account("alice").id, account_create_op_id, operation_history_id_type(),operation_history_id_type(), 100);
BOOST_CHECK_EQUAL(histories.size(), 1u);
BOOST_CHECK_EQUAL(histories[0].op.which(), account_create_op_id);
} catch (fc::exception &e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_SUITE_END()