Merge commit '58ce16d' into LokiMergeUpstream

This commit is contained in:
doy-lee 2018-12-10 14:54:27 +11:00
commit a07c5d9cba
32 changed files with 1342 additions and 247 deletions

View File

@ -518,15 +518,8 @@ else (HIDAPI_FOUND)
message(STATUS "Could not find HIDAPI")
endif()
# Protobuf, optional. Required for TREZOR.
include(FindProtobuf)
find_package(Protobuf)
if(Protobuf_FOUND)
set(HAVE_PROTOBUF 1)
add_definitions(-DHAVE_PROTOBUF=1)
else(Protobuf_FOUND)
message(STATUS "Could not find Protobuf")
endif()
# Trezor support check
include(CheckTrezor)
if(MSVC)
add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /FIinline_c.h /D__SSE4_1__")
@ -926,7 +919,7 @@ endif()
list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
if (HIDAPI_FOUND)
if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
if (APPLE)
if(DEPENDS)
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit")

79
cmake/CheckTrezor.cmake Normal file
View File

@ -0,0 +1,79 @@
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)
# Use Trezor master switch
if (USE_DEVICE_TREZOR)
# Protobuf is required to build protobuf messages for Trezor
include(FindProtobuf OPTIONAL)
find_package(Protobuf)
if(NOT Protobuf_FOUND)
message(STATUS "Could not find Protobuf")
endif()
else()
message(STATUS "Trezor support disabled by USE_DEVICE_TREZOR")
endif()
if(Protobuf_FOUND AND USE_DEVICE_TREZOR)
if (NOT "$ENV{TREZOR_PYTHON}" STREQUAL "")
set(TREZOR_PYTHON "$ENV{TREZOR_PYTHON}" CACHE INTERNAL "Copied from environment variable TREZOR_PYTHON")
else()
find_package(Python QUIET COMPONENTS Interpreter) # cmake 3.12+
if(Python_Interpreter_FOUND)
set(TREZOR_PYTHON "${Python_EXECUTABLE}")
endif()
endif()
if(NOT TREZOR_PYTHON)
find_package(PythonInterp)
if(PYTHONINTERP_FOUND AND PYTHON_EXECUTABLE)
set(TREZOR_PYTHON "${PYTHON_EXECUTABLE}")
endif()
endif()
if(NOT TREZOR_PYTHON)
message(STATUS "Trezor: Python not found")
endif()
endif()
# Try to build protobuf messages
if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON)
set(ENV{PROTOBUF_INCLUDE_DIRS} "${Protobuf_INCLUDE_DIRS}")
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)
if(RET)
message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})."
"OUT: ${OUT}, ERR: ${ERR}."
"Please read src/device_trezor/trezor/tools/README.md")
else()
message(STATUS "Trezor protobuf messages regenerated ${OUT}")
set(DEVICE_TREZOR_READY 1)
add_definitions(-DDEVICE_TREZOR_READY=1)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_definitions(-DTREZOR_DEBUG=1)
endif()
if(USE_DEVICE_TREZOR_UDP_RELEASE)
add_definitions(-DWITH_DEVICE_TREZOR_UDP_RELEASE=1)
endif()
if (Protobuf_INCLUDE_DIR)
include_directories(${Protobuf_INCLUDE_DIR})
endif()
# LibUSB support, check for particular version
# Include support only if compilation test passes
if (USE_DEVICE_TREZOR_LIBUSB)
find_package(LibUSB)
endif()
if (LibUSB_COMPILE_TEST_PASSED)
add_definitions(-DHAVE_TREZOR_LIBUSB=1)
if(LibUSB_INCLUDE_DIRS)
include_directories(${LibUSB_INCLUDE_DIRS})
endif()
endif()
endif()
endif()

140
cmake/FindLibUSB.cmake Normal file
View File

@ -0,0 +1,140 @@
# - Find libusb for portable USB support
# This module will find libusb as published by
# http://libusb.sf.net and
# http://libusb-win32.sf.net
#
# It will use PkgConfig if present and supported, else search
# it on its own. If the LibUSB_ROOT_DIR environment variable
# is defined, it will be used as base path.
# The following standard variables get defined:
# LibUSB_FOUND: true if LibUSB was found
# LibUSB_HEADER_FILE: the location of the C header file
# LibUSB_INCLUDE_DIRS: the directory that contains the include file
# LibUSB_LIBRARIES: the library
# source: https://github.com/IntelRealSense/librealsense
include ( CheckLibraryExists )
include ( CheckIncludeFile )
find_package ( PkgConfig )
if ( PKG_CONFIG_FOUND )
pkg_check_modules ( PKGCONFIG_LIBUSB libusb-1.0 )
if ( NOT PKGCONFIG_LIBUSB_FOUND )
pkg_check_modules ( PKGCONFIG_LIBUSB libusb )
endif ( NOT PKGCONFIG_LIBUSB_FOUND )
endif ( PKG_CONFIG_FOUND )
if ( PKGCONFIG_LIBUSB_FOUND )
set ( LibUSB_INCLUDE_DIRS ${PKGCONFIG_LIBUSB_INCLUDE_DIRS} )
foreach ( i ${PKGCONFIG_LIBUSB_LIBRARIES} )
string ( REGEX MATCH "[^-]*" ibase "${i}" )
find_library ( ${ibase}_LIBRARY
NAMES ${i}
PATHS ${PKGCONFIG_LIBUSB_LIBRARY_DIRS}
)
if ( ${ibase}_LIBRARY )
list ( APPEND LibUSB_LIBRARIES ${${ibase}_LIBRARY} )
endif ( ${ibase}_LIBRARY )
mark_as_advanced ( ${ibase}_LIBRARY )
endforeach ( i )
else ( PKGCONFIG_LIBUSB_FOUND )
find_file ( LibUSB_HEADER_FILE
NAMES
libusb.h usb.h
PATHS
$ENV{ProgramFiles}/LibUSB-Win32
$ENV{LibUSB_ROOT_DIR}
PATH_SUFFIXES
include
libusb-1.0
include/libusb-1.0
)
mark_as_advanced ( LibUSB_HEADER_FILE )
get_filename_component ( LibUSB_INCLUDE_DIRS "${LibUSB_HEADER_FILE}" PATH )
if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" )
# LibUSB-Win32 binary distribution contains several libs.
# Use the lib that got compiled with the same compiler.
if ( MSVC )
if ( WIN32 )
set ( LibUSB_LIBRARY_PATH_SUFFIX lib/msvc )
else ( WIN32 )
set ( LibUSB_LIBRARY_PATH_SUFFIX lib/msvc_x64 )
endif ( WIN32 )
elseif ( BORLAND )
set ( LibUSB_LIBRARY_PATH_SUFFIX lib/bcc )
elseif ( CMAKE_COMPILER_IS_GNUCC )
set ( LibUSB_LIBRARY_PATH_SUFFIX lib/gcc )
endif ( MSVC )
endif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" )
find_library ( usb_LIBRARY
NAMES
usb-1.0 libusb usb
PATHS
$ENV{ProgramFiles}/LibUSB-Win32
$ENV{LibUSB_ROOT_DIR}
PATH_SUFFIXES
${LibUSB_LIBRARY_PATH_SUFFIX}
)
mark_as_advanced ( usb_LIBRARY )
if ( usb_LIBRARY )
set ( LibUSB_LIBRARIES ${usb_LIBRARY} )
endif ( usb_LIBRARY )
endif ( PKGCONFIG_LIBUSB_FOUND )
if ( LibUSB_INCLUDE_DIRS AND LibUSB_LIBRARIES )
set ( LibUSB_FOUND true )
endif ( LibUSB_INCLUDE_DIRS AND LibUSB_LIBRARIES )
if ( LibUSB_FOUND )
set ( CMAKE_REQUIRED_INCLUDES "${LibUSB_INCLUDE_DIRS}" )
check_include_file ( "${LibUSB_HEADER_FILE}" LibUSB_FOUND )
endif ( LibUSB_FOUND )
if ( LibUSB_FOUND )
check_library_exists ( "${LibUSB_LIBRARIES}" usb_open "" LibUSB_FOUND )
check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_device_list "" LibUSB_VERSION_1.0 )
check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_port_numbers "" LibUSB_VERSION_1.0.16 )
# Library 1.0.16+ compilation test.
# The check_library_exists does not work well on Apple with shared libs.
if (APPLE OR LibUSB_VERSION_1.0.16)
if (APPLE)
if(DEPENDS)
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES "-framework Foundation -framework IOKit")
else()
find_library(COREFOUNDATION CoreFoundation)
find_library(IOKIT IOKit)
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${IOKIT})
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${COREFOUNDATION})
endif()
endif()
if (WIN32)
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES setupapi)
endif()
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${LibUSB_LIBRARIES})
try_compile(LibUSB_COMPILE_TEST_PASSED
${CMAKE_BINARY_DIR}
"${CMAKE_SOURCE_DIR}/cmake/test-libusb-version.c"
CMAKE_FLAGS
"-DINCLUDE_DIRECTORIES=${LibUSB_INCLUDE_DIRS}"
"-DLINK_DIRECTORIES=${LibUSB_LIBRARIES}"
LINK_LIBRARIES ${TEST_COMPILE_EXTRA_LIBRARIES}
OUTPUT_VARIABLE OUTPUT)
unset(TEST_COMPILE_EXTRA_LIBRARIES)
message(STATUS "LibUSB Compilation test: ${LibUSB_COMPILE_TEST_PASSED}")
endif()
endif ( LibUSB_FOUND )
if ( NOT LibUSB_FOUND )
if ( NOT LibUSB_FIND_QUIETLY )
message ( STATUS "LibUSB not found, try setting LibUSB_ROOT_DIR environment variable." )
endif ( NOT LibUSB_FIND_QUIETLY )
if ( LibUSB_FIND_REQUIRED )
message ( FATAL_ERROR "" )
endif ( LibUSB_FIND_REQUIRED )
endif ( NOT LibUSB_FOUND )

