From 32f25991193d88f07109ed0416ccd1cead078550 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Wed, 6 May 2026 13:45:34 -0400 Subject: [PATCH] add dummy device for Trezor --- src/device/device_io_dummy.cpp | 6 +- src/device/device_io_dummy.hpp | 2 +- src/device_trezor/trezor/transport.cpp | 178 ++++++++++++++++++++++++- src/device_trezor/trezor/transport.hpp | 51 +++++++ src/wallet/api/wallet.cpp | 127 ++++++++++++++---- src/wallet/api/wallet.h | 14 +- src/wallet/api/wallet2_api.h | 21 ++- 7 files changed, 351 insertions(+), 48 deletions(-) diff --git a/src/device/device_io_dummy.cpp b/src/device/device_io_dummy.cpp index 74742150d..6365bb427 100644 --- a/src/device/device_io_dummy.cpp +++ b/src/device/device_io_dummy.cpp @@ -138,9 +138,9 @@ namespace hw { MDEBUG("release()"); } - void device_io_dummy::setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len)) { - MDEBUG("setLedgerCallback()"); - sendToLedgerDeviceCallback = sendToLedgerDevice; + void device_io_dummy::setDeviceCallback(void (*sendToDeviceCallback)(unsigned char *command, unsigned int cmd_len)) { + MDEBUG("setDeviceCallback()"); + sendToLedgerDeviceCallback = sendToDeviceCallback; } void device_io_dummy::setDeviceReceivedData(unsigned char* data, size_t len) { diff --git a/src/device/device_io_dummy.hpp b/src/device/device_io_dummy.hpp index 87a5f109f..80b356a0b 100644 --- a/src/device/device_io_dummy.hpp +++ b/src/device/device_io_dummy.hpp @@ -73,7 +73,7 @@ namespace hw { int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input); - static void setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len)); + static void setDeviceCallback(void (*sendToDeviceCallback)(unsigned char *command, unsigned int cmd_len)); static void setDeviceReceivedData(unsigned char* data, size_t len); }; }; diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index 10dd82c53..5cf7e1cb8 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +#include "exceptions.hpp" #ifdef WITH_DEVICE_TREZOR_WEBUSB #include #endif @@ -1166,8 +1167,177 @@ namespace trezor{ return o << ">"; }; -#endif // WITH_DEVICE_TREZOR_WEBUSB +#endif // WITH_DEVICE_TREZOR_WEBUSB + +std::condition_variable hw::trezor::DummyTransport::cv_send; +std::condition_variable hw::trezor::DummyTransport::cv_receive; +bool hw::trezor::DummyTransport::stateIsConnected = false; +unsigned char* hw::trezor::DummyTransport::sendToDevice = {}; +size_t hw::trezor::DummyTransport::sendToDeviceLength = 0; +unsigned char* hw::trezor::DummyTransport::receivedFromDevice = {}; +size_t hw::trezor::DummyTransport::receivedFromDeviceLength = 0; +bool hw::trezor::DummyTransport::waitsForDeviceSend = false; +bool hw::trezor::DummyTransport::waitsForDeviceReceive = false; +void (*hw::trezor::DummyTransport::sendToTrezorDeviceCallback)(unsigned char *command, unsigned int cmd_len) = nullptr; +std::mutex hw::trezor::DummyTransport::mutex; + +const char * DummyTransport::PATH_PREFIX = "dummy:"; + +DummyTransport::DummyTransport( + boost::optional> proto +) +{ + m_proto = proto ? proto.get() : std::make_shared(); +} + +DummyTransport::~DummyTransport(){ + close(); +} + +std::shared_ptr DummyTransport::find_debug() { + return nullptr; +} + +void DummyTransport::require_device() const{ + if (!stateIsConnected){ + throw std::runtime_error("stateIsConnected is false"); + } +} + +void DummyTransport::require_connected() const{ + require_device(); +} + +void DummyTransport::enumerate(t_transport_vect & res) { + auto t = std::make_shared(); + res.push_back(t); +} + +std::string DummyTransport::get_path() const { + std::stringstream ss; + ss << DummyTransport::PATH_PREFIX << "dummy"; + return ss.str(); +}; + +void DummyTransport::open() { + if (sendToTrezorDeviceCallback == nullptr) { + throw exc::TrezorException("sendToTrezorDeviceCallback is null"); + } + stateIsConnected = true; + m_proto->session_begin(*this); +} + +void DummyTransport::close() { + stateIsConnected = false; +}; + +int DummyTransport::get_interface() const{ + const int INTERFACE_NORMAL = 0; +#ifdef WITH_TREZOR_DEBUGGING + const int INTERFACE_DEBUG = 1; + return m_debug_mode ? INTERFACE_DEBUG : INTERFACE_NORMAL; +#else + return INTERFACE_NORMAL; +#endif +} + +unsigned char DummyTransport::get_endpoint() const{ + const unsigned char ENDPOINT_NORMAL = 1; +#ifdef WITH_TREZOR_DEBUGGING + const unsigned char ENDPOINT_DEBUG = 2; + return m_debug_mode ? ENDPOINT_DEBUG : ENDPOINT_NORMAL; +#else + return ENDPOINT_NORMAL; +#endif +} + +void DummyTransport::write(const google::protobuf::Message &req) { + m_proto->write(*this, req); +}; + +void DummyTransport::read(std::shared_ptr & msg, messages::MessageType * msg_type) { + m_proto->read(*this, msg, msg_type); +}; + +void DummyTransport::write_chunk(const void * buff, size_t size) { + require_connected(); + if (size != REPLEN){ + throw exc::CommunicationException("Invalid chunk size: "); + } + + MDEBUG("write_chunk(): locking mutex"); + std::unique_lock lock(mutex); + + sendToDevice = const_cast(static_cast(buff)); + sendToDeviceLength = size; + waitsForDeviceSend = true; + waitsForDeviceReceive = true; + + if (sendToTrezorDeviceCallback != nullptr) { + MDEBUG("write_chunk(): calling sendToTrezorDeviceCallback"); + sendToTrezorDeviceCallback( + const_cast(static_cast(buff)), + static_cast(size) + ); + } + + while (waitsForDeviceSend) { + cv_send.wait(lock); + MDEBUG("write_chunk(): waitsForDeviceSend notified"); + } +} + +size_t DummyTransport::read_chunk(void * buff, size_t size) { + require_connected(); + MDEBUG("read_chunk(): locking mutex"); + std::unique_lock lock(mutex); + + while (waitsForDeviceReceive) { + cv_receive.wait(lock); + MDEBUG("read_chunk(): waitsForDeviceReceive notified"); + } + + if (receivedFromDeviceLength > size) { + MDEBUG("read_chunk(): receivedFromDeviceLength (" + << receivedFromDeviceLength << ") > buffer size (" << size << ")"); + throw exc::CommunicationException("Response chunk too large for buffer"); + } + + memset(buff, 0, size); + memcpy(buff, receivedFromDevice, receivedFromDeviceLength); + + waitsForDeviceReceive = true; + + return receivedFromDeviceLength; +} + +std::ostream& DummyTransport::dump(std::ostream& o) const { + o << "DummyTransport"; +}; + + +void DummyTransport::setDeviceCallback(void (*sendToDeviceCallback)(unsigned char *command, unsigned int cmd_len)) { + MDEBUG("setDeviceCallback()"); + sendToTrezorDeviceCallback = sendToDeviceCallback; +} + +void DummyTransport::setDeviceReceivedData(unsigned char* data, size_t len) { + MDEBUG("setDeviceReceivedData(len: " << len << ")"); + std::unique_lock lock(mutex); + + receivedFromDevice = static_cast(malloc(len)); + receivedFromDeviceLength = len; + memset(receivedFromDevice, 0, len); + memcpy(receivedFromDevice, data, len); + waitsForDeviceReceive = false; + waitsForDeviceSend = false; + cv_send.notify_all(); + cv_receive.notify_all(); +} + + void enumerate(t_transport_vect & res){ BridgeTransport bt; try{ @@ -1193,6 +1363,12 @@ namespace trezor{ MERROR("UdpTransport enumeration failed:" << e.what()); } #endif + hw::trezor::DummyTransport dt; + try{ + dt.enumerate(res); + } catch (const std::exception & e){ + MERROR("DummyTransport enumeration failed:" << e.what()); + } } void sort_transports_by_env(t_transport_vect & res){ diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp index 827b189fd..f1036383b 100644 --- a/src/device_trezor/trezor/transport.hpp +++ b/src/device_trezor/trezor/transport.hpp @@ -302,6 +302,57 @@ namespace trezor { #endif +class DummyTransport : public Transport { +public: + + explicit DummyTransport( + boost::optional> proto = boost::none + ); + + virtual ~DummyTransport(); + + static const char * PATH_PREFIX; + + std::string get_path() const override; + void enumerate(t_transport_vect & res) override; + + void open() override; + void close() override; + std::shared_ptr find_debug() override; + + void write(const google::protobuf::Message &req) override; + void read(std::shared_ptr & msg, messages::MessageType * msg_type=nullptr) override; + + void write_chunk(const void * buff, size_t size) override; + size_t read_chunk(void * buff, size_t size) override; + + std::ostream& dump(std::ostream& o) const override; + static std::mutex mutex; + static void setDeviceReceivedData(unsigned char* data, size_t len); + static void setDeviceCallback(void (*sendToDeviceCallback)(unsigned char *command, unsigned int cmd_len)); + static std::condition_variable cv_send; + static std::condition_variable cv_receive; + static bool stateIsConnected; + static unsigned char* sendToDevice; + static size_t sendToDeviceLength; + static unsigned char* receivedFromDevice; + static size_t receivedFromDeviceLength; + static bool waitsForDeviceSend; + static bool waitsForDeviceReceive; + static void (*sendToTrezorDeviceCallback)(unsigned char *command, unsigned int cmd_len); +private: + void require_device() const; + void require_connected() const; + int get_interface() const; + unsigned char get_endpoint() const; + + std::shared_ptr m_proto; + +#ifdef WITH_TREZOR_DEBUGGING + bool m_debug_mode; +#endif +}; + // // General helpers // diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index c24b4a97d..4cb37a4a6 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -42,6 +42,7 @@ #include "mnemonics/electrum-words.h" #include "mnemonics/english.h" +#include "wallet2_api.h" #include #include #include @@ -53,6 +54,7 @@ #include "bc-ur/src/bc-ur.hpp" #if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) #include "device/device_io_dummy.hpp" +#include "device_trezor/trezor/transport.hpp" #endif using namespace std; @@ -3379,98 +3381,173 @@ uint64_t WalletImpl::getBytesSent() // HIDAPI_DUMMY -bool Wallet::getStateIsConnected() { +bool Wallet::getStateIsConnected(Device device) { #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); return false; #else - return hw::io::device_io_dummy::stateIsConnected; + switch (device) { + case Monero::Wallet::Device_Ledger: + return hw::io::device_io_dummy::stateIsConnected; + case Monero::Wallet::Device_Trezor: + return hw::trezor::DummyTransport::stateIsConnected; + default: + throw std::runtime_error("unknown device type"); + } #endif } -unsigned char* Wallet::getSendToDevice() { +unsigned char* Wallet::getSendToDevice(Device device) { #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); return {}; #else - return hw::io::device_io_dummy::sendToDevice; + switch (device) { + case Monero::Wallet::Device_Ledger: + return hw::io::device_io_dummy::sendToDevice; + case Monero::Wallet::Device_Trezor: + return hw::trezor::DummyTransport::sendToDevice; + default: + throw std::runtime_error("unknown device type"); + } #endif } -size_t Wallet::getSendToDeviceLength() { +size_t Wallet::getSendToDeviceLength(Device device) { #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); return -1; #else - return hw::io::device_io_dummy::sendToDeviceLength; + switch (device) { + case Monero::Wallet::Device_Ledger: + return hw::io::device_io_dummy::sendToDeviceLength; + case Monero::Wallet::Device_Trezor: + return hw::trezor::DummyTransport::sendToDeviceLength; + default: + throw std::runtime_error("unknown device type"); + } #endif } -unsigned char* Wallet::getReceivedFromDevice() { +unsigned char* Wallet::getReceivedFromDevice(Device device) { #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); return {}; #else - return hw::io::device_io_dummy::receivedFromDevice; + switch (device) { + case Monero::Wallet::Device_Ledger: + return hw::io::device_io_dummy::receivedFromDevice; + case Monero::Wallet::Device_Trezor: + return hw::trezor::DummyTransport::receivedFromDevice; + default: + throw std::runtime_error("unknown device type"); + } #endif } -size_t Wallet::getReceivedFromDeviceLength() { +size_t Wallet::getReceivedFromDeviceLength(Device device) { #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); return -1; #else - return hw::io::device_io_dummy::receivedFromDeviceLength; + switch (device) { + case Monero::Wallet::Device_Ledger: + return hw::io::device_io_dummy::receivedFromDeviceLength; + case Monero::Wallet::Device_Trezor: + return hw::trezor::DummyTransport::receivedFromDeviceLength; + default: + throw std::runtime_error("unknown device type"); + } #endif } -bool Wallet::getWaitsForDeviceSend() { +bool Wallet::getWaitsForDeviceSend(Device device) { #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); return false; #else - return hw::io::device_io_dummy::waitsForDeviceSend; + switch (device) { + case Monero::Wallet::Device_Ledger: + return hw::io::device_io_dummy::waitsForDeviceSend; + case Monero::Wallet::Device_Trezor: + return hw::trezor::DummyTransport::waitsForDeviceSend; + default: + throw std::runtime_error("unknown device type"); + } #endif } -bool Wallet::getWaitsForDeviceReceive() { +bool Wallet::getWaitsForDeviceReceive(Device device) { #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); return false; #else - return hw::io::device_io_dummy::waitsForDeviceReceive; + switch (device) { + case Monero::Wallet::Device_Ledger: + return hw::io::device_io_dummy::waitsForDeviceReceive; + case Monero::Wallet::Device_Trezor: + return hw::trezor::DummyTransport::waitsForDeviceReceive; + default: + throw std::runtime_error("unknown device type"); + } #endif } -void Wallet::setDeviceReceivedData(unsigned char* data, size_t len) { +void Wallet::setDeviceReceivedData(Device device, unsigned char* data, size_t len) { #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); return; #else - hw::io::device_io_dummy::setDeviceReceivedData(data, len); + switch (device) { + case Monero::Wallet::Device_Ledger: + hw::io::device_io_dummy::setDeviceReceivedData(data, len); + case Monero::Wallet::Device_Trezor: + hw::trezor::DummyTransport::setDeviceReceivedData(data, len); + default: + throw std::runtime_error("unknown device type"); + } #endif } -void Wallet::setDeviceSendData(unsigned char* data, size_t len) { +void Wallet::setDeviceSendData(Device device, unsigned char* data, size_t len) { #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); return; #else - hw::io::device_io_dummy::sendToDevice = static_cast(malloc(len)); - hw::io::device_io_dummy::sendToDeviceLength = len; - memset(hw::io::device_io_dummy::sendToDevice, 0, len); - memcpy(hw::io::device_io_dummy::sendToDevice, data, len); - hw::io::device_io_dummy::waitsForDeviceSend = false; - hw::io::device_io_dummy::cv_send.notify_all(); + switch (device) { + case Monero::Wallet::Device_Ledger: + hw::io::device_io_dummy::sendToDevice = static_cast(malloc(len)); + hw::io::device_io_dummy::sendToDeviceLength = len; + memset(hw::io::device_io_dummy::sendToDevice, 0, len); + memcpy(hw::io::device_io_dummy::sendToDevice, data, len); + hw::io::device_io_dummy::waitsForDeviceSend = false; + hw::io::device_io_dummy::cv_send.notify_all(); + case Monero::Wallet::Device_Trezor: + hw::trezor::DummyTransport::sendToDevice = static_cast(malloc(len)); + hw::trezor::DummyTransport::sendToDeviceLength = len; + memset(hw::trezor::DummyTransport::sendToDevice, 0, len); + memcpy(hw::trezor::DummyTransport::sendToDevice, data, len); + hw::trezor::DummyTransport::waitsForDeviceSend = false; + hw::trezor::DummyTransport::cv_send.notify_all(); + default: + throw std::runtime_error("unknown device type"); + } #endif } -void Wallet::setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len)) { +void Wallet::setDeviceCallback(Monero::Wallet::Device device, void (*sendToDevice)(unsigned char *command, unsigned int cmd_len)) { #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) MERROR("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); return; #else - hw::io::device_io_dummy::setLedgerCallback(sendToLedgerDevice); + switch (device) { + case Monero::Wallet::Device_Ledger: + hw::io::device_io_dummy::setDeviceCallback(sendToDevice); + case Monero::Wallet::Device_Trezor: + hw::trezor::DummyTransport::setDeviceCallback(sendToDevice); + default: + throw std::runtime_error("unknown device type"); + } #endif } diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 98c03b9c1..2a29c07b3 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -322,19 +322,19 @@ private: mutable std::atomic m_is_connected; boost::optional m_daemon_login{}; - bool getStateIsConnected(); + bool getStateIsConnected(Device device); - unsigned char *getSendToDevice(); + unsigned char *getSendToDevice(Device device); - size_t getSendToDeviceLength(); + size_t getSendToDeviceLength(Device device); - unsigned char *getReceivedFromDevice(); + unsigned char *getReceivedFromDevice(Device device); - size_t getReceivedFromDeviceLength(); + size_t getReceivedFromDeviceLength(Device device); - bool getWaitsForDeviceSend(); + bool getWaitsForDeviceSend(Device device); - bool getWaitsForDeviceReceive(); + bool getWaitsForDeviceReceive(Device device); virtual std::string serializeCacheToJson() const override; }; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 3d11929f9..61756a127 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -1206,17 +1206,16 @@ struct Wallet virtual uint64_t getBytesSent() = 0; // HIDAPI_DUMMY - static bool getStateIsConnected(); - static unsigned char* getSendToDevice(); - static size_t getSendToDeviceLength(); - static unsigned char* getReceivedFromDevice(); - static size_t getReceivedFromDeviceLength(); - static bool getWaitsForDeviceSend(); - static bool getWaitsForDeviceReceive(); - - static void setDeviceReceivedData(unsigned char* data, size_t len); - static void setDeviceSendData(unsigned char* data, size_t len); - static void setLedgerCallback(void (*sendToLedgerDevice)(unsigned char *command, unsigned int cmd_len)); + static bool getStateIsConnected(Device device); + static unsigned char* getSendToDevice(Device device); + static size_t getSendToDeviceLength(Device device); + static unsigned char* getReceivedFromDevice(Device device); + static size_t getReceivedFromDeviceLength(Device device); + static bool getWaitsForDeviceSend(Device device); + static bool getWaitsForDeviceReceive(Device device); + static void setDeviceReceivedData(Device device, unsigned char* data, size_t len); + static void setDeviceSendData(Device device, unsigned char* data, size_t len); + static void setDeviceCallback(Device device, void (*sendToDevice)(unsigned char *command, unsigned int cmd_len)); //! serialize wallet cache to JSON virtual std::string serializeCacheToJson() const = 0; -- 2.50.1 (Apple Git-155)