diff --git a/CMakeLists.txt b/CMakeLists.txt
index 328e17646..357719ffd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -694,8 +694,8 @@ if(USE_READLINE)
   if(READLINE_FOUND AND GNU_READLINE_FOUND)
     add_definitions(-DHAVE_READLINE)
     include_directories(${Readline_INCLUDE_DIR})
-    list(APPEND EXTRA_LIBRARIES ${Readline_LIBRARY})
     message(STATUS "Found readline library at: ${Readline_ROOT_DIR}")
+    set(EPEE_READLINE epee_readline)
   else()
     message(STATUS "Could not find GNU readline library so building without readline support")
   endif()
diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt
index 3b41f415e..bd6714791 100644
--- a/contrib/epee/src/CMakeLists.txt
+++ b/contrib/epee/src/CMakeLists.txt
@@ -26,10 +26,9 @@
 # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp)
 if (USE_READLINE AND GNU_READLINE_FOUND)
-  add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp readline_buffer.cpp)
-else()
-  add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp)
+  add_library(epee_readline STATIC readline_buffer.cpp)
 endif()
 
 # Build and install libepee if we're building for GUI
@@ -41,6 +40,10 @@ if (BUILD_GUI_DEPS)
     endif()
     install(TARGETS epee
         ARCHIVE DESTINATION ${lib_folder})
+    if (USE_READLINE AND GNU_READLINE_FOUND)
+      install(TARGETS epee_readline
+          ARCHIVE DESTINATION ${lib_folder})
+    endif()
 endif()
 
 target_link_libraries(epee
@@ -51,3 +54,11 @@ target_link_libraries(epee
   PRIVATE
     ${OPENSSL_LIBRARIES}
     ${EXTRA_LIBRARIES})
+
+if (USE_READLINE AND GNU_READLINE_FOUND)
+  target_link_libraries(epee_readline
+    PUBLIC
+      easylogging
+    PRIVATE
+      ${Readline_LIBRARY})
+endif()
diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp
index 60672eeda..78bb51ab6 100644
--- a/src/blockchain_utilities/blockchain_export.cpp
+++ b/src/blockchain_utilities/blockchain_export.cpp
@@ -30,6 +30,7 @@
 #include "blocksdat_file.h"
 #include "common/command_line.h"
 #include "cryptonote_core/tx_pool.h"
+#include "cryptonote_core/cryptonote_core.h"
 #include "blockchain_db/blockchain_db.h"
 #include "blockchain_db/db_types.h"
 #include "version.h"
@@ -66,21 +67,16 @@ int main(int argc, char* argv[])
   const command_line::arg_descriptor<std::string> arg_output_file = {"output-file", "Specify output file", "", true};
   const command_line::arg_descriptor<std::string> arg_log_level  = {"log-level",  "0-4 or categories", ""};
   const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop};
-  const command_line::arg_descriptor<bool>     arg_testnet_on = {
-    "testnet"
-      , "Run on testnet."
-      , false
-  };
   const command_line::arg_descriptor<std::string> arg_database = {
     "database", available_dbs.c_str(), default_db_type
   };
   const command_line::arg_descriptor<bool> arg_blocks_dat = {"blocksdat", "Output in blocks.dat format", blocks_dat};
 
 
-  command_line::add_arg(desc_cmd_sett, command_line::arg_data_dir, default_data_path.string());
-  command_line::add_arg(desc_cmd_sett, command_line::arg_testnet_data_dir, default_testnet_data_path.string());
+  command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir, default_data_path.string());
+  command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_data_dir, default_testnet_data_path.string());
   command_line::add_arg(desc_cmd_sett, arg_output_file);
-  command_line::add_arg(desc_cmd_sett, arg_testnet_on);
+  command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
   command_line::add_arg(desc_cmd_sett, arg_log_level);
   command_line::add_arg(desc_cmd_sett, arg_database);
   command_line::add_arg(desc_cmd_sett, arg_block_stop);
@@ -117,12 +113,12 @@ int main(int argc, char* argv[])
 
   LOG_PRINT_L0("Starting...");
 
-  bool opt_testnet = command_line::get_arg(vm, arg_testnet_on);
+  bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
   bool opt_blocks_dat = command_line::get_arg(vm, arg_blocks_dat);
 
   std::string m_config_folder;
 
-  auto data_dir_arg = opt_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir;
+  auto data_dir_arg = opt_testnet ? cryptonote::arg_testnet_data_dir : cryptonote::arg_data_dir;
   m_config_folder = command_line::get_arg(vm, data_dir_arg);
 
   std::string db_type = command_line::get_arg(vm, arg_database);
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index 2153383bb..a50b0bad6 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -585,11 +585,6 @@ int main(int argc, char* argv[])
   const command_line::arg_descriptor<uint64_t> arg_batch_size  = {"batch-size", "", db_batch_size};
   const command_line::arg_descriptor<uint64_t> arg_pop_blocks  = {"pop-blocks", "Remove blocks from end of blockchain", num_blocks};
   const command_line::arg_descriptor<bool>        arg_drop_hf  = {"drop-hard-fork", "Drop hard fork subdbs", false};
-  const command_line::arg_descriptor<bool>     arg_testnet_on  = {
-    "testnet"
-      , "Run on testnet."
-      , false
-  };
   const command_line::arg_descriptor<bool>     arg_count_blocks = {
     "count-blocks"
       , "Count blocks in bootstrap file and exit"
@@ -674,8 +669,8 @@ int main(int argc, char* argv[])
     }
   }
 
-  opt_testnet = command_line::get_arg(vm, arg_testnet_on);
-  auto data_dir_arg = opt_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir;
+  opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
+  auto data_dir_arg = opt_testnet ? cryptonote::arg_testnet_data_dir : cryptonote::arg_data_dir;
   m_config_folder = command_line::get_arg(vm, data_dir_arg);
   db_arg_str = command_line::get_arg(vm, arg_database);
 
diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp
index d4a28fc85..4b9ca9559 100644
--- a/src/common/command_line.cpp
+++ b/src/common/command_line.cpp
@@ -36,10 +36,6 @@
 #include "cryptonote_config.h"
 #include "string_tools.h"
 
-#ifdef HAVE_READLINE
-  #include "readline_buffer.h"
-#endif
-
 namespace command_line
 {
   namespace
@@ -50,20 +46,6 @@ namespace command_line
     }
   }
 
-  std::string input_line(const std::string& prompt)
-  {
-#ifdef HAVE_READLINE
-    rdln::suspend_readline pause_readline;
-#endif
-    std::cout << prompt;
-
-    std::string buf;
-    std::getline(std::cin, buf);
-
-    return epee::string_tools::trim(buf);
-
-  }
-
   bool is_yes(const std::string& str)
   {
     if (str == "y" || str == "Y")
@@ -94,49 +76,4 @@ namespace command_line
 
   const arg_descriptor<bool> arg_help = {"help", "Produce help message"};
   const arg_descriptor<bool> arg_version = {"version", "Output version information"};
-  const arg_descriptor<std::string> arg_data_dir = {"data-dir", "Specify data directory"};
-  const arg_descriptor<std::string> arg_testnet_data_dir = {"testnet-data-dir", "Specify testnet data directory"};
-  const arg_descriptor<bool>		arg_test_drop_download  		= {"test-drop-download", "For net tests: in download, discard ALL blocks instead checking/saving them (very fast)"};
-  const arg_descriptor<uint64_t>	arg_test_drop_download_height  	= {"test-drop-download-height", "Like test-drop-download but disards only after around certain height", 0};
-  const arg_descriptor<int> 		arg_test_dbg_lock_sleep = {"test-dbg-lock-sleep", "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests."};
-  const arg_descriptor<bool, false> arg_testnet_on  = {
-    "testnet"
-  , "Run on testnet. The wallet must be launched with --testnet flag."
-  , false
-  };
-  const arg_descriptor<bool> arg_dns_checkpoints  = {
-    "enforce-dns-checkpointing"
-  , "checkpoints from DNS server will be enforced"
-  , false
-  };
-  const command_line::arg_descriptor<uint64_t> arg_fast_block_sync = {
-    "fast-block-sync"
-  , "Sync up most of the way by using embedded, known block hashes."
-  , 1
-  };
-  const command_line::arg_descriptor<uint64_t> arg_prep_blocks_threads = {
-    "prep-blocks-threads"
-  , "Max number of threads to use when preparing block hashes in groups."
-  , 4
-  };
-  const command_line::arg_descriptor<uint64_t> arg_show_time_stats  = {
-    "show-time-stats"
-  , "Show time-stats when processing blocks/txs and disk synchronization."
-  , 0
-  };
-  const command_line::arg_descriptor<size_t> arg_block_sync_size  = {
-    "block-sync-size"
-  , "How many blocks to sync at once during chain synchronization (0 = adaptive)."
-  , 0
-  };
-  const command_line::arg_descriptor<std::string> arg_check_updates = {
-    "check-updates"
-  , "Check for new versions of monero: [disabled|notify|download|update]"
-  , "notify"
-  };
-  const arg_descriptor<bool> arg_fluffy_blocks  = {
-    "fluffy-blocks"
-  , "Relay blocks as fluffy blocks where possible (automatic on testnet)"
-  , false
-  };
 }