View File

@ -0,0 +1,52 @@
// Copyright (c) 2014-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 <libusb.h>
#define UNUSED(expr) (void)(expr)
int main(int argc, char *argv[]) {
libusb_device **devs;
libusb_context *ctx = NULL;
int r = libusb_init(&ctx); UNUSED(r);
ssize_t cnt = libusb_get_device_list(ctx, &devs); UNUSED(cnt);
struct libusb_device_descriptor desc;
r = libusb_get_device_descriptor(devs[0], &desc); UNUSED(r);
uint8_t bus_id = libusb_get_bus_number(devs[0]); UNUSED(bus_id);
uint8_t addr = libusb_get_device_address(devs[0]); UNUSED(addr);
uint8_t tmp_path[16];
r = libusb_get_port_numbers(devs[0], tmp_path, sizeof(tmp_path));
UNUSED(r);
UNUSED(tmp_path);
libusb_free_device_list(devs, 1);
libusb_exit(ctx);
}

View File

@ -1,8 +1,8 @@
package=libusb
$(package)_version=1.0.9
$(package)_download_path=http://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-1.0.9/
$(package)_version=1.0.22
$(package)_download_path=http://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-$($(package)_version)/
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=e920eedc2d06b09606611c99ec7304413c6784cba6e33928e78243d323195f9b
$(package)_sha256_hash=75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157
define $(package)_preprocess_cmds
autoreconf -i
@ -10,7 +10,7 @@ endef
define $(package)_set_vars
$(package)_config_opts=--disable-shared
$(package)_config_opts_linux=--with-pic
$(package)_config_opts_linux=--with-pic --disable-udev
endef
define $(package)_config_cmds

View File

@ -155,7 +155,8 @@ namespace net_utils
//this should be the last one, because it could be wait on destructor, while other activities possible on other threads
t_protocol_handler m_protocol_handler;
//typename t_protocol_handler::config_type m_dummy_config;
std::list<boost::shared_ptr<connection<t_protocol_handler> > > m_self_refs; // add_ref/release support
size_t m_reference_count = 0; // reference count managed through add_ref/release support
boost::shared_ptr<connection<t_protocol_handler> > m_self_ref; // the reference to hold
critical_section m_self_refs_lock;
critical_section m_chunking_lock; // held while we add small chunks of the big do_send() to small do_send_chunk()
critical_section m_shutdown_lock; // held while shutting down

View File

@ -247,7 +247,8 @@ PRAGMA_WARNING_DISABLE_VS(4355)
//_dbg3("[sock " << socket_.native_handle() << "] add_ref 2, m_peer_number=" << mI->m_peer_number);
if(m_was_shutdown)
return false;
m_self_refs.push_back(self);
++m_reference_count;
m_self_ref = std::move(self);
return true;
CATCH_ENTRY_L0("connection<t_protocol_handler>::add_ref()", false);
}
@ -259,10 +260,12 @@ PRAGMA_WARNING_DISABLE_VS(4355)
boost::shared_ptr<connection<t_protocol_handler> > back_connection_copy;
LOG_TRACE_CC(context, "[sock " << socket_.native_handle() << "] release");
CRITICAL_REGION_BEGIN(m_self_refs_lock);
CHECK_AND_ASSERT_MES(m_self_refs.size(), false, "[sock " << socket_.native_handle() << "] m_self_refs empty at connection<t_protocol_handler>::release() call");
//erasing from container without additional copy can cause start deleting object, including m_self_refs
back_connection_copy = m_self_refs.back();
m_self_refs.pop_back();
CHECK_AND_ASSERT_MES(m_reference_count, false, "[sock " << socket_.native_handle() << "] m_reference_count already at 0 at connection<t_protocol_handler>::release() call");
// is this the last reference?
if (--m_reference_count == 0) {
// move the held reference to a local variable, keeping the object alive until the function terminates
std::swap(back_connection_copy, m_self_ref);
}
CRITICAL_REGION_END();
return true;
CATCH_ENTRY_L0("connection<t_protocol_handler>::release()", false);

View File

@ -109,6 +109,8 @@ namespace epee
constexpr std::size_t size() const noexcept { return len; }
constexpr std::size_t size_bytes() const noexcept { return size() * sizeof(value_type); }
const T &operator[](size_t idx) const { return ptr[idx]; }
private:
T* ptr;
std::size_t len;

View File

