diff options
author | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2016-03-25 00:48:11 +0000 |
---|---|---|
committer | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2016-03-25 00:56:11 +0000 |
commit | a2e378b91bf5d2a54284f8e20df5080f11d25ff6 (patch) | |
tree | ead687de0c31ddcc9f5e45248444f389d5462c72 /src/simplewallet | |
parent | Merge pull request #746 (diff) | |
download | monero-a2e378b91bf5d2a54284f8e20df5080f11d25ff6.tar.xz |
wallet: add a --generate-from-json flag
It takes a filename containing JSON data to generate a wallet.
The following fields are valid:
version: integer, should be 1
filename: string, path/filename for the newly created wallet
scan_from_height: 64 bit unsigned integer, optional
password: string, optional
viewkey: string, hex representation
spendkey: string, hex representation
seed: string, optional, list of words separated by spaces
Either seed or private keys should be given. If using private
keys, the spend key may be omitted (the wallet will not be
able to spend, but will see incoming transactions).
If scan_from_height is given, blocks below this height will not
be checked for transactions as an optimization.
Diffstat (limited to 'src/simplewallet')
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 186 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.h | 2 |
2 files changed, 182 insertions, 6 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ea1993c80..1856118f7 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -57,6 +57,7 @@ #include "version.h" #include "crypto/crypto.h" // for crypto::secret_key definition #include "mnemonics/electrum-words.h" +#include "rapidjson/document.h" #include <stdexcept> #if defined(WIN32) @@ -81,6 +82,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> or <address>.wallet by default"), ""}; 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_json = {"generate-from-json", sw::tr("Generate wallet from JSON format file"), ""}; const command_line::arg_descriptor<std::string> arg_daemon_address = {"daemon-address", sw::tr("Use daemon instance at <host>:<port>"), ""}; const command_line::arg_descriptor<std::string> arg_daemon_host = {"daemon-host", sw::tr("Use daemon instance at host <arg> instead of localhost"), ""}; const command_line::arg_descriptor<std::string> arg_password = {"password", sw::tr("Wallet password"), "", true}; @@ -716,7 +718,7 @@ bool simple_wallet::ask_wallet_create_if_needed() // add logic to error out if new wallet requested but named wallet file exists if (keys_file_exists || wallet_file_exists) { - if (!m_generate_new.empty() || m_restore_deterministic_wallet || !m_generate_from_view_key.empty() || !m_generate_from_keys.empty()) + if (!m_generate_new.empty() || m_restore_deterministic_wallet || !m_generate_from_view_key.empty() || !m_generate_from_keys.empty() || !m_generate_from_json.empty()) { fail_msg_writer() << tr("attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting."); return false; @@ -763,6 +765,13 @@ void simple_wallet::print_seed(std::string seed) //---------------------------------------------------------------------------------------------------- static bool get_password(const boost::program_options::variables_map& vm, bool allow_entry, tools::password_container &pwd_container) { + // has_arg returns true even when the parameter is not passed ?? + const std::string gfj = command_line::get_arg(vm, arg_generate_from_json); + if (!gfj.empty()) { + // will be in the json file, if any + return true; + } + if (has_arg(vm, arg_password) && has_arg(vm, arg_password_file)) { fail_msg_writer() << tr("can't specify more than one of --password and --password-file"); @@ -809,6 +818,153 @@ static bool get_password(const boost::program_options::variables_map& vm, bool a } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::generate_from_json(const boost::program_options::variables_map& vm, std::string &wallet_file, std::string &password) +{ + std::string buf; + bool r = epee::file_io_utils::load_file_to_string(m_generate_from_json, buf); + if (!r) { + fail_msg_writer() << tr("Failed to load file ") << m_generate_from_json; + return false; + } + + rapidjson::Document json; + if (json.Parse(buf.c_str()).HasParseError()) { + fail_msg_writer() << tr("Failed to parse JSON"); + return false; + } + + if (!json.HasMember("version")) { + fail_msg_writer() << tr("Version not found in JSON"); + return false; + } + unsigned int version = json["version"].GetUint(); + const int current_version = 1; + if (version > current_version) { + fail_msg_writer() << boost::format(tr("Version %u too new, we can only grok up to %u")) % version % current_version; + return false; + } + if (!json.HasMember("filename")) { + fail_msg_writer() << tr("Filename not found in JSON"); + return false; + } + std::string filename = json["filename"].GetString(); + + bool recover = false; + uint64_t scan_from_height = 0; + if (json.HasMember("scan_from_height")) { + scan_from_height = json["scan_from_height"].GetUint64(); + recover = true; + } + + password = ""; + if (json.HasMember("password")) { + password = json["password"].GetString(); + } + + std::string viewkey_string(""); + crypto::secret_key viewkey; + if (json.HasMember("viewkey")) { + viewkey_string = json["viewkey"].GetString(); + cryptonote::blobdata viewkey_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data)) + { + fail_msg_writer() << tr("failed to parse view key secret key"); + return false; + } + viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data()); + } + + std::string spendkey_string(""); + crypto::secret_key spendkey; + if (json.HasMember("spendkey")) { + spendkey_string = json["spendkey"].GetString(); + cryptonote::blobdata spendkey_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data)) + { + fail_msg_writer() << tr("failed to parse spend key secret key"); + return false; + } + spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data()); + } + + std::string seed(""); + std::string old_language; + if (json.HasMember("seed")) { + seed = json["seed"].GetString(); + if (!crypto::ElectrumWords::words_to_bytes(seed, m_recovery_key, old_language)) + { + fail_msg_writer() << tr("Electrum-style word list failed verification"); + return false; + } + m_electrum_seed = seed; + m_restore_deterministic_wallet = true; + } + + // compatibility checks + if (seed.empty() && viewkey_string.empty()) { + fail_msg_writer() << tr("At least one of Electrum-style word list and private view key must be specified"); + return false; + } + if (!seed.empty() && (!viewkey_string.empty() || !spendkey_string.empty())) { + fail_msg_writer() << tr("Both Electrum-style word list and private key(s) specified"); + return false; + } + + m_wallet_file = filename; + + bool was_deprecated_wallet = m_restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) || + crypto::ElectrumWords::get_is_old_style_seed(m_electrum_seed)); + if (was_deprecated_wallet) { + fail_msg_writer() << tr("Cannot create deprecated wallets from JSON"); + return false; + } + + bool testnet = command_line::get_arg(vm, arg_testnet); + m_wallet.reset(new tools::wallet2(testnet)); + m_wallet->callback(this); + + try + { + if (!seed.empty()) + { + m_wallet->generate(m_wallet_file, password, m_recovery_key, recover, false); + } + else + { + cryptonote::account_public_address address; + if (!crypto::secret_key_to_public_key(viewkey, address.m_view_public_key)) { + fail_msg_writer() << tr("failed to verify view key secret key"); + return false; + } + if (!crypto::secret_key_to_public_key(spendkey, address.m_spend_public_key)) { + fail_msg_writer() << tr("failed to verify spend key secret key"); + return false; + } + + if (spendkey_string.empty()) + { + m_wallet->generate(m_wallet_file, password, address, viewkey); + } + else + { + m_wallet->generate(m_wallet_file, password, address, spendkey, viewkey); + } + } + } + catch (const std::exception& e) + { + fail_msg_writer() << tr("failed to generate new wallet: ") << e.what(); + return false; + } + + m_wallet->set_refresh_from_block_height(scan_from_height); + + wallet_file = m_wallet_file; + + return r; +} + +//---------------------------------------------------------------------------------------------------- bool simple_wallet::init(const boost::program_options::variables_map& vm) { if (!handle_command_line(vm)) @@ -820,12 +976,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } - if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_keys.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_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\" 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-json=\"jsonfilename\" and --generate-from-keys=\"wallet_name\""); return false; } - else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_view_key.empty() && m_generate_from_keys.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_json.empty()) { if(!ask_wallet_create_if_needed()) return false; } @@ -847,7 +1003,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (!get_password(vm, true, pwd_container)) return false; - if (!m_generate_new.empty() || m_restore_deterministic_wallet || !m_generate_from_view_key.empty() || !m_generate_from_keys.empty()) + if (!m_generate_new.empty() || m_restore_deterministic_wallet || !m_generate_from_view_key.empty() || !m_generate_from_keys.empty() || !m_generate_from_json.empty()) { if (m_wallet_file.empty()) m_wallet_file = m_generate_new; // alias for simplicity later @@ -993,6 +1149,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) bool r = new_wallet(m_wallet_file, pwd_container.password(), address, spendkey, viewkey, testnet); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); } + else if (!m_generate_from_json.empty()) + { + std::string wallet_file, password; // we don't need to remember them + if (!generate_from_json(vm, wallet_file, password)) + return false; + } else { bool r = new_wallet(m_wallet_file, pwd_container.password(), m_recovery_key, m_restore_deterministic_wallet, @@ -1023,6 +1185,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_json = command_line::get_arg(vm, arg_generate_from_json); m_daemon_address = command_line::get_arg(vm, arg_daemon_address); m_daemon_host = command_line::get_arg(vm, arg_daemon_host); m_daemon_port = command_line::get_arg(vm, arg_daemon_port); @@ -2575,6 +2738,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_json); command_line::add_arg(desc_params, arg_password); command_line::add_arg(desc_params, arg_password_file); command_line::add_arg(desc_params, arg_daemon_address); @@ -2707,11 +2871,21 @@ int main(int argc, char* argv[]) if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); + std::string password; + const std::string gfj = command_line::get_arg(vm, arg_generate_from_json); + if (!gfj.empty()) { + if (!w.generate_from_json(vm, wallet_file, password)) + return 1; + } + else { + password = pwd_container.password(); + } + tools::wallet2 wal(testnet,restricted); try { LOG_PRINT_L0(sw::tr("Loading wallet...")); - wal.load(wallet_file, pwd_container.password()); + wal.load(wallet_file, password); wal.init(daemon_address); wal.refresh(); LOG_PRINT_GREEN(sw::tr("Loaded ok"), LOG_LEVEL_0); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index d1617dbf4..7dadb8536 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -69,6 +69,7 @@ namespace cryptonote bool run(); void stop(); void interrupt(); + bool generate_from_json(const boost::program_options::variables_map& vm, std::string &wallet_file, std::string &password); //wallet *create_wallet(); bool process_command(const std::vector<std::string> &args); @@ -221,6 +222,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_json; std::string m_import_path; std::string m_electrum_seed; // electrum-style seed parameter |