diff --git a/src/common/command_line.h b/src/common/command_line.h
index bfc8b19c6..c2bac9cc8 100644
--- a/src/common/command_line.h
+++ b/src/common/command_line.h
@@ -41,8 +41,6 @@
 namespace command_line
 {
 
-  std::string input_line(const std::string& prompt);
-
   //! \return True if `str` is `is_iequal("y" || "yes" || `tr("yes"))`.
   bool is_yes(const std::string& str);
   //! \return True if `str` is `is_iequal("n" || "no" || `tr("no"))`.
@@ -213,17 +211,4 @@ namespace command_line
 
   extern const arg_descriptor<bool> arg_help;
   extern const arg_descriptor<bool> arg_version;
-  extern const arg_descriptor<std::string> arg_data_dir;
-  extern const arg_descriptor<std::string> arg_testnet_data_dir;
-  extern const arg_descriptor<bool>		arg_test_drop_download;
-  extern const arg_descriptor<uint64_t>	arg_test_drop_download_height;
-  extern const arg_descriptor<int> 		arg_test_dbg_lock_sleep;
-  extern const arg_descriptor<bool, false> arg_testnet_on;
-  extern const arg_descriptor<bool> arg_dns_checkpoints;
-  extern const arg_descriptor<uint64_t> arg_fast_block_sync;
-  extern const arg_descriptor<uint64_t> arg_prep_blocks_threads;
-  extern const arg_descriptor<uint64_t> arg_show_time_stats;
-  extern const arg_descriptor<size_t> arg_block_sync_size;
-  extern const arg_descriptor<std::string> arg_check_updates;
-  extern const arg_descriptor<bool> arg_fluffy_blocks;
 }
diff --git a/src/common/password.cpp b/src/common/password.cpp
index b84d6fb2e..5d56464a5 100644
--- a/src/common/password.cpp
+++ b/src/common/password.cpp
@@ -238,9 +238,6 @@ namespace tools
 
   boost::optional<password_container> password_container::prompt(const bool verify, const char *message)
   {
-#ifdef HAVE_READLINE
-    rdln::suspend_readline pause_readline;
-#endif
     password_container pass1{};
     password_container pass2{};
     if (is_cin_tty() ? read_from_tty(verify, message, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password))
@@ -249,7 +246,7 @@ namespace tools
     return boost::none;
   }
 
-  boost::optional<login> login::parse(std::string&& userpass, bool verify, const char* message)
+  boost::optional<login> login::parse(std::string&& userpass, bool verify, const std::function<boost::optional<password_container>(bool)> &prompt)
   {
     login out{};
     password_container wipe{std::move(userpass)};
@@ -257,7 +254,7 @@ namespace tools
     const auto loc = wipe.password().find(':');
     if (loc == std::string::npos)
     {
-      auto result = tools::password_container::prompt(verify, message);
+      auto result = prompt(verify);
       if (!result)
         return boost::none;
 
diff --git a/src/common/password.h b/src/common/password.h
index 12f715df4..ba1c30a28 100644
--- a/src/common/password.h
+++ b/src/common/password.h
@@ -82,7 +82,7 @@ namespace tools
        \return The username and password, or boost::none if
          `password_container::prompt` fails.
      */
-    static boost::optional<login> parse(std::string&& userpass, bool verify, const char* message = "Password");
+    static boost::optional<login> parse(std::string&& userpass, bool verify, const std::function<boost::optional<password_container>(bool)> &prompt);
 
     login(const login&) = delete;
     login(login&&) = default;
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index a10692dad..acc76a8d6 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -38,6 +38,7 @@ using namespace epee;
 #include "common/updates.h"
 #include "common/download.h"
 #include "common/threadpool.h"
+#include "common/command_line.h"
 #include "warnings.h"
 #include "crypto/crypto.h"
 #include "cryptonote_config.h"
@@ -61,6 +62,69 @@ DISABLE_VS_WARNINGS(4355)
 
 namespace cryptonote
 {
+  const command_line::arg_descriptor<std::string> arg_data_dir = {
+    "data-dir"
+  , "Specify data directory"
+  };
+  const command_line::arg_descriptor<std::string> arg_testnet_data_dir = {
+    "testnet-data-dir"
+  , "Specify testnet data directory"
+  };
+  const command_line::arg_descriptor<bool, false> arg_testnet_on  = {
+    "testnet"
+  , "Run on testnet. The wallet must be launched with --testnet flag."
+  , false
+  };
+
+  static const command_line::arg_descriptor<bool> arg_test_drop_download = {
+    "test-drop-download"
+  , "For net tests: in download, discard ALL blocks instead checking/saving them (very fast)"
+  };
+  static const command_line::arg_descriptor<uint64_t> arg_test_drop_download_height = {
+    "test-drop-download-height"
+  , "Like test-drop-download but disards only after around certain height"
+  , 0
+  };
+  static const command_line::arg_descriptor<int> arg_test_dbg_lock_sleep = {
+    "test-dbg-lock-sleep"
+  , "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests."
+  , 0
+  };
+  static const command_line::arg_descriptor<bool> arg_dns_checkpoints  = {
+    "enforce-dns-checkpointing"
+  , "checkpoints from DNS server will be enforced"
+  , false
+  };
+  static const command_line::arg_descriptor<uint64_t> arg_fast_block_sync = {
+    "fast-block-sync"
+  , "Sync up most of the way by using embedded, known block hashes."
+  , 1
+  };
+  static const command_line::arg_descriptor<uint64_t> arg_prep_blocks_threads = {
+    "prep-blocks-threads"
+  , "Max number of threads to use when preparing block hashes in groups."
+  , 4
+  };
+  static const command_line::arg_descriptor<uint64_t> arg_show_time_stats  = {
+    "show-time-stats"
+  , "Show time-stats when processing blocks/txs and disk synchronization."
+  , 0
+  };
+  static const command_line::arg_descriptor<size_t> arg_block_sync_size  = {
+    "block-sync-size"
+  , "How many blocks to sync at once during chain synchronization (0 = adaptive)."
+  , 0
+  };
+  static const command_line::arg_descriptor<std::string> arg_check_updates = {
+    "check-updates"
+  , "Check for new versions of monero: [disabled|notify|download|update]"
+  , "notify"
+  };
+  static const command_line::arg_descriptor<bool> arg_fluffy_blocks  = {
+    "fluffy-blocks"
+  , "Relay blocks as fluffy blocks where possible (automatic on testnet)"
+  , false
+  };
 
   //-----------------------------------------------------------------------------------------------
   core::core(i_cryptonote_protocol* pprotocol):
@@ -148,20 +212,21 @@ namespace cryptonote
   //-----------------------------------------------------------------------------------
   void core::init_options(boost::program_options::options_description& desc)
   {
-    command_line::add_arg(desc, command_line::arg_data_dir, tools::get_default_data_dir());
-    command_line::add_arg(desc, command_line::arg_testnet_data_dir, (boost::filesystem::path(tools::get_default_data_dir()) / "testnet").string());
+    command_line::add_arg(desc, arg_data_dir, tools::get_default_data_dir());
+    command_line::add_arg(desc, arg_testnet_data_dir, (boost::filesystem::path(tools::get_default_data_dir()) / "testnet").string());
 
-    command_line::add_arg(desc, command_line::arg_test_drop_download);
-    command_line::add_arg(desc, command_line::arg_test_drop_download_height);
+    command_line::add_arg(desc, arg_test_drop_download);
+    command_line::add_arg(desc, arg_test_drop_download_height);
 
-    command_line::add_arg(desc, command_line::arg_testnet_on);
-    command_line::add_arg(desc, command_line::arg_dns_checkpoints);
-    command_line::add_arg(desc, command_line::arg_prep_blocks_threads);
-    command_line::add_arg(desc, command_line::arg_fast_block_sync);
-    command_line::add_arg(desc, command_line::arg_show_time_stats);
-    command_line::add_arg(desc, command_line::arg_block_sync_size);
-    command_line::add_arg(desc, command_line::arg_check_updates);
-    command_line::add_arg(desc, command_line::arg_fluffy_blocks);
+    command_line::add_arg(desc, arg_testnet_on);
+    command_line::add_arg(desc, arg_dns_checkpoints);
+    command_line::add_arg(desc, arg_prep_blocks_threads);
+    command_line::add_arg(desc, arg_fast_block_sync);
+    command_line::add_arg(desc, arg_show_time_stats);
+    command_line::add_arg(desc, arg_block_sync_size);
+    command_line::add_arg(desc, arg_check_updates);
+    command_line::add_arg(desc, arg_fluffy_blocks);
+    command_line::add_arg(desc, arg_test_dbg_lock_sleep);
 
     // we now also need some of net_node's options (p2p bind arg, for separate data dir)
     command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false);
@@ -173,9 +238,9 @@ namespace cryptonote
   //-----------------------------------------------------------------------------------------------
   bool core::handle_command_line(const boost::program_options::variables_map& vm)
   {
-    m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
+    m_testnet = command_line::get_arg(vm, arg_testnet_on);
 
-    auto data_dir_arg = m_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir;
+    auto data_dir_arg = m_testnet ? arg_testnet_data_dir : arg_data_dir;
     m_config_folder = command_line::get_arg(vm, data_dir_arg);
 
     auto data_dir = boost::filesystem::path(m_config_folder);
@@ -196,13 +261,15 @@ namespace cryptonote
     }
 
 
-    set_enforce_dns_checkpoints(command_line::get_arg(vm, command_line::arg_dns_checkpoints));
-    test_drop_download_height(command_line::get_arg(vm, command_line::arg_test_drop_download_height));
-    m_fluffy_blocks_enabled = m_testnet || get_arg(vm, command_line::arg_fluffy_blocks);
+    set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints));
+    test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height));
+    m_fluffy_blocks_enabled = m_testnet || get_arg(vm, arg_fluffy_blocks);
 