@ -1323,11 +1323,11 @@ public:
* get_output_data(const uint64_t& amount, const uint64_t& index)
* but for a list of outputs rather than just one.
*
* @param amount an output amount
* @param amounts an output amount, or as many as offsets
* @param offsets a list of amount-specific output indices
* @param outputs return-by-reference a list of outputs' metadata
*/
virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) const = 0;
virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) const = 0;
/*
* FIXME: Need to check with git blame and ask what this does to

View File

@ -1354,6 +1354,15 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
#if VERSION > 0
else if (db_version < VERSION)
{
if (mdb_flags & MDB_RDONLY)
{
txn.abort();
mdb_env_close(m_env);
m_open = false;
MFATAL("Existing lmdb database needs to be converted, which cannot be done on a read-only database.");
MFATAL("Please run monerod once to convert the database.");
return;
}
// Note that there was a schema change within version 0 as well.
// See commit e5d2680094ee15889934fe28901e4e133cda56f2 2015/07/10
// We don't handle the old format previous to that commit.
@ -3203,8 +3212,11 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6
TXN_POSTFIX_RDONLY();
}
void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial) const
void BlockchainLMDB::get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial) const
{
if (amounts.size() != 1 && amounts.size() != offsets.size())
throw0(DB_ERROR("Invalid sizes of amounts and offets"));
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
TIME_MEASURE_START(db3);
check_open();
@ -3215,10 +3227,11 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<ui
RCURSOR(output_amounts);
MDB_val_set(k, amount);
for (const uint64_t &index : offsets)
for (size_t i = 0; i < offsets.size(); ++i)
{
MDB_val_set(v, index);
const uint64_t amount = amounts.size() == 1 ? amounts[0] : amounts[i];
MDB_val_set(k, amount);
MDB_val_set(v, offsets[i]);
auto get_result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_BOTH);
if (get_result == MDB_NOTFOUND)
@ -3228,7 +3241,7 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<ui
MDEBUG("Partial result: " << outputs.size() << "/" << offsets.size());
break;
}
throw1(OUTPUT_DNE((std::string("Attempting to get output pubkey by global index (amount ") + boost::lexical_cast<std::string>(amount) + ", index " + boost::lexical_cast<std::string>(index) + ", count " + boost::lexical_cast<std::string>(get_num_outputs(amount)) + "), but key does not exist (current height " + boost::lexical_cast<std::string>(height()) + ")").c_str()));
throw1(OUTPUT_DNE((std::string("Attempting to get output pubkey by global index (amount ") + boost::lexical_cast<std::string>(amount) + ", index " + boost::lexical_cast<std::string>(offsets[i]) + ", count " + boost::lexical_cast<std::string>(get_num_outputs(amount)) + "), but key does not exist (current height " + boost::lexical_cast<std::string>(height()) + ")").c_str()));
}
else if (get_result)
throw0(DB_ERROR(lmdb_error("Error attempting to retrieve an output pubkey from the db", get_result).c_str()));

View File

@ -247,7 +247,7 @@ public:
virtual uint64_t get_num_outputs(const uint64_t& amount) const;
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) const;
virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) const;
virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) const;
virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const;
virtual void get_output_tx_and_index_from_global(const std::vector<uint64_t> &global_indices,

View File

@ -194,7 +194,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke
{
try
{
m_db->get_output_key(tx_in_to_key.amount, absolute_offsets, outputs, true);
m_db->get_output_key(epee::span<const uint64_t>(&tx_in_to_key.amount, 1), absolute_offsets, outputs, true);
if (absolute_offsets.size() != outputs.size())
{
MERROR_VER("Output does not exist! amount = " << tx_in_to_key.amount);
@ -220,7 +220,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke
add_offsets.push_back(absolute_offsets[i]);
try
{
m_db->get_output_key(tx_in_to_key.amount, add_offsets, add_outputs, true);
m_db->get_output_key(epee::span<const uint64_t>(&tx_in_to_key.amount, 1), add_offsets, add_outputs, true);
if (add_offsets.size() != add_outputs.size())
{
MERROR_VER("Output does not exist! amount = " << tx_in_to_key.amount);
@ -336,7 +336,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
if (m_hardfork == nullptr)
m_hardfork = new HardFork(*db, test_options->hard_forks[0].first);
for (size_t n = 1; n < test_options->hard_forks.size(); ++n) {
for (size_t n = 0; n < test_options->hard_forks.size(); ++n) {
const auto& hf = test_options->hard_forks.at(n);
m_hardfork->add_fork(hf.first, hf.second, 0, n + 1);
}
@ -1801,15 +1801,34 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA
res.outs.clear();
res.outs.reserve(req.outputs.size());
std::vector<cryptonote::output_data_t> data;
try
{
std::vector<uint64_t> amounts, offsets;
amounts.reserve(req.outputs.size());
offsets.reserve(req.outputs.size());
for (const auto &i: req.outputs)
{
// get tx_hash, tx_out_index from DB
const output_data_t od = m_db->get_output_key(i.amount, i.index);
tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index);
bool unlocked = is_output_spendtime_unlocked(od.unlock_time);
res.outs.push_back({od.pubkey, od.commitment, unlocked, od.height, toi.first});
amounts.push_back(i.amount);
offsets.push_back(i.index);
}
m_db->get_output_key(epee::span<const uint64_t>(amounts.data(), amounts.size()), offsets, data);
if (data.size() != req.outputs.size())
{
MERROR("Unexpected output data size: expected " << req.outputs.size() << ", got " << data.size());
return false;
}
for (const auto &t: data)
res.outs.push_back({t.pubkey, t.commitment, is_output_spendtime_unlocked(t.unlock_time), t.height, crypto::null_hash});
if (req.get_txid)
{
for (size_t i = 0; i < req.outputs.size(); ++i)
{
tx_out_index toi = m_db->get_output_tx_and_index(req.outputs[i].amount, req.outputs[i].index);
res.outs[i].txid = toi.first;
}
}
}
catch (const std::exception &e)
@ -3929,7 +3948,7 @@ void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uin
{
try
{
m_db->get_output_key(amount, offsets, outputs, true);
m_db->get_output_key(epee::span<const uint64_t>(&amount, 1), offsets, outputs, true);
}
catch (const std::exception& e)
{

View File

@ -63,37 +63,20 @@ set(trezor_sources
set(trezor_private_headers)
include(FindProtobuf)
find_package(Protobuf) # REQUIRED
# Test for HAVE_PROTOBUF from the parent
if(Protobuf_FOUND AND HAVE_PROTOBUF)
if ("$ENV{PYTHON3}" STREQUAL "")
set(PYTHON3 "python3")
else()
set(PYTHON3 "$ENV{PYTHON3}" CACHE INTERNAL "Copied from environment variable")
endif()
execute_process(COMMAND ${PYTHON3} tools/build_protob.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/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}."
"Please read src/device_trezor/trezor/tools/README.md")
else()
message(STATUS "Trezor protobuf messages regenerated ${OUT}")
set(TREZOR_PROTOBUF_GENERATED 1)
endif()
endif()
if(TREZOR_PROTOBUF_GENERATED)
# Protobuf and LibUSB processed by CheckTrezor
if(DEVICE_TREZOR_READY)
message(STATUS "Trezor support enabled")
add_definitions(-DPROTOBUF_INLINE_NOT_IN_HEADERS=0)
set(TREZOR_LIBUSB_LIBRARIES "")
if(LibUSB_COMPILE_TEST_PASSED)
list(APPEND TREZOR_LIBUSB_LIBRARIES ${LibUSB_LIBRARIES})
message(STATUS "Trezor compatible LibUSB found at: ${LibUSB_INCLUDE_DIRS}")
endif()
loki_private_headers(device_trezor
${device_private_headers}
${PROTOBUF_INCLUDE_DIR})
${device_private_headers})
loki_add_library(device_trezor
${trezor_sources}
@ -109,14 +92,13 @@ if(TREZOR_PROTOBUF_GENERATED)
common
${SODIUM_LIBRARY}
${Boost_CHRONO_LIBRARY}
${PROTOBUF_LIBRARY}
${Protobuf_LIBRARY}
${TREZOR_LIBUSB_LIBRARIES}
PRIVATE
${EXTRA_LIBRARIES})
# set(WITH_DEVICE_TREZOR 1 PARENT_SCOPE)
# add_definitions(-DWITH_DEVICE_TREZOR=1)
else()
message(STATUS "Trezor support disabled")
loki_private_headers(device_trezor)
loki_add_library(device_trezor device_trezor.cpp)
target_link_libraries(device_trezor PUBLIC cncrypto)

View File

@ -32,13 +32,12 @@
namespace hw {
namespace trezor {
#if WITH_DEVICE_TREZOR
#ifdef WITH_DEVICE_TREZOR
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
#define HW_TREZOR_NAME "Trezor"
#define HW_TREZOR_NAME_LITE "TrezorLite"
static device_trezor *trezor_device = nullptr;
static device_trezor *ensure_trezor_device(){

View File

@ -49,7 +49,7 @@ namespace trezor {
void register_all();
void register_all(std::map<std::string, std::unique_ptr<device>> &registry);
#if WITH_DEVICE_TREZOR
#ifdef WITH_DEVICE_TREZOR
class device_trezor;
/**

View File

@ -32,43 +32,11 @@
namespace hw {
namespace trezor {
#if WITH_DEVICE_TREZOR
#ifdef WITH_DEVICE_TREZOR
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor"
std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_button_request(const messages::common::ButtonRequest * msg){
MDEBUG("on_button_request");
device.on_button_request();
return std::make_shared<messages::common::ButtonAck>();
}
std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_pin_matrix_request(const messages::common::PinMatrixRequest * msg){
MDEBUG("on_pin_request");
epee::wipeable_string pin;
device.on_pin_request(pin);
auto resp = std::make_shared<messages::common::PinMatrixAck>();
resp->set_pin(pin.data(), pin.size());
return resp;
}
std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_passphrase_request(const messages::common::PassphraseRequest * msg){
MDEBUG("on_passhprase_request");
epee::wipeable_string passphrase;
device.on_passphrase_request(msg->on_device(), passphrase);
auto resp = std::make_shared<messages::common::PassphraseAck>();
if (!msg->on_device()){
resp->set_passphrase(passphrase.data(), passphrase.size());
}
return resp;
}
std::shared_ptr<google::protobuf::Message> trezor_protocol_callback::on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg){
MDEBUG("on_passhprase_state_request");
device.on_passphrase_state_request(msg->state());
return std::make_shared<messages::common::PassphraseStateAck>();
}
const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000};
device_trezor_base::device_trezor_base() {
@ -117,9 +85,6 @@ namespace trezor {
return false;
}
if (!m_protocol_callback){
m_protocol_callback = std::make_shared<trezor_protocol_callback>(*this);
}
return true;
}
@ -245,6 +210,49 @@ namespace trezor {
}
}
void device_trezor_base::write_raw(const google::protobuf::Message * msg){
require_connected();
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
this->getTransport()->write(*msg);
}
GenericMessage device_trezor_base::read_raw(){
require_connected();
std::shared_ptr<google::protobuf::Message> msg_resp;
hw::trezor::messages::MessageType msg_resp_type;
this->getTransport()->read(msg_resp, &msg_resp_type);
return GenericMessage(msg_resp_type, msg_resp);
}
GenericMessage device_trezor_base::call_raw(const google::protobuf::Message * msg) {
write_raw(msg);
return read_raw();
}
bool device_trezor_base::message_handler(GenericMessage & input){
// Later if needed this generic message handler can be replaced by a pointer to
// a protocol message handler which by default points to the device class which implements
// the default handler.
switch(input.m_type){
case messages::MessageType_ButtonRequest:
on_button_request(input, dynamic_cast<const messages::common::ButtonRequest*>(input.m_msg.get()));
return true;
case messages::MessageType_PassphraseRequest:
on_passphrase_request(input, dynamic_cast<const messages::common::PassphraseRequest*>(input.m_msg.get()));
return true;
case messages::MessageType_PassphraseStateRequest:
on_passphrase_state_request(input, dynamic_cast<const messages::common::PassphraseStateRequest*>(input.m_msg.get()));
return true;
case messages::MessageType_PinMatrixRequest:
on_pin_request(input, dynamic_cast<const messages::common::PinMatrixRequest*>(input.m_msg.get()));
return true;
default:
return false;
}
}
/* ======================================================================= */
/* TREZOR PROTOCOL */
/* ======================================================================= */
@ -269,32 +277,67 @@ namespace trezor {
return false;
}
void device_trezor_base::on_button_request()
void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg)
{
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
MDEBUG("on_button_request, code: " << msg->code());
messages::common::ButtonAck ack;
write_raw(&ack);
if (m_callback){
m_callback->on_button_request();
}
resp = read_raw();
}
void device_trezor_base::on_pin_request(epee::wipeable_string & pin)
void device_trezor_base::on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg)
{
MDEBUG("on_pin_request");
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
epee::wipeable_string pin;
if (m_callback){
m_callback->on_pin_request(pin);
}
// TODO: remove PIN from memory
messages::common::PinMatrixAck m;
m.set_pin(pin.data(), pin.size());
resp = call_raw(&m);
}
void device_trezor_base::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
void device_trezor_base::on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg)
{
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
MDEBUG("on_passhprase_request, on device: " << msg->on_device());
epee::wipeable_string passphrase;
if (m_callback){
m_callback->on_passphrase_request(on_device, passphrase);
m_callback->on_passphrase_request(msg->on_device(), passphrase);
}
messages::common::PassphraseAck m;
if (!msg->on_device()){
// TODO: remove passphrase from memory
m.set_passphrase(passphrase.data(), passphrase.size());
}
resp = call_raw(&m);
}
void device_trezor_base::on_passphrase_state_request(const std::string & state)
void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg)
{
MDEBUG("on_passhprase_state_request");
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
if (m_callback){
m_callback->on_passphrase_state_request(state);
m_callback->on_passphrase_state_request(msg->state());
}
messages::common::PassphraseStateAck m;
resp = call_raw(&m);
}
#endif //WITH_DEVICE_TREZOR

