From 40fc9d7b68ad454355af1fae6c2c13e72bd0c662 Mon Sep 17 00:00:00 2001 From: JollyMort Date: Sat, 29 Jul 2017 01:41:24 +0200 Subject: [PATCH 1/2] Add option to join multisig wallet pieces together Asks user for all the data required to merge secret keys from multisig wallets into one master wallet, which then gets full control of the multisig wallet. The resulting wallet will be the same as any other regular wallet. --- src/simplewallet/simplewallet.cpp | 153 +++++++++++++++++++++++++++++- src/simplewallet/simplewallet.h | 1 + 2 files changed, 151 insertions(+), 3 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index adf2fde80..cc8b884fb 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -115,6 +115,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_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view 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"), ""}; const auto arg_generate_from_json = wallet_args::arg_generate_from_json(); const command_line::arg_descriptor arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""}; const command_line::arg_descriptor arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false}; @@ -932,12 +933,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (!handle_command_line(vm)) return false; - if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_json.empty()) > 1) + if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_view_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-json=\"jsonfilename\" and --generate-from-keys=\"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-keys=\"wallet_name\", --generate-from-multisig-keys=\"wallet_name\" and --generate-from-json=\"jsonfilename\""); return false; } - else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_view_key.empty() && m_generate_from_keys.empty() && m_generate_from_json.empty()) + else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_view_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; } @@ -1110,6 +1111,149 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) bool r = new_wallet(vm, address, spendkey, viewkey); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); } + + // Asks user for all the data required to merge secret keys from multisig wallets into one master wallet, which then gets full control of the multisig wallet. The resulting wallet will be the same as any other regular wallet. + else if (!m_generate_from_multisig_keys.empty()) + { + m_wallet_file = m_generate_from_multisig_keys; + unsigned int multisig_m; + unsigned int multisig_n; + + // parse multisig type + std::string multisig_type_string = command_line::input_line("Multisig type (input as M/N with M <= N and M > 1): "); + if (std::cin.eof()) + return false; + if (multisig_type_string.empty()) + { + fail_msg_writer() << tr("No data supplied, cancelled"); + return false; + } + if (sscanf(multisig_type_string.c_str(), "%u/%u", &multisig_m, &multisig_n) != 2) + { + fail_msg_writer() << "Error: expected M/N, but got: " << multisig_type_string[0]; + return false; + } + if (multisig_m <= 1 || multisig_m > multisig_n) + { + fail_msg_writer() << "Error: expected N > 1 and N <= M, but got: " << multisig_type_string[0]; + return false; + } + if (multisig_m != multisig_n) + { + fail_msg_writer() << "Error: M/N is currently unsupported. "; + return false; + } + message_writer() << "Generating master wallet from " << multisig_m << " of " << multisig_n << " multisig wallet pieces"; + + // parse multisig address + std::string address_string = command_line::input_line("Multisig wallet address: "); + if (std::cin.eof()) + return false; + if (address_string.empty()) { + fail_msg_writer() << tr("No data supplied, cancelled"); + return false; + } + cryptonote::account_public_address address; + bool has_payment_id; + crypto::hash8 new_payment_id; + if(!get_account_integrated_address_from_str(address, has_payment_id, new_payment_id, tools::wallet2::has_testnet_option(vm), address_string)) + { + fail_msg_writer() << tr("failed to parse address"); + return false; + } + + // parse secret view key + std::string viewkey_string = command_line::input_line("Secret view key: "); + if (std::cin.eof()) + return false; + if (viewkey_string.empty()) + { + fail_msg_writer() << tr("No data supplied, cancelled"); + return false; + } + cryptonote::blobdata viewkey_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) + { + fail_msg_writer() << tr("failed to parse secret view key"); + return false; + } + crypto::secret_key viewkey = *reinterpret_cast(viewkey_data.data()); + + // check that 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 secret view key"); + return false; + } + if (address.m_view_public_key != pkey) + { + fail_msg_writer() << tr("view key does not match standard address"); + return false; + } + + // parse multisig spend keys + crypto::secret_key spendkey; + // parsing N/N + if(multisig_m == multisig_n) + { + std::vector multisig_secret_spendkeys(multisig_n); + std::string input_string; + std::string spendkey_string; + cryptonote::blobdata spendkey_data; + // get N secret spend keys from user + for(unsigned int i=0; i(spendkey_data.data()); + } + + // sum the spend keys together to get the master spend key + crypto::secret_key spendkey2; + spendkey = multisig_secret_spendkeys[0]; + for(unsigned int i=1; i(&spendkey2), reinterpret_cast(&spendkey), reinterpret_cast(&multisig_secret_spendkeys[i])); + spendkey = spendkey2; + } + } + // parsing M/N + else + { + fail_msg_writer() << "Error: M/N is currently unsupported. "; + return false; + } + + // check that the spend key matches the given address + if (!crypto::secret_key_to_public_key(spendkey, pkey)) + { + fail_msg_writer() << tr("failed to verify spend key secret key"); + return false; + } + if (address.m_spend_public_key != pkey) + { + fail_msg_writer() << tr("spend key does not match standard address"); + return false; + } + + // create wallet + bool r = new_wallet(vm, address, spendkey, viewkey); + CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); + } + else if (!m_generate_from_json.empty()) { m_wallet_file = m_generate_from_json; @@ -1235,6 +1379,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_view_key = command_line::get_arg(vm, arg_generate_from_view_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); m_generate_from_json = command_line::get_arg(vm, arg_generate_from_json); m_electrum_seed = command_line::get_arg(vm, arg_electrum_seed); m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet); @@ -1244,6 +1389,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_restore_height = command_line::get_arg(vm, arg_restore_height); m_restoring = !m_generate_from_view_key.empty() || !m_generate_from_keys.empty() || + !m_generate_from_multisig_keys.empty() || !m_generate_from_json.empty() || m_restore_deterministic_wallet; @@ -4585,6 +4731,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_view_key); command_line::add_arg(desc_params, arg_generate_from_keys); + command_line::add_arg(desc_params, arg_generate_from_multisig_keys); command_line::add_arg(desc_params, arg_generate_from_json); command_line::add_arg(desc_params, arg_command); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index e04352295..b73c6a941 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -264,6 +264,7 @@ namespace cryptonote std::string m_generate_new; std::string m_generate_from_view_key; std::string m_generate_from_keys; + std::string m_generate_from_multisig_keys; std::string m_generate_from_json; std::string m_import_path; From 02f13d6cdfa6ad524ed9eed93f9923c9af9786e6 Mon Sep 17 00:00:00 2001 From: JollyMort Date: Sat, 29 Jul 2017 13:49:12 +0200 Subject: [PATCH 2/2] Fix handling of strings & simplify summation of spendkeys --- src/simplewallet/simplewallet.cpp | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index cc8b884fb..6b87d253c 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1130,20 +1130,20 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } if (sscanf(multisig_type_string.c_str(), "%u/%u", &multisig_m, &multisig_n) != 2) { - fail_msg_writer() << "Error: expected M/N, but got: " << multisig_type_string[0]; + fail_msg_writer() << tr("Error: expected M/N, but got: ") << multisig_type_string; return false; } if (multisig_m <= 1 || multisig_m > multisig_n) { - fail_msg_writer() << "Error: expected N > 1 and N <= M, but got: " << multisig_type_string[0]; + fail_msg_writer() << tr("Error: expected N > 1 and N <= M, but got: ") << multisig_type_string; return false; } if (multisig_m != multisig_n) { - fail_msg_writer() << "Error: M/N is currently unsupported. "; + fail_msg_writer() << tr("Error: M/N is currently unsupported. "); return false; } - message_writer() << "Generating master wallet from " << multisig_m << " of " << multisig_n << " multisig wallet pieces"; + message_writer() << boost::format(tr("Generating master wallet from %u of %u multisig wallet keys")) % multisig_m % multisig_n; // parse multisig address std::string address_string = command_line::input_line("Multisig wallet address: "); @@ -1198,14 +1198,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if(multisig_m == multisig_n) { std::vector multisig_secret_spendkeys(multisig_n); - std::string input_string; std::string spendkey_string; cryptonote::blobdata spendkey_data; // get N secret spend keys from user for(unsigned int i=0; i(&spendkey2), reinterpret_cast(&spendkey), reinterpret_cast(&multisig_secret_spendkeys[i])); - spendkey = spendkey2; - } + sc_add(reinterpret_cast(&spendkey), reinterpret_cast(&spendkey), reinterpret_cast(&multisig_secret_spendkeys[i])); } // parsing M/N else { - fail_msg_writer() << "Error: M/N is currently unsupported. "; + fail_msg_writer() << tr("Error: M/N is currently unsupported"); return false; }