-    if (command_line::get_arg(vm, command_line::arg_test_drop_download) == true)
+    if (command_line::get_arg(vm, arg_test_drop_download) == true)
       test_drop_download();
 
+    epee::debug::g_test_dbg_lock_sleep() = command_line::get_arg(vm, arg_test_dbg_lock_sleep);
+
     return true;
   }
   //-----------------------------------------------------------------------------------------------
@@ -268,7 +335,7 @@ namespace cryptonote
 
     m_fakechain = test_options != NULL;
     bool r = handle_command_line(vm);
-    bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
+    bool testnet = command_line::get_arg(vm, arg_testnet_on);
     auto p2p_bind_arg = testnet ? nodetool::arg_testnet_p2p_bind_port : nodetool::arg_p2p_bind_port;
     std::string m_port = command_line::get_arg(vm, p2p_bind_arg);
     std::string m_config_folder_mempool = m_config_folder;
@@ -281,9 +348,9 @@ namespace cryptonote
     std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type);
     std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode);
     bool db_salvage = command_line::get_arg(vm, cryptonote::arg_db_salvage) != 0;
-    bool fast_sync = command_line::get_arg(vm, command_line::arg_fast_block_sync) != 0;
-    uint64_t blocks_threads = command_line::get_arg(vm, command_line::arg_prep_blocks_threads);
-    std::string check_updates_string = command_line::get_arg(vm, command_line::arg_check_updates);
+    bool fast_sync = command_line::get_arg(vm, arg_fast_block_sync) != 0;
+    uint64_t blocks_threads = command_line::get_arg(vm, arg_prep_blocks_threads);
+    std::string check_updates_string = command_line::get_arg(vm, arg_check_updates);
 
     boost::filesystem::path folder(m_config_folder);
     if (m_fakechain)
@@ -409,11 +476,11 @@ namespace cryptonote
     // transactions in the pool that do not conform to the current fork
     m_mempool.validate(m_blockchain_storage.get_current_hard_fork_version());
 
-    bool show_time_stats = command_line::get_arg(vm, command_line::arg_show_time_stats) != 0;
+    bool show_time_stats = command_line::get_arg(vm, arg_show_time_stats) != 0;
     m_blockchain_storage.set_show_time_stats(show_time_stats);
     CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage");
 
-    block_sync_size = command_line::get_arg(vm, command_line::arg_block_sync_size);
+    block_sync_size = command_line::get_arg(vm, arg_block_sync_size);
 
     MGINFO("Loading checkpoints");
 
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index a3d47280a..dc014206d 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -41,6 +41,7 @@
 #include "storages/portable_storage_template_helper.h"
 #include "common/download.h"
 #include "common/threadpool.h"
+#include "common/command_line.h"
 #include "tx_pool.h"
 #include "blockchain.h"
 #include "cryptonote_basic/miner.h"
@@ -58,6 +59,10 @@ namespace cryptonote
      const std::pair<uint8_t, uint64_t> *hard_forks;
    };
 
+  extern const command_line::arg_descriptor<std::string> arg_data_dir;
+  extern const command_line::arg_descriptor<std::string> arg_testnet_data_dir;
+  extern const command_line::arg_descriptor<bool, false> arg_testnet_on;
+
   /************************************************************************/
   /*                                                                      */
   /************************************************************************/
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index d0fc1d846..2f9c2b2f9 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -94,6 +94,8 @@ target_link_libraries(daemon
     daemonizer
     serialization
     daemon_rpc_server
+    epee
+    ${EPEE_READLINE}
     version
     ${Boost_CHRONO_LIBRARY}
     ${Boost_FILESYSTEM_LIBRARY}
@@ -102,6 +104,7 @@ target_link_libraries(daemon
     ${Boost_SYSTEM_LIBRARY}
     ${CMAKE_THREAD_LIBS_INIT}
     ${ZMQ_LIB}
+    ${Readline_LIBRARY}
     ${EXTRA_LIBRARIES})
 set_property(TARGET daemon
   PROPERTY
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index faa620c54..cf7d5f8ab 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -88,7 +88,7 @@ t_daemon::t_daemon(
   )
   : mp_internals{new t_internals{vm}}
 {
-  bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
+  bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
   if (testnet)
   {
     zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_testnet_rpc_bind_port);
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index 5d548f410..ae83943b6 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -82,7 +82,6 @@ int main(int argc, char const * argv[])
       command_line::add_arg(visible_options, daemon_args::arg_os_version);
       bf::path default_conf = default_data_dir / std::string(CRYPTONOTE_NAME ".conf");
       command_line::add_arg(visible_options, daemon_args::arg_config_file, default_conf.string());
-      command_line::add_arg(visible_options, command_line::arg_test_dbg_lock_sleep);
 
       // Settings
       bf::path default_log = default_data_dir / std::string(CRYPTONOTE_NAME ".log");
@@ -144,8 +143,6 @@ int main(int argc, char const * argv[])
       return 0;
     }
 
-    epee::debug::g_test_dbg_lock_sleep() = command_line::get_arg(vm, command_line::arg_test_dbg_lock_sleep);
-
     std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type);
 
     // verify that blockchaindb type is valid
@@ -156,9 +153,9 @@ int main(int argc, char const * argv[])
       return 0;
     }
 
