Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CLI get_account_history pagination issue #1176 #1177

Merged
merged 6 commits into from
Jul 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions libraries/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2947,23 +2947,47 @@ vector<operation_detail> wallet_api::get_account_history(string name, int limit)

while( limit > 0 )
{
bool skip_first_row = false;
operation_history_id_type start;
if( result.size() )
{
start = result.back().op.id;
start = start + 1;
if( start == operation_history_id_type() ) // no more data
break;
start = start + (-1);
if( start == operation_history_id_type() ) // will return most recent history if directly call remote API with this
{
start = start + 1;
skip_first_row = true;
}
}

int page_limit = skip_first_row ? std::min( 100, limit + 1 ) : std::min( 100, limit );

vector<operation_history_object> current = my->_remote_hist->get_account_history(name, operation_history_id_type(), std::min(100,limit), start);
for( auto& o : current ) {
vector<operation_history_object> current = my->_remote_hist->get_account_history( name, operation_history_id_type(),
page_limit, start );
bool first_row = true;
for( auto& o : current )
{
if( first_row )
{
first_row = false;
if( skip_first_row )
{
continue;
}
}
std::stringstream ss;
auto memo = o.op.visit(detail::operation_printer(ss, *my, o.result));
result.push_back( operation_detail{ memo, ss.str(), o } );
}
if( int(current.size()) < std::min(100,limit) )

if( int(current.size()) < page_limit )
break;

limit -= current.size();
if( skip_first_row )
++limit;
}

return result;
Expand Down
89 changes: 89 additions & 0 deletions tests/cli/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ std::shared_ptr<graphene::app::application> start_application(fc::temp_directory
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;
Expand Down Expand Up @@ -483,3 +486,89 @@ BOOST_AUTO_TEST_CASE( cli_confidential_tx_test )
}
app1->shutdown();
}

/******
* Check account history pagination (see bitshares-core/issue/1176)
*/
BOOST_AUTO_TEST_CASE( account_history_pagination )
{
using namespace graphene::chain;
using namespace graphene::app;
std::shared_ptr<graphene::app::application> app1;
try
{
fc::temp_directory app_dir ( graphene::utilities::temp_directory_path() );

int server_port_number = 0;
app1 = start_application(app_dir, server_port_number);

// connect to the server
client_connection con(app1, app_dir, server_port_number);

// set wallet password
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");
std::vector<std::string> nathan_keys{"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"};
BOOST_CHECK_EQUAL(nathan_keys[0], "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3");
BOOST_CHECK(con.wallet_api_ptr->import_key("nathan", nathan_keys[0]));

BOOST_TEST_MESSAGE("Importing nathan's balance");
std::vector<signed_transaction> import_txs = con.wallet_api_ptr->import_balance("nathan", nathan_keys, true);
account_object nathan_acct_before_upgrade = con.wallet_api_ptr->get_account("nathan");

// upgrade nathan
BOOST_TEST_MESSAGE("Upgrading Nathan to LTM");
signed_transaction upgrade_tx = con.wallet_api_ptr->upgrade_account("nathan", true);
account_object 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());

// 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 bitsahres
BOOST_TEST_MESSAGE("Transferring bitshares from Nathan to jmjatlanta");
for(int i = 1; i <= 200; i++)
{
signed_transaction transfer_tx = con.wallet_api_ptr->transfer("nathan", "jmjatlanta", std::to_string(i),
"1.3.0", "Here are some BTS for your new account", true);
}

BOOST_CHECK(generate_block(app1));

// now get account history and make sure everything is there (and no duplicates)
std::vector<graphene::wallet::operation_detail> history = con.wallet_api_ptr->get_account_history("jmjatlanta", 300);
BOOST_CHECK_EQUAL(201, history.size() );

std::set<object_id_type> operation_ids;

for(auto& op : history)
{
if( operation_ids.find(op.op.id) != operation_ids.end() )
{
BOOST_FAIL("Duplicate found");
}
operation_ids.insert(op.op.id);
}

} catch( fc::exception& e ) {
edump((e.to_detail_string()));
throw;
}
app1->shutdown();

}