Merge branch 'develop' into feature/GRPH-88
This commit is contained in:
commit
755383c121
26 changed files with 1327 additions and 257 deletions
|
|
@ -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
|
||||
|
|
@ -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" )
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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) );
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 >,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) );
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
440
tests/cli/main.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
594
tests/tests/history_api_tests.cpp
Normal file
594
tests/tests/history_api_tests.cpp
Normal 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()
|
||||
Loading…
Reference in a new issue