-    bool testnet_mode = command_line::get_arg(vm, command_line::arg_testnet_on);
+    bool testnet_mode = command_line::get_arg(vm, cryptonote::arg_testnet_on);
 
-    auto data_dir_arg = testnet_mode ? command_line::arg_testnet_data_dir : command_line::arg_data_dir;
+    auto data_dir_arg = testnet_mode ? cryptonote::arg_testnet_data_dir : cryptonote::arg_data_dir;
 
     // data_dir
     //   default: e.g. ~/.bitmonero/ or ~/.bitmonero/testnet
@@ -250,7 +247,12 @@ int main(int argc, char const * argv[])
         if (command_line::has_arg(vm, arg.rpc_login))
         {
           login = tools::login::parse(
-            command_line::get_arg(vm, arg.rpc_login), false, "Daemon client password"
+            command_line::get_arg(vm, arg.rpc_login), false, [](bool verify) {
+#ifdef HAVE_READLINE
+        rdln::suspend_readline pause_readline;
+#endif
+              return tools::password_container::prompt(verify, "Daemon client password");
+            }
           );
           if (!login)
           {
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index ee3ff160a..f64b29c1f 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -48,6 +48,7 @@
 #include "net/local_ip.h"
 #include "crypto/crypto.h"
 #include "storages/levin_abstract_invoke2.h"
+#include "cryptonote_core/cryptonote_core.h"
 
 // We have to look for miniupnpc headers in different places, dependent on if its compiled or external
 #ifdef UPNP_STATIC
@@ -434,7 +435,7 @@ namespace nodetool
   bool node_server<t_payload_net_handler>::init(const boost::program_options::variables_map& vm)
   {
     std::set<std::string> full_addrs;
-    m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
+    m_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
 
     if (m_testnet)
     {
@@ -535,7 +536,7 @@ namespace nodetool
     bool res = handle_command_line(vm);
     CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line");
 
-    auto config_arg = m_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir;
+    auto config_arg = m_testnet ? cryptonote::arg_testnet_data_dir : cryptonote::arg_data_dir;
     m_config_folder = command_line::get_arg(vm, config_arg);
 
     if ((!m_testnet && m_port != std::to_string(::config::P2P_DEFAULT_PORT))
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index ba30a11c2..595aaec0f 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -85,7 +85,7 @@ namespace cryptonote
       const boost::program_options::variables_map& vm
     )
   {
-    m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
+    m_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
     m_net_server.set_threads_prefix("RPC");
 
     auto p2p_bind_arg = m_testnet ? arg_testnet_rpc_bind_port : arg_rpc_bind_port;
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index 93309bf3c..e03c5472d 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -83,7 +83,9 @@ namespace cryptonote
 
     if (command_line::has_arg(vm, arg.rpc_login))
     {
-      config.login = tools::login::parse(command_line::get_arg(vm, arg.rpc_login), true, "RPC server password");
+      config.login = tools::login::parse(command_line::get_arg(vm, arg.rpc_login), true, [](bool verify) {
+        return tools::password_container::prompt(verify, "RPC server password");
+      });
       if (!config.login)
         return boost::none;
 
diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt
index 4ecda12d0..3ff4466fc 100644
--- a/src/simplewallet/CMakeLists.txt
+++ b/src/simplewallet/CMakeLists.txt
@@ -48,11 +48,14 @@ target_link_libraries(simplewallet
     cncrypto
     common
     mnemonics
+    epee
+    ${EPEE_READLINE}
     version
     ${Boost_CHRONO_LIBRARY}
     ${Boost_PROGRAM_OPTIONS_LIBRARY}
     ${Boost_FILESYSTEM_LIBRARY}
     ${Boost_THREAD_LIBRARY}
+    ${Readline_LIBRARY}
     ${CMAKE_THREAD_LIBS_INIT}
     ${EXTRA_LIBRARIES})
 set_property(TARGET simplewallet
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index d09067fe3..b274675d1 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -125,6 +125,37 @@ namespace
 
   const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""};
 
+  std::string input_line(const std::string& prompt)
+  {
+#ifdef HAVE_READLINE
+    rdln::suspend_readline pause_readline;
+#endif
+    std::cout << prompt;
+
+    std::string buf;
+    std::getline(std::cin, buf);
+
+    return epee::string_tools::trim(buf);
+  }
+
+  boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify)
+  {
+#ifdef HAVE_READLINE
+    rdln::suspend_readline pause_readline;
+#endif
+    auto pwd_container = tools::password_container::prompt(verify, prompt);
+    if (!pwd_container)
+    {
+      tools::fail_msg_writer() << tr("failed to read wallet password");
+    }
+    return pwd_container;
+  }
+
+  boost::optional<tools::password_container> default_password_prompter(bool verify)
+  {
+    return password_prompter(verify ? tr("Enter new wallet password") : tr("Wallet password"), verify);
+  }
+
   inline std::string interpret_rpc_response(bool ok, const std::string& status)
   {
     std::string err;
@@ -270,7 +301,7 @@ namespace
            << tr("Is this OK? (Y/n) ")
     ;
     // prompt the user for confirmation given the dns query and dnssec status
-    std::string confirm_dns_ok = command_line::input_line(prompt.str());
+    std::string confirm_dns_ok = input_line(prompt.str());
     if (std::cin.eof())
     {
       return {};
@@ -448,7 +479,7 @@ bool simple_wallet::change_password(const std::vector<std::string> &args)
   }
 
   // prompts for a new password, pass true to verify the password
-  const auto pwd_container = tools::wallet2::password_prompt(true);
+  const auto pwd_container = default_password_prompter(true);
 
   try
   {
@@ -1020,7 +1051,7 @@ bool simple_wallet::ask_wallet_create_if_needed()
 
   do{
       LOG_PRINT_L3("User asked to specify wallet file name.");
-      wallet_path = command_line::input_line(
+      wallet_path = input_line(
         tr(m_restoring ? "Specify a new wallet file name for your restored wallet (e.g., MyWallet).\n"
         "Wallet file name (or Ctrl-C to quit): " :
         "Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n"
@@ -1071,7 +1102,7 @@ bool simple_wallet::ask_wallet_create_if_needed()
           if (!m_restoring)
           {
             message_writer() << tr("No wallet found with that name. Confirm creation of new wallet named: ") << wallet_path;
-            confirm_creation = command_line::input_line(tr("(Y/Yes/N/No): "));
+            confirm_creation = input_line(tr("(Y/Yes/N/No): "));
             if(std::cin.eof())
             {
               LOG_ERROR("Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()");
@@ -1155,7 +1186,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
         do
         {
           const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: ";
-          std::string electrum_seed = command_line::input_line(prompt);
+          std::string electrum_seed = input_line(prompt);
           if (std::cin.eof())
             return false;
           if (electrum_seed.empty())
@@ -1184,7 +1215,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
     {
       m_wallet_file = m_generate_from_view_key;
       // parse address
-      std::string address_string = command_line::input_line("Standard address: ");
+      std::string address_string = input_line("Standard address: ");
       if (std::cin.eof())
         return false;
       if (address_string.empty()) {
@@ -1204,7 +1235,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
       }
 
       // parse view secret key
-      std::string viewkey_string = command_line::input_line("View key: ");
+      std::string viewkey_string = input_line("View key: ");
       if (std::cin.eof())
         return false;
       if (viewkey_string.empty()) {
@@ -1258,7 +1289,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
     {
       m_wallet_file = m_generate_from_keys;
       // parse address
-      std::string address_string = command_line::input_line("Standard address: ");
+      std::string address_string = input_line("Standard address: ");
       if (std::cin.eof())
         return false;
       if (address_string.empty()) {
@@ -1278,7 +1309,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
       }
 
       // parse spend secret key
-      std::string spendkey_string = command_line::input_line("Secret spend key: ");
+      std::string spendkey_string = input_line("Secret spend key: ");
       if (std::cin.eof())
         return false;
       if (spendkey_string.empty()) {
@@ -1294,7 +1325,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
       crypto::secret_key spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
 
       // parse view secret key
-      std::string viewkey_string = command_line::input_line("Secret view key: ");
+      std::string viewkey_string = input_line("Secret view key: ");
       if (std::cin.eof())
         return false;
       if (viewkey_string.empty()) {
@@ -1341,7 +1372,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
       unsigned int multisig_n;
       
       // parse multisig type
-      std::string multisig_type_string = command_line::input_line("Multisig type (input as M/N with M <= N and M > 1): ");
+      std::string multisig_type_string = input_line("Multisig type (input as M/N with M <= N and M > 1): ");
       if (std::cin.eof())
         return false;
       if (multisig_type_string.empty())
@@ -1367,7 +1398,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
       message_writer() << boost::format(tr("Generating master wallet from %u of %u multisig wallet keys")) % multisig_m % multisig_n;
       
       // parse multisig address
-      std::string address_string = command_line::input_line("Multisig wallet address: ");
+      std::string address_string = input_line("Multisig wallet address: ");
       if (std::cin.eof())
         return false;
       if (address_string.empty()) {
@@ -1382,7 +1413,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
       }
       
       // parse secret view key
-      std::string viewkey_string = command_line::input_line("Secret view key: ");
+      std::string viewkey_string = input_line("Secret view key: ");
       if (std::cin.eof())
         return false;
       if (viewkey_string.empty())
@@ -1422,7 +1453,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
         // get N secret spend keys from user
         for(unsigned int i=0; i<multisig_n; ++i)
         {
-          spendkey_string = command_line::input_line(tr((boost::format(tr("Secret spend key (%u of %u):")) % (i+i) % multisig_m).str().c_str()));
+          spendkey_string = input_line(tr((boost::format(tr("Secret spend key (%u of %u):")) % (i+i) % multisig_m).str().c_str()));
           if (std::cin.eof())
             return false;
           if (spendkey_string.empty())
@@ -1470,7 +1501,15 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
     else if (!m_generate_from_json.empty())
     {
       m_wallet_file = m_generate_from_json;
-      m_wallet = tools::wallet2::make_from_json(vm, m_wallet_file);
+      try
+      {
+        m_wallet = tools::wallet2::make_from_json(vm, m_wallet_file, password_prompter);
+      }
+      catch (const std::exception &e)
+      {
+        fail_msg_writer() << e.what();
+        return false;
+      }
       if (!m_wallet)
         return false;
     }
@@ -1492,9 +1531,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
       {
         std::string heightstr;
         if (!connected || version < MAKE_CORE_RPC_VERSION(1, 6))
-          heightstr = command_line::input_line("Restore from specific blockchain height (optional, default 0): ");
+          heightstr = input_line("Restore from specific blockchain height (optional, default 0): ");
         else
-          heightstr = command_line::input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD): ");
+          heightstr = input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD): ");
         if (std::cin.eof())
           return false;
         if (heightstr.empty())
@@ -1530,7 +1569,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
             day   = boost::lexical_cast<uint16_t>(heightstr.substr(8,2));
             m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day);
             success_msg_writer() << tr("Restore height is: ") << m_restore_height;
-            std::string confirm = command_line::input_line(tr("Is this okay?  (Y/Yes/N/No): "));
+            std::string confirm = input_line(tr("Is this okay?  (Y/Yes/N/No): "));
             if (std::cin.eof())
               return false;
             if(command_line::is_yes(confirm))
@@ -1661,7 +1700,7 @@ std::string simple_wallet::get_mnemonic_language()
   }
   while (language_number < 0)
   {
-    language_choice = command_line::input_line(tr("Enter the number corresponding to the language of your choice: "));
+    language_choice = input_line(tr("Enter the number corresponding to the language of your choice: "));
     if (std::cin.eof())
       return std::string();
     try
@@ -1683,7 +1722,7 @@ std::string simple_wallet::get_mnemonic_language()
 //----------------------------------------------------------------------------------------------------
 boost::optional<tools::password_container> simple_wallet::get_and_verify_password() const
 {
-  auto pwd_container = tools::wallet2::password_prompt(m_wallet_file.empty());
+  auto pwd_container = default_password_prompter(m_wallet_file.empty());
   if (!pwd_container)
     return boost::none;
 
@@ -1698,7 +1737,7 @@ boost::optional<tools::password_container> simple_wallet::get_and_verify_passwor
 bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
   const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language)
 {
-  auto rc = tools::wallet2::make_new(vm);
+  auto rc = tools::wallet2::make_new(vm, password_prompter);
   m_wallet = std::move(rc.first);
   if (!m_wallet)
   {
@@ -1779,7 +1818,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
   const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey,
   const crypto::secret_key& viewkey)
 {
-  auto rc = tools::wallet2::make_new(vm);
+  auto rc = tools::wallet2::make_new(vm, password_prompter);
   m_wallet = std::move(rc.first);
   if (!m_wallet)
   {
@@ -1821,7 +1860,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
   std::string password;
   try
   {
-    auto rc = tools::wallet2::make_from_file(vm, m_wallet_file);
+    auto rc = tools::wallet2::make_from_file(vm, m_wallet_file, password_prompter);
     m_wallet = std::move(rc.first);
     password = std::move(rc.second).password();
     if (!m_wallet)
@@ -2806,7 +2845,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
   // prompt is there is no payment id and confirmation is required
   if (!payment_id_seen && m_wallet->confirm_missing_payment_id())
   {
-     std::string accepted = command_line::input_line(tr("No payment id is included with this transaction. Is this okay?  (Y/Yes/N/No): "));
+     std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay?  (Y/Yes/N/No): "));
      if (std::cin.eof())
        return true;
      if (!command_line::is_yes(accepted))
@@ -2890,7 +2929,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
       std::string prompt_str = prompt.str();
       if (!prompt_str.empty())
       {
-        std::string accepted = command_line::input_line(prompt_str);
+        std::string accepted = input_line(prompt_str);
         if (std::cin.eof())
           return true;
         if (!command_line::is_yes(accepted))
@@ -2961,7 +3000,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
         }
         prompt << ENDL << tr("Is this okay?  (Y/Yes/N/No): ");
         
-        std::string accepted = command_line::input_line(prompt.str());
+        std::string accepted = input_line(prompt.str());
         if (std::cin.eof())
           return true;
         if (!command_line::is_yes(accepted))
@@ -3058,7 +3097,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
         print_money(total_unmixable) %
         print_money(total_fee)).str();
     }
-    std::string accepted = command_line::input_line(prompt_str);
+    std::string accepted = input_line(prompt_str);
     if (std::cin.eof())
       return true;
     if (!command_line::is_yes(accepted))
@@ -3219,7 +3258,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
   // prompt is there is no payment id and confirmation is required
   if (!payment_id_seen && m_wallet->confirm_missing_payment_id())
   {
-     std::string accepted = command_line::input_line(tr("No payment id is included with this transaction. Is this okay?  (Y/Yes/N/No): "));
+     std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay?  (Y/Yes/N/No): "));
      if (std::cin.eof())
        return true;
      if (!command_line::is_yes(accepted))
@@ -3277,7 +3316,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
         print_money(total_sent) %
         print_money(total_fee);
     }
-    std::string accepted = command_line::input_line(prompt.str());
+    std::string accepted = input_line(prompt.str());
     if (std::cin.eof())
       return true;
     if (!command_line::is_yes(accepted))
@@ -3490,7 +3529,7 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
 
   uint64_t fee = amount - amount_to_dests;
   std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay? (Y/Yes/N/No): ")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_ring_size % payment_id_string % extra_message).str();
-  return command_line::is_yes(command_line::input_line(prompt_str));
+  return command_line::is_yes(input_line(prompt_str));
 }
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
@@ -5434,6 +5473,7 @@ int main(int argc, char* argv[])
    "monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]",
     desc_params,
     positional_options,
+    [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; },
     "monero-wallet-cli.log"
   );
 
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index 4c00a4d51..7e061f480 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -108,6 +108,7 @@ if (NOT BUILD_GUI_DEPS)
     PRIVATE
       wallet
       epee
+      ${EPEE_READLINE}
       rpc
       cryptonote_core
       cncrypto
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 50d07536c..9ed8e7cc3 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -137,7 +137,7 @@ uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, ui
   return calculate_fee(fee_per_kb, blob.size(), fee_multiplier);
 }
 
-std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts)
+std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
 {
   const bool testnet = command_line::get_arg(vm, opts.testnet);
   const bool restricted = command_line::get_arg(vm, opts.restricted);
@@ -146,17 +146,16 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
   auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
   auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
 
-  if (!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port)
-  {
-    tools::fail_msg_writer() << tools::wallet2::tr("can't specify daemon host or port more than once");
-    return nullptr;
-  }
+  THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
+      tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
 
   boost::optional<epee::net_utils::http::login> login{};
   if (command_line::has_arg(vm, opts.daemon_login))
   {
     auto parsed = tools::login::parse(
-      command_line::get_arg(vm, opts.daemon_login), false, "Daemon client password"
+      command_line::get_arg(vm, opts.daemon_login), false, [password_prompter](bool verify) {
+        return password_prompter("Daemon client password", verify);
+      }
     );
     if (!parsed)
       return nullptr;
@@ -180,12 +179,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
   return wallet;
 }
 
-boost::optional<tools::password_container> get_password(const boost::program_options::variables_map& vm, const options& opts, const bool verify)
+boost::optional<tools::password_container> get_password(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char*, bool)> &password_prompter, const bool verify)
 {
   if (command_line::has_arg(vm, opts.password) && command_line::has_arg(vm, opts.password_file))
   {
-    tools::fail_msg_writer() << tools::wallet2::tr("can't specify more than one of --password and --password-file");
-    return boost::none;
+    THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("can't specify more than one of --password and --password-file"));
   }
 
   if (command_line::has_arg(vm, opts.password))
@@ -198,21 +196,17 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
     std::string password;
     bool r = epee::file_io_utils::load_file_to_string(command_line::get_arg(vm, opts.password_file),
                                                       password);
-    if (!r)
-    {
-      tools::fail_msg_writer() << tools::wallet2::tr("the password file specified could not be read");
-      return boost::none;
-    }
+    THROW_WALLET_EXCEPTION_IF(!r, tools::error::wallet_internal_error, tools::wallet2::tr("the password file specified could not be read"));
 
     // Remove line breaks the user might have inserted
     boost::trim_right_if(password, boost::is_any_of("\r\n"));
     return {tools::password_container{std::move(password)}};
   }
 
-  return tools::wallet2::password_prompt(verify);
+  return password_prompter(verify ? tr("Enter new wallet password") : tr("Wallet password"), verify);
 }
 
-std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts)
+std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
 {
   const bool testnet = command_line::get_arg(vm, opts.testnet);
 
@@ -223,22 +217,20 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
   const auto do_generate = [&]() -> bool {
     std::string buf;
     if (!epee::file_io_utils::load_file_to_string(json_file, buf)) {
-      tools::fail_msg_writer() << tools::wallet2::tr("Failed to load file ") << json_file;
+      THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("Failed to load file ")) + json_file);
       return false;
     }
 
     rapidjson::Document json;
     if (json.Parse(buf.c_str()).HasParseError()) {
-      tools::fail_msg_writer() << tools::wallet2::tr("Failed to parse JSON");
+      THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Failed to parse JSON"));
       return false;
     }
 
     GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, version, unsigned, Uint, true, 0);
     const int current_version = 1;
-    if (field_version > current_version) {
-      tools::fail_msg_writer() << boost::format(tools::wallet2::tr("Version %u too new, we can only grok up to %u")) % field_version % current_version;
-      return false;
-    }
+    THROW_WALLET_EXCEPTION_IF(field_version > current_version, tools::error::wallet_internal_error,
+      ((boost::format(tools::wallet2::tr("Version %u too new, we can only grok up to %u")) % field_version % current_version)).str());
 
     GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true, std::string());
 
@@ -254,14 +246,12 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
       cryptonote::blobdata viewkey_data;
       if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
       {
-        tools::fail_msg_writer() << tools::wallet2::tr("failed to parse view key secret key");
-        return false;
+        THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to parse view key secret key"));
       }
       viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
       crypto::public_key pkey;
       if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
-        tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key");
-        return false;
+        THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
       }
     }
 
@@ -272,14 +262,12 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
       cryptonote::blobdata spendkey_data;
       if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
       {
-        tools::fail_msg_writer() << tools::wallet2::tr("failed to parse spend key secret key");
-        return false;
+        THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to parse spend key secret key"));
       }
       spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
       crypto::public_key pkey;
       if (!crypto::secret_key_to_public_key(spendkey, pkey)) {
-        tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key");
-        return false;
+        THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
       }
     }
 
@@ -291,8 +279,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
     {
       if (!crypto::ElectrumWords::words_to_bytes(field_seed, recovery_key, old_language))
       {
-        tools::fail_msg_writer() << tools::wallet2::tr("Electrum-style word list failed verification");
-        return false;
+        THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Electrum-style word list failed verification"));
       }
       restore_deterministic_wallet = true;
 
@@ -309,13 +296,11 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
     // compatibility checks
     if (!field_seed_found && !field_viewkey_found && !field_spendkey_found)
     {
-      tools::fail_msg_writer() << tools::wallet2::tr("At least one of Electrum-style word list and private view key and private spend key must be specified");
-      return false;
+      THROW_WALLET_EXCEPTION(tools::wallet2::tr("At least one of Electrum-style word list and private view key and private spend key must be specified"));
     }
     if (field_seed_found && (field_viewkey_found || field_spendkey_found))
     {
-      tools::fail_msg_writer() << tools::wallet2::tr("Both Electrum-style word list and private key(s) specified");
-      return false;
+      THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Both Electrum-style word list and private key(s) specified"));
     }
 
     // if an address was given, we check keys against it, and deduce the spend
@@ -325,43 +310,36 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
       cryptonote::address_parse_info info;
       if(!get_account_address_from_str(info, testnet, field_address))
       {
-        tools::fail_msg_writer() << tools::wallet2::tr("invalid address");
-        return false;
+        THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("invalid address"));
       }
       if (field_viewkey_found)
       {
         crypto::public_key pkey;
         if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
-          tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key");
-          return false;
+          THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
         }
         if (info.address.m_view_public_key != pkey) {
-          tools::fail_msg_writer() << tools::wallet2::tr("view key does not match standard address");
-          return false;
+          THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view key does not match standard address"));
         }
       }
       if (field_spendkey_found)
       {
         crypto::public_key pkey;
         if (!crypto::secret_key_to_public_key(spendkey, pkey)) {
-          tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key");
-          return false;
+          THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
         }
         if (info.address.m_spend_public_key != pkey) {
-          tools::fail_msg_writer() << tools::wallet2::tr("spend key does not match standard address");
-          return false;
+          THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend key does not match standard address"));
         }
       }
     }
 
     const bool deprecated_wallet = restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) ||
       crypto::ElectrumWords::get_is_old_style_seed(field_seed));
-    if (deprecated_wallet) {
-      tools::fail_msg_writer() << tools::wallet2::tr("Cannot create deprecated wallets from JSON");
-      return false;
-    }
+    THROW_WALLET_EXCEPTION_IF(deprecated_wallet, tools::error::wallet_internal_error,
+      tools::wallet2::tr("Cannot create deprecated wallets from JSON"));
 
-    wallet.reset(make_basic(vm, opts).release());
+    wallet.reset(make_basic(vm, opts, password_prompter).release());
     wallet->set_refresh_from_block_height(field_scan_from_height);
 
     try
@@ -378,8 +356,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
       {
         cryptonote::account_public_address address;
         if (!crypto::secret_key_to_public_key(viewkey, address.m_view_public_key)) {
-          tools::fail_msg_writer() << tools::wallet2::tr("failed to verify view key secret key");
-          return false;
+          THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
         }
 
         if (field_spendkey.empty())
@@ -391,8 +368,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
             cryptonote::address_parse_info info;
             if(!get_account_address_from_str(info, testnet, field_address))
             {
-              tools::fail_msg_writer() << tools::wallet2::tr("failed to parse address: ") << field_address;
-              return false;
+              THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to parse address: ")) + field_address);
             }
             address.m_spend_public_key = info.address.m_spend_public_key;
           }
@@ -406,8 +382,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
         else
         {
           if (!crypto::secret_key_to_public_key(spendkey, address.m_spend_public_key)) {
-            tools::fail_msg_writer() << tools::wallet2::tr("failed to verify spend key secret key");
-            return false;
+            THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
           }
           wallet->generate(field_filename, field_password, address, spendkey, viewkey);
         }
@@ -415,8 +390,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
     }
     catch (const std::exception& e)
     {
-      tools::fail_msg_writer() << tools::wallet2::tr("failed to generate new wallet: ") << e.what();
-      return false;
+      THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, std::string(tools::wallet2::tr("failed to generate new wallet: ")) + e.what());
     }
     return true;
   };
@@ -498,34 +472,22 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
   command_line::add_arg(desc_params, opts.restricted);
 }
 
-boost::optional<password_container> wallet2::password_prompt(const bool new_password)
-{
-  auto pwd_container = tools::password_container::prompt(
-    new_password, (new_password ? tr("Enter new wallet password") : tr("Wallet password"))
-  );
-  if (!pwd_container)
-  {
-    tools::fail_msg_writer() << tr("failed to read wallet password");
-  }
-  return pwd_container;
-}
-
-std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file)
+std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
 {
   const options opts{};
-  return generate_from_json(json_file, vm, opts);
+  return generate_from_json(json_file, vm, opts, password_prompter);
 }
 
 std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
