mirror of https://github.com/oxen-io/oxen-core.git
Add LedgerTCP hardware wallet support
This communicates with the Ledger over TCP, which is what the ledger emulator requires. To use, specify: --hw-device LedgerTCP --hw-device-address localhost:9999 to the wallet command-line arguments.
This commit is contained in:
parent
b8ecb6724c
commit
06a9251f15
|
@ -595,6 +595,7 @@ build_external(zmq
|
|||
${zmq_patch}
|
||||
CONFIGURE_COMMAND ./configure ${zmq_cross_host} --prefix=${DEPS_DESTDIR} --enable-static --disable-shared
|
||||
--disable-curve-keygen --enable-curve --disable-drafts --disable-libunwind --with-libsodium
|
||||
--disable-libbsd --disable-perf
|
||||
--without-pgm --without-norm --without-vmci --without-docs --with-pic --disable-Werror
|
||||
"CC=${deps_cc}" "CXX=${deps_cxx}" "CFLAGS=-fstack-protector ${deps_CFLAGS}" "CXXFLAGS=-fstack-protector ${deps_CXXFLAGS}"
|
||||
${cross_extra}
|
||||
|
|
|
@ -70,6 +70,7 @@ if (HIDAPI_FOUND)
|
|||
target_sources(device PRIVATE
|
||||
device_ledger.cpp
|
||||
io_hid.cpp
|
||||
io_ledger_tcp.cpp
|
||||
)
|
||||
target_link_libraries(device PRIVATE hidapi_libusb)
|
||||
endif()
|
||||
|
|
|
@ -119,6 +119,10 @@ namespace hw {
|
|||
virtual bool set_name(std::string_view name) = 0;
|
||||
virtual std::string get_name() const = 0;
|
||||
|
||||
// Optional; can be used to take an address parameter if required (e.g. Ledger TCP uses this
|
||||
// to specify the TCP address).
|
||||
virtual void set_address(std::string_view address) {}
|
||||
|
||||
virtual bool init() = 0;
|
||||
virtual bool release() = 0;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
#include "device/io_ledger_tcp.hpp"
|
||||
#include "io_hid.hpp"
|
||||
#include "version.h"
|
||||
#include "device_ledger.hpp"
|
||||
|
@ -313,7 +314,7 @@ namespace hw::ledger {
|
|||
static_assert(BLAKE2B_HASH_CHUNK_SIZE <= 254, "Max BLAKE2b data chunk size exceeds the protocol limit");
|
||||
|
||||
|
||||
device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 2000) {
|
||||
device_ledger::device_ledger() : hw_device{std::make_unique<io::hid>(0x0101, 0x05, 64, 2000)} {
|
||||
id = device_id++;
|
||||
reset_buffer();
|
||||
has_view_key = false;
|
||||
|
@ -321,6 +322,14 @@ namespace hw::ledger {
|
|||
MDEBUG("Device " << id << " Created");
|
||||
}
|
||||
|
||||
device_ledger::device_ledger(io::ledger_tcp&& tcp) : hw_device{std::make_unique<io::ledger_tcp>(std::move(tcp))} {
|
||||
id = device_id++;
|
||||
reset_buffer();
|
||||
has_view_key = false;
|
||||
tx_in_progress = false;
|
||||
MDEBUG("Device " << id << " (tcp) created");
|
||||
}
|
||||
|
||||
device_ledger::~device_ledger() {
|
||||
release();
|
||||
MDEBUG("Device " << id << " Destroyed");
|
||||
|
@ -497,7 +506,7 @@ namespace hw::ledger {
|
|||
unsigned int device_ledger::exchange(bool wait_on_input) {
|
||||
logCMD();
|
||||
|
||||
length_recv = hw_device.exchange(buffer_send, length_send, buffer_recv, BUFFER_SEND_SIZE, wait_on_input);
|
||||
length_recv = hw_device->exchange(buffer_send, length_send, buffer_recv, BUFFER_RECV_SIZE, wait_on_input);
|
||||
CHECK_AND_ASSERT_THROW_MES(length_recv >= 2, "Communication error, less than two bytes received");
|
||||
|
||||
length_recv -= 2;
|
||||
|
@ -537,12 +546,25 @@ namespace hw::ledger {
|
|||
return name;
|
||||
}
|
||||
|
||||
void device_ledger::set_address(std::string_view addr) {
|
||||
if (addr.empty() || !hw_device)
|
||||
return;
|
||||
auto* tcp = dynamic_cast<io::ledger_tcp*>(hw_device.get());
|
||||
if (!tcp)
|
||||
return;
|
||||
if (auto pos = addr.rfind(':'); pos != addr.npos) {
|
||||
tcp->port = addr.substr(pos + 1);
|
||||
addr = addr.substr(0, pos);
|
||||
}
|
||||
tcp->host = addr;
|
||||
}
|
||||
|
||||
bool device_ledger::init() {
|
||||
#ifdef DEBUG_HWDEVICE
|
||||
debug_device = &get_device("default");
|
||||
#endif
|
||||
release();
|
||||
hw_device.init();
|
||||
hw_device->init();
|
||||
MDEBUG("Device " << id <<" HIDUSB inited");
|
||||
return true;
|
||||
}
|
||||
|
@ -554,7 +576,12 @@ namespace hw::ledger {
|
|||
|
||||
bool device_ledger::connect() {
|
||||
disconnect();
|
||||
hw_device.connect(known_devices);
|
||||
if (auto* hid_io = dynamic_cast<io::hid*>(hw_device.get()))
|
||||
hid_io->connect(known_devices);
|
||||
else if (auto* tcp = dynamic_cast<io::ledger_tcp*>(hw_device.get()))
|
||||
tcp->connect();
|
||||
else
|
||||
throw std::logic_error{"Invalid ledger hardware configure"};
|
||||
reset();
|
||||
|
||||
check_network_type();
|
||||
|
@ -571,17 +598,17 @@ namespace hw::ledger {
|
|||
}
|
||||
|
||||
bool device_ledger::connected() const {
|
||||
return hw_device.connected();
|
||||
return hw_device->connected();
|
||||
}
|
||||
|
||||
bool device_ledger::disconnect() {
|
||||
hw_device.disconnect();
|
||||
hw_device->disconnect();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool device_ledger::release() {
|
||||
disconnect();
|
||||
hw_device.release();
|
||||
hw_device->release();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1943,6 +1970,7 @@ namespace hw::ledger {
|
|||
|
||||
void register_all(std::map<std::string, std::unique_ptr<device>>& registry) {
|
||||
registry.emplace("Ledger", std::make_unique<device_ledger>());
|
||||
registry.emplace("LedgerTCP", std::make_unique<device_ledger>(io::ledger_tcp{}));
|
||||
}
|
||||
|
||||
#else //WITH_DEVICE_LEDGER
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <cstddef>
|
||||
#include <string>
|
||||
#include "device.hpp"
|
||||
#include "device/io_ledger_tcp.hpp"
|
||||
#include "log.hpp"
|
||||
#include "io_hid.hpp"
|
||||
|
||||
|
@ -142,7 +143,7 @@ namespace hw::ledger {
|
|||
mutable std::mutex command_locker;
|
||||
|
||||
//IO
|
||||
hw::io::hid hw_device;
|
||||
std::unique_ptr<io::device> hw_device;
|
||||
unsigned int length_send;
|
||||
unsigned char buffer_send[BUFFER_SEND_SIZE];
|
||||
unsigned int length_recv;
|
||||
|
@ -202,11 +203,20 @@ namespace hw::ledger {
|
|||
#endif
|
||||
|
||||
public:
|
||||
// Constructs a ledger device object that connects to a physical ledger device plugged into
|
||||
// the system.
|
||||
device_ledger();
|
||||
|
||||
// Constructs a ledger device object that uses a TCP socket to communicate with the ledger
|
||||
// device, typically for use with a Ledger emulator which doesn't emulated the USB layer.
|
||||
explicit device_ledger(io::ledger_tcp&& tcp);
|
||||
|
||||
~device_ledger();
|
||||
|
||||
device_ledger(const device_ledger &device) = delete ;
|
||||
device_ledger& operator=(const device_ledger &device) = delete;
|
||||
device_ledger(const device_ledger&) = delete ;
|
||||
device_ledger(device_ledger&&) = delete ;
|
||||
device_ledger& operator=(const device_ledger&) = delete;
|
||||
device_ledger&& operator=(device_ledger&&) = delete;
|
||||
|
||||
bool is_hardware_device() const override { return connected(); }
|
||||
|
||||
|
@ -217,6 +227,8 @@ namespace hw::ledger {
|
|||
/* ======================================================================= */
|
||||
bool set_name(std::string_view name) override;
|
||||
|
||||
void set_address(std::string_view addr) override;
|
||||
|
||||
std::string get_name() const override;
|
||||
bool init() override;
|
||||
bool release() override;
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace hw::io {
|
|||
virtual void disconnect() = 0;
|
||||
virtual bool connected() const = 0;
|
||||
|
||||
virtual int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) = 0;
|
||||
virtual int exchange(const unsigned char* command, unsigned int cmd_len, unsigned char* response, unsigned int max_resp_len, bool user_input) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ namespace hw::io {
|
|||
return usb_device != nullptr;
|
||||
}
|
||||
|
||||
int hid::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) {
|
||||
int hid::exchange(const unsigned char* command, unsigned int cmd_len, unsigned char* response, unsigned int max_resp_len, bool user_input) {
|
||||
unsigned char buffer[400];
|
||||
unsigned char padding_buffer[MAX_BLOCK+1];
|
||||
unsigned int result;
|
||||
|
|
|
@ -91,7 +91,7 @@ namespace hw {
|
|||
void connect(const std::vector<hid_conn_params>& conn);
|
||||
bool connect(unsigned int vid, unsigned int pid, std::optional<int> interface_number, std::optional<unsigned short> usage_page);
|
||||
bool connected() const override;
|
||||
int exchange(unsigned char* command, unsigned int cmd_len, unsigned char* response, unsigned int max_resp_len, bool user_input) override;
|
||||
int exchange(const unsigned char* command, unsigned int cmd_len, unsigned char* response, unsigned int max_resp_len, bool user_input) override;
|
||||
void disconnect() override;
|
||||
void release() override;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
#include "io_ledger_tcp.hpp"
|
||||
#include "common/oxen.h"
|
||||
#include <array>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include "epee/misc_log_ex.h"
|
||||
|
||||
extern "C" {
|
||||
#ifdef _WIN32
|
||||
# include <ws2tcpip.h>
|
||||
# include <winsock2.h>
|
||||
#else
|
||||
# include <arpa/inet.h>
|
||||
# include <netdb.h>
|
||||
# include <netinet/in.h>
|
||||
# include <sys/socket.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
}
|
||||
|
||||
#undef OXEN_DEFAULT_LOG_CATEGORY
|
||||
#define OXEN_DEFAULT_LOG_CATEGORY "device.io"
|
||||
|
||||
namespace hw::io {
|
||||
|
||||
static std::string to_string(const addrinfo* a) {
|
||||
std::array<char, INET6_ADDRSTRLEN> buf;
|
||||
std::string addr;
|
||||
#ifdef _WIN32
|
||||
unsigned long buflen = buf.size();
|
||||
if (auto rc = WSAAddressToString(a->ai_addr, a->ai_addrlen, nullptr, buf.data(), &buflen);
|
||||
rc == 0)
|
||||
addr = buf.data();
|
||||
else
|
||||
addr = "[error:"s + std::to_string(rc) + "]";
|
||||
#else
|
||||
if (inet_ntop(a->ai_family, a->ai_addr, buf.data(), buf.size()))
|
||||
addr = buf.data();
|
||||
else
|
||||
addr = "[error:"s + strerror(errno) + "]";
|
||||
#endif
|
||||
if (a->ai_family == AF_INET)
|
||||
(addr += ':') += std::to_string(reinterpret_cast<sockaddr_in*>(a->ai_addr)->sin_port);
|
||||
else if (a->ai_family == AF_INET6)
|
||||
(addr += ':') += std::to_string(reinterpret_cast<sockaddr_in6*>(a->ai_addr)->sin6_port);
|
||||
return addr;
|
||||
}
|
||||
|
||||
void ledger_tcp::connect() {
|
||||
disconnect();
|
||||
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0)
|
||||
throw std::runtime_error{"Failed to open socket: "s + strerror(errno)};
|
||||
auto closer = oxen::defer([&] { close(fd); });
|
||||
|
||||
#ifdef _WIN32
|
||||
unsigned long blocking_param = 1; // 1 = make non-blocking, 0 = blocking
|
||||
if (auto result = ioctlsocket(fd, FIONBIO, &blocking_param);
|
||||
result != NO_ERROR)
|
||||
throw std::runtime_error{"ioctlsocket failed with error: " + std::to_string(result)};
|
||||
#else
|
||||
if (-1 == fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK))
|
||||
throw std::runtime_error{"Failed to set socket non-blocking: "s + strerror(errno)};
|
||||
#endif
|
||||
|
||||
addrinfo* addr;
|
||||
if (int rc = getaddrinfo(host.data(), port.data(), nullptr, &addr);
|
||||
rc != 0)
|
||||
throw std::runtime_error{"Failed to resolve " + host + ":" + port + ": " + gai_strerror(rc)};
|
||||
auto addr_free = oxen::defer([&] { freeaddrinfo(addr); });
|
||||
|
||||
const addrinfo* a;
|
||||
bool connected = false;
|
||||
const char* err = "An unknown error occurred";
|
||||
for (a = addr; a && !connected; a = a->ai_next) {
|
||||
MDEBUG("Attempting to connect to " << to_string(a));
|
||||
int rc = ::connect(fd, a->ai_addr, a->ai_addrlen);
|
||||
connected = rc == 0;
|
||||
if (rc == -1) {
|
||||
if (errno == EINPROGRESS) {
|
||||
timeval timeo;
|
||||
timeo.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(connect_timeout).count();
|
||||
timeo.tv_usec = (connect_timeout % 1s).count();
|
||||
fd_set myset;
|
||||
FD_ZERO(&myset);
|
||||
FD_SET(fd, &myset);
|
||||
rc = select(fd + 1, nullptr, &myset, nullptr, &timeo);
|
||||
if (rc > 0)
|
||||
connected = true;
|
||||
else if (rc == 0)
|
||||
err = "Connection timed out";
|
||||
else
|
||||
err = strerror(errno);
|
||||
} else {
|
||||
err = strerror(errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!connected)
|
||||
throw std::runtime_error{"Failed to connect to " + host + ":" + port + ": " + err};
|
||||
|
||||
MDEBUG("Connected to " << to_string(a));
|
||||
|
||||
#ifdef _WIN32
|
||||
blocking_param = 0;
|
||||
if (auto result = ioctlsocket(fd, FIONBIO, &blocking_param);
|
||||
result != NO_ERROR)
|
||||
throw std::runtime_error{"ioctlsocket failed with error: " + std::to_string(result)};
|
||||
#else
|
||||
if (-1 == fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK))
|
||||
throw std::runtime_error{"Failed to set socket back to blocking: "s + strerror(errno)};
|
||||
#endif
|
||||
|
||||
timeval timeo;
|
||||
timeo.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(exchange_timeout).count();
|
||||
timeo.tv_usec = (exchange_timeout % 1s).count();
|
||||
|
||||
// The reinterpret_cast here is needed for Windows's shitty imitation of the api
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeo), sizeof(timeo));
|
||||
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const char*>(&timeo), sizeof(timeo));
|
||||
|
||||
sockfd = std::make_unique<int>(fd);
|
||||
closer.cancel();
|
||||
}
|
||||
|
||||
void ledger_tcp::disconnect() {
|
||||
if (!sockfd)
|
||||
return;
|
||||
|
||||
close(*sockfd);
|
||||
sockfd.reset();
|
||||
}
|
||||
|
||||
ledger_tcp::~ledger_tcp() {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
bool ledger_tcp::connected() const {
|
||||
return (bool) sockfd;
|
||||
}
|
||||
|
||||
void full_read(int fd, unsigned char* to, int size) {
|
||||
while (size > 0) {
|
||||
auto read_size = read(fd, to, size);
|
||||
if (read_size == -1)
|
||||
throw std::runtime_error{"Failed to read from hardware wallet socket: "s + strerror(errno)};
|
||||
size -= read_size;
|
||||
to += read_size;
|
||||
}
|
||||
}
|
||||
|
||||
void full_write(int fd, const unsigned char* from, int size) {
|
||||
while (size > 0) {
|
||||
auto wrote = write(fd, from, size);
|
||||
if (wrote == -1)
|
||||
throw std::runtime_error{"Failed to write to hardware wallet socket: "s + strerror(errno)};
|
||||
size -= wrote;
|
||||
from += wrote;
|
||||
}
|
||||
}
|
||||
|
||||
int ledger_tcp::exchange(const unsigned char* command, unsigned int cmd_len, unsigned char* response, unsigned int max_resp_len, bool user_input) {
|
||||
if (!sockfd)
|
||||
throw std::runtime_error{"Unable to exchange data with hardware wallet: not connected"};
|
||||
|
||||
// Sending: [SIZE][DATA], where SIZE is a uint32_t in network order
|
||||
uint32_t size = boost::endian::native_to_big(cmd_len);
|
||||
const unsigned char* size_bytes = reinterpret_cast<const unsigned char*>(&size);
|
||||
full_write(*sockfd, size_bytes, 4);
|
||||
full_write(*sockfd, command, cmd_len);
|
||||
|
||||
|
||||
// Receiving: [SIZE][DATA], where SIZE is the length of DATA minus 2 (WTF) because the last two
|
||||
// bytes of DATA are a 2-byte, u16 status code and... therefore not... included. Good job, Ledger
|
||||
// devs.
|
||||
full_read(*sockfd, reinterpret_cast<unsigned char*>(&size), 4);
|
||||
auto data_size = boost::endian::big_to_native(size) + 2;
|
||||
|
||||
if (data_size > max_resp_len)
|
||||
throw std::runtime_error{"Hardware wallet returned unexpectedly large response: got " +
|
||||
std::to_string(data_size) + " bytes, expected <= " + std::to_string(max_resp_len)};
|
||||
|
||||
full_read(*sockfd, response, data_size);
|
||||
|
||||
return data_size;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// TCP APDU interface, as used by Ledger's emulator system (Speculos).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "io_device.hpp"
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace hw::io {
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
class ledger_tcp : public device {
|
||||
|
||||
std::unique_ptr<int> sockfd;
|
||||
|
||||
public:
|
||||
std::string host = "localhost";
|
||||
std::string port = "9999";
|
||||
|
||||
std::chrono::microseconds connect_timeout = 10s;
|
||||
std::chrono::microseconds exchange_timeout = 120s;
|
||||
|
||||
ledger_tcp() = default;
|
||||
~ledger_tcp() override;
|
||||
|
||||
ledger_tcp(ledger_tcp&&) = default;
|
||||
ledger_tcp& operator=(ledger_tcp&&) = default;
|
||||
|
||||
void init() override {}
|
||||
void release() override {}
|
||||
void connect();
|
||||
bool connected() const override;
|
||||
int exchange(const unsigned char* command, unsigned int cmd_len, unsigned char* response, unsigned int max_resp_len, bool user_input) override;
|
||||
void disconnect() override;
|
||||
};
|
||||
|
||||
}
|
|
@ -282,6 +282,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"), ""};
|
||||
const command_line::arg_descriptor<std::string> hw_device_address = {"hw-device-address", tools::wallet2::tr("HW device address, if required"), ""};
|
||||
const command_line::arg_descriptor<std::string> hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""};
|
||||
const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
|
||||
const command_line::arg_descriptor<bool> offline = {"offline", tools::wallet2::tr("Do not connect to a daemon"), false};
|
||||
|
@ -345,6 +346,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
|||
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
|
||||
|
||||
auto device_name = command_line::get_arg(vm, opts.hw_device);
|
||||
auto device_addr = command_line::get_arg(vm, opts.hw_device_address);
|
||||
auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && (!daemon_host.empty() || 0 != daemon_port),
|
||||
|
@ -419,6 +421,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
|||
wallet->get_message_store().set_options(vm);
|
||||
#endif
|
||||
wallet->device_name(device_name);
|
||||
wallet->device_address(device_addr);
|
||||
wallet->device_derivation_path(device_derivation_path);
|
||||
wallet->m_long_poll_disabled = command_line::get_arg(vm, opts.disable_rpc_long_poll);
|
||||
wallet->m_http_client.set_https_client_cert(command_line::get_arg(vm, opts.daemon_ssl_certificate), command_line::get_arg(vm, opts.daemon_ssl_private_key));
|
||||
|
@ -1151,6 +1154,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
|
|||
mms::message_store::init_options(desc_params);
|
||||
#endif
|
||||
command_line::add_arg(desc_params, opts.hw_device);
|
||||
command_line::add_arg(desc_params, opts.hw_device_address);
|
||||
command_line::add_arg(desc_params, opts.hw_device_derivation_path);
|
||||
command_line::add_arg(desc_params, opts.tx_notify);
|
||||
command_line::add_arg(desc_params, opts.offline);
|
||||
|
@ -1366,6 +1370,7 @@ bool wallet2::reconnect_device()
|
|||
bool r = true;
|
||||
hw::device &hwdev = lookup_device(m_device_name);
|
||||
hwdev.set_name(m_device_name);
|
||||
hwdev.set_address(m_device_address);
|
||||
hwdev.set_network_type(m_nettype);
|
||||
hwdev.set_derivation_path(m_device_derivation_path);
|
||||
hwdev.set_callback(get_device_callback());
|
||||
|
@ -7887,7 +7892,7 @@ void wallet2::register_devices(){
|
|||
hw::trezor::register_all();
|
||||
}
|
||||
|
||||
hw::device& wallet2::lookup_device(const std::string & device_descriptor){
|
||||
hw::device& wallet2::lookup_device(const std::string & device_descriptor) {
|
||||
if (!m_devices_registered){
|
||||
m_devices_registered = true;
|
||||
register_devices();
|
||||
|
|
|
@ -1037,8 +1037,10 @@ private:
|
|||
void track_uses(bool value) { m_track_uses = value; }
|
||||
std::chrono::seconds inactivity_lock_timeout() const { return m_inactivity_lock_timeout; }
|
||||
void inactivity_lock_timeout(std::chrono::seconds seconds) { m_inactivity_lock_timeout = seconds; }
|
||||
const std::string & device_name() const { return m_device_name; }
|
||||
void device_name(const std::string & device_name) { m_device_name = device_name; }
|
||||
const std::string& device_name() const { return m_device_name; }
|
||||
const std::string& device_address() const { return m_device_address; }
|
||||
void device_name(std::string device_name) { m_device_name = std::move(device_name); }
|
||||
void device_address(std::string device_address) { m_device_address = std::move(device_address); }
|
||||
const std::string & device_derivation_path() const { return m_device_derivation_path; }
|
||||
void device_derivation_path(const std::string &device_derivation_path) { m_device_derivation_path = device_derivation_path; }
|
||||
const ExportFormat & export_format() const { return m_export_format; }
|
||||
|
@ -1682,6 +1684,7 @@ private:
|
|||
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;
|
||||
std::string m_device_address;
|
||||
std::string m_device_derivation_path;
|
||||
uint64_t m_device_last_key_image_sync;
|
||||
bool m_offline;
|
||||
|
|
Loading…
Reference in New Issue