From d28c4e574b2a2084a7015c49e25d802bcc42d442 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 5 Dec 2019 07:50:36 -0500 Subject: [PATCH] "Can create a wallet from keys" test passing; remove wallet.get_seed() because redundant with get_private_spend_key() --- src/wallet/monero_wallet.h | 9 -- src/wallet/monero_wallet_keys.cpp | 142 +++++++++++++++++++----------- src/wallet/monero_wallet_keys.h | 27 +++--- src/wallet/monero_wallet_w2.cpp | 10 +-- src/wallet/monero_wallet_w2.h | 4 +- 5 files changed, 116 insertions(+), 76 deletions(-) diff --git a/src/wallet/monero_wallet.h b/src/wallet/monero_wallet.h index cd91fc2..d5fd65b 100644 --- a/src/wallet/monero_wallet.h +++ b/src/wallet/monero_wallet.h @@ -221,15 +221,6 @@ namespace monero { throw runtime_error("get_network_type() not implemented"); } - /** - * Get the wallet's seed. - * - * @return the wallet's seed - */ - virtual string get_seed() const { - throw runtime_error("get_seed() not implemented"); - } - /** * Get the wallet's mnemonic phrase derived from the seed. * diff --git a/src/wallet/monero_wallet_keys.cpp b/src/wallet/monero_wallet_keys.cpp index 1f9c58c..22b948a 100644 --- a/src/wallet/monero_wallet_keys.cpp +++ b/src/wallet/monero_wallet_keys.cpp @@ -57,8 +57,8 @@ #include #include "mnemonics/electrum-words.h" #include "mnemonics/english.h" -#include "cryptonote_basic.h" -#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "string_tools.h" #include "device/device.hpp" @@ -89,27 +89,86 @@ namespace monero { if (!is_valid) throw runtime_error("Invalid mnemonic"); if (language == crypto::ElectrumWords::old_language_name) language = Language::English().get_language_name(); - // initialize keys - cryptonote::account_base account{}; - account.generate(recovery_key, true, false); - const cryptonote::account_keys& keys = account.get_keys(); - - // initialize wallet + // initialize wallet account monero_wallet_keys* wallet = new monero_wallet_keys(); - wallet->m_seed = epee::string_tools::pod_to_hex(recovery_key); + wallet->m_account = cryptonote::account_base{}; + wallet->m_account.generate(recovery_key, true, false); + + // initialize remaining wallet + wallet->m_network_type = network_type; wallet->m_mnemonic = mnemonic; - wallet->m_primary_address = account.get_public_address_str(static_cast(network_type)); wallet->m_language = language; - wallet->m_pub_view_key = epee::string_tools::pod_to_hex(keys.m_account_address.m_view_public_key); - wallet->m_prv_view_key = epee::string_tools::pod_to_hex(keys.m_view_secret_key); - wallet->m_pub_spend_key = epee::string_tools::pod_to_hex(keys.m_account_address.m_spend_public_key); - wallet->m_prv_spend_key = epee::string_tools::pod_to_hex(keys.m_spend_secret_key); + wallet->init_common(); + return wallet; } monero_wallet_keys* monero_wallet_keys::create_wallet_from_keys(const monero_network_type network_type, const string& address, const string& view_key, const string& spend_key, const string& language) { cout << "monero_wallet_keys::create_wallet_from_keys(...)" << endl; - throw runtime_error("create_wallet_from_keys() not implemented"); + + // validate and parse address + cryptonote::address_parse_info info; + if (!cryptonote::get_account_address_from_str(info, static_cast(network_type), address)) throw runtime_error("failed to parse address"); + + // validate and parse optional private spend key + crypto::secret_key spend_key_sk; + bool has_spend_key = false; + if (!spend_key.empty()) { + cryptonote::blobdata spend_key_data; + if (!epee::string_tools::parse_hexstr_to_binbuff(spend_key, spend_key_data) || spend_key_data.size() != sizeof(crypto::secret_key)) { + throw runtime_error("failed to parse secret spend key"); + } + has_spend_key = true; + spend_key_sk = *reinterpret_cast(spend_key_data.data()); + } + + // validate and parse private view key + bool has_view_key = true; + crypto::secret_key view_key_sk; + if (view_key.empty()) { + if (has_spend_key) has_view_key = false; + else throw runtime_error("Neither view key nor spend key supplied, cancelled"); + } + if (has_view_key) { + cryptonote::blobdata view_key_data; + if (!epee::string_tools::parse_hexstr_to_binbuff(view_key, view_key_data) || view_key_data.size() != sizeof(crypto::secret_key)) { + throw runtime_error("failed to parse secret view key"); + } + view_key_sk = *reinterpret_cast(view_key_data.data()); + } + + // check the spend and view keys match the given address + crypto::public_key pkey; + if (has_spend_key) { + if (!crypto::secret_key_to_public_key(spend_key_sk, pkey)) throw runtime_error("failed to verify secret spend key"); + if (info.address.m_spend_public_key != pkey) throw runtime_error("spend key does not match address"); + } + if (has_view_key) { + if (!crypto::secret_key_to_public_key(view_key_sk, pkey)) throw runtime_error("failed to verify secret view key"); + if (info.address.m_view_public_key != pkey) throw runtime_error("view key does not match address"); + } + + // initialize wallet account + monero_wallet_keys* wallet = new monero_wallet_keys(); + if (has_spend_key && has_view_key) { + wallet->m_account.create_from_keys(info.address, spend_key_sk, view_key_sk); + } else if (has_spend_key) { + wallet->m_account.generate(spend_key_sk, true, false); + } else { + wallet->m_account.create_from_viewkey(info.address, view_key_sk); + } + + // initialize remaining wallet + wallet->m_network_type = network_type; + wallet->m_language = language; + epee::wipeable_string wipeable_mnemonic; + if (!crypto::ElectrumWords::bytes_to_words(spend_key_sk, wipeable_mnemonic, wallet->m_language)) { + throw runtime_error("Failed to create mnemonic from private spend key for language: " + string(wallet->m_language)); + } + wallet->m_mnemonic = string(wipeable_mnemonic.data(), wipeable_mnemonic.size()); + wallet->init_common(); + + return wallet; } // ----------------------------- WALLET METHODS ----------------------------- @@ -122,47 +181,20 @@ namespace monero { monero_version monero_wallet_keys::get_version() const { monero_version version; version.m_number = 65552; // same as monero-wallet-rpc v0.15.0.1 release - version.m_is_release = false; // TODO: could pull from MONERO_VERSION_IS_RELEASE in version.cpp + version.m_is_release = false; // TODO: could pull from MONERO_VERSION_IS_RELEASE in version.cpp return version; } - monero_network_type monero_wallet_keys::get_network_type() const { - cout << "monero_wallet_keys::get_network_type()" << endl; - throw runtime_error("monero_wallet_keys::get_network_type() not implemented"); - } - - string monero_wallet_keys::get_language() const { - return m_language; - } - vector monero_wallet_keys::get_languages() const { cout << "monero_wallet_keys::get_languages()" << endl; throw runtime_error("monero_wallet_keys::get_languages() not implemented"); } - string monero_wallet_keys::get_mnemonic() const { - return m_mnemonic; - } - - string monero_wallet_keys::get_public_view_key() const { - return m_pub_view_key; - } - - string monero_wallet_keys::get_private_view_key() const { - return m_prv_view_key; - } - - string monero_wallet_keys::get_public_spend_key() const { - return m_pub_spend_key; - } - - string monero_wallet_keys::get_private_spend_key() const { - return m_prv_spend_key; - } - string monero_wallet_keys::get_address(uint32_t account_idx, uint32_t subaddress_idx) const { - cout << "monero_wallet_keys::get_address()" << endl; - throw runtime_error("monero_wallet_keys::get_address() not implemented"); + hw::device &hwdev = m_account.get_device(); + cryptonote::subaddress_index index{account_idx, subaddress_idx}; + cryptonote::account_public_address address = hwdev.get_subaddress(m_account.get_keys(), index); + return cryptonote::get_account_address_as_str(static_cast(m_network_type), !index.is_zero(), address); } monero_subaddress monero_wallet_keys::get_address_index(const string& address) const { @@ -209,10 +241,22 @@ namespace monero { cout << "monero_wallet_keys::create_subaddress()" << endl; throw runtime_error("monero_wallet_keys::create_subaddress() not implemented"); } - void monero_wallet_keys::close() { + + void monero_wallet_keys::close(bool save) { cout << "monero_wallet_keys::close()" << endl; - throw runtime_error("monero_wallet_keys::close() not implemented"); + if (save) throw runtime_error("MoneroWalletKeys does not support saving"); + // no pointers to destroy } // ------------------------------- PRIVATE HELPERS ---------------------------- + + void monero_wallet_keys::init_common() { + m_primary_address = m_account.get_public_address_str(static_cast(m_network_type)); + const cryptonote::account_keys& keys = m_account.get_keys(); + m_pub_view_key = epee::string_tools::pod_to_hex(keys.m_account_address.m_view_public_key); + m_prv_view_key = epee::string_tools::pod_to_hex(keys.m_view_secret_key); + m_pub_spend_key = epee::string_tools::pod_to_hex(keys.m_account_address.m_spend_public_key); + m_prv_spend_key = epee::string_tools::pod_to_hex(keys.m_spend_secret_key); + if (m_prv_spend_key == "0000000000000000000000000000000000000000000000000000000000000000") m_prv_spend_key = ""; + } } diff --git a/src/wallet/monero_wallet_keys.h b/src/wallet/monero_wallet_keys.h index c81dacd..e0872b9 100644 --- a/src/wallet/monero_wallet_keys.h +++ b/src/wallet/monero_wallet_keys.h @@ -53,6 +53,7 @@ #pragma once #include "monero_wallet.h" +#include "cryptonote_basic/account.h" using namespace std; using namespace monero; @@ -90,8 +91,8 @@ namespace monero { * * @param network_type is the wallet's network type * @param address is the address of the wallet to construct - * @param view_key is the view key of the wallet to construct - * @param spend_key is the spend key of the wallet to construct + * @param view_key is the private view key of the wallet to construct + * @param spend_key is the private spend key of the wallet to construct * @param language is the wallet and mnemonic's language (default = "English") */ static monero_wallet_keys* create_wallet_from_keys(const monero_network_type network_type, const string& address, const string& view_key, const string& spend_key, const string& language = "English"); @@ -105,14 +106,15 @@ namespace monero { * Supported wallet methods. */ monero_version get_version() const; - monero_network_type get_network_type() const; - string get_mnemonic() const; - string get_language() const; + monero_network_type get_network_type() const { return m_network_type; } + string get_mnemonic() const { return m_mnemonic; } + string get_language() const { return m_language; } vector get_languages() const; - string get_public_view_key() const; - string get_private_view_key() const; - string get_public_spend_key() const; - string get_private_spend_key() const; + string get_private_view_key() const { return m_prv_view_key; } + string get_private_spend_key() const { return m_prv_spend_key; } + string get_public_view_key() const { return m_pub_view_key; } + string get_public_spend_key() const { return m_pub_spend_key; } + string get_primary_address() const { return m_primary_address; } string get_address(const uint32_t account_idx, const uint32_t subaddress_idx) const; monero_subaddress get_address_index(const string& address) const; monero_integrated_address get_integrated_address(const string& standard_address = "", const string& payment_id = "") const; @@ -125,12 +127,13 @@ namespace monero { monero_subaddress create_subaddress(uint32_t account_idx, const string& label = ""); string sign(const string& msg) const; bool verify(const string& msg, const string& address, const string& signature) const; - void close(); + void close(bool save = false); // --------------------------------- PRIVATE -------------------------------- private: - string m_seed; + monero_network_type m_network_type; + cryptonote::account_base m_account; string m_mnemonic; string m_language; string m_pub_view_key; @@ -138,5 +141,7 @@ namespace monero { string m_pub_spend_key; string m_prv_spend_key; string m_primary_address; + + void init_common(); }; } diff --git a/src/wallet/monero_wallet_w2.cpp b/src/wallet/monero_wallet_w2.cpp index f5686bc..dceac63 100644 --- a/src/wallet/monero_wallet_w2.cpp +++ b/src/wallet/monero_wallet_w2.cpp @@ -911,8 +911,8 @@ namespace monero { // initialize wallet wallet->m_w2 = unique_ptr(new tools::wallet2(static_cast(network_type), 1, true)); if (has_spend_key && has_view_key) wallet->m_w2->generate(path, password, info.address, spend_key_sk, view_key_sk); - if (!has_spend_key && has_view_key) wallet->m_w2->generate(path, password, info.address, view_key_sk); - if (has_spend_key && !has_view_key) wallet->m_w2->generate(path, password, spend_key_sk, true, false); + else if (has_spend_key) wallet->m_w2->generate(path, password, spend_key_sk, true, false); + else wallet->m_w2->generate(path, password, info.address, view_key_sk); wallet->set_daemon_connection(daemon_connection); wallet->m_w2->set_refresh_from_block_height(restore_height); wallet->m_w2->set_seed_language(language); @@ -1010,9 +1010,9 @@ namespace monero { } string monero_wallet_w2::get_mnemonic() const { - epee::wipeable_string wipeablePassword; - m_w2->get_seed(wipeablePassword); - return string(wipeablePassword.data(), wipeablePassword.size()); + epee::wipeable_string wipeable_mnemonic; + m_w2->get_seed(wipeable_mnemonic); + return string(wipeable_mnemonic.data(), wipeable_mnemonic.size()); } string monero_wallet_w2::get_public_view_key() const { diff --git a/src/wallet/monero_wallet_w2.h b/src/wallet/monero_wallet_w2.h index 164b83e..4484ac3 100644 --- a/src/wallet/monero_wallet_w2.h +++ b/src/wallet/monero_wallet_w2.h @@ -131,8 +131,8 @@ namespace monero { * @param password is the password encrypt the wallet * @param network_type is the wallet's network type * @param address is the address of the wallet to construct - * @param view_key is the view key of the wallet to construct - * @param spend_key is the spend key of the wallet to construct + * @param view_key is the private view key of the wallet to construct + * @param spend_key is the private spend key of the wallet to construct * @param daemon_connection is connection information to a daemon (default = an unconnected wallet) * @param restore_height is the block height to restore (i.e. scan the chain) from (default = 0) * @param language is the wallet and mnemonic's language (default = "English")