-  const boost::program_options::variables_map& vm, const std::string& wallet_file)
+  const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
 {
   const options opts{};
-  auto pwd = get_password(vm, opts, false);
+  auto pwd = get_password(vm, opts, password_prompter, false);
   if (!pwd)
   {
     return {nullptr, password_container{}};
   }
-  auto wallet = make_basic(vm, opts);
+  auto wallet = make_basic(vm, opts, password_prompter);
   if (wallet)
   {
     wallet->load(wallet_file, pwd->password());
@@ -533,21 +495,21 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
   return {std::move(wallet), std::move(*pwd)};
 }
 
-std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm)
+std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
 {
   const options opts{};
-  auto pwd = get_password(vm, opts, true);
+  auto pwd = get_password(vm, opts, password_prompter, true);
   if (!pwd)
   {
     return {nullptr, password_container{}};
   }
-  return {make_basic(vm, opts), std::move(*pwd)};
+  return {make_basic(vm, opts, password_prompter), std::move(*pwd)};
 }
 
-std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm)
+std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
 {
   const options opts{};
-  return make_basic(vm, opts);
+  return make_basic(vm, opts, password_prompter);
 }
 
 //----------------------------------------------------------------------------------------------------
@@ -6525,16 +6487,12 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
   std::string data;
   bool r = epee::file_io_utils::load_file_to_string(filename, data);
 
