diff --git a/CMakeLists.txt b/CMakeLists.txt index 05c64002..d6b61e72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,13 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS") set(Boost_LIBRARY_DIRS "/Users/roky/projects/Zano/mobile_repo/ofxiOSBoost/libs/boost/ios/") set(Boost_LIBRARIES "libboost.a") set(Boost_VERSION "ofxiOSBoost 1.60.0") + #workaround for new XCode 12 policy for builds(now it includes a slice for the "arm64" when builds for simulator) + set(__iphoneos_archs "armv7 armv7s arm64") + set(__iphonesimulator_archs "i386 x86_64") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "${__iphoneos_archs}") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] "${__iphoneos_archs}") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "${__iphonesimulator_archs}") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] "${__iphonesimulator_archs}") elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") set(Boost_INCLUDE_DIRS "/Users/roky/projects/Zano/mobile_repo/Boost-for-Android-Prebuilt/1.69.0/include") set(Boost_LIBRARY_DIRS "/Users/roky/projects/Zano/mobile_repo/Boost-for-Android-Prebuilt/1.69.0/libs/llvm/${CMAKE_ANDROID_ARCH_ABI}/") diff --git a/src/common/pre_download.h b/src/common/pre_download.h index e40e26ef..e90fb2e7 100644 --- a/src/common/pre_download.h +++ b/src/common/pre_download.h @@ -188,7 +188,7 @@ namespace tools std::list blocks; std::list txs; bool r = source_core.get_blocks(i, 1, blocks, txs); - CHECK_AND_ASSERT_MES(r && blocks.size()==1, false, "Filed to get block " << i << " from core"); + CHECK_AND_ASSERT_MES(r && blocks.size()==1, false, "Failed to get block " << i << " from core"); currency::tx_verification_context tvc = AUTO_VAL_INIT(tvc); crypto::hash tx_hash = AUTO_VAL_INIT(tx_hash); for (auto& tx : txs) diff --git a/src/currency_core/account.cpp b/src/currency_core/account.cpp index ada25025..2494f056 100644 --- a/src/currency_core/account.cpp +++ b/src/currency_core/account.cpp @@ -60,18 +60,42 @@ namespace currency return m_keys; } //----------------------------------------------------------------- - std::string account_base::get_seed_phrase() const + void crypt_with_pass(const void* scr_data, std::size_t src_length, void* dst_data, const std::string& password) + { + crypto::chacha8_key key = AUTO_VAL_INIT(key); + crypto::generate_chacha8_key(password, key); + crypto::hash pass_hash = crypto::cn_fast_hash(password.data(), password.size()); + crypto::chacha8_iv iv = AUTO_VAL_INIT(iv); + CHECK_AND_ASSERT_THROW_MES(sizeof(pass_hash) >= sizeof(iv), "Invalid configuration: hash size is less than keys_file_data.iv"); + iv = *((crypto::chacha8_iv*)&pass_hash); + crypto::chacha8(scr_data, src_length, key, iv, (char*)dst_data); + } + + + std::string account_base::get_seed_phrase(const std::string& password) const { if (m_keys_seed_binary.empty()) return ""; - std::string keys_seed_text = tools::mnemonic_encoding::binary2text(m_keys_seed_binary); - std::string timestamp_word = currency::get_word_from_timstamp(m_creation_timestamp); + + std::vector processed_seed_binary = m_keys_seed_binary; + if (!password.empty()) + { + //encrypt seed phrase binary data + crypt_with_pass(&m_keys_seed_binary[0], m_keys_seed_binary.size(), &processed_seed_binary[0], password); + } + + std::string keys_seed_text = tools::mnemonic_encoding::binary2text(processed_seed_binary); + std::string timestamp_word = currency::get_word_from_timstamp(m_creation_timestamp, !password.empty()); // floor creation time to WALLET_BRAIN_DATE_QUANTUM to make checksum calculation stable - uint64_t creation_timestamp_rounded = get_timstamp_from_word(timestamp_word); + bool self_check_is_password_used = false; + uint64_t creation_timestamp_rounded = get_timstamp_from_word(timestamp_word, self_check_is_password_used); + CHECK_AND_ASSERT_THROW_MES(self_check_is_password_used == !password.empty(), "Account seed phrase internal error: password flag encoded wrong"); constexpr uint16_t checksum_max = tools::mnemonic_encoding::NUMWORDS >> 1; // maximum value of checksum - crypto::hash h = crypto::cn_fast_hash(m_keys_seed_binary.data(), m_keys_seed_binary.size()); + std::string binary_for_check_sum((const char*)&m_keys_seed_binary[0], m_keys_seed_binary.size()); + binary_for_check_sum.append(password); + crypto::hash h = crypto::cn_fast_hash(binary_for_check_sum.data(), binary_for_check_sum.size()); *reinterpret_cast(&h) = creation_timestamp_rounded; h = crypto::cn_fast_hash(&h, sizeof h); uint64_t h_64 = *reinterpret_cast(&h); @@ -104,7 +128,7 @@ namespace currency return true; } //----------------------------------------------------------------- - bool account_base::restore_from_seed_phrase(const std::string& seed_phrase) + bool account_base::restore_from_seed_phrase(const std::string& seed_phrase, const std::string& seed_password) { //cut the last timestamp word from restore_dats std::list words; @@ -138,9 +162,20 @@ namespace currency auditable_flag_and_checksum = tools::mnemonic_encoding::num_by_word(auditable_flag_and_checksum_word); std::vector keys_seed_binary = tools::mnemonic_encoding::text2binary(keys_seed_text); - CHECK_AND_ASSERT_MES(keys_seed_binary.size(), false, "text2binary failed to convert the given text"); // don't prints event incorrect seed into the log for security + std::vector keys_seed_processed_binary = keys_seed_binary; - m_creation_timestamp = get_timstamp_from_word(timestamp_word); + + bool has_password = false; + m_creation_timestamp = get_timstamp_from_word(timestamp_word, has_password); + //double check is password setting from timestamp word match with passed parameters + CHECK_AND_ASSERT_MES(has_password != seed_password.empty(), false, "Seed phrase password wrong interpretation"); + if (has_password) + { + CHECK_AND_ASSERT_MES(!seed_password.empty(), false, "Seed phrase password wrong interpretation: internal error"); + crypt_with_pass(&keys_seed_binary[0], keys_seed_binary.size(), &keys_seed_processed_binary[0], seed_password); + } + + CHECK_AND_ASSERT_MES(keys_seed_processed_binary.size(), false, "text2binary failed to convert the given text"); // don't prints event incorrect seed into the log for security bool auditable_flag = false; @@ -150,7 +185,9 @@ namespace currency auditable_flag = (auditable_flag_and_checksum & 1) != 0; // auditable flag is the lower 1 bit uint16_t checksum = static_cast(auditable_flag_and_checksum >> 1); // checksum -- everything else constexpr uint16_t checksum_max = tools::mnemonic_encoding::NUMWORDS >> 1; // maximum value of checksum - crypto::hash h = crypto::cn_fast_hash(keys_seed_binary.data(), keys_seed_binary.size()); + std::string binary_for_check_sum((const char*)&keys_seed_processed_binary[0], keys_seed_processed_binary.size()); + binary_for_check_sum.append(seed_password); + crypto::hash h = crypto::cn_fast_hash(binary_for_check_sum.data(), binary_for_check_sum.size()); *reinterpret_cast(&h) = m_creation_timestamp; h = crypto::cn_fast_hash(&h, sizeof h); uint64_t h_64 = *reinterpret_cast(&h); @@ -158,10 +195,10 @@ namespace currency CHECK_AND_ASSERT_MES(checksum == checksum_calculated, false, "seed phase has invalid checksum: " << checksum_calculated << ", while " << checksum << " is expected, check your words"); } - bool r = restore_keys(keys_seed_binary); + bool r = restore_keys(keys_seed_processed_binary); CHECK_AND_ASSERT_MES(r, false, "restore_keys failed"); - m_keys_seed_binary = keys_seed_binary; + m_keys_seed_binary = keys_seed_processed_binary; if (auditable_flag) m_keys.account_address.flags |= ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE; @@ -169,6 +206,34 @@ namespace currency return true; } //----------------------------------------------------------------- + bool account_base::is_seed_password_protected(const std::string& seed_phrase, bool& is_password_protected) + { + //cut the last timestamp word from restore_dats + std::list words; + boost::split(words, seed_phrase, boost::is_space()); + + std::string timestamp_word; + if (words.size() == SEED_PHRASE_V1_WORDS_COUNT) + { + // 24 seed words + one timestamp word = 25 total + timestamp_word = words.back(); + } + else if (words.size() == SEED_PHRASE_V2_WORDS_COUNT) + { + // 24 seed words + one timestamp word + one flags & checksum = 26 total + words.erase(--words.end()); + timestamp_word = words.back(); + } + else + { + return false; + } + + get_timstamp_from_word(timestamp_word, is_password_protected); + + return true; + } + //----------------------------------------------------------------- bool account_base::restore_from_tracking_seed(const std::string& tracking_seed) { set_null(); diff --git a/src/currency_core/account.h b/src/currency_core/account.h index 606ea74d..ba723670 100644 --- a/src/currency_core/account.h +++ b/src/currency_core/account.h @@ -53,9 +53,9 @@ namespace currency const account_public_address& get_public_address() const { return m_keys.account_address; }; std::string get_public_address_str() const; - std::string get_seed_phrase() const; + std::string get_seed_phrase(const std::string& seed_password) const; std::string get_tracking_seed() const; - bool restore_from_seed_phrase(const std::string& seed_phrase); + bool restore_from_seed_phrase(const std::string& seed_phrase, const std::string& seed_password); bool restore_from_tracking_seed(const std::string& tracking_seed); uint64_t get_createtime() const { return m_creation_timestamp; } @@ -65,6 +65,8 @@ namespace currency bool store(const std::string& file_path); void make_account_watch_only(); + bool is_watch_only() const { return m_keys.spend_secret_key == currency::null_skey; } + bool is_auditable() const { return m_keys.account_address.is_auditable(); } template inline void serialize(t_archive &a, const unsigned int /*ver*/) @@ -76,6 +78,7 @@ namespace currency static std::string vector_of_chars_to_string(const std::vector& v) { return std::string(v.begin(), v.end()); } static std::vector string_to_vector_of_chars(const std::string& v) { return std::vector(v.begin(), v.end()); } + static bool is_seed_password_protected(const std::string& seed_phrase, bool& is_password_protected); BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_keys) diff --git a/src/currency_core/currency_config.h b/src/currency_core/currency_config.h index 4e06493b..c6b106b4 100644 --- a/src/currency_core/currency_config.h +++ b/src/currency_core/currency_config.h @@ -150,9 +150,10 @@ #define WALLET_FILE_MAX_KEYS_SIZE 10000 // #define WALLET_BRAIN_DATE_OFFSET 1543622400 -#define WALLET_BRAIN_DATE_QUANTUM 604800 //by last word we encode a number of week since launch of the project, +#define WALLET_BRAIN_DATE_QUANTUM 604800 //by last word we encode a number of week since launch of the project AND password flag, //which let us to address tools::mnemonic_encoding::NUMWORDS weeks after project launch - //which is about 30 years + //which is about 15 years +#define WALLET_BRAIN_DATE_MAX_WEEKS_COUNT 800 #define OFFER_MAXIMUM_LIFE_TIME (60*60*24*30) // 30 days diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 740c7afc..23f82a8d 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -1316,21 +1316,34 @@ namespace currency return reward; } //--------------------------------------------------------------- - std::string get_word_from_timstamp(uint64_t timestamp) + std::string get_word_from_timstamp(uint64_t timestamp, bool use_password) { uint64_t date_offset = timestamp > WALLET_BRAIN_DATE_OFFSET ? timestamp - WALLET_BRAIN_DATE_OFFSET : 0; uint64_t weeks_count = date_offset / WALLET_BRAIN_DATE_QUANTUM; + CHECK_AND_ASSERT_THROW_MES(weeks_count < WALLET_BRAIN_DATE_MAX_WEEKS_COUNT, "SEED PHRASE need to be extended or refactored"); + + if (use_password) + weeks_count += WALLET_BRAIN_DATE_MAX_WEEKS_COUNT; + CHECK_AND_ASSERT_THROW_MES(weeks_count < std::numeric_limits::max(), "internal error: unable to convert to uint32, val = " << weeks_count); uint32_t weeks_count_32 = static_cast(weeks_count); return tools::mnemonic_encoding::word_by_num(weeks_count_32); } //--------------------------------------------------------------- - uint64_t get_timstamp_from_word(std::string word) + uint64_t get_timstamp_from_word(std::string word, bool& password_used) { uint64_t count_of_weeks = tools::mnemonic_encoding::num_by_word(word); + if (count_of_weeks >= WALLET_BRAIN_DATE_MAX_WEEKS_COUNT) + { + count_of_weeks -= WALLET_BRAIN_DATE_MAX_WEEKS_COUNT; + password_used = true; + } + else { + password_used = false; + } uint64_t timestamp = count_of_weeks * WALLET_BRAIN_DATE_QUANTUM + WALLET_BRAIN_DATE_OFFSET; - + return timestamp; } //--------------------------------------------------------------- diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index b53931e3..22642fb8 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -291,8 +291,8 @@ namespace currency bool fill_tx_rpc_details(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce, const crypto::hash& h, uint64_t timestamp, bool is_short = false); bool fill_block_rpc_details(block_rpc_extended_info& pei_rpc, const block_extended_info& bei_chain, const crypto::hash& h); void append_per_block_increments_for_tx(const transaction& tx, std::unordered_map& gindices); - std::string get_word_from_timstamp(uint64_t timestamp); - uint64_t get_timstamp_from_word(std::string word); + std::string get_word_from_timstamp(uint64_t timestamp, bool use_password); + uint64_t get_timstamp_from_word(std::string word, bool& password_used); template typename std::conditional::value, const std::vector, std::vector >::type& get_txin_etc_options(t_txin_v& in) diff --git a/src/gui/qt-daemon/application/mainwindow.cpp b/src/gui/qt-daemon/application/mainwindow.cpp index 0ebb05ef..2d8856a7 100644 --- a/src/gui/qt-daemon/application/mainwindow.cpp +++ b/src/gui/qt-daemon/application/mainwindow.cpp @@ -239,6 +239,28 @@ QString MainWindow::get_tx_pool_info() CATCH_ENTRY_FAIL_API_RESPONCE(); } +QString MainWindow::request_dummy() +{ + static int code_ = 0; + TRY_ENTRY(); + LOG_API_TIMING(); + PREPARE_RESPONSE(currency::COMMAND_RPC_GET_POOL_INFO::response, ar); + if (code_ == 2) + { + code_ = -1; + ar.error_code = API_RETURN_CODE_CORE_BUSY; + } + else + { + ar.error_code = API_RETURN_CODE_OK; + } + + ++code_; + return MAKE_RESPONSE(ar); + CATCH_ENTRY_FAIL_API_RESPONCE(); +} + + QString MainWindow::get_default_fee() { TRY_ENTRY(); @@ -1654,7 +1676,7 @@ QString MainWindow::restore_wallet(const QString& param) //return que_call2("restore_wallet", param, [this](const view::restore_wallet_request& owd, view::api_response& ar){ PREPARE_ARG_FROM_JSON(view::restore_wallet_request, owd); PREPARE_RESPONSE(view::open_wallet_response, ar); - ar.error_code = m_backend.restore_wallet(epee::string_encoding::utf8_to_wstring(owd.path), owd.pass, owd.restore_key, ar.response_data); + ar.error_code = m_backend.restore_wallet(epee::string_encoding::utf8_to_wstring(owd.path), owd.pass, owd.seed_phrase, owd.seed_pass, ar.response_data); return MAKE_RESPONSE(ar); CATCH_ENTRY_FAIL_API_RESPONCE(); } @@ -1855,9 +1877,9 @@ QString MainWindow::get_smart_wallet_info(const QString& param) { TRY_ENTRY(); LOG_API_TIMING(); - PREPARE_ARG_FROM_JSON(view::wallet_id_obj, wo); + PREPARE_ARG_FROM_JSON(view::request_get_smart_wallet_info, wo); PREPARE_RESPONSE(view::get_restore_info_response, ar); - ar.error_code = m_backend.get_wallet_restore_info(wo.wallet_id, ar.response_data.restore_key); + ar.error_code = m_backend.get_wallet_restore_info(wo.wallet_id, ar.response_data.seed_phrase, wo.seed_password); return MAKE_RESPONSE(ar); CATCH_ENTRY_FAIL_API_RESPONCE(); } @@ -1951,14 +1973,26 @@ QString MainWindow::open_url_in_browser(const QString& param) CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR); } - QString MainWindow::is_valid_restore_wallet_text(const QString& param) { TRY_ENTRY(); LOG_API_TIMING(); - return m_backend.is_valid_brain_restore_data(param.toStdString()).c_str(); + PREPARE_ARG_FROM_JSON(view::seed_info_param, rwtp); + return m_backend.is_valid_brain_restore_data(rwtp.seed_phrase, rwtp.seed_password).c_str(); CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR); } + +QString MainWindow::get_seed_phrase_info(const QString& param) +{ + TRY_ENTRY(); + LOG_API_TIMING(); + PREPARE_ARG_FROM_JSON(view::seed_info_param, rwtp); + PREPARE_RESPONSE(view::seed_phrase_info, ar); + ar.error_code = m_backend.get_seed_phrase_info(rwtp.seed_phrase, rwtp.seed_password, ar.response_data).c_str(); + return MAKE_RESPONSE(ar); + CATCH_ENTRY_FAIL_API_RESPONCE(); +} + void MainWindow::contextMenuEvent(QContextMenuEvent * event) { TRY_ENTRY(); diff --git a/src/gui/qt-daemon/application/mainwindow.h b/src/gui/qt-daemon/application/mainwindow.h index dff2f9bf..ba9216fc 100644 --- a/src/gui/qt-daemon/application/mainwindow.h +++ b/src/gui/qt-daemon/application/mainwindow.h @@ -137,6 +137,7 @@ public: QString is_autostart_enabled(); QString toggle_autostart(const QString& param); QString is_valid_restore_wallet_text(const QString& param); + QString get_seed_phrase_info(const QString& param); QString print_text(const QString& param); QString print_log(const QString& param); QString set_clipboard(const QString& param); @@ -159,6 +160,9 @@ public: QString is_remnotenode_mode_preconfigured(); QString start_backend(const QString& params); + //for test purposes onlys + QString request_dummy(); + signals: void quit_requested(const QString str); void update_daemon_state(const QString str); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3770251a..b7391f98 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -46,13 +46,14 @@ namespace const command_line::arg_descriptor arg_password = {"password", "Wallet password", "", true}; const command_line::arg_descriptor arg_dont_refresh = { "no-refresh", "Do not refresh after load", false, true }; const command_line::arg_descriptor arg_dont_set_date = { "no-set-creation-date", "Do not set wallet creation date", false, false }; - const command_line::arg_descriptor arg_print_brain_wallet = { "print-brain-wallet", "Print to conosole brain wallet", false, false }; const command_line::arg_descriptor arg_daemon_port = {"daemon-port", "Use daemon instance at port instead of default", 0}; const command_line::arg_descriptor arg_log_level = {"set-log", "", 0, true}; const command_line::arg_descriptor arg_do_pos_mining = { "do-pos-mining", "Do PoS mining", false, false }; const command_line::arg_descriptor arg_pos_mining_reward_address = { "pos-mining-reward-address", "Block reward will be sent to the giving address if specified", "" }; const command_line::arg_descriptor arg_restore_wallet = { "restore-wallet", "Restore wallet from seed phrase or tracking seed and save it to ", "" }; const command_line::arg_descriptor arg_offline_mode = { "offline-mode", "Don't connect to daemon, work offline (for cold-signing process)", false, true }; + const command_line::arg_descriptor arg_scan_for_wallet = { "scan-for-wallet", "", "", true }; + const command_line::arg_descriptor arg_addr_to_compare = { "addr-to-compare", "", "", true }; const command_line::arg_descriptor< std::vector > arg_command = {"command", ""}; @@ -178,7 +179,6 @@ bool simple_wallet::help(const std::vector &args/* = std::vectorreset_creation_time(0); - if (m_print_brain_wallet) - { - std::cout << "Seed phrase (keep it in secret): " << m_wallet->get_account().get_seed_phrase() << std::endl << std::flush; - } } catch (const std::exception& e) @@ -422,7 +431,7 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet) +bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet, const std::string& seed_password) { m_wallet_file = wallet_file; @@ -434,13 +443,13 @@ bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::st if (tracking_wallet) { // auditable watch-only aka tracking wallet - m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, true); + m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, true, ""); message_writer(epee::log_space::console_color_white, true) << "Tracking wallet restored: " << m_wallet->get_account().get_public_address_str(); } else { // normal or auditable wallet - m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, false); + m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, false, seed_password); message_writer(epee::log_space::console_color_white, true) << (m_wallet->is_auditable() ? "Auditable wallet" : "Wallet") << " restored: " << m_wallet->get_account().get_public_address_str(); std::cout << "view key: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().view_secret_key) << std::endl << std::flush; if (m_wallet->is_auditable()) @@ -454,7 +463,11 @@ bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::st fail_msg_writer() << "failed to restore wallet, check your " << (tracking_wallet ? "tracking seed!" : "seed phrase!") << ENDL << e.what(); return false; } - + catch (...) + { + fail_msg_writer() << "failed to restore wallet, check your " << (tracking_wallet ? "tracking seed!" : "seed phrase!") << ENDL; + return false; + } m_wallet->init(m_daemon_address); success_msg_writer() << @@ -482,9 +495,6 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa m_wallet->load(epee::string_encoding::utf8_to_wstring(m_wallet_file), password); message_writer(epee::log_space::console_color_white, true) << "Opened" << (m_wallet->is_auditable() ? " auditable" : "") << (m_wallet->is_watch_only() ? " watch-only" : "") << " wallet: " << m_wallet->get_account().get_public_address_str(); - if (m_print_brain_wallet) - std::cout << "Seed phrase (keep it in secret): " << m_wallet->get_account().get_seed_phrase() << std::endl << std::flush; - break; } catch (const tools::error::wallet_load_notice_wallet_restored& /*e*/) @@ -1359,9 +1369,24 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: //---------------------------------------------------------------------------------------------------- bool simple_wallet::show_seed(const std::vector &args) { - success_msg_writer() << "Here's your wallet's seed phrase. Write it down and keep in a safe place."; - success_msg_writer(true) << "Anyone who knows the following 26 words can access your wallet:"; - std::cout << m_wallet->get_account().get_seed_phrase() << std::endl << std::flush; + success_msg_writer() << "Please enter a password to secure this seed. Securing your seed is HIGHLY recommended. Leave password blank to stay unsecured."; + success_msg_writer(true) << "Remember, restoring a wallet from Secured Seed can only be done if you know its password."; + + tools::password_container seed_password_container1; + if (!seed_password_container1.read_password("Enter seed password: ")) + { + return false; + } + tools::password_container seed_password_container2; + if (!seed_password_container2.read_password("Confirm seed password: ")) + { + return false; + } + if(seed_password_container1.password() != seed_password_container2.password()) + { + std::cout << "Error: password mismatch. Please make sure you entered the correct password and confirmed it" << std::endl << std::flush; + } + std::cout << m_wallet->get_account().get_seed_phrase(seed_password_container2.password()) << std::endl << std::flush; return true; } //---------------------------------------------------------------------------------------------------- @@ -1712,6 +1737,109 @@ bool simple_wallet::sweep_below(const std::vector &args) return true; } + + +//---------------------------------------------------------------------------------------------------- +uint64_t +get_tick_count__() +{ + using namespace std::chrono; + return duration_cast(steady_clock::now().time_since_epoch()).count(); +} + +//---------------------------------------------------------------------------------------------------- +bool check_if_file_looks_like_a_wallet(const std::wstring& wallet_) +{ + std::string keys_buff; + + + boost::system::error_code e; + bool exists = boost::filesystem::exists(wallet_, e); + if (e || !exists) + return false; + + boost::filesystem::ifstream data_file; + data_file.open(wallet_, std::ios_base::binary | std::ios_base::in); + if (data_file.fail()) + return false; + + tools::wallet_file_binary_header wbh = AUTO_VAL_INIT(wbh); + + data_file.read((char*)&wbh, sizeof(wbh)); + if (data_file.fail()) + { + return false; + } + + if (wbh.m_signature != WALLET_FILE_SIGNATURE_OLD && wbh.m_signature != WALLET_FILE_SIGNATURE_V2) + { + return false; + } + + //std::cout << "\r \r"; + LOG_PRINT_L0("Found wallet file: " << epee::string_encoding::convert_to_ansii(wallet_)); + return false; +} + +bool search_for_wallet_file(const std::wstring &search_here/*, const std::string &addr_to_compare*/) +{ + if (search_here == L"/proc" || search_here == L"/bin" || search_here == L"/dev" || search_here == L"/etc" + || search_here == L"/lib" || search_here == L"/lib64" || search_here == L"/proc" || search_here == L"/run" + || search_here == L"/sbin" || search_here == L"/srv" || search_here == L"/sys" || search_here == L"/usr" + || search_here == L"/var") + { + LOG_PRINT_L0("Skiping " << epee::string_encoding::convert_to_ansii(search_here)); + return false; + } + + //LOG_PRINT_L0("FOLDER: " << epee::string_encoding::convert_to_ansii(search_here)); + static uint64_t last_tick = 0; + using namespace boost::filesystem; + //recursive_directory_iterator dir(search_here), end; + try + { + for (auto& dir : boost::make_iterator_range(directory_iterator(search_here), {})) + { + boost::system::error_code ec = AUTO_VAL_INIT(ec); + bool r = boost::filesystem::is_directory(dir.path(), ec); + if (r) + { + if (get_tick_count__() - last_tick > 300) + { + last_tick = get_tick_count__(); + //std::cout << "\r \r ->" << dir.path(); + } + bool r = search_for_wallet_file(dir.path().wstring()); + if (r) + return true; + } + else + { + boost::system::error_code ec = AUTO_VAL_INIT(ec); + bool r = boost::filesystem::is_regular_file(dir.path(), ec); + if (!r) + { + //LOG_PRINT_L0("Skiping as not regular: " << epee::string_encoding::convert_to_ansii(dir.path().wstring())); + return false; + + } + //LOG_PRINT_L0("FILE: " << dir.path().string()); + std::wstring pa = dir.path().wstring(); + r = check_if_file_looks_like_a_wallet(pa); + if (r) + return true; + + } + } + } + catch (std::exception& /* ex*/) + { + //std::cout << "\r \r"; + LOG_PRINT_CYAN("Skip: " << search_here, LOG_LEVEL_0); + return false; + } + return false; +} //---------------------------------------------------------------------------------------------------- #ifdef WIN32 int wmain( int argc, wchar_t* argv_w[ ], wchar_t* envp[ ] ) @@ -1763,14 +1891,14 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_log_level); command_line::add_arg(desc_params, arg_dont_refresh); command_line::add_arg(desc_params, arg_dont_set_date); - command_line::add_arg(desc_params, arg_print_brain_wallet); command_line::add_arg(desc_params, arg_do_pos_mining); command_line::add_arg(desc_params, arg_pos_mining_reward_address); command_line::add_arg(desc_params, arg_restore_wallet); command_line::add_arg(desc_params, arg_offline_mode); command_line::add_arg(desc_params, command_line::arg_log_file); command_line::add_arg(desc_params, command_line::arg_log_level); - + command_line::add_arg(desc_params, arg_scan_for_wallet); + command_line::add_arg(desc_params, arg_addr_to_compare); tools::wallet_rpc_server::init_options(desc_params); @@ -1827,6 +1955,17 @@ int main(int argc, char* argv[]) log_space::get_set_log_detalisation_level(true, command_line::get_arg(vm, command_line::arg_log_level)); } + + if (command_line::has_arg(vm, arg_scan_for_wallet)) + { + log_space::log_singletone::add_logger(LOGGER_CONSOLE, nullptr, nullptr, LOG_LEVEL_4); + LOG_PRINT_L0("Searching from " + << epee::string_encoding::convert_to_ansii(command_line::get_arg(vm, arg_scan_for_wallet))); + + search_for_wallet_file(epee::string_encoding::convert_to_unicode(command_line::get_arg(vm, arg_scan_for_wallet))); + return EXIT_SUCCESS; + } + bool offline_mode = command_line::get_arg(vm, arg_offline_mode); if (command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 8fc44fc9..156da7c8 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -46,7 +46,7 @@ namespace currency bool new_wallet(const std::string &wallet_file, const std::string& password, bool create_auditable_wallet); bool open_wallet(const std::string &wallet_file, const std::string& password); - bool restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet); + bool restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet, const std::string& seed_password); bool close_wallet(); bool help(const std::vector &args = std::vector()); @@ -163,7 +163,6 @@ namespace currency int m_daemon_port; bool m_do_refresh_after_load; bool m_do_not_set_date; - bool m_print_brain_wallet; bool m_do_pos_mining; bool m_offline_mode; std::string m_restore_wallet; diff --git a/src/version.h.in b/src/version.h.in index 2211696c..52bc0629 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -8,6 +8,6 @@ #define PROJECT_REVISION "7" #define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION -#define PROJECT_VERSION_BUILD_NO 105 +#define PROJECT_VERSION_BUILD_NO 110 #define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO) #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]" diff --git a/src/wallet/plain_wallet_api.cpp b/src/wallet/plain_wallet_api.cpp index ef8e0ff9..e7984ee7 100644 --- a/src/wallet/plain_wallet_api.cpp +++ b/src/wallet/plain_wallet_api.cpp @@ -400,13 +400,13 @@ namespace plain_wallet return epee::serialization::store_t_to_json(err_result); } - std::string restore(const std::string& seed, const std::string& path, const std::string& password) + std::string restore(const std::string& seed, const std::string& path, const std::string& password, const std::string& seed_password) { GET_INSTANCE_PTR(inst_ptr); std::string full_path = get_wallets_folder() + path; epee::json_rpc::response ok_response = AUTO_VAL_INIT(ok_response); - std::string rsp = inst_ptr->gwm.restore_wallet(epee::string_encoding::convert_to_unicode(full_path), password, seed, ok_response.result); + std::string rsp = inst_ptr->gwm.restore_wallet(epee::string_encoding::convert_to_unicode(full_path), password, seed, seed_password, ok_response.result); if (rsp == API_RETURN_CODE_OK || rsp == API_RETURN_CODE_FILE_RESTORED) { if (rsp == API_RETURN_CODE_FILE_RESTORED) @@ -526,7 +526,7 @@ namespace plain_wallet } async_callback = [job_id, rwr]() { - std::string res = restore(rwr.restore_key, rwr.path, rwr.pass); + std::string res = restore(rwr.seed_phrase, rwr.path, rwr.pass, rwr.seed_pass); put_result(job_id, res); }; } diff --git a/src/wallet/plain_wallet_api.h b/src/wallet/plain_wallet_api.h index 610a96a3..4b92939d 100644 --- a/src/wallet/plain_wallet_api.h +++ b/src/wallet/plain_wallet_api.h @@ -27,7 +27,7 @@ namespace plain_wallet std::string get_connectivity_status(); std::string open(const std::string& path, const std::string& password); - std::string restore(const std::string& seed, const std::string& path, const std::string& password); + std::string restore(const std::string& seed, const std::string& path, const std::string& password, const std::string& seed_password); std::string generate(const std::string& path, const std::string& password); std::string get_opened_wallets(); diff --git a/src/wallet/view_iface.h b/src/wallet/view_iface.h index 908d2cd0..1dc31f2c 100644 --- a/src/wallet/view_iface.h +++ b/src/wallet/view_iface.h @@ -272,6 +272,19 @@ public: END_KV_SERIALIZE_MAP() }; + + struct request_get_smart_wallet_info + { + uint64_t wallet_id; + std::string seed_password; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(wallet_id) + KV_SERIALIZE(seed_password) + END_KV_SERIALIZE_MAP() + }; + + struct response_mining_estimate { uint64_t final_amount; @@ -429,16 +442,19 @@ public: END_KV_SERIALIZE_MAP() }; + struct restore_wallet_request { std::string pass; + std::string seed_pass; std::string path; - std::string restore_key; + std::string seed_phrase; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(pass) KV_SERIALIZE(path) - KV_SERIALIZE(restore_key) + KV_SERIALIZE(seed_pass) + KV_SERIALIZE(seed_phrase) END_KV_SERIALIZE_MAP() }; @@ -548,6 +564,8 @@ public: END_KV_SERIALIZE_MAP() }; + typedef tools::wallet_public::seed_info_param seed_info_param; + typedef tools::wallet_public::seed_phrase_info seed_phrase_info; struct start_backend_params { @@ -587,12 +605,10 @@ public: struct get_restore_info_response { - std::string restore_key; - std::string error_code; + std::string seed_phrase; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(restore_key) - KV_SERIALIZE(error_code) + KV_SERIALIZE(seed_phrase) END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ebbd5969..eed0f821 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2324,6 +2324,8 @@ void wallet2::assign_account(const currency::account_base& acc) clear(); m_account = acc; init_log_prefix(); + if (m_account.is_watch_only()) + m_watch_only = true; } //---------------------------------------------------------------------------------------------------- void wallet2::generate(const std::wstring& path, const std::string& pass, bool auditable_wallet) @@ -2345,7 +2347,7 @@ void wallet2::generate(const std::wstring& path, const std::string& pass, bool a store(); } //---------------------------------------------------------------------------------------------------- -void wallet2::restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet) +void wallet2::restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet, const std::string& seed_password) { bool r = false; clear(); @@ -2361,7 +2363,7 @@ void wallet2::restore(const std::wstring& path, const std::string& pass, const s } else { - r = m_account.restore_from_seed_phrase(seed_or_tracking_seed); + r = m_account.restore_from_seed_phrase(seed_or_tracking_seed, seed_password); init_log_prefix(); THROW_IF_FALSE_WALLET_EX(r, error::wallet_wrong_seed_error, epee::string_encoding::convert_to_ansii(m_wallet_file)); } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index e0507696..37bf0b09 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -470,7 +470,7 @@ namespace tools void assign_account(const currency::account_base& acc); void generate(const std::wstring& path, const std::string& password, bool auditable_wallet); - void restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet); + void restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet, const std::string& seed_password); void load(const std::wstring& path, const std::string& password); void store(); void store(const std::wstring& path); diff --git a/src/wallet/wallet_helpers.h b/src/wallet/wallet_helpers.h index 9f415dfb..e4cee938 100644 --- a/src/wallet/wallet_helpers.h +++ b/src/wallet/wallet_helpers.h @@ -23,4 +23,16 @@ namespace tools wi.is_watch_only = w.is_watch_only(); return true; } + + inline std::string get_seed_phrase_info(const std::string& seed_phrase, const std::string& seed_password, view::seed_phrase_info& result) + { + //cut the last timestamp word from restore_dats + result.syntax_correct = currency::account_base::is_seed_password_protected(seed_phrase, result.require_password); + if (result.syntax_correct) + { + currency::account_base acc; + result.hash_sum_matched = acc.restore_from_seed_phrase(seed_phrase, seed_password); + } + return API_RETURN_CODE_OK; + } } \ No newline at end of file diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index 420c4953..7b0c344e 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -168,6 +168,29 @@ namespace wallet_public }; + struct seed_info_param + { + std::string seed_phrase; + std::string seed_password; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(seed_phrase) + KV_SERIALIZE(seed_password) + END_KV_SERIALIZE_MAP() + }; + + struct seed_phrase_info + { + bool syntax_correct; + bool require_password; + bool hash_sum_matched; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(syntax_correct) + KV_SERIALIZE(require_password) + KV_SERIALIZE(hash_sum_matched) + END_KV_SERIALIZE_MAP() + }; struct COMMAND_RPC_GET_BALANCE { @@ -221,7 +244,6 @@ namespace wallet_public { std::string address; std::string path; - std::string seed; uint64_t transfers_count; uint64_t transfer_entries_count; bool is_whatch_only; @@ -230,7 +252,6 @@ namespace wallet_public BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) KV_SERIALIZE(path) - KV_SERIALIZE(seed) KV_SERIALIZE(transfers_count) KV_SERIALIZE(transfer_entries_count) KV_SERIALIZE(is_whatch_only) @@ -239,6 +260,32 @@ namespace wallet_public }; }; + struct COMMAND_RPC_GET_WALLET_RESTORE_INFO + { + struct request + { + std::string seed_password; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(seed_password) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string seed_phrase; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(seed_phrase) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_SEED_PHRASE_INFO + { + typedef seed_info_param request; + typedef seed_phrase_info response; + }; struct wallet_provision_info { diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index a2ea9db3..dda68d94 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -15,6 +15,7 @@ using namespace epee; #include "misc_language.h" #include "crypto/hash.h" #include "wallet_rpc_server_error_codes.h" +#include "wallet_helpers.h" #define WALLET_RPC_BEGIN_TRY_ENTRY() try { #define WALLET_RPC_CATCH_TRY_ENTRY() } \ @@ -196,7 +197,6 @@ namespace tools res.path = epee::string_encoding::convert_to_ansii(m_wallet.get_wallet_path()); res.transfers_count = m_wallet.get_recent_transfers_total_count(); res.transfer_entries_count = m_wallet.get_transfer_entries_count(); - res.seed = m_wallet.get_account().get_seed_phrase(); std::map distribution; m_wallet.get_utxo_distribution(distribution); for (const auto& ent : distribution) @@ -211,6 +211,25 @@ namespace tools return false; } } + bool wallet_rpc_server::on_getwallet_restore_info(const wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::request& req, wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + try + { + res.seed_phrase = m_wallet.get_account().get_seed_phrase(res.seed_phrase); + return true; + } + catch (std::exception& e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = e.what(); + return false; + } + } + bool wallet_rpc_server::on_get_seed_phrase_info(const wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::request& req, wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + tools::get_seed_phrase_info(req.seed_phrase, req.seed_password, res); + return true; + } bool wallet_rpc_server::on_get_recent_txs_and_info(const wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::request& req, wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx) { try diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 702a7c44..ccf26798 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -51,6 +51,8 @@ namespace tools MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_public::COMMAND_SIGN_TRANSFER) MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_public::COMMAND_SUBMIT_TRANSFER) MAP_JON_RPC_WE("search_for_transactions", on_search_for_transactions, wallet_public::COMMAND_RPC_SEARCH_FOR_TRANSACTIONS) + MAP_JON_RPC_WE("get_restore_info", on_getwallet_restore_info, wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO) + MAP_JON_RPC_WE("get_seed_phrase_info", on_get_seed_phrase_info, wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO) //contracts API MAP_JON_RPC_WE("contracts_send_proposal", on_contracts_send_proposal, wallet_public::COMMAND_CONTRACTS_SEND_PROPOSAL) MAP_JON_RPC_WE("contracts_accept_proposal", on_contracts_accept_proposal, wallet_public::COMMAND_CONTRACTS_ACCEPT_PROPOSAL) @@ -70,6 +72,8 @@ namespace tools bool on_getbalance(const wallet_public::COMMAND_RPC_GET_BALANCE::request& req, wallet_public::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_getaddress(const wallet_public::COMMAND_RPC_GET_ADDRESS::request& req, wallet_public::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_getwallet_info(const wallet_public::COMMAND_RPC_GET_WALLET_INFO::request& req, wallet_public::COMMAND_RPC_GET_WALLET_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_getwallet_restore_info(const wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::request& req, wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_get_seed_phrase_info(const wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::request& req, wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_get_recent_txs_and_info(const wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::request& req, wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_transfer(const wallet_public::COMMAND_RPC_TRANSFER::request& req, wallet_public::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_store(const wallet_public::COMMAND_RPC_STORE::request& req, wallet_public::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx); diff --git a/src/wallet/wallets_manager.cpp b/src/wallet/wallets_manager.cpp index b8d63e2b..ec07c3b2 100644 --- a/src/wallet/wallets_manager.cpp +++ b/src/wallet/wallets_manager.cpp @@ -911,7 +911,7 @@ std::string wallets_manager::open_wallet(const std::wstring& path, const std::st w->get_unconfirmed_transfers(owr.recent_history.history, exclude_mining_txs); owr.wallet_local_bc_size = w->get_blockchain_current_size(); //workaround for missed fee - owr.seed = w->get_account().get_seed_phrase(); + //owr.seed = w->get_account().get_seed_phrase(); break; } catch (const tools::error::file_not_found& /**/) @@ -1015,7 +1015,7 @@ std::string wallets_manager::generate_wallet(const std::wstring& path, const std { w->generate(path, password, false); w->set_minimum_height(m_last_daemon_height); - owr.seed = w->get_account().get_seed_phrase(); + //owr.seed = w->get_account().get_seed_phrase(); } catch (const tools::error::file_exists&) { @@ -1058,10 +1058,10 @@ std::string wallets_manager::is_pos_allowed() else return API_RETURN_CODE_FALSE; } -std::string wallets_manager::is_valid_brain_restore_data(const std::string& seed_phrase) +std::string wallets_manager::is_valid_brain_restore_data(const std::string& seed_phrase, const std::string& seed_password) { currency::account_base acc; - if (acc.restore_from_seed_phrase(seed_phrase)) + if (acc.restore_from_seed_phrase(seed_phrase, seed_password)) return API_RETURN_CODE_TRUE; currency::account_public_address addr; @@ -1081,11 +1081,18 @@ void wallets_manager::subscribe_to_core_events(currency::i_core_event_handler* p } #endif + +std::string wallets_manager::get_seed_phrase_info(const std::string& seed_phrase, const std::string& seed_password, view::seed_phrase_info& result) +{ + return tools::get_seed_phrase_info(seed_phrase, seed_password, result); +} + + void wallets_manager::get_gui_options(view::gui_options& opt) { opt = m_ui_opt; } -std::string wallets_manager::restore_wallet(const std::wstring& path, const std::string& password, const std::string& restore_key, view::open_wallet_response& owr) +std::string wallets_manager::restore_wallet(const std::wstring& path, const std::string& password, const std::string& seed_phrase, const std::string& seed_password, view::open_wallet_response& owr) { std::shared_ptr w(new tools::wallet2()); w->set_use_deffered_global_outputs(m_use_deffered_global_outputs); @@ -1108,9 +1115,9 @@ std::string wallets_manager::restore_wallet(const std::wstring& path, const std: currency::account_base acc; try { - bool auditable_watch_only = restore_key.find(':') != std::string::npos; - w->restore(path, password, restore_key, auditable_watch_only); - owr.seed = w->get_account().get_seed_phrase(); + bool auditable_watch_only = seed_phrase.find(':') != std::string::npos; + w->restore(path, password, seed_phrase, auditable_watch_only, seed_password); + //owr.seed = w->get_account().get_seed_phrase(); } catch (const tools::error::file_exists&) { @@ -1625,14 +1632,14 @@ std::string wallets_manager::get_mining_history(uint64_t wallet_id, tools::walle wo.w->get()->get_mining_history(mh); return API_RETURN_CODE_OK; } -std::string wallets_manager::get_wallet_restore_info(uint64_t wallet_id, std::string& restore_key) +std::string wallets_manager::get_wallet_restore_info(uint64_t wallet_id, std::string& seed_phrase, const std::string& seed_password) { GET_WALLET_OPT_BY_ID(wallet_id, wo); if (wo.wallet_state != view::wallet_status_info::wallet_state_ready || wo.long_refresh_in_progress) return API_RETURN_CODE_CORE_BUSY; - restore_key = wo.w->get()->get_account().get_seed_phrase(); + seed_phrase = wo.w->get()->get_account().get_seed_phrase(seed_password); return API_RETURN_CODE_OK; } diff --git a/src/wallet/wallets_manager.h b/src/wallet/wallets_manager.h index 0ed2ff61..151680d1 100644 --- a/src/wallet/wallets_manager.h +++ b/src/wallet/wallets_manager.h @@ -99,7 +99,7 @@ public: bool get_opened_wallets(std::list& result); std::string open_wallet(const std::wstring& path, const std::string& password, uint64_t txs_to_return, view::open_wallet_response& owr, bool exclude_mining_txs = false); std::string generate_wallet(const std::wstring& path, const std::string& password, view::open_wallet_response& owr); - std::string restore_wallet(const std::wstring& path, const std::string& password, const std::string& restore_key, view::open_wallet_response& owr); + std::string restore_wallet(const std::wstring& path, const std::string& password, const std::string& seed_phrase, const std::string& seed_password, view::open_wallet_response& owr); std::string invoke(uint64_t wallet_id, std::string params); std::string get_wallet_status(uint64_t wallet_id); std::string run_wallet(uint64_t wallet_id); @@ -132,7 +132,7 @@ public: std::string stop_pos_mining(uint64_t wallet_id); std::string check_available_sources(uint64_t wallet_id, std::list& amounts); std::string get_mining_history(uint64_t wallet_id, tools::wallet_public::mining_history& wrpc); - std::string get_wallet_restore_info(uint64_t wallet_id, std::string& restore_key); + std::string get_wallet_restore_info(uint64_t wallet_id, std::string& seed_phrase, const std::string& seed_password); std::string backup_wallet(uint64_t wallet_id, const std::wstring& path); std::string reset_wallet_password(uint64_t wallet_id, const std::string& pass); std::string is_wallet_password_valid(uint64_t wallet_id, const std::string& pass); @@ -149,7 +149,8 @@ public: void toggle_pos_mining(); std::string transfer(size_t wallet_id, const view::transfer_params& tp, currency::transaction& res_tx); std::string get_config_folder(); - std::string is_valid_brain_restore_data(const std::string& seed_phrase); + std::string is_valid_brain_restore_data(const std::string& seed_phrase, const std::string& seed_password); + std::string get_seed_phrase_info(const std::string& seed_phrase, const std::string& seed_password, view::seed_phrase_info& result); #ifndef MOBILE_WALLET_BUILD void subscribe_to_core_events(currency::i_core_event_handler* pevents_handler); //void unsubscribe_to_core_events(); diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index be070ceb..15a3fb43 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -863,6 +863,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(wallet_unconfirmed_tx_expiration); GENERATE_AND_PLAY(wallet_unconfimed_tx_balance); GENERATE_AND_PLAY(packing_outputs_on_pos_minting_wallet); + GENERATE_AND_PLAY(wallet_watch_only_and_chain_switch); GENERATE_AND_PLAY(wallet_rpc_integrated_address); GENERATE_AND_PLAY(wallet_rpc_integrated_address_transfer); diff --git a/tests/core_tests/wallet_tests.cpp b/tests/core_tests/wallet_tests.cpp index 85aec7c4..de118309 100644 --- a/tests/core_tests/wallet_tests.cpp +++ b/tests/core_tests/wallet_tests.cpp @@ -3441,5 +3441,103 @@ bool wallet_sending_to_integrated_address::c1(currency::core& c, size_t ev_index miner_wlt_2->refresh(); CHECK_AND_ASSERT_MES(callback_succeded, false, "callback was not succeded (2)"); + return true; +} + +//------------------------------------------------------------------------------ + +wallet_watch_only_and_chain_switch::wallet_watch_only_and_chain_switch() + : m_split_point_block_height(0) +{ + REGISTER_CALLBACK_METHOD(wallet_watch_only_and_chain_switch, c1); +} + +bool wallet_watch_only_and_chain_switch::generate(std::vector& events) const +{ + bool r = false; + + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.make_account_watch_only(); + account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(true); bob_acc.make_account_watch_only(); // Bob gonna have a tracking wallet + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + + // 0 10 11 12 13 23 15 <- blockchain height + // (0 )- (0r)- (1 )- (2 )- (3 )- (3r)- <- main chain + // | tx_0 + // \ tx_1 + // \ + // \- (2a)- (3a)- (3ar)- + + + MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner_acc); + MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_acc); + + MAKE_TX_FEE(events, tx_0, miner_acc, alice_acc, MK_TEST_COINS(9), TESTS_DEFAULT_FEE, blk_2); + MAKE_TX_FEE(events, tx_1, miner_acc, bob_acc, MK_TEST_COINS(7), TESTS_DEFAULT_FEE, blk_2); + + MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2, miner_acc, std::list({ tx_0, tx_1 })); + + REWIND_BLOCKS_N(events, blk_3r, blk_3, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE); + + m_split_point_block_id = get_block_hash(blk_1); + m_split_point_block_height = get_block_height(blk_1); + + DO_CALLBACK(events, "c1"); + + return true; +} + +bool wallet_watch_only_and_chain_switch::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + bool r = false; + std::shared_ptr miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX); + std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX); + std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX); + + // make sure Alice's wallet is watch-only but not a tracking wallet (not auditable) + CHECK_AND_ASSERT_MES(alice_wlt->is_watch_only() && !alice_wlt->is_auditable(), false, "incorrect type of Alice's wallet"); + // make sure Bob's wallet is a tracking wallet + CHECK_AND_ASSERT_MES(bob_wlt->is_watch_only() && bob_wlt->is_auditable(), false, "incorrect type of Bob's wallet"); + + miner_wlt->refresh(); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // check the balances + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(9), false, UINT64_MAX, MK_TEST_COINS(9), 0, 0, 0), false, ""); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, MK_TEST_COINS(7), false, UINT64_MAX, MK_TEST_COINS(7), 0, 0, 0), false, ""); + + // make a split + block out_block; + crypto::hash prev_id = m_split_point_block_id; + uint64_t current_height = m_split_point_block_height + 1; + + for (size_t i = 0; i < CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3; ++i) + { + r = mine_next_pow_block_in_playtime_with_given_txs(m_accounts[MINER_ACC_IDX].get_public_address(), c, std::vector(), prev_id, current_height, &out_block); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime_with_given_txs failed"); + current_height++; + prev_id = get_block_hash(out_block); + } + + // make sure the split has happened + uint64_t top_height = 0; + crypto::hash top_id; + c.get_blockchain_top(top_height, top_id); + CHECK_AND_ASSERT_MES(top_height == current_height - 1, false, "incorrect top_hegiht = " << top_height); + CHECK_AND_ASSERT_MES(top_id == prev_id, false, "incorrect top id = " << top_id); + + // tx_0 and tx_1 should be moved back to the pool + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 2, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + // check the balances, should be zero in actual and few coins in awaiting + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(9), false, UINT64_MAX, 0, 0, MK_TEST_COINS(9), 0), false, ""); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, MK_TEST_COINS(7), false, UINT64_MAX, 0, 0, MK_TEST_COINS(7), 0), false, ""); + + return true; } diff --git a/tests/core_tests/wallet_tests.h b/tests/core_tests/wallet_tests.h index 04637b3d..17c83bcd 100644 --- a/tests/core_tests/wallet_tests.h +++ b/tests/core_tests/wallet_tests.h @@ -265,4 +265,14 @@ struct wallet_sending_to_integrated_address : public wallet_test wallet_sending_to_integrated_address(); bool generate(std::vector& events) const; bool c1(currency::core& c, size_t ev_index, const std::vector& events); -}; \ No newline at end of file +}; + +struct wallet_watch_only_and_chain_switch : public wallet_test +{ + wallet_watch_only_and_chain_switch(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); + + mutable crypto::hash m_split_point_block_id; + mutable uint64_t m_split_point_block_height; +}; diff --git a/tests/unit_tests/db_tests.cpp b/tests/unit_tests/db_tests.cpp index 23fa7027..5fb03650 100644 --- a/tests/unit_tests/db_tests.cpp +++ b/tests/unit_tests/db_tests.cpp @@ -842,7 +842,7 @@ namespace db_test ptr = db_array[4]; ASSERT_EQ(ptr->v, "X"); - ASSERT_TRUE(db_array.clear()); + ASSERT_TRUE((db_array.clear()? true: false)); db_array.commit_transaction(); diff --git a/tests/unit_tests/wallet_seed_test.cpp b/tests/unit_tests/wallet_seed_test.cpp index 7030907a..d2ae5236 100644 --- a/tests/unit_tests/wallet_seed_test.cpp +++ b/tests/unit_tests/wallet_seed_test.cpp @@ -13,10 +13,10 @@ TEST(wallet_seed, store_restore_test) { currency::account_base acc; acc.generate(); - auto seed_phrase = acc.get_seed_phrase(); + auto seed_phrase = acc.get_seed_phrase(""); currency::account_base acc2; - bool r = acc2.restore_from_seed_phrase(seed_phrase); + bool r = acc2.restore_from_seed_phrase(seed_phrase, ""); ASSERT_TRUE(r); if (memcmp(&acc2.get_keys(), &acc.get_keys(), sizeof(currency::account_keys))) @@ -29,10 +29,10 @@ TEST(wallet_seed, store_restore_test) { currency::account_base acc; acc.generate(); - auto seed_phrase = acc.get_seed_phrase(); + auto seed_phrase = acc.get_seed_phrase(""); currency::account_base acc2; - bool r = acc2.restore_from_seed_phrase(seed_phrase); + bool r = acc2.restore_from_seed_phrase(seed_phrase, ""); ASSERT_TRUE(r); if (memcmp(&acc2.get_keys(), &acc.get_keys(), sizeof(currency::account_keys))) @@ -57,7 +57,7 @@ wallet_seed_entry wallet_seed_entries[] = { { // legacy 24-word seed phrase -- invalid - "dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew", + "dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew god", "", "", 0, @@ -66,7 +66,7 @@ wallet_seed_entry wallet_seed_entries[] = }, { // old-style 25-word seed phrase -- valid - "dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew", + "dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew god", "5e051454d7226b5734ebd64f754b57db4c655ecda00bd324f1b241d0b6381c0f", "7dde5590fdf430568c00556ac2accf09da6cde9a29a4bc7d1cb6fd267130f006", 0, @@ -148,7 +148,41 @@ TEST(wallet_seed, basic_test) bool r = false; try { - r = acc.restore_from_seed_phrase(wse.seed_phrase); + r = acc.restore_from_seed_phrase(wse.seed_phrase, ""); + if (r) + { + for (size_t j = 0; j != 100; j++) + { + //generate random password + std::string pass = epee::string_tools::pod_to_hex(crypto::cn_fast_hash(&j, sizeof(j))); + if (j!= 0 && j < 64) + { + pass.resize(j); + } + //get secured seed + std::string secured_seed = acc.get_seed_phrase(pass); + + //try to restore it without password(should fail) + currency::account_base acc2; + bool r_fail = acc2.restore_from_seed_phrase(secured_seed, ""); + ASSERT_EQ(r_fail, false); + + //try to restore it with wrong password + bool r_fake_pass = acc2.restore_from_seed_phrase(secured_seed, "fake_password"); + if (r_fake_pass) + { + //accidentally checksumm matched(quite possible) + ASSERT_EQ(false, acc2.get_keys() == acc.get_keys()); + } + + //try to restore it from right password + currency::account_base acc3; + bool r_true_res = acc3.restore_from_seed_phrase(secured_seed, pass); + ASSERT_EQ(true, r_true_res); + ASSERT_EQ(true, acc3.get_keys() == acc.get_keys()); + + } + } } catch (...) {