View File

@ -54,7 +54,7 @@
namespace hw {
namespace trezor {
#if WITH_DEVICE_TREZOR
#ifdef WITH_DEVICE_TREZOR
class device_trezor_base;
/**
@ -68,41 +68,6 @@ namespace trezor {
virtual void on_passphrase_state_request(const std::string & state) {};
};
/**
* Default Trezor protocol client callback
*/
class trezor_protocol_callback {
protected:
device_trezor_base & device;
public:
explicit trezor_protocol_callback(device_trezor_base & device): device(device) {}
std::shared_ptr<google::protobuf::Message> on_button_request(const messages::common::ButtonRequest * msg);
std::shared_ptr<google::protobuf::Message> on_pin_matrix_request(const messages::common::PinMatrixRequest * msg);
std::shared_ptr<google::protobuf::Message> on_passphrase_request(const messages::common::PassphraseRequest * msg);
std::shared_ptr<google::protobuf::Message> on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg);
std::shared_ptr<google::protobuf::Message> on_message(const google::protobuf::Message * msg, messages::MessageType message_type){
MDEBUG("on_general_message");
return on_message_dispatch(msg, message_type);
}
std::shared_ptr<google::protobuf::Message> on_message_dispatch(const google::protobuf::Message * msg, messages::MessageType message_type){
if (message_type == messages::MessageType_ButtonRequest){
return on_button_request(dynamic_cast<const messages::common::ButtonRequest*>(msg));
} else if (message_type == messages::MessageType_PassphraseRequest) {
return on_passphrase_request(dynamic_cast<const messages::common::PassphraseRequest*>(msg));
} else if (message_type == messages::MessageType_PassphraseStateRequest) {
return on_passphrase_state_request(dynamic_cast<const messages::common::PassphraseStateRequest*>(msg));
} else if (message_type == messages::MessageType_PinMatrixRequest) {
return on_pin_matrix_request(dynamic_cast<const messages::common::PinMatrixRequest*>(msg));
} else {
return nullptr;
}
}
};
/**
* TREZOR device template with basic functions
*/
@ -114,7 +79,6 @@ namespace trezor {
mutable boost::mutex command_locker;
std::shared_ptr<Transport> m_transport;
std::shared_ptr<trezor_protocol_callback> m_protocol_callback;
std::shared_ptr<trezor_callback> m_callback;
std::string full_name;
@ -129,6 +93,15 @@ namespace trezor {
void call_ping_unsafe();
void test_ping();
// Communication methods
void write_raw(const google::protobuf::Message * msg);
GenericMessage read_raw();
GenericMessage call_raw(const google::protobuf::Message * msg);
// Trezor message protocol handler. Handles specific signalling messages.
bool message_handler(GenericMessage & input);
/**
* Client communication wrapper, handles specific Trezor protocol.
*
@ -141,8 +114,7 @@ namespace trezor {
const boost::optional<messages::MessageType> & resp_type = boost::none,
const boost::optional<std::vector<messages::MessageType>> & resp_types = boost::none,
const boost::optional<messages::MessageType*> & resp_type_ptr = boost::none,
bool open_session = false,
unsigned depth=0)
bool open_session = false)
{
// Require strictly protocol buffers response in the template.
BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
@ -151,8 +123,12 @@ namespace trezor {
throw std::invalid_argument("Cannot specify list of accepted types and not using generic response");
}
// Determine type of expected message response
const messages::MessageType required_type = accepting_base ? messages::MessageType_Success :
(resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>());
// Open session if required
if (open_session && depth == 0){
if (open_session){
try {
m_transport->open();
} catch (const std::exception& e) {
@ -162,47 +138,37 @@ namespace trezor {
// Scoped session closer
BOOST_SCOPE_EXIT_ALL(&, this) {
if (open_session && depth == 0){
if (open_session){
this->getTransport()->close();
}
};
// Write the request
// Write/read the request
CHECK_AND_ASSERT_THROW_MES(req, "Request is null");
this->getTransport()->write(*req);
auto msg_resp = call_raw(req.get());
// Read the response
std::shared_ptr<google::protobuf::Message> msg_resp;
hw::trezor::messages::MessageType msg_resp_type;
bool processed = false;
do {
processed = message_handler(msg_resp);
} while(processed);
// We may have several roundtrips with the handler
this->getTransport()->read(msg_resp, &msg_resp_type);
// Response section
if (resp_type_ptr){
*(resp_type_ptr.get()) = msg_resp_type;
*(resp_type_ptr.get()) = msg_resp.m_type;
}
// Determine type of expected message response
messages::MessageType required_type = accepting_base ? messages::MessageType_Success :
(resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>());
if (msg_resp.m_type == messages::MessageType_Failure) {
throw_failure_exception(dynamic_cast<messages::common::Failure *>(msg_resp.m_msg.get()));
if (msg_resp_type == messages::MessageType_Failure) {
throw_failure_exception(dynamic_cast<messages::common::Failure *>(msg_resp.get()));
} else if (!accepting_base && msg_resp.m_type == required_type) {
return message_ptr_retype<t_message>(msg_resp.m_msg);
} else if (!accepting_base && msg_resp_type == required_type) {
return message_ptr_retype<t_message>(msg_resp);
} else if (accepting_base && (!resp_types ||
std::find(resp_types.get().begin(), resp_types.get().end(), msg_resp.m_type) != resp_types.get().end())) {
return message_ptr_retype<t_message>(msg_resp.m_msg);
} else {
auto resp = this->getProtocolCallback()->on_message(msg_resp.get(), msg_resp_type);
if (resp) {
return this->client_exchange<t_message>(resp, boost::none, resp_types, resp_type_ptr, false, depth + 1);
} else if (accepting_base && (!resp_types ||
std::find(resp_types.get().begin(), resp_types.get().end(), msg_resp_type) != resp_types.get().end())) {
return message_ptr_retype<t_message>(msg_resp);
} else {
throw exc::UnexpectedMessageException(msg_resp_type, msg_resp);
}
throw exc::UnexpectedMessageException(msg_resp.m_type, msg_resp.m_msg);
}
}
@ -252,10 +218,6 @@ namespace trezor {
return m_transport;
}
std::shared_ptr<trezor_protocol_callback> getProtocolCallback(){
return m_protocol_callback;
}
std::shared_ptr<trezor_callback> getCallback(){
return m_callback;
}
@ -288,10 +250,10 @@ namespace trezor {
bool ping();
// Protocol callbacks
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_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg);
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);
};
#endif

View File

@ -32,7 +32,7 @@
#include "trezor/trezor_defs.hpp"
#ifdef HAVE_PROTOBUF
#ifdef WITH_DEVICE_TREZOR
#include "trezor/transport.hpp"
#include "trezor/messages/messages.pb.h"
#include "trezor/messages/messages-common.pb.h"

View File

@ -2,33 +2,28 @@
## Messages rebuild
Install `protoc` for your distribution.
Install `protoc` for your distribution. Requirements:
- `protobuf-compiler`
- `libprotobuf-dev`
- `libprotoc-dev`
- `python-protobuf`
- `python`
Python 3 is required. If you don't have python 3 quite an easy way is
to use [pyenv].
It is also advised to create own python virtual environment so dependencies
are installed in this project-related virtual environment.
Soft requirement: Python 3, can be easily installed with [pyenv].
### Python 2
Workaround if there is no Python3 available:
```bash
python -m venv /
pip install backports.tempfile
```
Make sure your python has `protobuf` package installed
### Regenerate messages
```bash
pip install protobuf
```
Regenerate messages:
```
./venv/bin/python3 src/device_trezor/trezor/tools/build_protob.py
cd src/device_trezor/trezor
python tools/build_protob.py
```
The messages regeneration is done also automatically via cmake.

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/env python
# Converts Google's protobuf python definitions of TREZOR wire messages
# to plain-python objects as used in TREZOR Core and python-trezor
@ -8,11 +8,19 @@ import os
import re
import shutil
import subprocess
import sys
import glob
import tempfile
import hashlib
try:
from tempfile import TemporaryDirectory
except:
# Py2 backward compatibility, optionally installed by user
# pip install backports.tempfile
try:
from backports.tempfile import TemporaryDirectory
except:
raise EnvironmentError('TemporaryDirectory could not be imported. Try: pip install backports.tempfile')
AUTO_HEADER = "# Automatically generated by pb2cpp\n"
@ -23,6 +31,9 @@ UNDEF_STATEMENT = """
#endif
"""
PROTOC = None
PROTOC_INCLUDE = None
def which(pgm):
path = os.getenv('PATH')
@ -32,15 +43,6 @@ def which(pgm):
return p
PROTOC = which("protoc")
if not PROTOC:
print("protoc command not found")
sys.exit(1)
PROTOC_PREFIX = os.path.dirname(os.path.dirname(PROTOC))
PROTOC_INCLUDE = os.path.join(PROTOC_PREFIX, "include")
def namespace_file(fpath, package):
"""Adds / replaces package name. Simple regex parsing, may use https://github.com/ph4r05/plyprotobuf later"""
with open(fpath) as fh:
@ -82,9 +84,10 @@ def protoc(files, out_dir, additional_includes=(), package=None, force=False):
include_dirs = set()
include_dirs.add(PROTOC_INCLUDE)
include_dirs.update(additional_includes)
if additional_includes:
include_dirs.update(additional_includes)
with tempfile.TemporaryDirectory() as tmpdir_protob, tempfile.TemporaryDirectory() as tmpdir_out:
with TemporaryDirectory() as tmpdir_protob, TemporaryDirectory() as tmpdir_out:
include_dirs.add(tmpdir_protob)
new_files = []
@ -125,9 +128,9 @@ def update_message_files(tmpdir_out, out_dir, force=False):
dest_file = os.path.join(out_dir, bname)
if not force and os.path.exists(dest_file):
data = open(fname, 'rb').read()
data_hash = hashlib.sha3_256(data).digest()
data_hash = hashlib.sha256(data).digest()
data_dest = open(dest_file, 'rb').read()
data_dest_hash = hashlib.sha3_256(data_dest).digest()
data_dest_hash = hashlib.sha256(data_dest).digest()
if data_hash == data_dest_hash:
continue
@ -163,7 +166,8 @@ def strip_leader(s, prefix):
return s
if __name__ == "__main__":
def main():
global PROTOC, PROTOC_INCLUDE
logging.basicConfig(level=logging.DEBUG)
parser = argparse.ArgumentParser()
@ -179,8 +183,31 @@ if __name__ == "__main__":
protoc_includes = args.protoc_include or (os.environ.get("PROTOC_INCLUDE"),)
PROTOBUF_INCLUDE_DIRS = os.getenv("PROTOBUF_INCLUDE_DIRS", None)
PROTOBUF_PROTOC_EXECUTABLE = os.getenv("PROTOBUF_PROTOC_EXECUTABLE", None)
if PROTOBUF_PROTOC_EXECUTABLE and not os.path.exists(PROTOBUF_PROTOC_EXECUTABLE):
raise ValueError("PROTOBUF_PROTOC_EXECUTABLE set but not found: %s" % PROTOBUF_PROTOC_EXECUTABLE)
elif PROTOBUF_PROTOC_EXECUTABLE:
PROTOC = PROTOBUF_PROTOC_EXECUTABLE
else:
if os.name == "nt":
PROTOC = which("protoc.exe")
else:
PROTOC = which("protoc")
if not PROTOC:
raise ValueError("protoc command not found. Set PROTOBUF_PROTOC_EXECUTABLE env var to the protoc binary and optionally PROTOBUF_INCLUDE_DIRS")
PROTOC_PREFIX = os.path.dirname(os.path.dirname(PROTOC))
PROTOC_INCLUDE = PROTOBUF_INCLUDE_DIRS if PROTOBUF_INCLUDE_DIRS else os.path.join(PROTOC_PREFIX, "include")
protoc(
args.proto, args.out_dir, protoc_includes, package=args.namespace, force=args.force
)
if __name__ == "__main__":
main()

View File

@ -27,13 +27,21 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#ifdef WITH_DEVICE_TREZOR_WEBUSB
#include <libusb.h>
#endif
#include <boost/endian/conversion.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/format.hpp>
#include "transport.hpp"
#include "messages/messages-common.pb.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor.transport"
using namespace std;
using json = rapidjson::Document;
@ -581,12 +589,400 @@ namespace trezor{
<< ">";
}
#ifdef WITH_DEVICE_TREZOR_WEBUSB
static bool is_trezor1(libusb_device_descriptor * info){
return info->idVendor == 0x534C && info->idProduct == 0x0001;
}
static bool is_trezor2(libusb_device_descriptor * info){
return info->idVendor == 0x1209 && info->idProduct == 0x53C1;
}
static bool is_trezor2_bl(libusb_device_descriptor * info){
return info->idVendor == 0x1209 && info->idProduct == 0x53C0;
}
static uint8_t get_trezor_dev_mask(libusb_device_descriptor * info){
uint8_t mask = 0;
CHECK_AND_ASSERT_THROW_MES(info, "Empty device descriptor");
mask |= is_trezor1(info) ? 1 : 0;
mask |= is_trezor2(info) ? 2 : 0;
mask |= is_trezor2_bl(info) ? 4 : 0;
return mask;
}
static void set_libusb_log(libusb_context *ctx){
CHECK_AND_ASSERT_THROW_MES(ctx, "Null libusb context");
// http://libusb.sourceforge.net/api-1.0/group__libusb__lib.html
#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000106)
# define TREZOR_LIBUSB_SET_DEBUG(ctx, level) libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level)
#else
# define TREZOR_LIBUSB_SET_DEBUG(ctx, level) libusb_set_debug(ctx, level)
#endif
if (ELPP->vRegistry()->allowed(el::Level::Debug, MONERO_DEFAULT_LOG_CATEGORY))
TREZOR_LIBUSB_SET_DEBUG(ctx, 3);
else if (ELPP->vRegistry()->allowed(el::Level::Warning, MONERO_DEFAULT_LOG_CATEGORY))
TREZOR_LIBUSB_SET_DEBUG(ctx, 2);
else if (ELPP->vRegistry()->allowed(el::Level::Error, MONERO_DEFAULT_LOG_CATEGORY))
TREZOR_LIBUSB_SET_DEBUG(ctx, 1);
#undef TREZOR_LIBUSB_SET_DEBUG
}
static int get_libusb_ports(libusb_device *dev, std::vector<uint8_t> &path){
uint8_t tmp_path[16];
int r = libusb_get_port_numbers(dev, tmp_path, sizeof(tmp_path));
CHECK_AND_ASSERT_MES(r != LIBUSB_ERROR_OVERFLOW, -1, "Libusb path array too small");
CHECK_AND_ASSERT_MES(r >= 0, -1, "Libusb path array error");
path.resize(r);
for (int i = 0; i < r; i++){
path[i] = tmp_path[i];
}
return 0;
}
static std::string get_usb_path(uint8_t bus_id, const std::vector<uint8_t> &path){
std::stringstream ss;
ss << WebUsbTransport::PATH_PREFIX << (boost::format("%03d") % ((int)bus_id));
for(uint8_t port : path){
ss << ":" << ((int) port);
}
return ss.str();
}
const char * WebUsbTransport::PATH_PREFIX = "webusb:";
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_bus_id(-1), m_device_addr(-1)
{
if (descriptor){
libusb_device_descriptor * desc = new libusb_device_descriptor;
memcpy(desc, descriptor.get(), sizeof(libusb_device_descriptor));
this->m_usb_device_desc.reset(desc);
}
m_proto = proto ? proto.get() : std::make_shared<ProtocolV1>();
#ifdef WITH_TREZOR_DEBUG
m_debug_mode = false;
#endif
}
WebUsbTransport::~WebUsbTransport(){
if (m_usb_device){
close();
}
if (m_usb_session) {
libusb_exit(m_usb_session);
m_usb_session = nullptr;
}
}
void WebUsbTransport::require_device() const{
if (!m_usb_device_desc){
throw std::runtime_error("No USB device specified");
}
}
void WebUsbTransport::require_connected() const{
require_device();
if (!m_usb_device_handle){
throw std::runtime_error("USB Device not opened");
}
}
void WebUsbTransport::enumerate(t_transport_vect & res) {
int r;
libusb_device **devs;
libusb_context *ctx = nullptr;
r = libusb_init(&ctx);
CHECK_AND_ASSERT_THROW_MES(r >= 0, "Unable to init libusb");
set_libusb_log(ctx);
ssize_t cnt = libusb_get_device_list(ctx, &devs);
if (cnt < 0){
libusb_exit(ctx);
throw std::runtime_error("Unable to enumerate libusb devices");
}
MTRACE("Libusb devices: " << cnt);
for(ssize_t i = 0; i < cnt; i++) {
libusb_device_descriptor desc{};
r = libusb_get_device_descriptor(devs[i], &desc);
if (r < 0){
MERROR("Unable to get libusb device descriptor " << i);
continue;
}
const auto trezor_mask = get_trezor_dev_mask(&desc);
if (!trezor_mask){
continue;
}
MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct << " mask " << (int)trezor_mask);
auto t = std::make_shared<WebUsbTransport>(boost::make_optional(&desc));
t->m_bus_id = libusb_get_bus_number(devs[i]);
t->m_device_addr = libusb_get_device_address(devs[i]);
// Port resolution may fail. Non-critical error, just addressing precision is decreased.
get_libusb_ports(devs[i], t->m_port_numbers);
res.push_back(t);
}
libusb_free_device_list(devs, 1);
libusb_exit(ctx);
}
std::string WebUsbTransport::get_path() const {
if (!m_usb_device_desc){
return "";
}
return get_usb_path(static_cast<uint8_t>(m_bus_id), m_port_numbers);
};
void WebUsbTransport::open() {
const int interface = get_interface();
if (m_conn_count > 0){
MTRACE("Already opened, count: " << m_conn_count);
m_conn_count += 1;
return;
}
#define TREZOR_DESTROY_SESSION() do { libusb_exit(m_usb_session); m_usb_session = nullptr; } while(0)
int r;
libusb_device **devs = nullptr;
if (m_usb_session) {
TREZOR_DESTROY_SESSION();
}
r = libusb_init(&m_usb_session);
CHECK_AND_ASSERT_THROW_MES(r >= 0, "Unable to init libusb");
set_libusb_log(m_usb_session);
bool found = false;
int open_res = 0;
ssize_t cnt = libusb_get_device_list(m_usb_session, &devs);
if (cnt < 0){
TREZOR_DESTROY_SESSION();
throw std::runtime_error("Unable to enumerate libusb devices");
}
for (ssize_t i = 0; i < cnt; i++) {
libusb_device_descriptor desc{};
r = libusb_get_device_descriptor(devs[i], &desc);
if (r < 0){
MERROR("Unable to get libusb device descriptor " << i);
continue;
}
const auto trezor_mask = get_trezor_dev_mask(&desc);
if (!trezor_mask) {
continue;
}
auto bus_id = libusb_get_bus_number(devs[i]);
std::vector<uint8_t> path;
// Port resolution may fail. Non-critical error, just addressing precision is decreased.
get_libusb_ports(devs[i], path);
MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct
<< ", mask: " << (int)trezor_mask
<< ". path: " << get_usb_path(bus_id, path));
if (bus_id == m_bus_id && path == m_port_numbers) {
found = true;
m_usb_device = devs[i];
open_res = libusb_open(m_usb_device, &m_usb_device_handle);
break;
}
}
libusb_free_device_list(devs, 1);
if (!found){
TREZOR_DESTROY_SESSION();
throw exc::DeviceAcquireException("Device not found");
} else if (found && open_res != 0) {
m_usb_device_handle = nullptr;
m_usb_device = nullptr;
TREZOR_DESTROY_SESSION();
throw exc::DeviceAcquireException("Unable to open libusb device");
}
r = libusb_claim_interface(m_usb_device_handle, interface);
if (r != 0){
libusb_close(m_usb_device_handle);
m_usb_device_handle = nullptr;
m_usb_device = nullptr;
TREZOR_DESTROY_SESSION();
throw exc::DeviceAcquireException("Unable to claim libusb device");
}
m_conn_count += 1;
m_proto->session_begin(*this);
#undef TREZOR_DESTROY_SESSION
};
void WebUsbTransport::close() {
m_conn_count -= 1;
if (m_conn_count < 0){
MERROR("Close counter is negative: " << m_conn_count);
} else if (m_conn_count == 0){
MTRACE("Closing webusb device");
m_proto->session_end(*this);
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;
}
}
};
int WebUsbTransport::get_interface() const{
const int INTERFACE_NORMAL = 0;
#ifdef WITH_TREZOR_DEBUG
const int INTERFACE_DEBUG = 1;
return m_debug_mode ? INTERFACE_DEBUG : INTERFACE_NORMAL;
#else
return INTERFACE_NORMAL;
#endif
}
unsigned char WebUsbTransport::get_endpoint() const{
const unsigned char ENDPOINT_NORMAL = 1;
#ifdef WITH_TREZOR_DEBUG
const unsigned char ENDPOINT_DEBUG = 2;
return m_debug_mode ? ENDPOINT_DEBUG : ENDPOINT_NORMAL;
#else
return ENDPOINT_NORMAL;
#endif
}
void WebUsbTransport::write(const google::protobuf::Message &req) {
m_proto->write(*this, req);
};
void WebUsbTransport::read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type) {
m_proto->read(*this, msg, msg_type);
};
void WebUsbTransport::write_chunk(const void * buff, size_t size) {
require_connected();
if (size != REPLEN){
throw exc::CommunicationException("Invalid chunk size: ");
}
unsigned char endpoint = get_endpoint();
endpoint = (endpoint & ~LIBUSB_ENDPOINT_DIR_MASK) | LIBUSB_ENDPOINT_OUT;
int transferred = 0;
int r = libusb_interrupt_transfer(m_usb_device_handle, endpoint, (unsigned char*)buff, (int)size, &transferred, 0);
CHECK_AND_ASSERT_THROW_MES(r == 0, "Unable to transfer, r: " << r);
if (transferred != (int)size){
throw exc::CommunicationException("Could not transfer chunk");
}
};
size_t WebUsbTransport::read_chunk(void * buff, size_t size) {
require_connected();
unsigned char endpoint = get_endpoint();
endpoint = (endpoint & ~LIBUSB_ENDPOINT_DIR_MASK) | LIBUSB_ENDPOINT_IN;
int transferred = 0;
int r = libusb_interrupt_transfer(m_usb_device_handle, endpoint, (unsigned char*)buff, (int)size, &transferred, 0);
CHECK_AND_ASSERT_THROW_MES(r == 0, "Unable to transfer, r: " << r);
if (transferred != (int)size){
throw exc::CommunicationException("Could not read the chunk");
}
return transferred;
};
std::ostream& WebUsbTransport::dump(std::ostream& o) const {
o << "WebUsbTransport<path=" << get_path()
<< ", vendorId=" << (m_usb_device_desc ? std::to_string(m_usb_device_desc->idVendor) : "?")
<< ", productId=" << (m_usb_device_desc ? std::to_string(m_usb_device_desc->idProduct) : "?")
<< ", deviceType=";
if (m_usb_device_desc){
if (is_trezor1(m_usb_device_desc.get()))
o << "TrezorOne";
else if (is_trezor2(m_usb_device_desc.get()))
o << "TrezorT";
else if (is_trezor2_bl(m_usb_device_desc.get()))
o << "TrezorT BL";
} else {
o << "?";
}
return o << ">";
};
#endif // WITH_DEVICE_TREZOR_WEBUSB
void enumerate(t_transport_vect & res){
BridgeTransport bt;
bt.enumerate(res);
try{
bt.enumerate(res);
} catch (const std::exception & e){
MERROR("BridgeTransport enumeration failed:" << e.what());
}
#ifdef WITH_DEVICE_TREZOR_WEBUSB
hw::trezor::WebUsbTransport btw;
try{
btw.enumerate(res);
} catch (const std::exception & e){
MERROR("WebUsbTransport enumeration failed:" << e.what());
}
#endif
#ifdef WITH_DEVICE_TREZOR_UDP
hw::trezor::UdpTransport btu;
btu.enumerate(res);
try{
btu.enumerate(res);
} catch (const std::exception & e){
MERROR("UdpTransport enumeration failed:" << e.what());
}
#endif
}
std::shared_ptr<Transport> transport(const std::string & path){
@ -633,6 +1029,9 @@ namespace trezor{
}
}
GenericMessage::GenericMessage(messages::MessageType m_type, const shared_ptr<google::protobuf::Message> &m_msg)
: m_type(m_type), m_msg(m_msg), m_empty(false) {}
std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t){
return t.dump(o);
}

View File

@ -239,6 +239,59 @@ namespace trezor {
udp::endpoint m_endpoint;
};
#ifdef WITH_DEVICE_TREZOR_WEBUSB
#include <libusb.h>
class WebUsbTransport : public Transport {
public:
explicit WebUsbTransport(
boost::optional<libusb_device_descriptor*> descriptor = boost::none,
boost::optional<std::shared_ptr<Protocol>> proto = boost::none
);
virtual ~WebUsbTransport();
static const char * PATH_PREFIX;
std::string get_path() const override;
void enumerate(t_transport_vect & res) override;
void open() override;
void close() override;
void write(const google::protobuf::Message &req) override;
void read(std::shared_ptr<google::protobuf::Message> & 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;
private:
void require_device() const;
void require_connected() const;
int get_interface() const;
unsigned char get_endpoint() const;
int m_conn_count;
std::shared_ptr<Protocol> m_proto;
libusb_context *m_usb_session;
libusb_device *m_usb_device;
libusb_device_handle *m_usb_device_handle;
std::unique_ptr<libusb_device_descriptor> m_usb_device_desc;
std::vector<uint8_t> m_port_numbers;
int m_bus_id;
int m_device_addr;
#ifdef WITH_TREZOR_DEBUG
bool m_debug_mode;
#endif
};
#endif
//
// General helpers
//
@ -289,6 +342,20 @@ namespace trezor {
*/
[[ noreturn ]] void throw_failure_exception(const messages::common::Failure * failure);
/**
* Generic message holder, type + obj
*/
class GenericMessage {
public:
GenericMessage(): m_empty(true) {}
GenericMessage(messages::MessageType m_type, const std::shared_ptr<google::protobuf::Message> &m_msg);
bool empty() const { return m_empty; }
hw::trezor::messages::MessageType m_type;
std::shared_ptr<google::protobuf::Message> m_msg;
bool m_empty;
};
/**
* Simple wrapper for write-read message exchange with expected message response type.
*

View File

@ -27,14 +27,41 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#if defined(HAVE_PROTOBUF) && !defined(WITHOUT_TREZOR)
#define WITH_DEVICE_TREZOR 1
#else
#define WITH_DEVICE_TREZOR 0
#ifndef USE_DEVICE_TREZOR
#define USE_DEVICE_TREZOR 1
#endif
#ifndef WITH_DEVICE_TREZOR_LITE
#define WITH_DEVICE_TREZOR_LITE 0
// HAVE_TREZOR_READY set by cmake after protobuf messages
// were generated successfully and all minimal dependencies are met.
#ifndef DEVICE_TREZOR_READY
#undef USE_DEVICE_TREZOR
#define USE_DEVICE_TREZOR 0
#endif
#if USE_DEVICE_TREZOR
#define WITH_DEVICE_TREZOR 1
#endif
#ifndef WITH_DEVICE_TREZOR
#undef WITH_DEVICE_TREZOR_LITE
#endif
#if defined(HAVE_TREZOR_LIBUSB) && USE_DEVICE_TREZOR
#define WITH_DEVICE_TREZOR_WEBUSB 1
#endif
// Enable / disable UDP in the enumeration
#ifndef USE_DEVICE_TREZOR_UDP
#define USE_DEVICE_TREZOR_UDP 1
#endif
// Enable / disable UDP in the enumeration for release
#ifndef USE_DEVICE_TREZOR_UDP_RELEASE
#define USE_DEVICE_TREZOR_UDP_RELEASE 0
#endif
#if USE_DEVICE_TREZOR_UDP && (USE_DEVICE_TREZOR_UDP_RELEASE || defined(TREZOR_DEBUG))
#define WITH_DEVICE_TREZOR_UDP 1
#endif
// Avoids protobuf undefined macro warning

View File

@ -31,6 +31,7 @@
#include <stdlib.h>
#include <boost/thread/mutex.hpp>
#include "misc_log_ex.h"
#include "span.h"
#include "common/perf_timer.h"
#include "cryptonote_config.h"
extern "C"
@ -218,7 +219,7 @@ static rct::key vector_power_sum(const rct::key &x, size_t n)
}
/* Given two scalar arrays, construct the inner product */
static rct::key inner_product(const rct::keyV &a, const rct::keyV &b)
static rct::key inner_product(const epee::span<const rct::key> &a, const epee::span<const rct::key> &b)
{
CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b");
rct::key res = rct::zero();
@ -229,6 +230,11 @@ static rct::key inner_product(const rct::keyV &a, const rct::keyV &b)
return res;
}
static rct::key inner_product(const rct::keyV &a, const rct::keyV &b)
{
return inner_product(epee::span<const rct::key>(a.data(), a.size()), epee::span<const rct::key>(b.data(), b.size()));
}
/* Given two scalar arrays, construct the Hadamard product */
static rct::keyV hadamard(const rct::keyV &a, const rct::keyV &b)
{
@ -294,7 +300,7 @@ static rct::keyV vector_subtract(const rct::keyV &a, const rct::key &b)
}
/* Multiply a scalar and a vector */
static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x)
static rct::keyV vector_scalar(const epee::span<const rct::key> &a, const rct::key &x)
{
rct::keyV res(a.size());
for (size_t i = 0; i < a.size(); ++i)
@ -304,6 +310,11 @@ static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x)
return res;
}
static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x)
{
return vector_scalar(epee::span<const rct::key>(a.data(), a.size()), x);
}
/* Create a vector from copies of a single value */
static rct::keyV vector_dup(const rct::key &x, size_t N)
{
@ -401,17 +412,12 @@ static rct::keyV invert(rct::keyV x)
}
/* Compute the slice of a vector */
static rct::keyV slice(const rct::keyV &a, size_t start, size_t stop)
static epee::span<const rct::key> slice(const rct::keyV &a, size_t start, size_t stop)
{
CHECK_AND_ASSERT_THROW_MES(start < a.size(), "Invalid start index");
CHECK_AND_ASSERT_THROW_MES(stop <= a.size(), "Invalid stop index");
CHECK_AND_ASSERT_THROW_MES(start < stop, "Invalid start/stop indices");
rct::keyV res(stop - start);
for (size_t i = start; i < stop; ++i)
{
res[i - start] = a[i];
}
return res;
return epee::span<const rct::key>(&a[start], stop - start);
}
static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1)

View File

@ -2181,7 +2181,7 @@ namespace cryptonote
return false;
}
res.distributions.push_back({std::move(*data), amount, req.binary});
res.distributions.push_back({std::move(*data), amount, "", req.binary, req.compress});
}
}
catch (const std::exception &e)
@ -2195,6 +2195,47 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res)
{
PERF_TIMER(on_get_output_distribution_bin);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_DISTRIBUTION>(invoke_http_mode::BIN, "/get_output_distribution.bin", req, res, r))
return r;
res.status = "Failed";
if (!req.binary)
{
res.status = "Binary only call";
return false;
}
try
{
// 0 is placeholder for the whole chain
const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
for (uint64_t amount: req.amounts)
{
auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative);
if (!data)
{
res.status = "Failed to get output distribution";
return false;
}
res.distributions.push_back({std::move(*data), amount, "", req.binary, req.compress});
}
}
catch (const std::exception &e)
{
res.status = "Failed to get output distribution";
return false;
}
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_service_node_registration_cmd(const COMMAND_RPC_GET_SERVICE_NODE_REGISTRATION_CMD::request& req,
COMMAND_RPC_GET_SERVICE_NODE_REGISTRATION_CMD::response& res,
epee::json_rpc::error& error_resp)

View File

@ -117,6 +117,7 @@ namespace cryptonote
MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted)
MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS)
MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted)
MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION)
BEGIN_JSON_RPC_MAP("/json_rpc")
MAP_JON_RPC("get_block_count", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT)
MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT)
@ -192,7 +193,8 @@ namespace cryptonote
bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res);
bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res);
bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res);
bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res);
//json_rpc
bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res);
bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp);