-  if (!r)
-  {
-    fail_msg_writer() << tr("failed to read file ") << filename;
-    return 0;
-  }
+  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename);
+
   const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC);
   if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen))
   {
-    fail_msg_writer() << "Bad key image export file magic in " << filename;
-    return 0;
+    THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename);
   }
 
   try
@@ -6543,31 +6501,22 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
   }
   catch (const std::exception &e)
   {
-    fail_msg_writer() << "Failed to decrypt " << filename << ": " << e.what();
-    return 0;
+    THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what());
   }
 
   const size_t headerlen = 2 * sizeof(crypto::public_key);
-  if (data.size() < headerlen)
-  {
-    fail_msg_writer() << "Bad data size from file " << filename;
-    return 0;
-  }
+  THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename);
   const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0];
   const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)];
   const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
   if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
   {
-    fail_msg_writer() << "Key images from " << filename << " are for a different account";
-    return 0;
+    THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account");
   }
 
   const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature);
-  if ((data.size() - headerlen) % record_size)
-  {
-    fail_msg_writer() << "Bad data size from file " << filename;
-    return 0;
-  }
+  THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size,
+      error::wallet_internal_error, std::string("Bad data size from file ") + filename);
   size_t nki = (data.size() - headerlen) / record_size;
 
   std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 8576227e8..b07295253 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -155,21 +155,18 @@ namespace tools
     static bool has_testnet_option(const boost::program_options::variables_map& vm);
     static void init_options(boost::program_options::options_description& desc_params);
 
