aboutsummaryrefslogtreecommitdiff
path: root/src/simplewallet
diff options
context:
space:
mode:
authorAntonio Juarez <antonio.maria.juarez@live.com>2014-04-02 17:00:17 +0100
committerAntonio Juarez <antonio.maria.juarez@live.com>2014-04-02 17:00:17 +0100
commit29c2859a3e8a935ef605534c6c36333894980d50 (patch)
tree1d31daa09741cc82ecc24c0c89f15b84e74cb12d /src/simplewallet
parentsome fixes (diff)
downloadmonero-29c2859a3e8a935ef605534c6c36333894980d50.tar.xz
json rpc for wallet and bugfix
Diffstat (limited to 'src/simplewallet')
-rw-r--r--src/simplewallet/simplewallet.cpp665
-rw-r--r--src/simplewallet/simplewallet.h66
2 files changed, 564 insertions, 167 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 2ba031325..a8aa7dc00 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -15,6 +15,7 @@
#include "cryptonote_core/cryptonote_format_utils.h"
#include "storages/http_abstract_invoke.h"
#include "rpc/core_rpc_server_commands_defs.h"
+#include "wallet/wallet_rpc_server.h"
#include "version.h"
#if defined(WIN32)
@@ -37,27 +38,109 @@ namespace
const command_line::arg_descriptor<std::string> arg_daemon_address = {"daemon-address", "Use daemon instance at <host>:<port>", ""};
const command_line::arg_descriptor<std::string> arg_daemon_host = {"daemon-host", "Use daemon instance at host <arg> instead of localhost", ""};
const command_line::arg_descriptor<std::string> arg_password = {"password", "Wallet password", "", true};
- const command_line::arg_descriptor<int> arg_daemon_port = {"daemon-port", "Use daemon instance at port <arg> instead of 8080", 0};
+ const command_line::arg_descriptor<int> arg_daemon_port = {"daemon-port", "Use daemon instance at port <arg> instead of 8081", 0};
const command_line::arg_descriptor<uint32_t> arg_log_level = {"set_log", "", 0, true};
const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""};
- void print_success_msg(const std::string& msg, bool color = false)
+ inline std::string interpret_rpc_response(bool ok, const std::string& status)
{
- LOG_PRINT_L4(msg);
- if (color) epee::log_space::set_console_color(epee::log_space::console_color_green, false);
- std::cout << msg;
- if (color) epee::log_space::reset_console_color();
- std::cout << std::endl;
+ std::string err;
+ if (ok)
+ {
+ if (status == CORE_RPC_STATUS_BUSY)
+ {
+ err = "daemon is busy. Please try later";
+ }
+ else if (status != CORE_RPC_STATUS_OK)
+ {
+ err = status;
+ }
+ }
+ else
+ {
+ err = "possible lost connection to daemon";
+ }
+ return err;
+ }
+
+ class message_writer
+ {
+ public:
+ message_writer(epee::log_space::console_colors color = epee::log_space::console_color_default, bool bright = false,
+ std::string&& prefix = std::string(), int log_level = LOG_LEVEL_2)
+ : m_flush(true)
+ , m_color(color)
+ , m_bright(bright)
+ , m_log_level(log_level)
+ {
+ m_oss << prefix;
+ }
+
+ message_writer(message_writer&& rhs)
+ : m_flush(std::move(rhs.m_flush))
+#if defined(_MSC_VER)
+ , m_oss(std::move(rhs.m_oss))
+#else
+ // GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316
+ , m_oss(rhs.m_oss.str(), ios_base::out | ios_base::ate)
+#endif
+ , m_color(std::move(rhs.m_color))
+ , m_log_level(std::move(rhs.m_log_level))
+ {
+ rhs.m_flush = false;
+ }
+
+ template<typename T>
+ message_writer& operator<<(const T& val)
+ {
+ m_oss << val;
+ return *this;
+ }
+
+ ~message_writer()
+ {
+ if (m_flush)
+ {
+ m_flush = false;
+
+ LOG_PRINT(m_oss.str(), m_log_level)
+
+ if (epee::log_space::console_color_default == m_color)
+ {
+ std::cout << m_oss.str();
+ }
+ else
+ {
+ epee::log_space::set_console_color(m_color, m_bright);
+ std::cout << m_oss.str();
+ epee::log_space::reset_console_color();
+ }
+ std::cout << std::endl;
+ }
+ }
+
+ private:
+ message_writer(message_writer& rhs);
+ message_writer& operator=(message_writer& rhs);
+ message_writer& operator=(message_writer&& rhs);
+
+ private:
+ bool m_flush;
+ std::stringstream m_oss;
+ epee::log_space::console_colors m_color;
+ bool m_bright;
+ int m_log_level;
+ };
+
+ message_writer success_msg_writer(bool color = false)
+ {
+ return message_writer(color ? epee::log_space::console_color_green : epee::log_space::console_color_default, false, std::string(), LOG_LEVEL_2);
}
- void print_fail_msg(const std::string& msg)
+ message_writer fail_msg_writer()
{
- LOG_PRINT_L1("Error:" << msg);
- epee::log_space::set_console_color(epee::log_space::console_color_red, true);
- std::cout << "Error: " << msg;
- epee::log_space::reset_console_color();
- std::cout << std::endl;
+ return message_writer(epee::log_space::console_color_red, true, "Error: ", LOG_LEVEL_0);
}
}
@@ -75,21 +158,22 @@ std::string simple_wallet::get_commands_str()
bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
- std::cout << get_commands_str();
+ success_msg_writer() << get_commands_str();
return true;
}
simple_wallet::simple_wallet()
: m_daemon_port(0)
+ , m_refresh_progress_reporter(*this)
{
- m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "Start mining in daemon, start_mining <threads_count>");
+ m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining <threads_count> - Start mining in daemon");
m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon");
m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance");
- m_cmd_binder.set_handler("show_balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance");
- m_cmd_binder.set_handler("show_incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "Show incoming transfers");
- m_cmd_binder.set_handler("show_bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height");
- m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer <mixin_count> {<addr> <amount>} Transfer <amount> to <address>. <mixin_count> is the number of transactions yours is indistinguishable from (from 0 to maximum available)");
- m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "Change current log detalization level, <level> is a number 0-4");
+ m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance");
+ m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability");
+ m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height");
+ m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer <mixin_count> <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of transactions yours is indistinguishable from (from 0 to maximum available)");
+ m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log <level> - Change current log detalization level, <level> is a number 0-4");
m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address");
m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data");
m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), "Show this help");
@@ -99,18 +183,18 @@ bool simple_wallet::set_log(const std::vector<std::string> &args)
{
if(args.size() != 1)
{
- std::cout << "use: set_log <log_level_number_0-4>" << ENDL;
+ fail_msg_writer() << "use: set_log <log_level_number_0-4>";
return true;
}
uint16_t l = 0;
if(!string_tools::get_xtype_from_string(l, args[0]))
{
- std::cout << "wrong number format, use: set_log <log_level_number_0-4>" << ENDL;
+ fail_msg_writer() << "wrong number format, use: set_log <log_level_number_0-4>";
return true;
}
if(LOG_LEVEL_4 < l)
{
- std::cout << "wrong number range, use: set_log <log_level_number_0-4>" << ENDL;
+ fail_msg_writer() << "wrong number range, use: set_log <log_level_number_0-4>";
return true;
}
@@ -122,19 +206,27 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
handle_command_line(vm);
- CHECK_AND_ASSERT_MES(m_daemon_address.empty() || (m_daemon_host.empty() && !m_daemon_port), false, "you can't specify daemon host or port several times");
+ if (!m_daemon_address.empty() && !m_daemon_host.empty() && 0 != m_daemon_port)
+ {
+ fail_msg_writer() << "you can't specify daemon host or port several times";
+ return false;
+ }
size_t c = 0;
if(!m_generate_new.empty()) ++c;
if(!m_wallet_file.empty()) ++c;
- CHECK_AND_ASSERT_MES(c == 1, false, "you must specify --wallet-file or --generate-new-wallet params");
+ if (1 != c)
+ {
+ fail_msg_writer() << "you must specify --wallet-file or --generate-new-wallet params";
+ return false;
+ }
if (m_daemon_host.empty())
m_daemon_host = "localhost";
if (!m_daemon_port)
m_daemon_port = RPC_DEFAULT_PORT;
if (m_daemon_address.empty())
- m_daemon_address = string("http://") + m_daemon_host + ":" + lexical_cast<string>(m_daemon_port);
+ m_daemon_address = std::string("http://") + m_daemon_host + ":" + std::to_string(m_daemon_port);
tools::password_container pwd_container;
if (command_line::has_arg(vm, arg_password))
@@ -144,7 +236,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
else
{
bool r = pwd_container.read_password();
- CHECK_AND_ASSERT_MES(r, false, "failed to read wallet password");
+ if (!r)
+ {
+ fail_msg_writer() << "failed to read wallet password";
+ return false;
+ }
}
if (!m_generate_new.empty())
@@ -169,25 +265,22 @@ bool simple_wallet::deinit()
return close_wallet();
}
//----------------------------------------------------------------------------------------------------
-bool simple_wallet::handle_command_line(const boost::program_options::variables_map& vm)
+void simple_wallet::handle_command_line(const boost::program_options::variables_map& vm)
{
m_wallet_file = command_line::get_arg(vm, arg_wallet_file);
m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet);
m_daemon_address = command_line::get_arg(vm, arg_daemon_address);
m_daemon_host = command_line::get_arg(vm, arg_daemon_host);
m_daemon_port = command_line::get_arg(vm, arg_daemon_port);
-
- return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::try_connect_to_daemon()
{
if (!m_wallet->check_connection())
{
- std::string msg = "wallet failed to connect to daemon (" + m_daemon_address + "). " +
- "Daemon either is not started or passed wrong port. " +
+ fail_msg_writer() << "wallet failed to connect to daemon (" << m_daemon_address << "). " <<
+ "Daemon either is not started or passed wrong port. " <<
"Please, make sure that daemon is running or restart the wallet with correct daemon address.";
- print_fail_msg(msg);
return false;
}
return true;
@@ -196,29 +289,31 @@ bool simple_wallet::try_connect_to_daemon()
bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password)
{
m_wallet_file = wallet_file;
- if(boost::filesystem::exists(wallet_file))
- {
- std::cout << "wallet creation failed, file " << wallet_file << " already exists" << std::endl;
- return false;
- }
m_wallet.reset(new tools::wallet2());
- bool r = m_wallet->generate(wallet_file, password);
- if(!r)
+ m_wallet->callback(this);
+ try
+ {
+ m_wallet->generate(wallet_file, password);
+ message_writer(epee::log_space::console_color_white, true) << "Generated new wallet: " << m_wallet->get_account().get_public_address_str();
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << "failed to generate new wallet: " << e.what();
return false;
+ }
- cout << "Generated new wallet" << ENDL;
- print_address(std::vector<std::string>());
- r = m_wallet->init(m_daemon_address);
- CHECK_AND_ASSERT_MES(r, false, "failed to init wallet");
- std::cout << "**********************************************************************" << ENDL
- << "Your wallet has been generated. " << ENDL
- << "To start synchronizing with the daemon use \"refresh\" command." << ENDL
- << "Use \"help\" command to see the list of available commands." << ENDL
- << "Always use \"exit\" command when closing simplewallet to save "
- << "current session's state. Otherwise, you will possibly need to synchronize " << ENDL
- << "your wallet again. Your wallet key is NOT under risk anyway." << ENDL
- << "**********************************************************************" << ENDL;
+ m_wallet->init(m_daemon_address);
+
+ success_msg_writer() <<
+ "**********************************************************************\n" <<
+ "Your wallet has been generated.\n" <<
+ "To start synchronizing with the daemon use \"refresh\" command.\n" <<
+ "Use \"help\" command to see the list of available commands.\n" <<
+ "Always use \"exit\" command when closing simplewallet to save\n" <<
+ "current session's state. Otherwise, you will possibly need to synchronize \n" <<
+ "your wallet again. Your wallet key is NOT under risk anyway.\n" <<
+ "**********************************************************************";
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -226,35 +321,63 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa
{
m_wallet_file = wallet_file;
m_wallet.reset(new tools::wallet2());
+ m_wallet->callback(this);
- bool r = m_wallet->load(m_wallet_file, password);
- CHECK_AND_ASSERT_MES(r, false, "failed to load wallet " + m_wallet_file);
- r = m_wallet->init(m_daemon_address);
- CHECK_AND_ASSERT_MES(r, false, "failed to init wallet");
+ try
+ {
+ m_wallet->load(m_wallet_file, password);
+ message_writer(epee::log_space::console_color_white, true) << "Opened wallet: " << m_wallet->get_account().get_public_address_str();
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << "failed to load wallet: " << e.what();
+ return false;
+ }
+
+ m_wallet->init(m_daemon_address);
refresh(std::vector<std::string>());
- std::cout << "**********************************************************************" << ENDL
- << "Use \"help\" command to see the list of available commands." << ENDL
- << "**********************************************************************" << ENDL ;
+ success_msg_writer() <<
+ "**********************************************************************\n" <<
+ "Use \"help\" command to see the list of available commands.\n" <<
+ "**********************************************************************";
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::close_wallet()
{
bool r = m_wallet->deinit();
- CHECK_AND_ASSERT_MES(r, false, "failed to deinit wallet");
- r = m_wallet->store();
- CHECK_AND_ASSERT_MES(r, false, "failed to store wallet " + m_wallet_file);
+ if (!r)
+ {
+ fail_msg_writer() << "failed to deinit wallet";
+ return false;
+ }
+
+ try
+ {
+ m_wallet->store();
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << e.what();
+ return false;
+ }
+
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::save(const std::vector<std::string> &args)
{
- bool r = m_wallet->store();
- if (r)
- print_success_msg("Wallet data saved");
- else
- print_fail_msg("failed to store wallet " + m_wallet_file);
+ try
+ {
+ m_wallet->store();
+ success_msg_writer() << "Wallet data saved";
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << e.what();
+ }
+
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -276,24 +399,24 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
bool ok = string_tools::get_xtype_from_string(num, args[0]);
if(!ok || 0 == num)
{
- print_fail_msg("wrong number of mining threads: \"" + args[0] + "\"");
+ fail_msg_writer() << "wrong number of mining threads: \"" << args[0] << "\"";
return true;
}
req.threads_count = num;
}
else
{
- print_fail_msg("wrong number of arguments, expected the number of mining threads");
+ fail_msg_writer() << "wrong number of arguments, expected the number of mining threads";
return true;
}
COMMAND_RPC_START_MINING::response res;
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/start_mining", req, res, m_http_client);
- std::string err = tools::interpret_rpc_response(r, res.status);
+ std::string err = interpret_rpc_response(r, res.status);
if (err.empty())
- print_success_msg("Mining started in daemon");
+ success_msg_writer() << "Mining started in daemon";
else
- print_fail_msg("mining has NOT been started: " + err);
+ fail_msg_writer() << "mining has NOT been started: " << err;
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -305,82 +428,158 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args)
COMMAND_RPC_STOP_MINING::request req;
COMMAND_RPC_STOP_MINING::response res;
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/stop_mining", req, res, m_http_client);
- std::string err = tools::interpret_rpc_response(r, res.status);
+ std::string err = interpret_rpc_response(r, res.status);
if (err.empty())
- print_success_msg("Mining stopped in daemon");
+ success_msg_writer() << "Mining stopped in daemon";
else
- print_fail_msg("mining has NOT been stopped: " + err);
+ fail_msg_writer() << "mining has NOT been stopped: " << err;
return true;
}
//----------------------------------------------------------------------------------------------------
+void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block)
+{
+ m_refresh_progress_reporter.update(height, false);
+}
+//----------------------------------------------------------------------------------------------------
+void simple_wallet::on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index)
+{
+ message_writer(epee::log_space::console_color_green, false) <<
+ "Height " << height <<
+ ", transaction " << get_transaction_hash(tx) <<
+ ", received " << print_money(tx.vout[out_index].amount);
+ m_refresh_progress_reporter.update(height, true);
+}
+//----------------------------------------------------------------------------------------------------
+void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx)
+{
+ message_writer(epee::log_space::console_color_magenta, false) <<
+ "Height " << height <<
+ ", transaction " << get_transaction_hash(spend_tx) <<
+ ", spent " << print_money(in_tx.vout[out_index].amount);
+ m_refresh_progress_reporter.update(height, true);
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::refresh(const std::vector<std::string>& args)
{
if (!try_connect_to_daemon())
return true;
- std::cout << "Starting refresh..." << endl;
- std::atomic<bool> refresh_is_done(false);
- std::thread th([&]()
- {
- epee::misc_utils::sleep_no_w(1000);
- while(!refresh_is_done)
- {
- std::string err;
- uint64_t bc_height = get_daemon_blockchain_height(err);
- if (err.empty())
- cout << "Height " << m_wallet->get_blockchain_current_height() << " of " << bc_height << endl;
- epee::misc_utils::sleep_no_w(1000);
- }
- });
- uint64_t initial_height = m_wallet->get_blockchain_current_height();
+ message_writer() << "Starting refresh...";
uint64_t fetched_blocks = 0;
- bool money_received = false;
- tools::wallet2::fail_details fd;
- bool ok = m_wallet->refresh(fetched_blocks, money_received, fd);
- refresh_is_done = true;
- th.join();
- if (ok)
+ bool ok = false;
+ std::ostringstream ss;
+ try
{
- std::stringstream ss;
- ss << "Refresh done, blocks received: " << fetched_blocks;
- print_success_msg(ss.str(), true);
-
+ m_wallet->refresh(fetched_blocks);
+ ok = true;
+ // Clear line "Height xxx of xxx"
+ std::cout << "\r \r";
+ success_msg_writer(true) << "Refresh done, blocks received: " << fetched_blocks;
show_balance();
}
- else
+ catch (const tools::error::daemon_busy&)
+ {
+ ss << "daemon is busy. Please try later";
+ }
+ catch (const tools::error::no_connection_to_daemon&)
+ {
+ ss << "no connection to daemon. Please, make sure daemon is running";
+ }
+ catch (const tools::error::wallet_rpc_error& e)
+ {
+ LOG_ERROR("Unknown RPC error: " << e.to_string());
+ ss << "RPC error \"" << e.what() << '"';
+ }
+ catch (const tools::error::refresh_error& e)
+ {
+ LOG_ERROR("refresh error: " << e.to_string());
+ ss << e.what();
+ }
+ catch (const tools::error::wallet_internal_error& e)
{
- fetched_blocks = m_wallet->get_blockchain_current_height() - initial_height;
- std::stringstream ss;
- ss << "refresh failed: " << fd.what() << ". Blocks received: " << fetched_blocks;
- print_fail_msg(ss.str());
+ LOG_ERROR("internal error: " << e.to_string());
+ ss << "internal error: " << e.what();
}
+ catch (const std::exception& e)
+ {
+ LOG_ERROR("unexpected error: " << e.what());
+ ss << "unexpected error: " << e.what();
+ }
+ catch (...)
+ {
+ LOG_ERROR("Unknown error");
+ ss << "unknown error";
+ }
+
+ if (!ok)
+ {
+ fail_msg_writer() << "refresh failed: " << ss.str() << ". Blocks received: " << fetched_blocks;
+ }
+
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::show_balance(const std::vector<std::string>& args/* = std::vector<std::string>()*/)
{
- std::stringstream ss;
- ss << "balance: " << print_money(m_wallet->balance()) << ", unlocked balance: " << print_money(m_wallet->unlocked_balance());
- print_success_msg(ss.str());
+ success_msg_writer() << "balance: " << print_money(m_wallet->balance()) << ", unlocked balance: " << print_money(m_wallet->unlocked_balance());
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args)
{
- std::cout << " amount \tspent\tglobal index\t tx id" << std::endl;
- bool ok = m_wallet->enum_incoming_transfers([](const cryptonote::transaction& tx, uint64_t global_out_index, uint64_t amount, bool spent) {
- epee::log_space::set_console_color(spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, true);
- std::cout << std::setw(21) << print_money(amount) << '\t'
- << std::setw(3) << (spent ? 'T' : 'F') << " \t"
- << std::setw(12) << global_out_index << '\t'
- << get_transaction_hash(tx)
- << '\n';
- });
- epee::log_space::reset_console_color();
- if (ok)
- std::cout.flush();
- else
- print_fail_msg("No incoming transfers");
+ bool filter = false;
+ bool available = false;
+ if (!args.empty())
+ {
+ if (args[0] == "available")
+ {
+ filter = true;
+ available = true;
+ }
+ else if (args[0] == "unavailable")
+ {
+ filter = true;
+ available = false;
+ }
+ }
+
+ tools::wallet2::transfer_container transfers;
+ m_wallet->get_transfers(transfers);
+
+ bool transfers_found = false;
+ for (const auto& td : transfers)
+ {
+ if (!filter || available != td.m_spent)
+ {
+ if (!transfers_found)
+ {
+ message_writer() << " amount \tspent\tglobal index\t tx id";
+ transfers_found = true;
+ }
+ message_writer(td.m_spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) <<
+ std::setw(21) << print_money(td.amount()) << '\t' <<
+ std::setw(3) << (td.m_spent ? 'T' : 'F') << " \t" <<
+ std::setw(12) << td.m_global_output_index << '\t' <<
+ get_transaction_hash(td.m_tx);
+ }
+ }
+
+ if (!transfers_found)
+ {
+ if (!filter)
+ {
+ success_msg_writer() << "No incoming transfers";
+ }
+ else if (available)
+ {
+ success_msg_writer() << "No incoming available transfers";
+ }
+ else
+ {
+ success_msg_writer() << "No incoming unavailable transfers";
+ }
+ }
+
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -389,7 +588,7 @@ uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err)
COMMAND_RPC_GET_HEIGHT::request req;
COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized<COMMAND_RPC_GET_HEIGHT::response>();
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client);
- err = tools::interpret_rpc_response(r, res.status);
+ err = interpret_rpc_response(r, res.status);
return res.height;
}
//----------------------------------------------------------------------------------------------------
@@ -401,9 +600,9 @@ bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
std::string err;
uint64_t bc_height = get_daemon_blockchain_height(err);
if (err.empty())
- print_success_msg(boost::lexical_cast<std::string>(bc_height));
+ success_msg_writer() << bc_height;
else
- print_fail_msg("failed to get blockchain height: " + err);
+ fail_msg_writer() << "failed to get blockchain height: " << err;
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -415,65 +614,132 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_)
std::vector<std::string> local_args = args_;
if(local_args.size() < 3)
{
- print_fail_msg("wrong number of arguments, expected at least 3, got " + boost::lexical_cast<std::string>(local_args.size()));
+ fail_msg_writer() << "wrong number of arguments, expected at least 3, got " << local_args.size();
return true;
}
size_t fake_outs_count;
if(!string_tools::get_xtype_from_string(fake_outs_count, local_args[0]))
{
- print_fail_msg("mixin_count should be non-negative integer, got " + local_args[0]);
+ fail_msg_writer() << "mixin_count should be non-negative integer, got " << local_args[0];
return true;
}
- local_args.erase(local_args.begin());
vector<cryptonote::tx_destination_entry> dsts;
- uint64_t summary_amount = 0;
- for (size_t i = 0; i < local_args.size(); i += 2)
+ for (size_t i = 1; i < local_args.size(); i += 2)
{
cryptonote::tx_destination_entry de;
if(!get_account_address_from_str(de.addr, local_args[i]))
{
- print_fail_msg("wrong address: " + local_args[i]);
+ fail_msg_writer() << "wrong address: " << local_args[i];
return true;
}
if (local_args.size() <= i + 1)
{
- print_fail_msg("amount for the last address " + local_args[i] + " is not specified");
+ fail_msg_writer() << "amount for the last address " << local_args[i] << " is not specified";
return true;
}
bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]);
if(!ok || 0 == de.amount)
{
- print_fail_msg("amount is wrong: " + local_args[i] + " " + local_args[i + 1]);
+ fail_msg_writer() << "amount is wrong: " << local_args[i] << ' ' << local_args[i + 1] <<
+ ", expected number from 0 to " << print_money(std::numeric_limits<uint64_t>::max());
return true;
}
- summary_amount += de.amount;
dsts.push_back(de);
}
- if(summary_amount > m_wallet->unlocked_balance())
+ try
{
- print_fail_msg("not enough money to transfer " + print_money(summary_amount) + ", available (unlocked) only " + print_money(m_wallet->unlocked_balance()));
- return true;
+ cryptonote::transaction tx;
+ m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE, tx);
+ success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx);
+ }
+ catch (const tools::error::daemon_busy&)
+ {
+ fail_msg_writer() << "daemon is busy. Please try later";
+ }
+ catch (const tools::error::no_connection_to_daemon&)
+ {
+ fail_msg_writer() << "no connection to daemon. Please, make sure daemon is running.";
+ }
+ catch (const tools::error::wallet_rpc_error& e)
+ {
+ LOG_ERROR("Unknown RPC error: " << e.to_string());
+ fail_msg_writer() << "RPC error \"" << e.what() << '"';
+ }
+ catch (const tools::error::get_random_outs_error&)
+ {
+ fail_msg_writer() << "failed to get random outputs to mix";
+ }
+ catch (const tools::error::not_enough_money& e)
+ {
+ fail_msg_writer() << "not enough money to transfer, available only " << print_money(e.available()) <<
+ ", transaction amount " << print_money(e.tx_amount() + e.fee()) << " = " << print_money(e.tx_amount()) <<
+ " + " << print_money(e.fee()) << " (fee)";
+ }
+ catch (const tools::error::not_enough_outs_to_mix& e)
+ {
+ auto writer = fail_msg_writer();
+ writer << "not enough outputs for specified mixin_count = " << e.mixin_count() << ":";
+ for (const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& outs_for_amount : e.scanty_outs())
+ {
+ writer << "\noutput amount = " << print_money(outs_for_amount.amount) << ", fount outputs to mix = " << outs_for_amount.outs.size();
+ }
+ }
+ catch (const tools::error::tx_not_constructed&)
+ {
+ fail_msg_writer() << "transaction was not constructed";
+ }
+ catch (const tools::error::tx_rejected& e)
+ {
+ fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " was rejected by daemon with status \"" << e.status() << '"';
+ }
+ catch (const tools::error::tx_sum_overflow& e)
+ {
+ fail_msg_writer() << e.what();
+ }
+ catch (const tools::error::tx_too_big& e)
+ {
+ cryptonote::transaction tx = e.tx();
+ fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " is too big. Transaction size: " <<
+ get_object_blobsize(e.tx()) << " bytes, transaction size limit: " << e.tx_size_limit() << " bytes";
+ }
+ catch (const tools::error::zero_destination&)
+ {
+ fail_msg_writer() << "one of destinations is zero";
+ }
+ catch (const tools::error::transfer_error& e)
+ {
+ LOG_ERROR("unknown transfer error: " << e.to_string());
+ fail_msg_writer() << "unknown transfer error: " << e.what();
+ }
+ catch (const tools::error::wallet_internal_error& e)
+ {
+ LOG_ERROR("internal error: " << e.to_string());
+ fail_msg_writer() << "internal error: " << e.what();
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR("unexpected error: " << e.what());
+ fail_msg_writer() << "unexpected error: " << e.what();
+ }
+ catch (...)
+ {
+ LOG_ERROR("Unknown error");
+ fail_msg_writer() << "unknown error";
}
- cryptonote::transaction tx;
- tools::wallet2::fail_details tfd;
- bool ok = m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE, tx, tfd);
- if (ok)
- print_success_msg("Money successfully sent", true);
- else
- print_fail_msg("failed to transfer money: " + tfd.what());
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::run()
{
- return m_cmd_binder.run_handling("[wallet]# ", "");
+ std::string addr_start = m_wallet->get_account().get_public_address_str().substr(0, 6);
+ return m_cmd_binder.run_handling("[wallet " + addr_start + "]: ", "");
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::stop()
@@ -482,9 +748,9 @@ void simple_wallet::stop()
m_wallet->stop();
}
//----------------------------------------------------------------------------------------------------
-bool simple_wallet::print_address(const std::vector<std::string> &args)
+bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
- print_success_msg(m_wallet->get_account().get_public_address_str());
+ success_msg_writer() << m_wallet->get_account().get_public_address_str();
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -495,7 +761,6 @@ bool simple_wallet::process_command(const std::vector<std::string> &args)
//----------------------------------------------------------------------------------------------------
int main(int argc, char* argv[])
{
-
#ifdef WIN32
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
@@ -517,6 +782,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_daemon_port);
command_line::add_arg(desc_params, arg_command);
command_line::add_arg(desc_params, arg_log_level);
+ tools::wallet_rpc_server::init_options(desc_params);
po::positional_options_description positional_options;
positional_options.add(arg_command.name, -1);
@@ -531,13 +797,13 @@ int main(int argc, char* argv[])
if (command_line::get_arg(vm, command_line::arg_help))
{
- std::cout << "Usage: simplewallet [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>]\n";
- std::cout << desc_all << '\n' << w.get_commands_str() << std::endl;
+ success_msg_writer() << "Usage: simplewallet [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>]";
+ success_msg_writer() << desc_all << '\n' << w.get_commands_str();
return false;
}
else if (command_line::get_arg(vm, command_line::arg_version))
{
- std::cout << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG << ENDL;
+ success_msg_writer() << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG;
return false;
}
@@ -551,33 +817,104 @@ int main(int argc, char* argv[])
//set up logging options
log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2);
- log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_0);
+ //log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_0);
log_space::log_singletone::add_logger(LOGGER_FILE,
log_space::log_singletone::get_default_log_file().c_str(),
log_space::log_singletone::get_default_log_folder().c_str(), LOG_LEVEL_4);
- LOG_PRINT_L0(CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG);
+ message_writer(epee::log_space::console_color_white, true) << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG;
if(command_line::has_arg(vm, arg_log_level))
{
LOG_PRINT_L0("Setting log level = " << command_line::get_arg(vm, arg_log_level));
log_space::get_set_log_detalisation_level(true, command_line::get_arg(vm, arg_log_level));
}
+
+ if(command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port))
+ {
+ log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2);
+ //runs wallet with rpc interface
+ if(!command_line::has_arg(vm, arg_wallet_file) )
+ {
+ LOG_ERROR("Wallet file not set.");
+ return 1;
+ }
+ if(!command_line::has_arg(vm, arg_daemon_address) )
+ {
+ LOG_ERROR("Daemon address not set.");
+ return 1;
+ }
+ if(!command_line::has_arg(vm, arg_password) )
+ {
+ LOG_ERROR("Wallet password not set.");
+ return 1;
+ }
- r = w.init(vm);
- CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet");
-
- std::vector<std::string> command = command_line::get_arg(vm, arg_command);
- if (!command.empty())
- w.process_command(command);
+ std::string wallet_file = command_line::get_arg(vm, arg_wallet_file);
+ std::string wallet_password = command_line::get_arg(vm, arg_password);
+ std::string daemon_address = command_line::get_arg(vm, arg_daemon_address);
+ std::string daemon_host = command_line::get_arg(vm, arg_daemon_host);
+ int daemon_port = command_line::get_arg(vm, arg_daemon_port);
+ if (daemon_host.empty())
+ daemon_host = "localhost";
+ if (!daemon_port)
+ daemon_port = RPC_DEFAULT_PORT;
+ if (daemon_address.empty())
+ daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
+
+ tools::wallet2 wal;
+ try
+ {
+ LOG_PRINT_L0("Loading wallet...");
+ wal.load(wallet_file, wallet_password);
+ wal.init(daemon_address);
+ wal.refresh();
+ LOG_PRINT_GREEN("Loaded ok", LOG_LEVEL_0);
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR("Wallet initialize failed: " << e.what());
+ return 1;
+ }
+ tools::wallet_rpc_server wrpc(wal);
+ bool r = wrpc.init(vm);
+ CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet rpc server");
+
+ tools::signal_handler::install([&wrpc, &wal] {
+ wrpc.send_stop_signal();
+ wal.store();
+ });
+ LOG_PRINT_L0("Starting wallet rpc server");
+ wrpc.run();
+ LOG_PRINT_L0("Stopped wallet rpc server");
+ try
+ {
+ LOG_PRINT_L0("Storing wallet...");
+ wal.store();
+ LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0);
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR("Failed to store wallet: " << e.what());
+ return 1;
+ }
+ }else
+ {
+ //runs wallet with console interface
+ r = w.init(vm);
+ CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet");
- tools::signal_handler::install([&w] {
- w.stop();
- });
- w.run();
+ std::vector<std::string> command = command_line::get_arg(vm, arg_command);
+ if (!command.empty())
+ w.process_command(command);
- w.deinit();
+ tools::signal_handler::install([&w] {
+ w.stop();
+ });
+ w.run();
+ w.deinit();
+ }
return 1;
//CATCH_ENTRY_L0("main", 1);
}
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index c448467f8..002490e89 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -20,7 +20,7 @@ namespace cryptonote
/************************************************************************/
/* */
/************************************************************************/
- class simple_wallet
+ class simple_wallet : public tools::i_wallet2_callback
{
public:
typedef std::vector<std::string> command_type;
@@ -35,7 +35,7 @@ namespace cryptonote
bool process_command(const std::vector<std::string> &args);
std::string get_commands_str();
private:
- bool handle_command_line(const boost::program_options::variables_map& vm);
+ void handle_command_line(const boost::program_options::variables_map& vm);
bool run_console_handler();
@@ -51,13 +51,72 @@ namespace cryptonote
bool show_incoming_transfers(const std::vector<std::string> &args);
bool show_blockchain_height(const std::vector<std::string> &args);
bool transfer(const std::vector<std::string> &args);
- bool print_address(const std::vector<std::string> &args);
+ bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
bool save(const std::vector<std::string> &args);
bool set_log(const std::vector<std::string> &args);
uint64_t get_daemon_blockchain_height(std::string& err);
bool try_connect_to_daemon();
+ //----------------- i_wallet2_callback ---------------------
+ virtual void on_new_block(uint64_t height, const cryptonote::block& block);
+ virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index);
+ virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx);
+ //----------------------------------------------------------
+
+ friend class refresh_progress_reporter_t;
+
+ class refresh_progress_reporter_t
+ {
+ public:
+ refresh_progress_reporter_t(cryptonote::simple_wallet& simple_wallet)
+ : m_simple_wallet(simple_wallet)
+ , m_blockchain_height(0)
+ , m_blockchain_height_update_time()
+ , m_print_time()
+ {
+ }
+
+ void update(uint64_t height, bool force = false)
+ {
+ auto current_time = std::chrono::system_clock::now();
+ if (std::chrono::seconds(DIFFICULTY_TARGET / 2) < current_time - m_blockchain_height_update_time || m_blockchain_height <= height)
+ {
+ update_blockchain_height();
+ m_blockchain_height = (std::max)(m_blockchain_height, height);
+ }
+
+ if (std::chrono::milliseconds(1) < current_time - m_print_time || force)
+ {
+ std::cout << "Height " << height << " of " << m_blockchain_height << '\r';
+ m_print_time = current_time;
+ }
+ }
+
+ private:
+ void update_blockchain_height()
+ {
+ std::string err;
+ uint64_t blockchain_height = m_simple_wallet.get_daemon_blockchain_height(err);
+ if (err.empty())
+ {
+ m_blockchain_height = blockchain_height;
+ m_blockchain_height_update_time = std::chrono::system_clock::now();
+ }
+ else
+ {
+ LOG_ERROR("Failed to get current blockchain height: " << err);
+ }
+ }
+
+ private:
+ cryptonote::simple_wallet& m_simple_wallet;
+ uint64_t m_blockchain_height;
+ std::chrono::system_clock::time_point m_blockchain_height_update_time;
+ std::chrono::system_clock::time_point m_print_time;
+ };
+
+ private:
std::string m_wallet_file;
std::string m_generate_new;
std::string m_import_path;
@@ -70,5 +129,6 @@ namespace cryptonote
std::unique_ptr<tools::wallet2> m_wallet;
net_utils::http::http_simple_client m_http_client;
+ refresh_progress_reporter_t m_refresh_progress_reporter;
};
}