View File

@ -37,6 +37,40 @@
#include "cryptonote_config.h"
#include "cryptonote_core/service_node_deregister.h"
#include "rpc/rpc_handler.h"
#include "common/varint.h"
#include "common/perf_timer.h"
namespace
{
template<typename T>
std::string compress_integer_array(const std::vector<T> &v)
{
std::string s;
s.resize(v.size() * (sizeof(T) * 8 / 7 + 1));
char *ptr = (char*)s.data();
for (const T &t: v)
tools::write_varint(ptr, t);
s.resize(ptr - s.data());
return s;
}
template<typename T>
std::vector<T> decompress_integer_array(const std::string &s)
{
std::vector<T> v;
v.reserve(s.size());
int read = 0;
const std::string::const_iterator end = s.end();
for (std::string::const_iterator i = s.begin(); i != end; std::advance(i, read))
{
T t;
read = tools::read_varint(std::string::const_iterator(i), s.end(), t);
CHECK_AND_ASSERT_THROW_MES(read > 0 && read <= 256, "Error decompressing data");
v.push_back(t);
}
return v;
}
}
namespace cryptonote
{
@ -53,7 +87,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 2
#define CORE_RPC_VERSION_MINOR 1
#define CORE_RPC_VERSION_MINOR 2
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@ -700,9 +734,11 @@ namespace cryptonote
struct request
{
std::vector<get_outputs_out> outputs;
bool get_txid;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(outputs)
KV_SERIALIZE_OPT(get_txid, true)
END_KV_SERIALIZE_MAP()
};
@ -2239,6 +2275,7 @@ namespace cryptonote
uint64_t to_height;
bool cumulative;
bool binary;
bool compress;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amounts)
@ -2246,6 +2283,7 @@ namespace cryptonote
KV_SERIALIZE_OPT(to_height, (uint64_t)0)
KV_SERIALIZE_OPT(cumulative, false)
KV_SERIALIZE_OPT(binary, true)
KV_SERIALIZE_OPT(compress, false)
END_KV_SERIALIZE_MAP()
};
@ -2253,14 +2291,38 @@ namespace cryptonote
{
rpc::output_distribution_data data;
uint64_t amount;
std::string compressed_data;
bool binary;
bool compress;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE_N(data.start_height, "start_height")
KV_SERIALIZE(binary)
KV_SERIALIZE(compress)
if (this_ref.binary)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution")
{
if (is_store)
{
if (this_ref.compress)
{
const_cast<std::string&>(this_ref.compressed_data) = compress_integer_array(this_ref.data.distribution);
KV_SERIALIZE(compressed_data)
}
else
KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution")
}
else
{
if (this_ref.compress)
{
KV_SERIALIZE(compressed_data)
const_cast<std::vector<uint64_t>&>(this_ref.data.distribution) = decompress_integer_array<uint64_t>(this_ref.compressed_data);
}
else
KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution")
}
}
else
KV_SERIALIZE_N(data.distribution, "distribution")
KV_SERIALIZE_N(data.base, "base")

View File

@ -113,7 +113,7 @@ struct json_archive;
template <>
struct json_archive<true> : public json_archive_base<std::ostream, true>
{
json_archive(stream_type &s, bool indent = false) : base_type(s, indent) { }
json_archive(stream_type &s, bool indent = false) : base_type(s, indent), inner_array_size_(0) { }
template<typename T>
static auto promote_to_printable_integer_type(T v) -> decltype(+v)

View File

@ -2931,10 +2931,11 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t>
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response res = AUTO_VAL_INIT(res);
req.amounts.push_back(0);
req.from_height = 0;
req.cumulative = true;
req.cumulative = false;
req.binary = true;
req.compress = true;
m_daemon_rpc_mutex.lock();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req, res, m_http_client, rpc_timeout);
bool r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
if (!r)
{
@ -2961,6 +2962,8 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t>
MWARNING("Failed to request output distribution: results are not for amount 0");
return false;
}
for (size_t i = 1; i < res.distributions[0].data.distribution.size(); ++i)
res.distributions[0].data.distribution[i] += res.distributions[0].data.distribution[i-1];
start_height = res.distributions[0].data.start_height;
distribution = std::move(res.distributions[0].data.distribution);
return true;
@ -3224,7 +3227,6 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
account_data = buffer.GetString();
// Encrypt the entire JSON object.
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
std::string cipher;
cipher.resize(account_data.size());
keys_file_data.iv = crypto::rand<crypto::chacha_iv>();
@ -7335,6 +7337,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
}
// get the keys for those
req.get_txid = false;
m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();

