device/trezor: debugging features, trezor tests

This commit is contained in:
Dusan Klinec 2018-11-20 14:40:51 +01:00
parent 31bdf7bd11
commit 5ea17909ca
No known key found for this signature in database
GPG Key ID: 6337E118CCBCE103
24 changed files with 3616 additions and 177 deletions

View File

@ -63,6 +63,10 @@ debug-test:
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE) && $(MAKE) ARGS="-E libwallet_api_tests" test
debug-test-trezor:
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D TREZOR_DEBUG=ON -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE) && $(MAKE) ARGS="-E libwallet_api_tests" test
debug-all:
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D BUILD_SHARED_LIBS=OFF -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE)

View File

@ -1,6 +1,8 @@
OPTION(USE_DEVICE_TREZOR "Trezor support compilation" ON)
OPTION(USE_DEVICE_TREZOR_LIBUSB "Trezor LibUSB compilation" ON)
OPTION(USE_DEVICE_TREZOR_UDP_RELEASE "Trezor UdpTransport in release mode" OFF)
OPTION(USE_DEVICE_TREZOR_DEBUG "Trezor Debugging enabled" OFF)
OPTION(TREZOR_DEBUG "Main trezor debugging switch" OFF)
# Helper function to fix cmake < 3.6.0 FindProtobuf variables
function(_trezor_protobuf_fix_vars)
@ -53,6 +55,14 @@ if (USE_DEVICE_TREZOR)
set(Protobuf_FOUND 1) # override found if all rquired info was provided by variables
endif()
if(TREZOR_DEBUG)
set(USE_DEVICE_TREZOR_DEBUG 1)
endif()
# Compile debugging support (for tests)
if (USE_DEVICE_TREZOR_DEBUG)
add_definitions(-DWITH_TREZOR_DEBUGGING=1)
endif()
else()
message(STATUS "Trezor support disabled by USE_DEVICE_TREZOR")
endif()
@ -106,7 +116,12 @@ endif()
if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON AND Protobuf_COMPILE_TEST_PASSED)
set(ENV{PROTOBUF_INCLUDE_DIRS} "${Protobuf_INCLUDE_DIR}")
set(ENV{PROTOBUF_PROTOC_EXECUTABLE} "${Protobuf_PROTOC_EXECUTABLE}")
execute_process(COMMAND ${TREZOR_PYTHON} tools/build_protob.py WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../src/device_trezor/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR)
set(TREZOR_PROTOBUF_PARAMS "")
if (USE_DEVICE_TREZOR_DEBUG)
set(TREZOR_PROTOBUF_PARAMS "--debug")
endif()
execute_process(COMMAND ${TREZOR_PYTHON} tools/build_protob.py ${TREZOR_PROTOBUF_PARAMS} WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../src/device_trezor/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR)
if(RET)
message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})."
"OUT: ${OUT}, ERR: ${ERR}."

View File

@ -67,6 +67,12 @@ set(trezor_private_headers)
if(DEVICE_TREZOR_READY)
message(STATUS "Trezor support enabled")
if(USE_DEVICE_TREZOR_DEBUG)
list(APPEND trezor_headers trezor/debug_link.hpp trezor/messages/messages-debug.pb.h)
list(APPEND trezor_sources trezor/debug_link.cpp trezor/messages/messages-debug.pb.cc)
message(STATUS "Trezor debugging enabled")
endif()
monero_private_headers(device_trezor
${device_private_headers})

View File

