From 0c4998b09172c51039be66981cf5053372ee458d Mon Sep 17 00:00:00 2001 From: somerandomcryptoguy <139346562+somerandomcryptoguy@users.noreply.github.com> Date: Wed, 3 Sep 2025 12:33:09 +0100 Subject: [PATCH] added --generate-from-svb-key functionality (#53) Co-authored-by: Some Random Crypto Guy --- src/carrot_core/account.cpp | 30 ++++++++++++ src/carrot_core/account.h | 1 + src/simplewallet/simplewallet.cpp | 78 +++++++++++++++++++++++++++++-- src/simplewallet/simplewallet.h | 8 +++- src/wallet/wallet2.cpp | 8 +++- src/wallet/wallet2.h | 1 + 6 files changed, 117 insertions(+), 9 deletions(-) diff --git a/src/carrot_core/account.cpp b/src/carrot_core/account.cpp index 67e086d..9c6cae6 100644 --- a/src/carrot_core/account.cpp +++ b/src/carrot_core/account.cpp @@ -336,6 +336,34 @@ void carrot_and_legacy_account::set_keys(const cryptonote::account_keys& keys, b m_keys.m_carrot_main_address = keys.m_carrot_main_address; } //---------------------------------------------------------------------------------------------------------------------- +void carrot_and_legacy_account::create_from_svb_key(const cryptonote::account_public_address& address, const crypto::secret_key& svb_key) +{ + // top level keys + m_keys.s_master = crypto::null_skey; + make_carrot_provespend_key(m_keys.s_master, m_keys.k_prove_spend); + m_keys.s_view_balance = svb_key; + + // view balance keys + make_carrot_viewincoming_key(m_keys.s_view_balance, m_keys.k_view_incoming); + make_carrot_generateimage_key(m_keys.s_view_balance, m_keys.k_generate_image); + make_carrot_generateaddress_secret(m_keys.s_view_balance, m_keys.s_generate_address); + + // carrot account address - use the provided address spend pubkey + m_keys.m_carrot_account_address = address; + k_view_incoming_dev.view_key_scalar_mult_ed25519(m_keys.m_carrot_account_address.m_spend_public_key, + m_keys.m_carrot_account_address.m_view_public_key + ); + + // carrot main wallet address + m_keys.m_carrot_main_address = address; + k_view_incoming_dev.view_key_scalar_mult_ed25519(crypto::get_G(), + m_keys.m_carrot_main_address.m_view_public_key + ); + + this->default_derive_type = AddressDeriveType::Carrot; + generate_subaddress_map(); +} +//---------------------------------------------------------------------------------------------------------------------- void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_derive_type) { // top level keys @@ -354,6 +382,7 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_ m_keys.m_carrot_account_address.m_spend_public_key, m_keys.m_carrot_account_address.m_view_public_key ); + m_keys.m_carrot_account_address.m_is_carrot = true; // carrot main wallet address m_keys.m_carrot_main_address.m_spend_public_key = m_keys.m_carrot_account_address.m_spend_public_key; @@ -361,6 +390,7 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_ crypto::get_G(), m_keys.m_carrot_main_address.m_view_public_key ); + m_keys.m_carrot_main_address.m_is_carrot = true; this->default_derive_type = default_derive_type; generate_subaddress_map(); diff --git a/src/carrot_core/account.h b/src/carrot_core/account.h index 07f45a5..0df6460 100644 --- a/src/carrot_core/account.h +++ b/src/carrot_core/account.h @@ -153,6 +153,7 @@ namespace carrot const AddressDeriveType default_derive_type = AddressDeriveType::Carrot ); + void create_from_svb_key(const cryptonote::account_public_address& address, const crypto::secret_key& svb_key); void set_carrot_keys(const AddressDeriveType default_derive_type = AddressDeriveType::Carrot); void insert_subaddresses(const std::unordered_map& subaddress_map); void insert_return_output_info( diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index fae3b71..2676cc2 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -192,6 +192,7 @@ namespace const command_line::arg_descriptor arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to "), ""}; const command_line::arg_descriptor arg_generate_from_device = {"generate-from-device", sw::tr("Generate new wallet from device and save it to "), ""}; const command_line::arg_descriptor arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""}; + const command_line::arg_descriptor arg_generate_from_svb_key = {"generate-from-svb-key", sw::tr("Generate full view-only wallet from view key"), ""}; const command_line::arg_descriptor arg_generate_from_spend_key = {"generate-from-spend-key", sw::tr("Generate deterministic wallet from spend key"), ""}; const command_line::arg_descriptor arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""}; const command_line::arg_descriptor arg_generate_from_multisig_keys = {"generate-from-multisig-keys", sw::tr("Generate a master wallet from multisig wallet keys"), ""}; @@ -4424,12 +4425,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) bool welcome = false; - if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_device.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_spend_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_multisig_keys.empty()) + (!m_generate_from_json.empty()) > 1) + if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_device.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_svb_key.empty()) + (!m_generate_from_spend_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_multisig_keys.empty()) + (!m_generate_from_json.empty()) > 1) { - fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\", --generate-from-spend-key=\"wallet_name\", --generate-from-keys=\"wallet_name\", --generate-from-multisig-keys=\"wallet_name\", --generate-from-json=\"jsonfilename\" and --generate-from-device=\"wallet_name\""); + fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\", --generate-from-svb-key=\"wallet_name\", --generate-from-spend-key=\"wallet_name\", --generate-from-keys=\"wallet_name\", --generate-from-multisig-keys=\"wallet_name\", --generate-from-json=\"jsonfilename\" and --generate-from-device=\"wallet_name\""); return false; } - else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_device.empty() && m_generate_from_view_key.empty() && m_generate_from_spend_key.empty() && m_generate_from_keys.empty() && m_generate_from_multisig_keys.empty() && m_generate_from_json.empty()) + else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_device.empty() && m_generate_from_view_key.empty() && m_generate_from_svb_key.empty() && m_generate_from_spend_key.empty() && m_generate_from_keys.empty() && m_generate_from_multisig_keys.empty() && m_generate_from_json.empty()) { if(!ask_wallet_create_if_needed()) return false; } @@ -4584,6 +4585,69 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) password = *r; welcome = true; } + else if (!m_generate_from_svb_key.empty()) + { + m_wallet_file = m_generate_from_svb_key; + // parse address + std::string address_string = input_line("Standard address"); + if (std::cin.eof()) + return false; + if (address_string.empty()) { + fail_msg_writer() << tr("No data supplied, cancelled"); + return false; + } + cryptonote::address_parse_info info; + if(!get_account_address_from_str(info, nettype, address_string)) + { + fail_msg_writer() << tr("failed to parse address"); + return false; + } + if (info.is_subaddress) + { + fail_msg_writer() << tr("This address is a subaddress which cannot be used here."); + return false; + } + if (!info.is_carrot) + { + fail_msg_writer() << tr("This address is not a Carrot address, and cannot be used here."); + return false; + } + + // parse view secret key + epee::wipeable_string viewkey_string = input_secure_line("Secret view key"); + if (std::cin.eof()) + return false; + if (viewkey_string.empty()) { + fail_msg_writer() << tr("No data supplied, cancelled"); + return false; + } + crypto::secret_key viewkey; + if (!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey)))) + { + fail_msg_writer() << tr("failed to parse view key secret key"); + return false; + } + + // Create all of the necessary keys for Carrot view-only wallet + + /* + // check the view key matches the given address + crypto::public_key pkey; + if (!crypto::secret_key_to_public_key(viewkey, pkey)) { + fail_msg_writer() << tr("failed to verify view key secret key"); + return false; + } + if (info.address.m_view_public_key != pkey) { + fail_msg_writer() << tr("view key does not match standard address"); + return false; + } + */ + + auto r = new_wallet(vm, info.address, boost::none, viewkey); + CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); + password = *r; + welcome = true; + } else if (!m_generate_from_spend_key.empty()) { m_wallet_file = m_generate_from_spend_key; @@ -5049,6 +5113,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet); m_generate_from_device = command_line::get_arg(vm, arg_generate_from_device); m_generate_from_view_key = command_line::get_arg(vm, arg_generate_from_view_key); + m_generate_from_svb_key = command_line::get_arg(vm, arg_generate_from_svb_key); m_generate_from_spend_key = command_line::get_arg(vm, arg_generate_from_spend_key); m_generate_from_keys = command_line::get_arg(vm, arg_generate_from_keys); m_generate_from_multisig_keys = command_line::get_arg(vm, arg_generate_from_multisig_keys); @@ -5064,6 +5129,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_subaddress_lookahead = command_line::get_arg(vm, arg_subaddress_lookahead); m_use_english_language_names = command_line::get_arg(vm, arg_use_english_language_names); m_restoring = !m_generate_from_view_key.empty() || + !m_generate_from_svb_key.empty() || !m_generate_from_spend_key.empty() || !m_generate_from_keys.empty() || !m_generate_from_multisig_keys.empty() || @@ -5287,8 +5353,9 @@ boost::optional simple_wallet::new_wallet(const boost::pr } //---------------------------------------------------------------------------------------------------- boost::optional simple_wallet::new_wallet(const boost::program_options::variables_map& vm, - const cryptonote::account_public_address& address, const boost::optional& spendkey, - const crypto::secret_key& viewkey) + const cryptonote::account_public_address& address, + const boost::optional& spendkey, + const crypto::secret_key& viewkey) { std::pair, tools::password_container> rc; try { rc = tools::wallet2::make_new(vm, false, password_prompter); } @@ -11829,6 +11896,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_generate_new_wallet); command_line::add_arg(desc_params, arg_generate_from_device); command_line::add_arg(desc_params, arg_generate_from_view_key); + command_line::add_arg(desc_params, arg_generate_from_svb_key); command_line::add_arg(desc_params, arg_generate_from_spend_key); command_line::add_arg(desc_params, arg_generate_from_keys); command_line::add_arg(desc_params, arg_generate_from_multisig_keys); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 13de5b0..d0d2569 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -101,8 +101,11 @@ namespace cryptonote boost::optional new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language); - boost::optional new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address, - const boost::optional& spendkey, const crypto::secret_key& viewkey); + boost::optional new_wallet(const boost::program_options::variables_map& vm, + const cryptonote::account_public_address& address, + const boost::optional& spendkey, + const crypto::secret_key& viewkey + ); boost::optional new_wallet(const boost::program_options::variables_map& vm, const epee::wipeable_string &multisig_keys, const epee::wipeable_string &seed_pass, const std::string &old_language); boost::optional new_wallet(const boost::program_options::variables_map& vm); @@ -430,6 +433,7 @@ namespace cryptonote std::string m_generate_new; std::string m_generate_from_device; std::string m_generate_from_view_key; + std::string m_generate_from_svb_key; std::string m_generate_from_spend_key; std::string m_generate_from_keys; std::string m_generate_from_multisig_keys; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 1fac786..ae098a7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5845,7 +5845,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip */ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password, const cryptonote::account_public_address &account_public_address, - const crypto::secret_key& viewkey, bool create_address_file) + const crypto::secret_key& viewkey, bool create_address_file) { clear(); prepare_file_names(wallet_); @@ -5857,7 +5857,11 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file); } - m_account.create_from_viewkey(account_public_address, viewkey); + if (account_public_address.m_is_carrot) { + m_account.create_from_svb_key(account_public_address, viewkey); + } else { + m_account.create_from_viewkey(account_public_address, viewkey); + } init_type(hw::device::device_type::SOFTWARE); m_watch_only = true; m_account_public_address = account_public_address; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 7c9ea22..77a2082 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1019,6 +1019,7 @@ private: * \param account_public_address The account's public address * \param viewkey view secret key * \param create_address_file Whether to create an address file + * \param is_carrot Whether viewkey is k_v (CN) or s_vb (Carrot) */ void generate(const std::string& wallet, const epee::wipeable_string& password, const cryptonote::account_public_address &account_public_address,