View File

@ -54,6 +54,7 @@ set(unit_tests_sources
hashchain.cpp
http.cpp
keccak.cpp
logging.cpp
main.cpp
memwipe.cpp
mlocker.cpp

View File

@ -0,0 +1,177 @@
// Copyright (c) 2016-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.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <boost/filesystem.hpp>
#include "gtest/gtest.h"
#include "file_io_utils.h"
#include "misc_log_ex.h"
static std::string log_filename;
static void init()
{
boost::filesystem::path p = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
log_filename = p.string();
mlog_configure(log_filename, false, 0);
}
static void cleanup()
{
boost::filesystem::remove(log_filename);
}
static size_t nlines(const std::string &str)
{
size_t n = 0;
for (const char *ptr = str.c_str(); *ptr; ++ptr)
if (*ptr == '\n')
++n;
return n;
}
static bool load_log_to_string(const std::string &filename, std::string &str)
{
if (!epee::file_io_utils::load_file_to_string(filename, str))
return false;
for (const char *ptr = str.c_str(); *ptr; ++ptr)
{
if (*ptr == '\n')
{
std::string prefix = std::string(str.c_str(), ptr - str.c_str());
if (prefix.find("New log categories:") != std::string::npos)
{
str = std::string(ptr + 1, strlen(ptr + 1));
break;
}
}
}
return true;
}
static void log()
{
MFATAL("fatal");
MERROR("error");
MWARNING("warning");
MINFO("info");
MDEBUG("debug");
MTRACE("trace");
MCINFO("a.b.c.d", "a.b.c.d");
MCINFO("a.b.c.e", "a.b.c.e");
MCINFO("global", "global");
MCINFO("x.y.z", "x.y.z");
MCINFO("y.y.z", "y.y.z");
MCINFO("x.y.x", "x.y.x");
}
TEST(logging, no_logs)
{
init();
mlog_set_categories("");
log();
std::string str;
ASSERT_TRUE(load_log_to_string(log_filename, str));
ASSERT_TRUE(str == "");
cleanup();
}
TEST(logging, default)
{
init();
log();
std::string str;
ASSERT_TRUE(load_log_to_string(log_filename, str));
ASSERT_TRUE(str.find("global") != std::string::npos);
ASSERT_TRUE(str.find("fatal") != std::string::npos);
ASSERT_TRUE(str.find("error") != std::string::npos);
ASSERT_TRUE(str.find("debug") == std::string::npos);
ASSERT_TRUE(str.find("trace") == std::string::npos);
cleanup();
}
TEST(logging, all)
{
init();
mlog_set_categories("*:TRACE");
log();
std::string str;
ASSERT_TRUE(load_log_to_string(log_filename, str));
ASSERT_TRUE(str.find("global") != std::string::npos);
ASSERT_TRUE(str.find("fatal") != std::string::npos);
ASSERT_TRUE(str.find("error") != std::string::npos);
ASSERT_TRUE(str.find("debug") != std::string::npos);
ASSERT_TRUE(str.find("trace") != std::string::npos);
cleanup();
}
TEST(logging, glob_suffix)
{
init();
mlog_set_categories("x.y*:TRACE");
log();
std::string str;
ASSERT_TRUE(load_log_to_string(log_filename, str));
ASSERT_TRUE(str.find("global") == std::string::npos);
ASSERT_TRUE(str.find("x.y.z") != std::string::npos);
ASSERT_TRUE(str.find("x.y.x") != std::string::npos);
ASSERT_TRUE(str.find("y.y.z") == std::string::npos);
cleanup();
}
TEST(logging, glob_prefix)
{
init();
mlog_set_categories("*y.z:TRACE");
log();
std::string str;
ASSERT_TRUE(load_log_to_string(log_filename, str));
ASSERT_TRUE(str.find("global") == std::string::npos);
ASSERT_TRUE(str.find("x.y.z") != std::string::npos);
ASSERT_TRUE(str.find("x.y.x") == std::string::npos);
ASSERT_TRUE(str.find("y.y.z") != std::string::npos);
cleanup();
}
TEST(logging, last_precedence)
{
init();
mlog_set_categories("gobal:FATAL,glo*:DEBUG");
log();
std::string str;
ASSERT_TRUE(load_log_to_string(log_filename, str));
ASSERT_TRUE(nlines(str) == 1);
ASSERT_TRUE(str.find("global") != std::string::npos);
ASSERT_TRUE(str.find("x.y.z") == std::string::npos);
ASSERT_TRUE(str.find("x.y.x") == std::string::npos);
ASSERT_TRUE(str.find("y.y.z") == std::string::npos);
cleanup();
}

View File

@ -93,7 +93,7 @@ public:
virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return cryptonote::tx_out_index(); }
virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return cryptonote::tx_out_index(); }
virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<cryptonote::tx_out_index> &indices) const {}
virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<cryptonote::output_data_t> &outputs, bool allow_partial = false) const {}
virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<cryptonote::output_data_t> &outputs, bool allow_partial = false) const {}
virtual bool can_thread_bulk_indices() const { return false; }
virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const { return std::vector<uint64_t>(); }
virtual std::vector<uint64_t> get_tx_amount_output_indices(const uint64_t tx_index) const { return std::vector<uint64_t>(); }