@ -97,7 +97,14 @@ namespace trezor {
auto res = get_view_key();
CHECK_AND_ASSERT_MES(res->watch_key().size() == 32, false, "Trezor returned invalid view key");
// Trezor does not make use of spendkey of the device API.
// Ledger loads encrypted spendkey, Trezor loads null key (never leaves device).
// In the test (debugging mode) we need to leave this field intact as it is already set by
// the debugging code and need to remain same for the testing purposes.
#ifndef WITH_TREZOR_DEBUGGING
spendkey = crypto::null_skey; // not given
#endif
memcpy(viewkey.data, res->watch_key().data(), 32);
return true;
@ -362,13 +369,9 @@ namespace trezor {
CHECK_AND_ASSERT_THROW_MES(m_features, "Device state not initialized"); // make sure the caller did not reset features
const bool nonce_required = init_msg->tsx_data().has_payment_id() && init_msg->tsx_data().payment_id().size() > 0;
if (nonce_required){
if (nonce_required && init_msg->tsx_data().payment_id().size() == 8){
// Versions 2.0.9 and lower do not support payment ID
CHECK_AND_ASSERT_THROW_MES(m_features->has_major_version() && m_features->has_minor_version() && m_features->has_patch_version(), "Invalid Trezor firmware version information");
const uint32_t vma = m_features->major_version();
const uint32_t vmi = m_features->minor_version();
const uint32_t vpa = m_features->patch_version();
if (vma < 2 || (vma == 2 && vmi == 0 && vpa <= 9)) {
if (get_version() <= pack_version(2, 0, 9)) {
throw exc::TrezorException("Trezor firmware 2.0.9 and lower does not support transactions with short payment IDs or integrated addresses. Please update.");
}
}

View File

@ -44,7 +44,9 @@ namespace trezor {
const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080};
device_trezor_base::device_trezor_base(): m_callback(nullptr) {
#ifdef WITH_TREZOR_DEBUGGING
m_debug = false;
#endif
}
device_trezor_base::~device_trezor_base() {
@ -130,6 +132,10 @@ namespace trezor {
}
m_transport->open();
#ifdef WITH_TREZOR_DEBUGGING
setup_debug();
#endif
return true;
} catch(std::exception const& e){
@ -153,6 +159,13 @@ namespace trezor {
return false;
}
}
#ifdef WITH_TREZOR_DEBUGGING
if (m_debug_callback) {
m_debug_callback->on_disconnect();
m_debug_callback = nullptr;
}
#endif
return true;
}
@ -355,6 +368,32 @@ namespace trezor {
device_state_reset_unsafe();
}
#ifdef WITH_TREZOR_DEBUGGING
#define TREZOR_CALLBACK(method, ...) do { \
if (m_debug_callback) m_debug_callback->method(__VA_ARGS__); \
if (m_callback) m_callback->method(__VA_ARGS__); \
}while(0)
void device_trezor_base::setup_debug(){
if (!m_debug){
return;
}
if (!m_debug_callback){
CHECK_AND_ASSERT_THROW_MES(m_transport, "Transport does not exist");
auto debug_transport = m_transport->find_debug();
if (debug_transport) {
m_debug_callback = std::make_shared<trezor_debug_callback>(debug_transport);
} else {
MDEBUG("Transport does not have debug link option");
}
}
}
#else
#define TREZOR_CALLBACK(method, ...) do { if (m_callback) m_callback->method(__VA_ARGS__); } while(0)
#endif
void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg)
{
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
@ -363,10 +402,7 @@ namespace trezor {
messages::common::ButtonAck ack;
write_raw(&ack);
if (m_callback){
m_callback->on_button_request();
}
TREZOR_CALLBACK(on_button_request);
resp = read_raw();
}
@ -377,9 +413,7 @@ namespace trezor {
epee::wipeable_string pin;
if (m_callback){
m_callback->on_pin_request(pin);
}
TREZOR_CALLBACK(on_pin_request, pin);
// TODO: remove PIN from memory
messages::common::PinMatrixAck m;
@ -393,9 +427,7 @@ namespace trezor {
MDEBUG("on_passhprase_request, on device: " << msg->on_device());
epee::wipeable_string passphrase;
if (m_callback){
m_callback->on_passphrase_request(msg->on_device(), passphrase);
}
TREZOR_CALLBACK(on_passphrase_request, msg->on_device(), passphrase);
messages::common::PassphraseAck m;
if (!msg->on_device()){
@ -421,5 +453,67 @@ namespace trezor {
resp = call_raw(&m);
}
#ifdef WITH_TREZOR_DEBUGGING
void device_trezor_base::wipe_device()
{
auto msg = std::make_shared<messages::management::WipeDevice>();
auto ret = client_exchange<messages::common::Success>(msg);
(void)ret;
init_device();
}
void device_trezor_base::init_device()
{
auto msg = std::make_shared<messages::management::Initialize>();
m_features = client_exchange<messages::management::Features>(msg);
}
void device_trezor_base::load_device(const std::string & mnemonic, const std::string & pin,
bool passphrase_protection, const std::string & label, const std::string & language,
bool skip_checksum, bool expand)
{
if (m_features && m_features->initialized()){
throw std::runtime_error("Device is initialized already. Call device.wipe() and try again.");
}
auto msg = std::make_shared<messages::management::LoadDevice>();
msg->set_mnemonic(mnemonic);
msg->set_pin(pin);
msg->set_passphrase_protection(passphrase_protection);
msg->set_label(label);
msg->set_language(language);
msg->set_skip_checksum(skip_checksum);
auto ret = client_exchange<messages::common::Success>(msg);
(void)ret;
init_device();
}
trezor_debug_callback::trezor_debug_callback(std::shared_ptr<Transport> & debug_transport){
m_debug_link = std::make_shared<DebugLink>();
m_debug_link->init(debug_transport);
}
void trezor_debug_callback::on_button_request() {
if (m_debug_link) m_debug_link->press_yes();
}
void trezor_debug_callback::on_pin_request(epee::wipeable_string &pin) {
}
void trezor_debug_callback::on_passphrase_request(bool on_device, epee::wipeable_string &passphrase) {
}
void trezor_debug_callback::on_passphrase_state_request(const std::string &state) {
}
void trezor_debug_callback::on_disconnect(){
if (m_debug_link) m_debug_link->close();
}
#endif
#endif //WITH_DEVICE_TREZOR
}}

View File

@ -42,6 +42,10 @@
#include "cryptonote_config.h"
#include "trezor.hpp"
#ifdef WITH_TREZOR_DEBUGGING
#include "trezor/debug_link.hpp"
#endif
//automatic lock one more level on device ensuring the current thread is allowed to use it
#define AUTO_LOCK_CMD() \
/* lock both mutexes without deadlock*/ \
@ -57,6 +61,23 @@ namespace trezor {
#ifdef WITH_DEVICE_TREZOR
class device_trezor_base;
#ifdef WITH_TREZOR_DEBUGGING
class trezor_debug_callback {
public:
trezor_debug_callback()=default;
explicit trezor_debug_callback(std::shared_ptr<Transport> & debug_transport);
void on_button_request();
void on_pin_request(epee::wipeable_string &pin);
void on_passphrase_request(bool on_device, epee::wipeable_string &passphrase);
void on_passphrase_state_request(const std::string &state);
void on_disconnect();
protected:
std::shared_ptr<DebugLink> m_debug_link;
};
#endif
/**
* TREZOR device template with basic functions
*/
@ -77,6 +98,13 @@ namespace trezor {
cryptonote::network_type network_type;
#ifdef WITH_TREZOR_DEBUGGING
std::shared_ptr<trezor_debug_callback> m_debug_callback;
bool m_debug;
void setup_debug();
#endif
//
// Internal methods
//
@ -103,7 +131,7 @@ namespace trezor {
* @throws UnexpectedMessageException if the response message type is different than expected.
* Exception contains message type and the message itself.
*/
template<class t_message>
template<class t_message=google::protobuf::Message>
std::shared_ptr<t_message>
client_exchange(const std::shared_ptr<const google::protobuf::Message> &req,
const boost::optional<messages::MessageType> & resp_type = boost::none,
@ -229,6 +257,12 @@ namespace trezor {
return m_features;
}
uint64_t get_version() const {
CHECK_AND_ASSERT_THROW_MES(m_features, "Features not loaded");
CHECK_AND_ASSERT_THROW_MES(m_features->has_major_version() && m_features->has_minor_version() && m_features->has_patch_version(), "Invalid Trezor firmware version information");
return pack_version(m_features->major_version(), m_features->minor_version(), m_features->patch_version());
}
void set_derivation_path(const std::string &deriv_path) override;
/* ======================================================================= */
@ -268,6 +302,23 @@ namespace trezor {
void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg);
void on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg);
void on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg);
#ifdef WITH_TREZOR_DEBUGGING
void set_debug(bool debug){
m_debug = debug;
}
void set_debug_callback(std::shared_ptr<trezor_debug_callback> & debug_callback){
m_debug_callback = debug_callback;
}
void wipe_device();
void init_device();
void load_device(const std::string & mnemonic, const std::string & pin="", bool passphrase_protection=false,
const std::string & label="test", const std::string & language="english",
bool skip_checksum=false, bool expand=false);
#endif
};
#endif

View File

@ -0,0 +1,90 @@
// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#include "debug_link.hpp"
namespace hw{
namespace trezor{
DebugLink::DebugLink(){
}
DebugLink::~DebugLink(){
if (m_transport){
close();
}
}
void DebugLink::init(std::shared_ptr<Transport> & transport){
CHECK_AND_ASSERT_THROW_MES(!m_transport, "Already initialized");
m_transport = transport;
m_transport->open();
}
void DebugLink::close(){
CHECK_AND_ASSERT_THROW_MES(m_transport, "Not initialized");
if (m_transport) m_transport->close();
}
std::shared_ptr<messages::debug::DebugLinkState> DebugLink::state(){
return call<messages::debug::DebugLinkState>(
messages::debug::DebugLinkGetState(),
boost::make_optional(messages::MessageType_DebugLinkGetState));
}
void DebugLink::input_word(const std::string & word){
messages::debug::DebugLinkDecision decision;
decision.set_input(word);
call(decision, boost::none, true);
}
void DebugLink::input_button(bool button){
messages::debug::DebugLinkDecision decision;
decision.set_yes_no(button);
call(decision, boost::none, true);
}
void DebugLink::input_swipe(bool swipe){
messages::debug::DebugLinkDecision decision;
decision.set_up_down(swipe);
call(decision, boost::none, true);
}
void DebugLink::stop(){
messages::debug::DebugLinkStop msg;
call(msg, boost::none, true);
}
}
}

View File

@ -0,0 +1,93 @@
// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#ifndef MONERO_DEBUG_LINK_H
#define MONERO_DEBUG_LINK_H
#include "transport.hpp"
#include "messages/messages-debug.pb.h"
namespace hw {
namespace trezor {
class DebugLink {
public:
DebugLink();
virtual ~DebugLink();
void init(std::shared_ptr<Transport> & transport);
void close();
std::shared_ptr<messages::debug::DebugLinkState> state();
void input_word(const std::string & word);
void input_button(bool button);
void input_swipe(bool swipe);
void press_yes() { input_button(true); }
void press_no() { input_button(false); }
void stop();
template<class t_message=messages::debug::DebugLinkState>
std::shared_ptr<t_message> call(
const google::protobuf::Message & req,
const boost::optional<messages::MessageType> &resp_type = boost::none,
bool no_wait = false)
{
BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
m_transport->write(req);
if (no_wait){
return nullptr;
}
// Read the response
std::shared_ptr<google::protobuf::Message> msg_resp;
hw::trezor::messages::MessageType msg_resp_type;
m_transport->read(msg_resp, &msg_resp_type);
messages::MessageType required_type = resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>();
if (msg_resp_type == required_type) {
return message_ptr_retype<t_message>(msg_resp);
} else if (msg_resp_type == messages::MessageType_Failure){
throw_failure_exception(dynamic_cast<messages::common::Failure*>(msg_resp.get()));
} else {
throw exc::UnexpectedMessageException(msg_resp_type, msg_resp);
}
};
private:
std::shared_ptr<Transport> m_transport;
};
}
}
#endif //MONERO_DEBUG_LINK_H

View File

@ -33,6 +33,10 @@
#include "messages/messages-management.pb.h"
#include "messages/messages-monero.pb.h"
#ifdef WITH_TREZOR_DEBUGGING
#include "messages/messages-debug.pb.h"
#endif
using namespace std;
using namespace hw::trezor;
@ -45,6 +49,9 @@ namespace trezor
"hw.trezor.messages.",
"hw.trezor.messages.common.",
"hw.trezor.messages.management.",
#ifdef WITH_TREZOR_DEBUGGING
"hw.trezor.messages.debug.",
#endif
"hw.trezor.messages.monero."
};
@ -68,6 +75,10 @@ namespace trezor
hw::trezor::messages::management::Cancel::default_instance();
hw::trezor::messages::monero::MoneroGetAddress::default_instance();
#ifdef WITH_TREZOR_DEBUGGING
hw::trezor::messages::debug::DebugLinkDecision::default_instance();
#endif
google::protobuf::Descriptor const * desc = nullptr;
for(const string &text : PACKAGES){
desc = google::protobuf::DescriptorPool::generated_pool()

View File

@ -62,14 +62,14 @@ namespace trezor {
static messages::MessageType get_message_wire_number(const google::protobuf::Message & msg);
static messages::MessageType get_message_wire_number(const std::string & msg_name);
template<class t_message>
template<class t_message=google::protobuf::Message>
static messages::MessageType get_message_wire_number() {
BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
return get_message_wire_number(t_message::default_instance().GetDescriptor()->name());
}
};
template<class t_message>
template<class t_message=google::protobuf::Message>
std::shared_ptr<t_message> message_ptr_retype(std::shared_ptr<google::protobuf::Message> & in){
BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
if (!in){
@ -79,7 +79,7 @@ namespace trezor {
return std::dynamic_pointer_cast<t_message>(in);
}
template<class t_message>
template<class t_message=google::protobuf::Message>
std::shared_ptr<t_message> message_ptr_retype_static(std::shared_ptr<google::protobuf::Message> & in){
BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
if (!in){

View File

@ -2,6 +2,12 @@
import os
import subprocess
import sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-d", "--debug-msg", default=False, action="store_const", const=True, help="Build debug messages")
args = parser.parse_args()
CWD = os.path.dirname(os.path.realpath(__file__))
ROOT_DIR = os.path.abspath(os.path.join(CWD, "..", "..", "..", ".."))
@ -24,6 +30,10 @@ try:
"messages-management.proto",
"messages-monero.proto",
]
if args.debug_msg:
selected += ["messages-debug.proto"]
proto_srcs = [os.path.join(TREZOR_COMMON, "protob", x) for x in selected]
exec_args = [
sys.executable,

View File

@ -84,6 +84,17 @@ namespace trezor{
return std::string(in.GetString());
}
uint64_t pack_version(uint32_t major, uint32_t minor, uint32_t patch)
{
// packing (major, minor, patch) to 64 B: 16 B | 24 B | 24 B
const unsigned bits_1 = 16;
const unsigned bits_2 = 24;
const uint32_t mask_1 = (1 << bits_1) - 1;
const uint32_t mask_2 = (1 << bits_2) - 1;
CHECK_AND_ASSERT_THROW_MES(major <= mask_1 && minor <= mask_2 && patch <= mask_2, "Version numbers overflow packing scheme");
return patch | (((uint64_t)minor) << bits_2) | (((uint64_t)major) << (bits_1 + bits_2));
}
//
// Helpers
//
@ -212,6 +223,40 @@ namespace trezor{
msg = msg_wrap;
}
Transport::Transport(): m_open_counter(0) {
}
bool Transport::pre_open(){
if (m_open_counter > 0){
MTRACE("Already opened, count: " << m_open_counter);
m_open_counter += 1;
return false;
} else if (m_open_counter < 0){
MTRACE("Negative open value: " << m_open_counter);
}
// Caller should set m_open_counter to 1 after open
m_open_counter = 0;
return true;
}
bool Transport::pre_close(){
m_open_counter -= 1;
if (m_open_counter < 0){
MDEBUG("Already closed. Counter " << m_open_counter);
} else if (m_open_counter == 0) {
return true;
}
return false;
}
//
// Bridge transport
//
@ -246,6 +291,10 @@ namespace trezor{
}
void BridgeTransport::open() {
if (!pre_open()){
return;
}
if (!m_device_path){
throw exc::CommunicationException("Coud not open, empty device path");
}
@ -259,9 +308,15 @@ namespace trezor{
}
m_session = boost::make_optional(json_get_string(bridge_res["session"]));
m_open_counter = 1;
}
void BridgeTransport::close() {
if (!pre_close()){
return;
}
MTRACE("Closing Trezor:BridgeTransport");
if (!m_device_path || !m_session){
throw exc::CommunicationException("Device not open");
}
@ -423,6 +478,10 @@ namespace trezor{
}
void UdpTransport::open() {
if (!pre_open()){
return;
}
udp::resolver resolver(m_io_service);
udp::resolver::query query(udp::v4(), m_device_host, std::to_string(m_device_port));
m_endpoint = *resolver.resolve(query);
@ -434,10 +493,16 @@ namespace trezor{
check_deadline();
m_proto->session_begin(*this);
m_open_counter = 1;
}
void UdpTransport::close() {
if (!m_socket){
if (!pre_close()){
return;
}
MTRACE("Closing Trezor:UdpTransport");
if (!m_socket) {
throw exc::CommunicationException("Socket is already closed");
}
@ -446,6 +511,19 @@ namespace trezor{
m_socket = nullptr;
}
std::shared_ptr<Transport> UdpTransport::find_debug() {
#ifdef WITH_TREZOR_DEBUGGING
std::shared_ptr<UdpTransport> t = std::make_shared<UdpTransport>();
t->m_proto = std::make_shared<ProtocolV1>();
t->m_device_host = m_device_host;
t->m_device_port = m_device_port + 1;
return t;
#else
MINFO("Debug link is disabled in production");
return nullptr;
#endif
}
void UdpTransport::write_chunk(const void * buff, size_t size){
require_socket();
@ -660,8 +738,7 @@ namespace trezor{
WebUsbTransport::WebUsbTransport(
boost::optional<libusb_device_descriptor*> descriptor,
boost::optional<std::shared_ptr<Protocol>> proto
): m_conn_count(0),
m_usb_session(nullptr), m_usb_device(nullptr), m_usb_device_handle(nullptr),
): m_usb_session(nullptr), m_usb_device(nullptr), m_usb_device_handle(nullptr),
m_bus_id(-1), m_device_addr(-1)
{
if (descriptor){
@ -672,7 +749,7 @@ namespace trezor{
m_proto = proto ? proto.get() : std::make_shared<ProtocolV1>();
#ifdef WITH_TREZOR_DEBUG
#ifdef WITH_TREZOR_DEBUGGING
m_debug_mode = false;
#endif
}
@ -757,12 +834,10 @@ namespace trezor{
};
void WebUsbTransport::open() {
const int interface = get_interface();
if (m_conn_count > 0){
MTRACE("Already opened, count: " << m_conn_count);
m_conn_count += 1;
if (!pre_open()){
return;
}
const int interface = get_interface();
#define TREZOR_DESTROY_SESSION() do { libusb_exit(m_usb_session); m_usb_session = nullptr; } while(0)
@ -840,45 +915,55 @@ namespace trezor{
throw exc::DeviceAcquireException("Unable to claim libusb device");
}
m_conn_count = 1;
m_open_counter = 1;
m_proto->session_begin(*this);
#undef TREZOR_DESTROY_SESSION
};
void WebUsbTransport::close() {
m_conn_count -= 1;
if (!pre_close()){
return;
}
if (m_conn_count < 0){
MERROR("Close counter is negative: " << m_conn_count);
MTRACE("Closing Trezor:WebUsbTransport");
m_proto->session_end(*this);
} else if (m_conn_count == 0){
MTRACE("Closing webusb device");
int r = libusb_release_interface(m_usb_device_handle, get_interface());
if (r != 0){
MERROR("Could not release libusb interface: " << r);
}
m_proto->session_end(*this);
m_usb_device = nullptr;
if (m_usb_device_handle) {
libusb_close(m_usb_device_handle);
m_usb_device_handle = nullptr;
}
int r = libusb_release_interface(m_usb_device_handle, get_interface());
if (r != 0){
MERROR("Could not release libusb interface: " << r);
}
m_usb_device = nullptr;
if (m_usb_device_handle) {
libusb_close(m_usb_device_handle);
m_usb_device_handle = nullptr;
}
if (m_usb_session) {
libusb_exit(m_usb_session);
m_usb_session = nullptr;
}
if (m_usb_session) {
libusb_exit(m_usb_session);
m_usb_session = nullptr;
}
};
std::shared_ptr<Transport> WebUsbTransport::find_debug() {
#ifdef WITH_TREZOR_DEBUGGING
require_device();
auto t = std::make_shared<WebUsbTransport>(boost::make_optional(m_usb_device_desc.get()));
t->m_bus_id = m_bus_id;
t->m_device_addr = m_device_addr;
t->m_port_numbers = m_port_numbers;
t->m_debug_mode = true;
return t;
#else
MINFO("Debug link is disabled in production");
return nullptr;
#endif
}
int WebUsbTransport::get_interface() const{
const int INTERFACE_NORMAL = 0;
#ifdef WITH_TREZOR_DEBUG
#ifdef WITH_TREZOR_DEBUGGING
const int INTERFACE_DEBUG = 1;
return m_debug_mode ? INTERFACE_DEBUG : INTERFACE_NORMAL;
#else
@ -888,7 +973,7 @@ namespace trezor{
unsigned char WebUsbTransport::get_endpoint() const{
const unsigned char ENDPOINT_NORMAL = 1;
#ifdef WITH_TREZOR_DEBUG
#ifdef WITH_TREZOR_DEBUGGING
const unsigned char ENDPOINT_DEBUG = 2;
return m_debug_mode ? ENDPOINT_DEBUG : ENDPOINT_NORMAL;
#else
@ -1047,4 +1132,3 @@ namespace trezor{
}
}

View File

@ -62,6 +62,8 @@ namespace trezor {
const std::string DEFAULT_BRIDGE = "127.0.0.1:21325";
uint64_t pack_version(uint32_t major, uint32_t minor=0, uint32_t patch=0);
// Base HTTP comm serialization.
bool t_serialize(const std::string & in, std::string & out);
bool t_serialize(const json_val & in, std::string & out);
@ -134,7 +136,7 @@ namespace trezor {
class Transport {
public:
Transport() = default;
Transport();
virtual ~Transport() = default;
virtual bool ping() { return false; };
@ -144,10 +146,16 @@ namespace trezor {
virtual void close(){};
virtual void write(const google::protobuf::Message & req) =0;
virtual void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) =0;
virtual std::shared_ptr<Transport> find_debug() { return nullptr; };
virtual void write_chunk(const void * buff, size_t size) { };
virtual size_t read_chunk(void * buff, size_t size) { return 0; };
virtual std::ostream& dump(std::ostream& o) const { return o << "Transport<>"; }
protected:
long m_open_counter;
virtual bool pre_open();
virtual bool pre_close();
};
// Bridge transport
@ -212,6 +220,7 @@ namespace trezor {
void open() override;
void close() override;
std::shared_ptr<Transport> find_debug() override;
void write(const google::protobuf::Message &req) override;
void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override;
@ -259,6 +268,7 @@ namespace trezor {
void open() override;
void close() override;
std::shared_ptr<Transport> find_debug() override;
void write(const google::protobuf::Message &req) override;
void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override;
@ -274,7 +284,6 @@ namespace trezor {
int get_interface() const;
unsigned char get_endpoint() const;
int m_conn_count;
std::shared_ptr<Protocol> m_proto;
libusb_context *m_usb_session;
@ -285,7 +294,7 @@ namespace trezor {
int m_bus_id;
int m_device_addr;
#ifdef WITH_TREZOR_DEBUG
#ifdef WITH_TREZOR_DEBUGGING
bool m_debug_mode;
#endif
};
@ -309,7 +318,7 @@ namespace trezor {
/**
* Transforms path to the particular transport
*/
template<class t_transport>
template<class t_transport=Transport>
std::shared_ptr<t_transport> transport_typed(const std::string & path){
auto t = transport(path);
if (!t){
@ -362,7 +371,7 @@ namespace trezor {
* @throws UnexpectedMessageException if the response message type is different than expected.
* Exception contains message type and the message itself.
*/
template<class t_message>
template<class t_message=google::protobuf::Message>
std::shared_ptr<t_message>
exchange_message(Transport & transport, const google::protobuf::Message & req,
boost::optional<messages::MessageType> resp_type = boost::none)

View File

@ -30,5 +30,5 @@ set(net_sources error.cpp parse.cpp socks.cpp tor_address.cpp)
set(net_headers error.h parse.h socks.h tor_address.h)
monero_add_library(net ${net_sources} ${net_headers})
target_link_libraries(net epee ${Boost_ASIO_LIBRARY})
target_link_libraries(net common epee ${Boost_ASIO_LIBRARY})

View File

@ -67,6 +67,7 @@
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
class Serialization_portability_wallet_Test;
class wallet_accessor_test;
namespace tools
{
@ -171,6 +172,7 @@ namespace tools
class wallet2
{
friend class ::Serialization_portability_wallet_Test;
friend class ::wallet_accessor_test;
friend class wallet_keys_unlocker;
friend class wallet_device_callback;
public:

View File

@ -91,6 +91,10 @@ if (BUILD_GUI_DEPS)
add_subdirectory(libwallet_api_tests)
endif()
if (TREZOR_DEBUG)
add_subdirectory(trezor)
endif()
# add_subdirectory(daemon_tests)
set(hash_targets_sources

View File

@ -42,7 +42,8 @@ set(core_tests_sources
tx_validation.cpp
v2_tests.cpp
rct.cpp
bulletproofs.cpp)
bulletproofs.cpp
wallet_tools.cpp)
set(core_tests_headers
block_reward.h
@ -60,7 +61,8 @@ set(core_tests_headers
tx_validation.h
v2_tests.h
rct.h
bulletproofs.h)
bulletproofs.h
wallet_tools.h)
add_executable(core_tests
${core_tests_sources}
@ -73,6 +75,7 @@ target_link_libraries(core_tests
version
epee
device
wallet
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
enable_stack_trace(core_tests)

View File

@ -31,6 +31,11 @@
#include <vector>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <array>
#include <random>
#include <sstream>
#include <fstream>
#include "include_base_utils.h"
@ -105,10 +110,11 @@ void test_generator::add_block(const cryptonote::block& blk, size_t txs_weight,
bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id,
const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins,
std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list)
std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list,
const boost::optional<uint8_t>& hf_ver)
{
blk.major_version = CURRENT_BLOCK_MAJOR_VERSION;
blk.minor_version = CURRENT_BLOCK_MINOR_VERSION;
blk.major_version = hf_ver ? hf_ver.get() : CURRENT_BLOCK_MAJOR_VERSION;
blk.minor_version = hf_ver ? hf_ver.get() : CURRENT_BLOCK_MINOR_VERSION;
blk.timestamp = timestamp;
blk.prev_id = prev_id;
@ -135,7 +141,7 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
size_t target_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
while (true)
{
if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10))
if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10, hf_ver ? hf_ver.get() : 1))
return false;
size_t actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
@ -180,10 +186,10 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
// Nonce search...
blk.nonce = 0;
while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height))
while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(hf_ver), height))
blk.timestamp++;
add_block(blk, txs_weight, block_weights, already_generated_coins);
add_block(blk, txs_weight, block_weights, already_generated_coins, hf_ver ? hf_ver.get() : 1);
return true;
}
@ -197,17 +203,18 @@ bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::a
bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev,
const cryptonote::account_base& miner_acc,
const std::list<cryptonote::transaction>& tx_list/* = std::list<cryptonote::transaction>()*/)
const std::list<cryptonote::transaction>& tx_list/* = std::list<cryptonote::transaction>()*/,
const boost::optional<uint8_t>& hf_ver)
{
uint64_t height = boost::get<txin_gen>(blk_prev.miner_tx.vin.front()).height + 1;
crypto::hash prev_id = get_block_hash(blk_prev);
// Keep difficulty unchanged
uint64_t timestamp = blk_prev.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN;
uint64_t timestamp = blk_prev.timestamp + current_difficulty_window(hf_ver); // DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN;
uint64_t already_generated_coins = get_already_generated_coins(prev_id);
std::vector<size_t> block_weights;
get_last_n_block_weights(block_weights, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_weights, tx_list);
return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_weights, tx_list, hf_ver);
}
bool test_generator::construct_block_manually(block& blk, const block& prev_block, const account_base& miner_acc,
@ -244,7 +251,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
//blk.tree_root_hash = get_tx_tree_hash(blk);
difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty();
difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty(hf_version);
fill_nonce(blk, a_diffic, height);
add_block(blk, txs_weight, block_weights, already_generated_coins, hf_version);
@ -259,49 +266,6 @@ bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const c
return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_weight);
}
struct output_index {
const cryptonote::txout_target_v out;
uint64_t amount;
size_t blk_height; // block height
size_t tx_no; // index of transaction in block
size_t out_no; // index of out in transaction
size_t idx;
bool spent;
const cryptonote::block *p_blk;
const cryptonote::transaction *p_tx;
output_index(const cryptonote::txout_target_v &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const cryptonote::block *_pb, const cryptonote::transaction *_pt)
: out(_out), amount(_a), blk_height(_h), tx_no(tno), out_no(ono), idx(0), spent(false), p_blk(_pb), p_tx(_pt) { }
output_index(const output_index &other)
: out(other.out), amount(other.amount), blk_height(other.blk_height), tx_no(other.tx_no), out_no(other.out_no), idx(other.idx), spent(other.spent), p_blk(other.p_blk), p_tx(other.p_tx) { }
const std::string toString() const {
std::stringstream ss;
ss << "output_index{blk_height=" << blk_height
<< " tx_no=" << tx_no
<< " out_no=" << out_no
<< " amount=" << amount
<< " idx=" << idx
<< " spent=" << spent
<< "}";
return ss.str();
}
output_index& operator=(const output_index& other)
{
new(this) output_index(other);
return *this;
}
};
typedef std::map<uint64_t, std::vector<size_t> > map_output_t;
typedef std::map<uint64_t, std::vector<output_index> > map_output_idx_t;
typedef pair<uint64_t, size_t> outloc_t;
namespace
{
uint64_t get_inputs_amount(const vector<tx_source_entry> &s)
@ -339,6 +303,9 @@ bool init_output_indices(map_output_idx_t& outs, std::map<uint64_t, std::vector<
const tx_out &out = tx.vout[j];
output_index oi(out.target, out.amount, boost::get<txin_gen>(*blk.miner_tx.vin.begin()).height, i, j, &blk, vtx[i]);
oi.set_rct(tx.version == 2);
oi.unlock_time = tx.unlock_time;
oi.is_coin_base = i == 0;
if (2 == out.target.which()) { // out_to_key
outs[out.amount].push_back(oi);
@ -416,8 +383,9 @@ bool fill_output_entries(std::vector<output_index>& out_indices, size_t sender_o
if (append)
{
rct::key comm = oi.commitment();
const txout_to_key& otk = boost::get<txout_to_key>(oi.out);
output_entries.push_back(tx_source_entry::output_entry(oi.idx, rct::ctkey({rct::pk2rct(otk.key), rct::identity()})));
output_entries.push_back(tx_source_entry::output_entry(oi.idx, rct::ctkey({rct::pk2rct(otk.key), comm})));
}
}
@ -452,6 +420,8 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te
const output_index& oi = outs[o.first][sender_out];
if (oi.spent)
continue;
if (oi.rct)
continue;
cryptonote::tx_source_entry ts;
ts.amount = oi.amount;
@ -463,6 +433,11 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te
ts.real_output = realOutput;
ts.rct = false;
ts.mask = rct::identity(); // non-rct has identity mask by definition
rct::key comm = rct::zeroCommit(ts.amount);
for(auto & ot : ts.outputs)
ot.second.mask = comm;
sources.push_back(ts);
@ -477,14 +452,327 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te
return sources_found;
}
bool fill_tx_destination(tx_destination_entry &de, const cryptonote::account_base &to, uint64_t amount) {
de.addr = to.get_keys().m_account_address;
bool fill_tx_destination(tx_destination_entry &de, const cryptonote::account_public_address &to, uint64_t amount) {
de.addr = to;
de.amount = amount;
return true;
}
map_txid_output_t::iterator block_tracker::find_out(const crypto::hash &txid, size_t out)
{
return find_out(std::make_pair(txid, out));
}
map_txid_output_t::iterator block_tracker::find_out(const output_hasher &id)
{
return m_map_outs.find(id);
}
void block_tracker::process