-    //! \return Password retrieved from prompt. Logs error on failure.
-    static boost::optional<password_container> password_prompt(const bool new_password);
-
     //! Uses stdin and stdout. Returns a wallet2 if no errors.
-    static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file);
+    static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
 
     //! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors.
     static std::pair<std::unique_ptr<wallet2>, password_container>
-      make_from_file(const boost::program_options::variables_map& vm, const std::string& wallet_file);
+      make_from_file(const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
 
     //! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors.
-    static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm);
+    static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
 
     //! Just parses variables.
-    static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm);
+    static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
 
     static bool verify_password(const std::string& keys_file_name, const std::string& password, bool watch_only);
 
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index df01ec238..cc6bb1de2 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -30,7 +30,6 @@
 #include <boost/filesystem/path.hpp>
 #include <boost/format.hpp>
 #include "common/i18n.h"
-#include "common/scoped_message_writer.h"
 #include "common/util.h"
 #include "misc_log_ex.h"
 #include "string_tools.h"
@@ -50,6 +49,20 @@
 #define DEFAULT_MAX_CONCURRENCY 0
 #endif
 
+namespace
+{
+  class Print
+  {
+  public:
+    Print(const std::function<void(const std::string&, bool)> &p, bool em = false): print(p), emphasis(em) {}
+    ~Print() { print(ss.str(), emphasis); }
+    template<typename T> std::ostream &operator<<(const T &t) { ss << t; return ss; }
+  private:
+    const std::function<void(const std::string&, bool)> &print;
+    std::stringstream ss;
+    bool emphasis;
+  };
+}
 
 namespace wallet_args
 {
@@ -73,6 +86,7 @@ namespace wallet_args
     const char* const usage,
     boost::program_options::options_description desc_params,
     const boost::program_options::positional_options_description& positional_options,
+    const std::function<void(const std::string&, bool)> &print,
     const char *default_log_name,
     bool log_to_console)
   
@@ -118,16 +132,16 @@ namespace wallet_args
 
       if (command_line::get_arg(vm, command_line::arg_help))
       {
-        tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
-        tools::msg_writer() << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n"
+        Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
+        Print(print) << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n"
 												  "daemon to work correctly.") << ENDL;
-        tools::msg_writer() << wallet_args::tr("Usage:") << ENDL << "  " << usage;
-        tools::msg_writer() << desc_all;
+        Print(print) << wallet_args::tr("Usage:") << ENDL << "  " << usage;
+        Print(print) << desc_all;
         return false;
       }
       else if (command_line::get_arg(vm, command_line::arg_version))
       {
-        tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
+        Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
         return false;
       }
 
@@ -142,7 +156,7 @@ namespace wallet_args
         }
         else
         {
-          tools::fail_msg_writer() << wallet_args::tr("Can't find config file ") << config;
+          MERROR(wallet_args::tr("Can't find config file ") << config);
           return false;
         }
       }
