From 62606f11f50b4c68446d3f581c96d5505a0ad6ef Mon Sep 17 00:00:00 2001 From: Ilya Kitaev Date: Tue, 15 Mar 2016 23:11:38 +0300 Subject: [PATCH] Wallet::store_to(path, password) implemented; --- src/wallet/wallet2.cpp | 72 ++++++++++++++++++++++++++++++ src/wallet/wallet2.h | 6 +++ src/wallet/wallet2_api.cpp | 50 ++++++++++++++------- src/wallet/wallet2_api.h | 1 + src/wallet/wallet_errors.h | 1 - tests/libwallet_api_tests/main.cpp | 65 ++++++++++++++++++++++++++- 6 files changed, 177 insertions(+), 18 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c4f60306b..2afe08cb1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1461,6 +1461,78 @@ void wallet2::store() THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e); boost::filesystem::remove(old_file); } + +void wallet2::store_to(const std::string &path, const std::string &password) +{ + // TODO: merge it with wallet2::store() function + + // check if we want to store to directory which doesn't exists yet + boost::filesystem::path parent_path = boost::filesystem::path(path).parent_path(); + + // if path is not exists, try to create it + if (!parent_path.empty() && !boost::filesystem::exists(parent_path)) { + boost::system::error_code ec; + if (!boost::filesystem::create_directories(parent_path, ec)) { + throw std::logic_error(ec.message()); + } + } + + + std::stringstream oss; + boost::archive::binary_oarchive ar(oss); + ar << *this; + + wallet2::cache_file_data cache_file_data = boost::value_initialized(); + cache_file_data.cache_data = oss.str(); + crypto::chacha8_key key; + generate_chacha8_key_from_secret_keys(key); + std::string cipher; + cipher.resize(cache_file_data.cache_data.size()); + cache_file_data.iv = crypto::rand(); + crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]); + cache_file_data.cache_data = cipher; + + + const std::string new_file = path; + const std::string old_file = m_wallet_file; + const std::string old_keys_file = m_keys_file; + const std::string old_address_file = m_wallet_file + ".address.txt"; + + // save to new file + std::ofstream ostr; + ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + binary_archive oar(ostr); + bool success = ::serialization::serialize(oar, cache_file_data); + ostr.close(); + THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file); + + // save keys to the new file + // if we here, main wallet file is saved and we only need to save keys and address files + prepare_file_names(path); + store_keys(m_keys_file, password, false); + + // save address to the new file + const std::string address_file = m_wallet_file + ".address.txt"; + bool r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_testnet)); + THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_wallet_file); + + + // remove old wallet file + r = boost::filesystem::remove(old_file); + if (!r) { + LOG_ERROR("error removing file: " << old_file); + } + // remove old keys file + r = boost::filesystem::remove(old_keys_file); + if (!r) { + LOG_ERROR("error removing file: " << old_keys_file); + } + // remove old address file + r = boost::filesystem::remove(old_address_file); + if (!r) { + LOG_ERROR("error removing file: " << old_address_file); + } +} //---------------------------------------------------------------------------------------------------- uint64_t wallet2::unlocked_balance() const { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 2835815b7..f798f404d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -212,6 +212,12 @@ namespace tools void write_watch_only_wallet(const std::string& wallet_name, const std::string& password); void load(const std::string& wallet, const std::string& password); void store(); + /*! + * \brief store_to - stores wallet to another file(s), deleting old ones + * \param path - path to the wallet file (keys and address filenames will be generated based on this filename) + * \param password - password to protect new wallet (TODO: probably better save the password in the wallet object?) + */ + void store_to(const std::string &path, const std::string &password); /*! * \brief verifies given password is correct for default wallet keys file diff --git a/src/wallet/wallet2_api.cpp b/src/wallet/wallet2_api.cpp index 6428e7b32..0644e3690 100644 --- a/src/wallet/wallet2_api.cpp +++ b/src/wallet/wallet2_api.cpp @@ -69,6 +69,7 @@ public: std::string errorString() const; bool setPassword(const std::string &password); std::string address() const; + bool store(const std::string &path); private: void clearStatus(); @@ -78,7 +79,7 @@ private: tools::wallet2 * m_wallet; int m_status; std::string m_errorString; - + std::string m_password; }; WalletImpl::WalletImpl() @@ -118,34 +119,37 @@ bool WalletImpl::create(const std::string &path, const std::string &password, co crypto::secret_key recovery_val, secret_key; try { recovery_val = m_wallet->generate(path, password, secret_key, false, false); + m_password = password; + m_status = Status_Ok; } catch (const std::exception &e) { LOG_ERROR("Error creating wallet: " << e.what()); m_status = Status_Error; m_errorString = e.what(); return false; } + return true; } bool WalletImpl::open(const std::string &path, const std::string &password) { clearStatus(); - bool result = false; try { // TODO: handle "deprecated" m_wallet->load(path, password); - result = true; + + m_password = password; } catch (const std::exception &e) { LOG_ERROR("Error opening wallet: " << e.what()); m_status = Status_Error; m_errorString = e.what(); } - return result; + return m_status == Status_Ok; } bool WalletImpl::recover(const std::string &path, const std::string &seed) { - bool result = false; + clearStatus(); m_errorString.clear(); if (seed.empty()) { m_errorString = "Electrum seed is empty"; @@ -162,23 +166,20 @@ bool WalletImpl::recover(const std::string &path, const std::string &seed) return false; } - try { m_wallet->set_seed_language(old_language); m_wallet->generate(path, "", recovery_key, true, false); // TODO: wallet->init(daemon_address); - m_status = Status_Ok; } catch (const std::exception &e) { m_status = Status_Error; m_errorString = e.what(); - } - result = m_status == Status_Ok; - return result; + return m_status == Status_Ok; } bool WalletImpl::close() { + clearStatus(); bool result = false; try { m_wallet->store(); @@ -222,16 +223,15 @@ std::string WalletImpl::errorString() const bool WalletImpl::setPassword(const std::string &password) { - bool result = false; + clearStatus(); try { m_wallet->rewrite(m_wallet->get_wallet_file(), password); - result = true; + m_password = password; } catch (const std::exception &e) { - result = false; m_status = Status_Error; m_errorString = e.what(); } - return result; + return m_status == Status_Ok; } std::string WalletImpl::address() const @@ -239,6 +239,24 @@ std::string WalletImpl::address() const return m_wallet->get_account().get_public_address_str(m_wallet->testnet()); } +bool WalletImpl::store(const std::string &path) +{ + clearStatus(); + try { + if (path.empty()) { + m_wallet->store(); + } else { + m_wallet->store_to(path, m_password); + } + } catch (const std::exception &e) { + LOG_ERROR("Error storing wallet: " << e.what()); + m_status = Status_Error; + m_errorString = e.what(); + } + + return m_status == Status_Ok; +} + void WalletImpl::clearStatus() { m_status = Status_Ok; @@ -316,9 +334,9 @@ std::string WalletManagerImpl::errorString() const ///////////////////// WalletManagerFactory implementation ////////////////////// WalletManager *WalletManagerFactory::getWalletManager() { - // TODO: initialize logger here - epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_0); + if (!g_walletManager) { + epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_0); g_walletManager = new WalletManagerImpl(); } diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index 7a22a88de..c7e7c536c 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -66,6 +66,7 @@ struct Wallet virtual std::string errorString() const = 0; virtual bool setPassword(const std::string &password) = 0; virtual std::string address() const = 0; + virtual bool store(const std::string &path) = 0; }; /** diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 80482c1ba..6074e0858 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -210,7 +210,6 @@ namespace tools //---------------------------------------------------------------------------------------------------- typedef file_error_base file_exists; typedef file_error_base file_not_found; - typedef file_error_base file_not_found; typedef file_error_base file_read_error; typedef file_error_base file_save_error; //---------------------------------------------------------------------------------------------------- diff --git a/tests/libwallet_api_tests/main.cpp b/tests/libwallet_api_tests/main.cpp index 8f4aba6bd..9701c300c 100644 --- a/tests/libwallet_api_tests/main.cpp +++ b/tests/libwallet_api_tests/main.cpp @@ -47,6 +47,9 @@ struct WalletManagerTest : public testing::Test Bitmonero::WalletManager * wmgr; const char * WALLET_NAME = "testwallet"; + const char * WALLET_NAME_COPY = "testwallet_copy"; + const char * WALLET_NAME_WITH_DIR = "walletdir/testwallet_test"; + const char * WALLET_NAME_WITH_DIR_NON_WRITABLE = "/var/walletdir/testwallet_test"; const char * WALLET_PASS = "password"; const char * WALLET_PASS2 = "password22"; const char * WALLET_LANG = "English"; @@ -56,6 +59,7 @@ struct WalletManagerTest : public testing::Test std::cout << __FUNCTION__ << std::endl; wmgr = Bitmonero::WalletManagerFactory::getWalletManager(); deleteWallet(WALLET_NAME); + deleteDir(boost::filesystem::path(WALLET_NAME_WITH_DIR).parent_path().string()); } @@ -68,12 +72,18 @@ struct WalletManagerTest : public testing::Test void deleteWallet(const std::string & walletname) { - std::cout << "** deleting wallet " << std::endl; + std::cout << "** deleting wallet: " << walletname << std::endl; boost::filesystem::remove(walletname); boost::filesystem::remove(walletname + ".address.txt"); boost::filesystem::remove(walletname + ".keys"); } + void deleteDir(const std::string &path) + { + std::cout << "** removing dir recursively: " << path << std::endl; + boost::filesystem::remove_all(path); + } + }; @@ -139,6 +149,59 @@ TEST_F(WalletManagerTest, WalletManagerRecoversWallet) } +TEST_F(WalletManagerTest, WalletManagerStoresWallet1) +{ + Bitmonero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG); + std::string seed1 = wallet1->seed(); + std::string address1 = wallet1->address(); + + ASSERT_TRUE(wallet1->store("")); + ASSERT_TRUE(wallet1->store(WALLET_NAME_COPY)); + ASSERT_TRUE(wmgr->closeWallet(wallet1)); + Bitmonero::Wallet * wallet2 = wmgr->openWallet(WALLET_NAME_COPY, WALLET_PASS); + ASSERT_TRUE(wallet2->status() == Bitmonero::Wallet::Status_Ok); + ASSERT_TRUE(wallet2->seed() == seed1); + ASSERT_TRUE(wallet2->address() == address1); + ASSERT_TRUE(wmgr->closeWallet(wallet2)); +} + +TEST_F(WalletManagerTest, WalletManagerStoresWallet2) +{ + Bitmonero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG); + std::string seed1 = wallet1->seed(); + std::string address1 = wallet1->address(); + + ASSERT_TRUE(wallet1->store(WALLET_NAME_WITH_DIR)); + ASSERT_TRUE(wmgr->closeWallet(wallet1)); + + wallet1 = wmgr->openWallet(WALLET_NAME_WITH_DIR, WALLET_PASS); + ASSERT_TRUE(wallet1->status() == Bitmonero::Wallet::Status_Ok); + ASSERT_TRUE(wallet1->seed() == seed1); + ASSERT_TRUE(wallet1->address() == address1); + ASSERT_TRUE(wmgr->closeWallet(wallet1)); +} + +TEST_F(WalletManagerTest, WalletManagerStoresWallet3) +{ + Bitmonero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG); + std::string seed1 = wallet1->seed(); + std::string address1 = wallet1->address(); + + ASSERT_FALSE(wallet1->store(WALLET_NAME_WITH_DIR_NON_WRITABLE)); + ASSERT_TRUE(wmgr->closeWallet(wallet1)); + + wallet1 = wmgr->openWallet(WALLET_NAME_WITH_DIR_NON_WRITABLE, WALLET_PASS); + ASSERT_FALSE(wallet1->status() == Bitmonero::Wallet::Status_Ok); + + ASSERT_FALSE(wmgr->closeWallet(wallet1)); + + wallet1 = wmgr->openWallet(WALLET_NAME, WALLET_PASS); + ASSERT_TRUE(wallet1->status() == Bitmonero::Wallet::Status_Ok); + ASSERT_TRUE(wallet1->seed() == seed1); + ASSERT_TRUE(wallet1->address() == address1); + ASSERT_TRUE(wmgr->closeWallet(wallet1)); + +} int main(int argc, char** argv)