diff --git a/src/SimpleWallet/SimpleWallet.cpp b/src/SimpleWallet/SimpleWallet.cpp index e9c627dfed..714d5fe0c2 100644 --- a/src/SimpleWallet/SimpleWallet.cpp +++ b/src/SimpleWallet/SimpleWallet.cpp @@ -469,6 +469,7 @@ simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::C m_consoleHandler.setHandler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [] - Start mining in daemon"); m_consoleHandler.setHandler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon"); //m_consoleHandler.setHandler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); + m_consoleHandler.setHandler("export_keys", boost::bind(&simple_wallet::export_keys, this, _1), "Show the private spend and view keys of the opened wallet"); m_consoleHandler.setHandler("balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance"); m_consoleHandler.setHandler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "Show incoming transfers"); m_consoleHandler.setHandler("list_transfers", boost::bind(&simple_wallet::listTransfers, this, _1), "Show all known transfers"); @@ -516,13 +517,13 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { } if (m_generate_new.empty() && m_wallet_file_arg.empty()) { - std::cout << "Nor 'generate-new-wallet' neither 'wallet-file' argument was specified.\nWhat do you want to do?\n[O]pen existing wallet, [G]enerate new wallet file or [E]xit.\n"; + std::cout << "Nor 'generate-new-wallet' neither 'wallet-file' argument was specified.\nWhat do you want to do?\n[O]pen existing wallet, [G]enerate new wallet file, [I]mport wallet or [E]xit.\n"; char c; do { std::string answer; std::getline(std::cin, answer); c = answer[0]; - if (!(c == 'O' || c == 'G' || c == 'E' || c == 'o' || c == 'g' || c == 'e')) { + if (!(c == 'O' || c == 'G' || c == 'E' || c == 'I' || c == 'o' || c == 'g' || c == 'e' || c == 'i')) { std::cout << "Unknown command: " << c <getAddress())) { logger(WARNING, BRIGHT_RED) << "Couldn't write wallet address file: " + walletAddressFile; - } - } else { - m_wallet.reset(new WalletLegacy(m_currency, *m_node)); + } + } else if (!m_import_new.empty()) { + std::string walletAddressFile = prepareWalletAddressFilename(m_import_new); + boost::system::error_code ignore; + if (boost::filesystem::exists(walletAddressFile, ignore)) { + logger(ERROR, BRIGHT_RED) << "Address file already exists: " + walletAddressFile; + return false; + } - try { - m_wallet_file = tryToOpenWalletOrLoadKeysOrThrow(logger, m_wallet, m_wallet_file_arg, pwd_container.password()); - } catch (const std::exception& e) { - fail_msg_writer() << "failed to load wallet: " << e.what(); - return false; - } + std::string private_spend_key_string; + std::string private_view_key_string; + + do { + std::cout << "Private Spend Key: "; + std::getline(std::cin, private_spend_key_string); + boost::algorithm::trim(private_spend_key_string); + } while (private_spend_key_string.empty()); + + do { + std::cout << "Private View Key: "; + std::getline(std::cin, private_view_key_string); + boost::algorithm::trim(private_view_key_string); + } while (private_view_key_string.empty()); + + Crypto::Hash private_spend_key_hash; + Crypto::Hash private_view_key_hash; + size_t size; + if (!Common::fromHex(private_spend_key_string, &private_spend_key_hash, sizeof(private_spend_key_hash), size) || size != sizeof(private_spend_key_hash)) { + return false; + } + if (!Common::fromHex(private_view_key_string, &private_view_key_hash, sizeof(private_view_key_hash), size) || size != sizeof(private_spend_key_hash)) { + return false; + } + Crypto::SecretKey private_spend_key = *(struct Crypto::SecretKey *) &private_spend_key_hash; + Crypto::SecretKey private_view_key = *(struct Crypto::SecretKey *) &private_view_key_hash; - m_wallet->addObserver(this); - m_node->addObserver(static_cast(this)); + if (!new_wallet(private_spend_key, private_view_key, walletFileName, pwd_container.password())) { + logger(ERROR, BRIGHT_RED) << "account creation failed"; + return false; + } - logger(INFO, BRIGHT_WHITE) << "Opened wallet: " << m_wallet->getAddress(); + if (!writeAddressFile(walletAddressFile, m_wallet->getAddress())) { + logger(WARNING, BRIGHT_RED) << "Couldn't write wallet address file: " + walletAddressFile; + } + } else { + m_wallet.reset(new WalletLegacy(m_currency, *m_node)); - success_msg_writer() << - "**********************************************************************\n" << - "Use \"help\" command to see the list of available commands.\n" << - "**********************************************************************"; - } + try { + m_wallet_file = tryToOpenWalletOrLoadKeysOrThrow(logger, m_wallet, m_wallet_file_arg, pwd_container.password()); + } catch (const std::exception& e) { + fail_msg_writer() << "failed to load wallet: " << e.what(); + return false; + } + + m_wallet->addObserver(this); + m_node->addObserver(static_cast(this)); + + logger(INFO, BRIGHT_WHITE) << "Opened wallet: " << m_wallet->getAddress(); + + success_msg_writer() << + "**********************************************************************\n" << + "Use \"help\" command to see the list of available commands.\n" << + "**********************************************************************"; + } return true; } + //---------------------------------------------------------------------------------------------------- bool simple_wallet::deinit() { m_wallet->removeObserver(this); @@ -706,6 +757,59 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::new_wallet(Crypto::SecretKey &secret_key, Crypto::SecretKey &view_key, const std::string &wallet_file, const std::string& password) { + m_wallet_file = wallet_file; + + m_wallet.reset(new WalletLegacy(m_currency, *m_node.get())); + m_node->addObserver(static_cast(this)); + m_wallet->addObserver(this); + try { + m_initResultPromise.reset(new std::promise()); + std::future f_initError = m_initResultPromise->get_future(); + + AccountKeys wallet_keys; + wallet_keys.spendSecretKey = secret_key; + wallet_keys.viewSecretKey = view_key; + Crypto::secret_key_to_public_key(wallet_keys.spendSecretKey, wallet_keys.address.spendPublicKey); + Crypto::secret_key_to_public_key(wallet_keys.viewSecretKey, wallet_keys.address.viewPublicKey); + + m_wallet->initWithKeys(wallet_keys, password); + auto initError = f_initError.get(); + m_initResultPromise.reset(nullptr); + if (initError) { + fail_msg_writer() << "failed to generate new wallet: " << initError.message(); + return false; + } + + try { + CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file); + } catch (std::exception& e) { + fail_msg_writer() << "failed to save new wallet: " << e.what(); + throw; + } + + AccountKeys keys; + m_wallet->getAccountKeys(keys); + + logger(INFO, BRIGHT_WHITE) << + "Imported wallet: " << m_wallet->getAddress() << std::endl; + } + catch (const std::exception& e) { + fail_msg_writer() << "failed to import wallet: " << e.what(); + return false; + } + + success_msg_writer() << + "**********************************************************************\n" << + "Your wallet has been imported.\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; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::close_wallet() { try { @@ -877,6 +981,16 @@ void simple_wallet::synchronizationProgressUpdated(uint32_t current, uint32_t to } } +bool simple_wallet::export_keys(const std::vector& args/* = std::vector()*/) { + AccountKeys keys; + m_wallet->getAccountKeys(keys); + success_msg_writer(true) << "Private spend key: " << Common::podToHex(keys.spendSecretKey); + success_msg_writer(true) << "Private view key: " << Common::podToHex(keys.viewSecretKey); + + return true; +} + + bool simple_wallet::show_balance(const std::vector& args/* = std::vector()*/) { success_msg_writer() << "available balance: " << m_currency.formatAmount(m_wallet->actualBalance()) << ", locked amount: " << m_currency.formatAmount(m_wallet->pendingBalance()); diff --git a/src/SimpleWallet/SimpleWallet.h b/src/SimpleWallet/SimpleWallet.h index c6e19bc124..e5f7db677c 100755 --- a/src/SimpleWallet/SimpleWallet.h +++ b/src/SimpleWallet/SimpleWallet.h @@ -75,6 +75,7 @@ namespace CryptoNote bool run_console_handler(); bool new_wallet(const std::string &wallet_file, const std::string& password); + bool new_wallet(Crypto::SecretKey &secret_key, Crypto::SecretKey &view_key, const std::string &wallet_file, const std::string& password); bool open_wallet(const std::string &wallet_file, const std::string& password); bool close_wallet(); @@ -83,6 +84,7 @@ namespace CryptoNote bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); bool show_balance(const std::vector &args = std::vector()); + bool export_keys(const std::vector &args = std::vector()); bool show_incoming_transfers(const std::vector &args); bool show_payments(const std::vector &args); bool show_blockchain_height(const std::vector &args); @@ -154,6 +156,7 @@ namespace CryptoNote private: std::string m_wallet_file_arg; std::string m_generate_new; + std::string m_import_new; std::string m_import_path; std::string m_daemon_address;