@@ -167,14 +181,15 @@ namespace wallet_args
     if(command_line::has_arg(vm, arg_max_concurrency))
       tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency));
 
-    tools::scoped_message_writer(epee::console_color_white, true) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
+    Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
 
     if (!command_line::is_arg_defaulted(vm, arg_log_level))
       MINFO("Setting log level = " << command_line::get_arg(vm, arg_log_level));
     else
       MINFO("Setting log levels = " << getenv("MONERO_LOGS"));
     MINFO(wallet_args::tr("Logging to: ") << log_path);
-    tools::scoped_message_writer(epee::console_color_white, true) << boost::format(wallet_args::tr("Logging to %s")) % log_path;
+
+    Print(print) << boost::format(wallet_args::tr("Logging to %s")) % log_path;
 
     return {std::move(vm)};
   }
diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h
index cf23ffded..8974098ad 100644
--- a/src/wallet/wallet_args.h
+++ b/src/wallet/wallet_args.h
@@ -50,5 +50,6 @@ namespace wallet_args
     const char* const usage,
     boost::program_options::options_description desc_params,
     const boost::program_options::positional_options_description& positional_options,
+    const std::function<void(const std::string&, bool)> &print,
     const char *default_log_name, bool log_to_console = false);
 }
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 9d66f125e..41eb77451 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -765,6 +765,12 @@ namespace tools
 #define STRINGIZE_DETAIL(x) #x
 #define STRINGIZE(x) STRINGIZE_DETAIL(x)
 
+#define THROW_WALLET_EXCEPTION(err_type, ...)                                                               \
+  do {                                                                                                      \
+    LOG_ERROR("THROW EXCEPTION: " << #err_type);                                                 \
+    tools::error::throw_wallet_ex<err_type>(std::string(__FILE__ ":" STRINGIZE(__LINE__)), ## __VA_ARGS__); \
+  } while(0)
+
 #define THROW_WALLET_EXCEPTION_IF(cond, err_type, ...)                                                      \
   if (cond)                                                                                                 \
   {                                                                                                         \
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index fda8f244a..173cc7bbe 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -38,6 +38,7 @@ using namespace epee;
 #include "wallet/wallet_args.h"
 #include "common/command_line.h"
 #include "common/i18n.h"
+#include "common/scoped_message_writer.h"
 #include "cryptonote_basic/cryptonote_format_utils.h"
 #include "cryptonote_basic/account.h"
 #include "wallet_rpc_server_commands_defs.h"
@@ -60,6 +61,16 @@ namespace
   const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
 
   constexpr const char default_rpc_username[] = "monero";
+
+  boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify)
+  {
+    auto pwd_container = tools::password_container::prompt(verify, prompt);
+    if (!pwd_container)
+    {
+      MERROR("failed to read wallet password");
+    }
+    return pwd_container;
+  }
 }
 
 namespace tools
@@ -131,7 +142,7 @@ namespace tools
       walvars = m_wallet;
     else
     {
-      tmpwal = tools::wallet2::make_dummy(*m_vm);
+      tmpwal = tools::wallet2::make_dummy(*m_vm, password_prompter);
       walvars = tmpwal.get();
     }
     boost::optional<epee::net_utils::http::login> http_login{};
@@ -1798,7 +1809,7 @@ namespace tools
       command_line::add_arg(desc, arg_password);
       po::store(po::parse_command_line(argc, argv, desc), vm2);
     }
-    std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2).first;
+    std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, password_prompter).first;
     if (!wal)
     {
       er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -1872,7 +1883,7 @@ namespace tools
     }
     std::unique_ptr<tools::wallet2> wal = nullptr;
     try {
-      wal = tools::wallet2::make_from_file(vm2, wallet_file).first;
+      wal = tools::wallet2::make_from_file(vm2, wallet_file, password_prompter).first;
     }
     catch (const std::exception& e)
     {
@@ -1971,6 +1982,7 @@ int main(int argc, char** argv) {
     "monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]",
     desc_params,
     po::positional_options_description(),
+    [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; },
     "monero-wallet-rpc.log",
     true
   );
@@ -2007,11 +2019,19 @@ int main(int argc, char** argv) {
     LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
     if(!wallet_file.empty())
     {
-      wal = tools::wallet2::make_from_file(*vm, wallet_file).first;
+      wal = tools::wallet2::make_from_file(*vm, wallet_file, password_prompter).first;
     }
     else
     {
-      wal = tools::wallet2::make_from_json(*vm, from_json);
+      try
+      {
+        wal = tools::wallet2::make_from_json(*vm, from_json, password_prompter);
+      }
+      catch (const std::exception &e)
+      {
+        MERROR("Error creating wallet: " << e.what());
+        return 1;
+      }
     }
     if (!wal)
     {
diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp
index 2c8f92d1f..fd07a5d2e 100644
--- a/tests/core_proxy/core_proxy.cpp
+++ b/tests/core_proxy/core_proxy.cpp
@@ -79,7 +79,7 @@ int main(int argc, char* argv[])
 
   po::options_description desc("Allowed options");
   // tools::get_default_data_dir() can't be called during static initialization
-  command_line::add_arg(desc, command_line::arg_data_dir, tools::get_default_data_dir());
+  command_line::add_arg(desc, cryptonote::arg_data_dir, tools::get_default_data_dir());
   nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<tests::proxy_core> >::init_options(desc);
 
   po::variables_map vm;
diff --git a/tests/fuzz/block.cpp b/tests/fuzz/block.cpp
index 099e8e3bd..03751fc14 100644
--- a/tests/fuzz/block.cpp
+++ b/tests/fuzz/block.cpp
@@ -27,7 +27,6 @@
 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "include_base_utils.h"
-#include "common/command_line.h"
 #include "file_io_utils.h"
 #include "cryptonote_protocol/blobdatatype.h"
 #include "cryptonote_basic/cryptonote_basic.h"
diff --git a/tests/fuzz/cold-transaction.cpp b/tests/fuzz/cold-transaction.cpp
index c35d604a8..25a80fc68 100644
--- a/tests/fuzz/cold-transaction.cpp
+++ b/tests/fuzz/cold-transaction.cpp
@@ -27,7 +27,6 @@
 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "include_base_utils.h"
-#include "common/command_line.h"
 #include "file_io_utils.h"
 #include "cryptonote_protocol/blobdatatype.h"
 #include "cryptonote_basic/cryptonote_basic.h"
diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp
index 071297ba8..42a8fa997 100644
--- a/tests/fuzz/signature.cpp
+++ b/tests/fuzz/signature.cpp
@@ -27,7 +27,6 @@
 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "include_base_utils.h"
-#include "common/command_line.h"
 #include "file_io_utils.h"
 #include "cryptonote_protocol/blobdatatype.h"
 #include "cryptonote_basic/cryptonote_basic.h"
diff --git a/tests/fuzz/transaction.cpp b/tests/fuzz/transaction.cpp
index 21cd01bef..9e2b9f2cb 100644
--- a/tests/fuzz/transaction.cpp
+++ b/tests/fuzz/transaction.cpp
@@ -27,7 +27,6 @@
 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "include_base_utils.h"
-#include "common/command_line.h"
 #include "file_io_utils.h"
 #include "cryptonote_protocol/blobdatatype.h"
 #include "cryptonote_basic/cryptonote_basic.h"
diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp
index 76f2b2749..86e7bcef3 100644
--- a/tests/unit_tests/main.cpp
+++ b/tests/unit_tests/main.cpp
@@ -53,7 +53,7 @@ int main(int argc, char** argv)
 
   po::options_description desc_options("Command line options");
   const command_line::arg_descriptor<std::string> arg_data_dir = {"data-dir", "Data files directory", "", true};
-  command_line::add_arg(desc_options, command_line::arg_data_dir, "");
+  command_line::add_arg(desc_options, arg_data_dir, "");
 
   po::variables_map vm;
   bool r = command_line::handle_error_helper(desc_options, [&]()