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.
This commit is contained in:
parent
ab594cfee9
commit
40fc9d7b68
|
@ -115,6 +115,7 @@ namespace
|
||||||
const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""};
|
const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""};
|
||||||
const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""};
|
const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""};
|
||||||
const command_line::arg_descriptor<std::string> arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""};
|
const command_line::arg_descriptor<std::string> arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""};
|
||||||
|
const command_line::arg_descriptor<std::string> 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 auto arg_generate_from_json = wallet_args::arg_generate_from_json();
|
||||||
const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""};
|
const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""};
|
||||||
const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false};
|
const command_line::arg_descriptor<bool> 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))
|
if (!handle_command_line(vm))
|
||||||
return false;
|
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;
|
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;
|
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);
|
bool r = new_wallet(vm, address, spendkey, viewkey);
|
||||||
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
|
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<const crypto::secret_key*>(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<crypto::secret_key> 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<multisig_n; ++i)
|
||||||
|
{
|
||||||
|
input_string = "Secret spend key (" + std::to_string(i+1) + tr(" of ") + std::to_string(multisig_m) + "): ";
|
||||||
|
spendkey_string = command_line::input_line(tr(input_string.c_str()));
|
||||||
|
if (std::cin.eof())
|
||||||
|
return false;
|
||||||
|
if (spendkey_string.empty())
|
||||||
|
{
|
||||||
|
fail_msg_writer() << tr("No data supplied, cancelled");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
|
||||||
|
{
|
||||||
|
fail_msg_writer() << tr("failed to parse spend key secret key");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
multisig_secret_spendkeys[i] = *reinterpret_cast<const crypto::secret_key*>(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<multisig_n; ++i)
|
||||||
|
{
|
||||||
|
sc_add(reinterpret_cast<unsigned char*>(&spendkey2), reinterpret_cast<unsigned char*>(&spendkey), reinterpret_cast<unsigned char*>(&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())
|
else if (!m_generate_from_json.empty())
|
||||||
{
|
{
|
||||||
m_wallet_file = m_generate_from_json;
|
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_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_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_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_generate_from_json = command_line::get_arg(vm, arg_generate_from_json);
|
||||||
m_electrum_seed = command_line::get_arg(vm, arg_electrum_seed);
|
m_electrum_seed = command_line::get_arg(vm, arg_electrum_seed);
|
||||||
m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet);
|
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_restore_height = command_line::get_arg(vm, arg_restore_height);
|
||||||
m_restoring = !m_generate_from_view_key.empty() ||
|
m_restoring = !m_generate_from_view_key.empty() ||
|
||||||
!m_generate_from_keys.empty() ||
|
!m_generate_from_keys.empty() ||
|
||||||
|
!m_generate_from_multisig_keys.empty() ||
|
||||||
!m_generate_from_json.empty() ||
|
!m_generate_from_json.empty() ||
|
||||||
m_restore_deterministic_wallet;
|
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_new_wallet);
|
||||||
command_line::add_arg(desc_params, arg_generate_from_view_key);
|
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_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_generate_from_json);
|
||||||
command_line::add_arg(desc_params, arg_command);
|
command_line::add_arg(desc_params, arg_command);
|
||||||
|
|
||||||
|
|
|
@ -264,6 +264,7 @@ namespace cryptonote
|
||||||
std::string m_generate_new;
|
std::string m_generate_new;
|
||||||
std::string m_generate_from_view_key;
|
std::string m_generate_from_view_key;
|
||||||
std::string m_generate_from_keys;
|
std::string m_generate_from_keys;
|
||||||
|
std::string m_generate_from_multisig_keys;
|
||||||
std::string m_generate_from_json;
|
std::string m_generate_from_json;
|
||||||
std::string m_import_path;
|
std::string m_import_path;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue