Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hw_device: support for multiple devices added [for review] #4299

Merged
merged 1 commit into from
Sep 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/cryptonote_basic/account.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,14 @@ DISABLE_VS_WARNINGS(4244 4345)
//-----------------------------------------------------------------
void account_base::create_from_device(const std::string &device_name)
{

hw::device &hwdev = hw::get_device(device_name);
m_keys.set_device(hwdev);
hwdev.set_name(device_name);
create_from_device(hwdev);
}

void account_base::create_from_device(hw::device &hwdev)
{
m_keys.set_device(hwdev);
MCDEBUG("ledger", "device type: "<<typeid(hwdev).name());
hwdev.init();
hwdev.connect();
Expand Down
3 changes: 2 additions & 1 deletion src/cryptonote_basic/account.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ namespace cryptonote
public:
account_base();
crypto::secret_key generate(const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false, bool two_random = false);
void create_from_device(const std::string &device_name) ;
void create_from_device(const std::string &device_name);
void create_from_device(hw::device &hwdev);
void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey);
void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey);
bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys);
Expand Down
70 changes: 49 additions & 21 deletions src/device/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,32 +39,60 @@ namespace hw {

/* ======================================================================= */
/* SETUP */
/* ======================================================================= */
device& get_device(const std::string device_descriptor) {

struct s_devices {
std::map<std::string, std::unique_ptr<device>> registry;
s_devices() : registry() {
hw::core::register_all(registry);
#ifdef HAVE_PCSC
hw::ledger::register_all(registry);
#endif
};
};

static const s_devices devices;
/* ======================================================================= */

static std::unique_ptr<device_registry> registry;

device_registry::device_registry(){
hw::core::register_all(registry);
#ifdef HAVE_PCSC
hw::ledger::register_all(registry);
#endif
}

bool device_registry::register_device(const std::string & device_name, device * hw_device){
auto search = registry.find(device_name);
if (search != registry.end()){
return false;
}

registry.insert(std::make_pair(device_name, std::unique_ptr<device>(hw_device)));
return true;
}

device& device_registry::get_device(const std::string & device_descriptor){
// Device descriptor can contain further specs after first :
auto delim = device_descriptor.find(':');
auto device_descriptor_lookup = device_descriptor;
if (delim != std::string::npos) {
device_descriptor_lookup = device_descriptor.substr(0, delim);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if adding a mutex for acessing the global would be better. It seems safe reading this anyway.


auto device = devices.registry.find(device_descriptor);
if (device == devices.registry.end()) {
MERROR("device not found in registry: '" << device_descriptor << "'\n" <<
"known devices:");

for( const auto& sm_pair : devices.registry ) {
auto device = registry.find(device_descriptor_lookup);
if (device == registry.end()) {
MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: ");
for( const auto& sm_pair : registry ) {
MERROR(" - " << sm_pair.first);
}
throw std::runtime_error("device not found: "+ device_descriptor);
throw std::runtime_error("device not found: " + device_descriptor);
}
return *device->second;
}

device& get_device(const std::string & device_descriptor) {
if (!registry){
registry.reset(new device_registry());
}

return registry->get_device(device_descriptor);
}

bool register_device(const std::string & device_name, device * hw_device){
if (!registry){
registry.reset(new device_registry());
}

return registry->register_device(device_name, hw_device);
}

}
13 changes: 12 additions & 1 deletion src/device/device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,17 @@ namespace hw {
~reset_mode() { hwref.set_mode(hw::device::NONE);}
};

device& get_device(const std::string device_descriptor) ;
class device_registry {
private:
std::map<std::string, std::unique_ptr<device>> registry;

public:
device_registry();
bool register_device(const std::string & device_name, device * hw_device);
device& get_device(const std::string & device_descriptor);
};

device& get_device(const std::string & device_descriptor);
bool register_device(const std::string & device_name, device * hw_device);
}

39 changes: 35 additions & 4 deletions src/simplewallet/simplewallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2523,6 +2523,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::import_key_images, this, _1),
tr("import_key_images <file>"),
tr("Import a signed key images list and verify their spent status."));
m_cmd_binder.set_handler("hw_reconnect",
boost::bind(&simple_wallet::hw_reconnect, this, _1),
tr("hw_reconnect"),
tr("Attempts to reconnect HW wallet."));
m_cmd_binder.set_handler("export_outputs",
boost::bind(&simple_wallet::export_outputs, this, _1),
tr("export_outputs <file>"),
Expand Down Expand Up @@ -2650,6 +2654,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second;
success_msg_writer() << "segregation-height = " << m_wallet->segregation_height();
success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs();
success_msg_writer() << "device_name = " << m_wallet->device_name();
return true;
}
else
Expand Down Expand Up @@ -3295,7 +3300,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
m_wallet_file = m_generate_from_device;
// create wallet
auto r = new_wallet(vm, "Ledger");
auto r = new_wallet(vm);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
password = *r;
// if no block_height is specified, assume its a new account and start it "now"
Expand Down Expand Up @@ -3703,8 +3708,8 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
}

//----------------------------------------------------------------------------------------------------
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const std::string &device_name) {
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm)
{
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
m_wallet = std::move(rc.first);
if (!m_wallet)
Expand All @@ -3723,10 +3728,11 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
if (m_restore_height)
m_wallet->set_refresh_from_block_height(m_restore_height);

auto device_desc = tools::wallet2::device_name_option(vm);
try
{
bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_name, create_address_file);
m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file);
message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ")
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
}
Expand Down Expand Up @@ -7745,6 +7751,31 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
{
if (!m_wallet->key_on_device())
{
fail_msg_writer() << tr("command only supported by HW wallet");
return true;
}

LOCK_IDLE_SCOPE();
try
{
bool r = m_wallet->reconnect_device();
if (!r){
fail_msg_writer() << tr("Failed to reconnect device");
}
}
catch (const std::exception &e)
{
fail_msg_writer() << tr("Failed to reconnect device: ") << tr(e.what());
return true;
}

return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::export_outputs(const std::vector<std::string> &args)
{
if (m_wallet->key_on_device())
Expand Down
3 changes: 2 additions & 1 deletion src/simplewallet/simplewallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ namespace cryptonote
const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm,
const epee::wipeable_string &multisig_keys, const std::string &old_language);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm);
bool open_wallet(const boost::program_options::variables_map& vm);
bool close_wallet();

Expand Down Expand Up @@ -201,6 +201,7 @@ namespace cryptonote
bool verify(const std::vector<std::string> &args);
bool export_key_images(const std::vector<std::string> &args);
bool import_key_images(const std::vector<std::string> &args);
bool hw_reconnect(const std::vector<std::string> &args);
bool export_outputs(const std::vector<std::string> &args);
bool import_outputs(const std::vector<std::string> &args);
bool show_transfer(const std::vector<std::string> &args);
Expand Down
51 changes: 49 additions & 2 deletions src/wallet/wallet2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ struct options {
}
};
const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
};

void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
Expand Down Expand Up @@ -211,6 +212,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
auto device_name = command_line::get_arg(vm, opts.hw_device);

THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
Expand Down Expand Up @@ -265,6 +267,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
wallet->init(unattended, std::move(daemon_address), std::move(login), 0, false, *trusted_daemon);
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
wallet->device_name(device_name);
return wallet;
}

Expand Down Expand Up @@ -814,6 +817,11 @@ bool wallet2::has_stagenet_option(const boost::program_options::variables_map& v
return command_line::get_arg(vm, options().stagenet);
}

std::string wallet2::device_name_option(const boost::program_options::variables_map& vm)
{
return command_line::get_arg(vm, options().hw_device);
}

void wallet2::init_options(boost::program_options::options_description& desc_params)
{
const options opts{};
Expand All @@ -829,6 +837,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.stagenet);
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
command_line::add_arg(desc_params, opts.kdf_rounds);
command_line::add_arg(desc_params, opts.hw_device);
}

std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
Expand Down Expand Up @@ -984,6 +993,27 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl
return true;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::reconnect_device()
{
bool r = true;
hw::device &hwdev = hw::get_device(m_device_name);
hwdev.set_name(m_device_name);
r = hwdev.init();
if (!r){
LOG_PRINT_L2("Could not init device");
return false;
}

r = hwdev.connect();
if (!r){
LOG_PRINT_L2("Could not connect to the device");
return false;
}

m_account.set_device(hwdev);
return true;
}
//----------------------------------------------------------------------------------------------------
/*!
* \brief Gets the seed language
*/
Expand Down Expand Up @@ -2980,6 +3010,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(1);
json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());

value.SetString(m_device_name.c_str(), m_device_name.size());
json.AddMember("device_name", value, json.GetAllocator());

// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
Expand Down Expand Up @@ -3088,6 +3121,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_ignore_fractional_outputs = true;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
m_device_name = "";
m_key_on_device = false;
encrypted_secret_keys = false;
}
Expand Down Expand Up @@ -3219,8 +3253,15 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;

GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
encrypted_secret_keys = field_encrypted_secret_keys;

GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string());
if (m_device_name.empty() && field_device_name_found)
{
m_device_name = field_device_name;
}
}
else
{
Expand All @@ -3231,7 +3272,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
r = epee::serialization::load_t_from_binary(m_account, account_data);
if (r && m_key_on_device) {
LOG_PRINT_L0("Account on device. Initing device...");
hw::device &hwdev = hw::get_device("Ledger");
hw::device &hwdev = hw::get_device(m_device_name);
ph4r05 marked this conversation as resolved.
Show resolved Hide resolved
hwdev.set_name(m_device_name);
hwdev.init();
hwdev.connect();
m_account.set_device(hwdev);
Expand Down Expand Up @@ -3673,13 +3715,18 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
}
m_key_on_device = true;
m_account.create_from_device(device_name);

auto &hwdev = hw::get_device(device_name);
hwdev.set_name(device_name);

m_account.create_from_device(hwdev);
m_account_public_address = m_account.get_keys().m_account_address;
m_watch_only = false;
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
setup_keys(password);
m_device_name = device_name;

create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR)
Expand Down
5 changes: 5 additions & 0 deletions src/wallet/wallet2.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ namespace tools

static bool has_testnet_option(const boost::program_options::variables_map& vm);
static bool has_stagenet_option(const boost::program_options::variables_map& vm);
static std::string device_name_option(const boost::program_options::variables_map& vm);
static void init_options(boost::program_options::options_description& desc_params);

//! Uses stdin and stdout. Returns a wallet2 if no errors.
Expand Down Expand Up @@ -709,6 +710,7 @@ namespace tools
bool has_unknown_key_images() const;
bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
bool key_on_device() const { return m_key_on_device; }
bool reconnect_device();

// locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major) const;
Expand Down Expand Up @@ -938,6 +940,8 @@ namespace tools
void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; }
bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; }
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
const std::string & device_name() const { return m_device_name; }
void device_name(const std::string & device_name) { m_device_name = device_name; }

bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
Expand Down Expand Up @@ -1319,6 +1323,7 @@ namespace tools
NodeRPCProxy m_node_rpc_proxy;
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
std::string m_device_name;

// Light wallet
bool m_light_wallet; /* sends view key to daemon for scanning */
Expand Down