Merge commit '7e2483e' into LokiMergeUpstream

This commit is contained in:
doy-lee 2018-11-05 11:27:45 +11:00
commit 42f0933e58
67 changed files with 5858 additions and 814 deletions

3
.gitmodules vendored
View File

@ -9,3 +9,6 @@
[submodule "external/rapidjson"]
path = external/rapidjson
url = https://github.com/Tencent/rapidjson
[submodule "external/trezor-common"]
path = external/trezor-common
url = https://github.com/trezor/trezor-common.git

View File

@ -195,6 +195,7 @@ if(NOT MANUAL_SUBMODULES)
check_submodule(external/miniupnp)
check_submodule(external/unbound)
check_submodule(external/rapidjson)
check_submodule(external/trezor-common)
endif()
endif()
@ -517,6 +518,16 @@ 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()
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__")
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline")

1
external/trezor-common vendored Submodule

@ -0,0 +1 @@
Subproject commit 588f8e03f5ac111adf719f0a437de67481a26aed

View File

@ -146,3 +146,4 @@ if(PER_BLOCK_CHECKPOINT)
endif()
add_subdirectory(device)
add_subdirectory(device_trezor)

View File

@ -102,6 +102,16 @@ set(blockchain_depth_private_headers)
loki_private_headers(blockchain_depth
${blockchain_depth_private_headers})
set(blockchain_stats_sources
blockchain_stats.cpp
)
set(blockchain_stats_private_headers)
loki_private_headers(blockchain_stats
${blockchain_stats_private_headers})
loki_add_executable(blockchain_import
${blockchain_import_sources}
${blockchain_import_private_headers})
@ -236,3 +246,23 @@ set_property(TARGET blockchain_depth
OUTPUT_NAME "loki-blockchain-depth")
install(TARGETS blockchain_depth DESTINATION bin)
loki_add_executable(blockchain_stats
${blockchain_stats_sources}
${blockchain_stats_private_headers})
target_link_libraries(blockchain_stats
PRIVATE
cryptonote_core
blockchain_db
version
epee
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET blockchain_stats
PROPERTY
OUTPUT_NAME "loki-blockchain-stats")
install(TARGETS blockchain_stats DESTINATION bin)

View File

@ -408,8 +408,7 @@ int main(int argc, char* argv[])
for (uint64_t h = state.height; h < db_height; ++h)
{
size_t block_ancestry_size = 0;
const crypto::hash block_hash = db->get_block_hash_from_height(h);
const cryptonote::blobdata bd = db->get_block_blob(block_hash);
const cryptonote::blobdata bd = db->get_block_blob_from_height(h);
++total_blocks;
cryptonote::block b;
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
@ -483,8 +482,7 @@ int main(int argc, char* argv[])
}
else
{
const crypto::hash block_hash = db->get_block_hash_from_height(od.height);
cryptonote::blobdata bd = db->get_block_blob(block_hash);
cryptonote::blobdata bd = db->get_block_blob_from_height(od.height);
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{
LOG_PRINT_L0("Bad block from db");
@ -621,8 +619,7 @@ int main(int argc, char* argv[])
}
else
{
const crypto::hash block_hash = db->get_block_hash_from_height(opt_height);
const cryptonote::blobdata bd = db->get_block_blob(block_hash);
const cryptonote::blobdata bd = db->get_block_blob_from_height(opt_height);
cryptonote::block b;
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{
@ -679,8 +676,7 @@ int main(int argc, char* argv[])
{
add_ancestor(ancestry, amount, offset);
const output_data_t od = db->get_output_key(amount, offset);
const crypto::hash block_hash = db->get_block_hash_from_height(od.height);
bd = db->get_block_blob(block_hash);
bd = db->get_block_blob_from_height(od.height);
cryptonote::block b;
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{

View File

@ -188,8 +188,7 @@ int main(int argc, char* argv[])
}
else
{
const crypto::hash block_hash = db->get_block_hash_from_height(opt_height);
const cryptonote::blobdata bd = db->get_block_blob(block_hash);
const cryptonote::blobdata bd = db->get_block_blob_from_height(opt_height);
cryptonote::block b;
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
{

View File

@ -0,0 +1,337 @@
// 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 <boost/algorithm/string.hpp>
#include "common/command_line.h"
#include "common/varint.h"
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/db_types.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "bcutil"
namespace po = boost::program_options;
using namespace epee;
using namespace cryptonote;
static bool stop_requested = false;
int main(int argc, char* argv[])
{
TRY_ENTRY();
epee::string_tools::set_module_name_and_folder(argv[0]);
std::string default_db_type = "lmdb";
std::string available_dbs = cryptonote::blockchain_db_types(", ");
available_dbs = "available: " + available_dbs;
uint32_t log_level = 0;
uint64_t block_start = 0;
uint64_t block_stop = 0;
tools::on_startup();
boost::filesystem::path output_file_path;
po::options_description desc_cmd_only("Command line options");
po::options_description desc_cmd_sett("Command line options and settings options");
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
const command_line::arg_descriptor<std::string> arg_database = {
"database", available_dbs.c_str(), default_db_type
};
const command_line::arg_descriptor<uint64_t> arg_block_start = {"block-start", "start at block number", block_start};
const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop};
const command_line::arg_descriptor<bool> arg_inputs = {"with-inputs", "with input stats", false};
const command_line::arg_descriptor<bool> arg_outputs = {"with-outputs", "with output stats", false};
const command_line::arg_descriptor<bool> arg_ringsize = {"with-ringsize", "with ringsize stats", false};
const command_line::arg_descriptor<bool> arg_hours = {"with-hours", "with txns per hour", false};
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_block_start);
command_line::add_arg(desc_cmd_sett, arg_block_stop);
command_line::add_arg(desc_cmd_sett, arg_inputs);
command_line::add_arg(desc_cmd_sett, arg_outputs);
command_line::add_arg(desc_cmd_sett, arg_ringsize);
command_line::add_arg(desc_cmd_sett, arg_hours);
command_line::add_arg(desc_cmd_only, command_line::arg_help);
po::options_description desc_options("Allowed options");
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
po::variables_map vm;
bool r = command_line::handle_error_helper(desc_options, [&]()
{
auto parser = po::command_line_parser(argc, argv).options(desc_options);
po::store(parser.run(), vm);
po::notify(vm);
return true;
});
if (! r)
return 1;
if (command_line::get_arg(vm, command_line::arg_help))
{
std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL;
std::cout << desc_options << std::endl;
return 1;
}
mlog_configure(mlog_get_default_log_path("monero-blockchain-stats.log"), true);
if (!command_line::is_arg_defaulted(vm, arg_log_level))
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
else
mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str());
LOG_PRINT_L0("Starting...");
std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
block_start = command_line::get_arg(vm, arg_block_start);
block_stop = command_line::get_arg(vm, arg_block_stop);
bool do_inputs = command_line::get_arg(vm, arg_inputs);
bool do_outputs = command_line::get_arg(vm, arg_outputs);
bool do_ringsize = command_line::get_arg(vm, arg_ringsize);
bool do_hours = command_line::get_arg(vm, arg_hours);
std::string db_type = command_line::get_arg(vm, arg_database);
if (!cryptonote::blockchain_valid_db_type(db_type))
{
std::cerr << "Invalid database type: " << db_type << std::endl;
return 1;
}
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
std::unique_ptr<Blockchain> core_storage;
tx_memory_pool m_mempool(*core_storage);
core_storage.reset(new Blockchain(m_mempool));
BlockchainDB *db = new_db(db_type);
if (db == NULL)
{
LOG_ERROR("Attempted to use non-existent database type: " << db_type);
throw std::runtime_error("Attempting to use non-existent database type");
}
LOG_PRINT_L0("database: " << db_type);
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
try
{
db->open(filename, DBF_RDONLY);
}
catch (const std::exception& e)
{
LOG_PRINT_L0("Error opening database: " << e.what());
return 1;
}
r = core_storage->init(db, net_type);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
LOG_PRINT_L0("Source blockchain storage initialized OK");
tools::signal_handler::install([](int type) {
stop_requested = true;
});
const uint64_t db_height = db->height();
if (!block_stop)
block_stop = db_height;
MINFO("Starting from height " << block_start << ", stopping at height " << block_stop);
/*
* The default output can be plotted with GnuPlot using these commands:
set key autotitle columnhead
set title "Monero Blockchain Growth"
set timefmt "%Y-%m-%d"
set xdata time
set xrange ["2014-04-17":*]
set format x "%Y-%m-%d"
set yrange [0:*]
set y2range [0:*]
set ylabel "Txs/Day"
set y2label "Bytes"
set y2tics nomirror
plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' using (timecolumn(1,"%Y-%m-%d")):7 axes x1y2 with lines
*/
// spit out a comment that GnuPlot can use as an index
std::cout << ENDL << "# DATA" << ENDL;
std::cout << "Date\tBlocks/day\tBlocks\tTxs/Day\tTxs\tBytes/Day\tBytes";
if (do_inputs)
std::cout << "\tInMin\tInMax\tInAvg";
if (do_outputs)
std::cout << "\tOutMin\tOutMax\tOutAvg";
if (do_ringsize)
std::cout << "\tRingMin\tRingMax\tRingAvg";
if (do_hours) {
char buf[8];
unsigned int i;
for (i=0; i<24; i++) {
sprintf(buf, "\t%02d:00", i);
std::cout << buf;
}
}
std::cout << ENDL;
struct tm prevtm = {0}, currtm;
uint64_t prevsz = 0, currsz = 0;
uint64_t prevtxs = 0, currtxs = 0;
uint64_t currblks = 0;
uint64_t totins = 0, totouts = 0, totrings = 0;
uint32_t minins = 10, maxins = 0;
uint32_t minouts = 10, maxouts = 0;
uint32_t minrings = 50, maxrings = 0;
uint32_t io, tottxs = 0;
uint32_t txhr[24] = {0};
unsigned int i;
for (uint64_t h = block_start; h < block_stop; ++h)
{
cryptonote::blobdata bd = db->get_block_blob_from_height(h);
cryptonote::block blk;
if (!cryptonote::parse_and_validate_block_from_blob(bd, blk))
{
LOG_PRINT_L0("Bad block from db");
return 1;
}
time_t tt = blk.timestamp;
char timebuf[64];
gmtime_r(&tt, &currtm);
if (!prevtm.tm_year)
prevtm = currtm;
// catch change of day
if (currtm.tm_mday > prevtm.tm_mday || (currtm.tm_mday == 1 && prevtm.tm_mday > 27))
{
// check for timestamp fudging around month ends
if (prevtm.tm_mday == 1 && currtm.tm_mday > 27)
goto skip;
strftime(timebuf, sizeof(timebuf), "%Y-%m-%d", &prevtm);
prevtm = currtm;
std::cout << timebuf << "\t" << currblks << "\t" << h << "\t" << currtxs << "\t" << prevtxs + currtxs << "\t" << currsz << "\t" << prevsz + currsz;
prevsz += currsz;
currsz = 0;
currblks = 0;
prevtxs += currtxs;
currtxs = 0;
if (!tottxs)
tottxs = 1;
if (do_inputs) {
std::cout << "\t" << (maxins ? minins : 0) << "\t" << maxins << "\t" << totins / tottxs;
minins = 10; maxins = 0; totins = 0;
}
if (do_outputs) {
std::cout << "\t" << (maxouts ? minouts : 0) << "\t" << maxouts << "\t" << totouts / tottxs;
minouts = 10; maxouts = 0; totouts = 0;
}
if (do_ringsize) {
std::cout << "\t" << (maxrings ? minrings : 0) << "\t" << maxrings << "\t" << totrings / tottxs;
minrings = 50; maxrings = 0; totrings = 0;
}
tottxs = 0;
if (do_hours) {
for (i=0; i<24; i++) {
std::cout << "\t" << txhr[i];
txhr[i] = 0;
}
}
std::cout << ENDL;
}
skip:
currsz += bd.size();
for (const auto& tx_id : blk.tx_hashes)
{
if (tx_id == crypto::null_hash)
{
throw std::runtime_error("Aborting: tx == null_hash");
}
if (!db->get_tx_blob(tx_id, bd))
{
throw std::runtime_error("Aborting: tx not found");
}
transaction tx;
if (!parse_and_validate_tx_from_blob(bd, tx))
{
LOG_PRINT_L0("Bad txn from db");
return 1;
}
currsz += bd.size();
currtxs++;
if (do_hours)
txhr[currtm.tm_hour]++;
if (do_inputs) {
io = tx.vin.size();
if (io < minins)
minins = io;
else if (io > maxins)
maxins = io;
totins += io;
}
if (do_ringsize) {
const cryptonote::txin_to_key& tx_in_to_key
= boost::get<cryptonote::txin_to_key>(tx.vin[0]);
io = tx_in_to_key.key_offsets.size();
if (io < minrings)
minrings = io;
else if (io > maxrings)
maxrings = io;
totrings += io;
}
if (do_outputs) {
io = tx.vout.size();
if (io < minouts)
minouts = io;
else if (io > maxouts)
maxouts = io;
totouts += io;
}
tottxs++;
}
currblks++;
if (stop_requested)
break;
}
core_storage->deinit();
return 0;
CATCH_ENTRY("Stats reporting error", 1);
}

View File

@ -20,9 +20,15 @@ const uint8_t shift_Values[2][8] = {{0,1,2,3,4,5,6,7},{1,3,5,7,0,2,4,6}};
const uint8_t indices_cyclic[15] = {0,1,2,3,4,5,6,7,0,1,2,3,4,5,6};
#if BYTE_ORDER == LITTLE_ENDIAN
#define ROTATE_COLUMN_DOWN(v1, v2, amount_bytes, temp_var) {temp_var = (v1<<(8*amount_bytes))|(v2>>(8*(4-amount_bytes))); \
v2 = (v2<<(8*amount_bytes))|(v1>>(8*(4-amount_bytes))); \
v1 = temp_var;}
#else
#define ROTATE_COLUMN_DOWN(v1, v2, amount_bytes, temp_var) {temp_var = (v1>>(8*amount_bytes))|(v2<<(8*(4-amount_bytes))); \
v2 = (v2>>(8*amount_bytes))|(v1<<(8*(4-amount_bytes))); \
v1 = temp_var;}
#endif
#define COLUMN(x,y,i,c0,c1,c2,c3,c4,c5,c6,c7,tv1,tv2,tu,tl,t) \
@ -68,14 +74,14 @@ const uint8_t indices_cyclic[15] = {0,1,2,3,4,5,6,7,0,1,2,3,4,5,6};
static void RND512P(uint8_t *x, uint32_t *y, uint32_t r) {
uint32_t temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp;
uint32_t* x32 = (uint32_t*)x;
x32[ 0] ^= 0x00000000^r;
x32[ 2] ^= 0x00000010^r;
x32[ 4] ^= 0x00000020^r;
x32[ 6] ^= 0x00000030^r;
x32[ 8] ^= 0x00000040^r;
x32[10] ^= 0x00000050^r;
x32[12] ^= 0x00000060^r;
x32[14] ^= 0x00000070^r;
x32[ 0] ^= SWAP32LE(0x00000000)^r;
x32[ 2] ^= SWAP32LE(0x00000010)^r;
x32[ 4] ^= SWAP32LE(0x00000020)^r;
x32[ 6] ^= SWAP32LE(0x00000030)^r;
x32[ 8] ^= SWAP32LE(0x00000040)^r;
x32[10] ^= SWAP32LE(0x00000050)^r;
x32[12] ^= SWAP32LE(0x00000060)^r;
x32[14] ^= SWAP32LE(0x00000070)^r;
COLUMN(x,y, 0, 0, 2, 4, 6, 9, 11, 13, 15, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp);
COLUMN(x,y, 2, 2, 4, 6, 8, 11, 13, 15, 1, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp);
COLUMN(x,y, 4, 4, 6, 8, 10, 13, 15, 1, 3, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp);
@ -91,21 +97,22 @@ static void RND512Q(uint8_t *x, uint32_t *y, uint32_t r) {
uint32_t temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp;
uint32_t* x32 = (uint32_t*)x;
x32[ 0] = ~x32[ 0];
x32[ 1] ^= 0xffffffff^r;
x32[ 1] ^= SWAP32LE(0xffffffff)^r;
x32[ 2] = ~x32[ 2];
x32[ 3] ^= 0xefffffff^r;
x32[ 3] ^= SWAP32LE(0xefffffff)^r;
x32[ 4] = ~x32[ 4];
x32[ 5] ^= 0xdfffffff^r;
x32[ 5] ^= SWAP32LE(0xdfffffff)^r;
x32[ 6] = ~x32[ 6];
x32[ 7] ^= 0xcfffffff^r;
x32[ 7] ^= SWAP32LE(0xcfffffff)^r;
x32[ 8] = ~x32[ 8];
x32[ 9] ^= 0xbfffffff^r;
x32[ 9] ^= SWAP32LE(0xbfffffff)^r;
x32[10] = ~x32[10];
x32[11] ^= 0xafffffff^r;
x32[11] ^= SWAP32LE(0xafffffff)^r;
x32[12] = ~x32[12];
x32[13] ^= 0x9fffffff^r;
x32[13] ^= SWAP32LE(0x9fffffff)^r;
x32[14] = ~x32[14];
x32[15] ^= 0x8fffffff^r;
x32[15] ^= SWAP32LE(0x8fffffff)^r;
COLUMN(x,y, 0, 2, 6, 10, 14, 1, 5, 9, 13, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp);
COLUMN(x,y, 2, 4, 8, 12, 0, 3, 7, 11, 15, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp);
COLUMN(x,y, 4, 6, 10, 14, 2, 5, 9, 13, 1, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp);
@ -130,28 +137,28 @@ static void F512(uint32_t *h, const uint32_t *m) {
}
/* compute Q(m) */
RND512Q((uint8_t*)z, y, 0x00000000);
RND512Q((uint8_t*)y, z, 0x01000000);
RND512Q((uint8_t*)z, y, 0x02000000);
RND512Q((uint8_t*)y, z, 0x03000000);
RND512Q((uint8_t*)z, y, 0x04000000);
RND512Q((uint8_t*)y, z, 0x05000000);
RND512Q((uint8_t*)z, y, 0x06000000);
RND512Q((uint8_t*)y, z, 0x07000000);
RND512Q((uint8_t*)z, y, 0x08000000);
RND512Q((uint8_t*)y, Qtmp, 0x09000000);
RND512Q((uint8_t*)z, y, SWAP32LE(0x00000000));
RND512Q((uint8_t*)y, z, SWAP32LE(0x01000000));
RND512Q((uint8_t*)z, y, SWAP32LE(0x02000000));
RND512Q((uint8_t*)y, z, SWAP32LE(0x03000000));
RND512Q((uint8_t*)z, y, SWAP32LE(0x04000000));
RND512Q((uint8_t*)y, z, SWAP32LE(0x05000000));
RND512Q((uint8_t*)z, y, SWAP32LE(0x06000000));
RND512Q((uint8_t*)y, z, SWAP32LE(0x07000000));
RND512Q((uint8_t*)z, y, SWAP32LE(0x08000000));
RND512Q((uint8_t*)y, Qtmp, SWAP32LE(0x09000000));
/* compute P(h+m) */
RND512P((uint8_t*)Ptmp, y, 0x00000000);
RND512P((uint8_t*)y, z, 0x00000001);
RND512P((uint8_t*)z, y, 0x00000002);
RND512P((uint8_t*)y, z, 0x00000003);
RND512P((uint8_t*)z, y, 0x00000004);
RND512P((uint8_t*)y, z, 0x00000005);
RND512P((uint8_t*)z, y, 0x00000006);
RND512P((uint8_t*)y, z, 0x00000007);
RND512P((uint8_t*)z, y, 0x00000008);
RND512P((uint8_t*)y, Ptmp, 0x00000009);
RND512P((uint8_t*)Ptmp, y, SWAP32LE(0x00000000));
RND512P((uint8_t*)y, z, SWAP32LE(0x00000001));
RND512P((uint8_t*)z, y, SWAP32LE(0x00000002));
RND512P((uint8_t*)y, z, SWAP32LE(0x00000003));
RND512P((uint8_t*)z, y, SWAP32LE(0x00000004));
RND512P((uint8_t*)y, z, SWAP32LE(0x00000005));
RND512P((uint8_t*)z, y, SWAP32LE(0x00000006));
RND512P((uint8_t*)y, z, SWAP32LE(0x00000007));
RND512P((uint8_t*)z, y, SWAP32LE(0x00000008));
RND512P((uint8_t*)y, Ptmp, SWAP32LE(0x00000009));
/* compute P(h+m) + Q(m) + h */
for (i = 0; i < 2*COLS512; i++) {
@ -188,16 +195,16 @@ static void OutputTransformation(hashState *ctx) {
for (j = 0; j < 2*COLS512; j++) {
temp[j] = ctx->chaining[j];
}
RND512P((uint8_t*)temp, y, 0x00000000);
RND512P((uint8_t*)y, z, 0x00000001);
RND512P((uint8_t*)z, y, 0x00000002);
RND512P((uint8_t*)y, z, 0x00000003);
RND512P((uint8_t*)z, y, 0x00000004);
RND512P((uint8_t*)y, z, 0x00000005);
RND512P((uint8_t*)z, y, 0x00000006);
RND512P((uint8_t*)y, z, 0x00000007);
RND512P((uint8_t*)z, y, 0x00000008);
RND512P((uint8_t*)y, temp, 0x00000009);
RND512P((uint8_t*)temp, y, SWAP32LE(0x00000000));
RND512P((uint8_t*)y, z, SWAP32LE(0x00000001));
RND512P((uint8_t*)z, y, SWAP32LE(0x00000002));
RND512P((uint8_t*)y, z, SWAP32LE(0x00000003));
RND512P((uint8_t*)z, y, SWAP32LE(0x00000004));
RND512P((uint8_t*)y, z, SWAP32LE(0x00000005));
RND512P((uint8_t*)z, y, SWAP32LE(0x00000006));
RND512P((uint8_t*)y, z, SWAP32LE(0x00000007));
RND512P((uint8_t*)z, y, SWAP32LE(0x00000008));
RND512P((uint8_t*)y, temp, SWAP32LE(0x00000009));
for (j = 0; j < 2*COLS512; j++) {
ctx->chaining[j] ^= temp[j];
}
@ -213,7 +220,7 @@ static void Init(hashState* ctx) {
}
/* set initial value */
ctx->chaining[2*COLS512-1] = u32BIG((uint32_t)HASH_BIT_LEN);
ctx->chaining[2*COLS512-1] = SWAP32LE(u32BIG((uint32_t)HASH_BIT_LEN));
/* set other variables */
ctx->buf_ptr = 0;

View File

@ -29,7 +29,10 @@
#ifndef __tables_h
#define __tables_h
#include "common/int-util.h"
#if BYTE_ORDER == LITTLE_ENDIAN
const uint32_t T[512] = {0xa5f432c6, 0xc6a597f4, 0x84976ff8, 0xf884eb97, 0x99b05eee, 0xee99c7b0, 0x8d8c7af6, 0xf68df78c, 0xd17e8ff, 0xff0de517, 0xbddc0ad6, 0xd6bdb7dc, 0xb1c816de, 0xdeb1a7c8, 0x54fc6d91, 0x915439fc
, 0x50f09060, 0x6050c0f0, 0x3050702, 0x2030405, 0xa9e02ece, 0xcea987e0, 0x7d87d156, 0x567dac87, 0x192bcce7, 0xe719d52b, 0x62a613b5, 0xb56271a6, 0xe6317c4d, 0x4de69a31, 0x9ab559ec, 0xec9ac3b5
, 0x45cf408f, 0x8f4505cf, 0x9dbca31f, 0x1f9d3ebc, 0x40c04989, 0x894009c0, 0x879268fa, 0xfa87ef92, 0x153fd0ef, 0xef15c53f, 0xeb2694b2, 0xb2eb7f26, 0xc940ce8e, 0x8ec90740, 0xb1de6fb, 0xfb0bed1d
@ -62,5 +65,39 @@ const uint32_t T[512] = {0xa5f432c6, 0xc6a597f4, 0x84976ff8, 0xf884eb97, 0x99b05
, 0xb6c1ec2d, 0x2db65ac1, 0x22665a3c, 0x3c227866, 0x92adb815, 0x15922aad, 0x2060a9c9, 0xc9208960, 0x49db5c87, 0x874915db, 0xff1ab0aa, 0xaaff4f1a, 0x7888d850, 0x5078a088, 0x7a8e2ba5, 0xa57a518e
, 0x8f8a8903, 0x38f068a, 0xf8134a59, 0x59f8b213, 0x809b9209, 0x980129b, 0x1739231a, 0x1a173439, 0xda751065, 0x65daca75, 0x315384d7, 0xd731b553, 0xc651d584, 0x84c61351, 0xb8d303d0, 0xd0b8bbd3
, 0xc35edc82, 0x82c31f5e, 0xb0cbe229, 0x29b052cb, 0x7799c35a, 0x5a77b499, 0x11332d1e, 0x1e113c33, 0xcb463d7b, 0x7bcbf646, 0xfc1fb7a8, 0xa8fc4b1f, 0xd6610c6d, 0x6dd6da61, 0x3a4e622c, 0x2c3a584e};
#else
const uint32_t T[512] = {0xc632f4a5, 0xf497a5c6, 0xf86f9784, 0x97eb84f8, 0xee5eb099, 0xb0c799ee, 0xf67a8c8d, 0x8cf78df6, 0xffe8170d, 0x17e50dff, 0xd60adcbd, 0xdcb7bdd6, 0xde16c8b1, 0xc8a7b1de, 0x916dfc54, 0xfc395491
, 0x6090f050, 0xf0c05060, 0x02070503, 0x05040302, 0xce2ee0a9, 0xe087a9ce, 0x56d1877d, 0x87ac7d56, 0xe7cc2b19, 0x2bd519e7, 0xb513a662, 0xa67162b5, 0x4d7c31e6, 0x319ae64d, 0xec59b59a, 0xb5c39aec
, 0x8f40cf45, 0xcf05458f, 0x1fa3bc9d, 0xbc3e9d1f, 0x8949c040, 0xc0094089, 0xfa689287, 0x92ef87fa, 0xefd03f15, 0x3fc515ef, 0xb29426eb, 0x267febb2, 0x8ece40c9, 0x4007c98e, 0xfbe61d0b, 0x1ded0bfb
, 0x416e2fec, 0x2f82ec41, 0xb31aa967, 0xa97d67b3, 0x5f431cfd, 0x1cbefd5f, 0x456025ea, 0x258aea45, 0x23f9dabf, 0xda46bf23, 0x535102f7, 0x02a6f753, 0xe445a196, 0xa1d396e4, 0x9b76ed5b, 0xed2d5b9b
, 0x75285dc2, 0x5deac275, 0xe1c5241c, 0x24d91ce1, 0x3dd4e9ae, 0xe97aae3d, 0x4cf2be6a, 0xbe986a4c, 0x6c82ee5a, 0xeed85a6c, 0x7ebdc341, 0xc3fc417e, 0xf5f30602, 0x06f102f5, 0x8352d14f, 0xd11d4f83
, 0x688ce45c, 0xe4d05c68, 0x515607f4, 0x07a2f451, 0xd18d5c34, 0x5cb934d1, 0xf9e11808, 0x18e908f9, 0xe24cae93, 0xaedf93e2, 0xab3e9573, 0x954d73ab, 0x6297f553, 0xf5c45362, 0x2a6b413f, 0x41543f2a
, 0x081c140c, 0x14100c08, 0x9563f652, 0xf6315295, 0x46e9af65, 0xaf8c6546, 0x9d7fe25e, 0xe2215e9d, 0x30487828, 0x78602830, 0x37cff8a1, 0xf86ea137, 0x0a1b110f, 0x11140f0a, 0x2febc4b5, 0xc45eb52f
, 0x0e151b09, 0x1b1c090e, 0x247e5a36, 0x5a483624, 0x1badb69b, 0xb6369b1b, 0xdf98473d, 0x47a53ddf, 0xcda76a26, 0x6a8126cd, 0x4ef5bb69, 0xbb9c694e, 0x7f334ccd, 0x4cfecd7f, 0xea50ba9f, 0xbacf9fea
, 0x123f2d1b, 0x2d241b12, 0x1da4b99e, 0xb93a9e1d, 0x58c49c74, 0x9cb07458, 0x3446722e, 0x72682e34, 0x3641772d, 0x776c2d36, 0xdc11cdb2, 0xcda3b2dc, 0xb49d29ee, 0x2973eeb4, 0x5b4d16fb, 0x16b6fb5b
, 0xa4a501f6, 0x0153f6a4, 0x76a1d74d, 0xd7ec4d76, 0xb714a361, 0xa37561b7, 0x7d3449ce, 0x49face7d, 0x52df8d7b, 0x8da47b52, 0xdd9f423e, 0x42a13edd, 0x5ecd9371, 0x93bc715e, 0x13b1a297, 0xa2269713
, 0xa6a204f5, 0x0457f5a6, 0xb901b868, 0xb86968b9, 0x00000000, 0x00000000, 0xc1b5742c, 0x74992cc1, 0x40e0a060, 0xa0806040, 0xe3c2211f, 0x21dd1fe3, 0x793a43c8, 0x43f2c879, 0xb69a2ced, 0x2c77edb6
, 0xd40dd9be, 0xd9b3bed4, 0x8d47ca46, 0xca01468d, 0x671770d9, 0x70ced967, 0x72afdd4b, 0xdde44b72, 0x94ed79de, 0x7933de94, 0x98ff67d4, 0x672bd498, 0xb09323e8, 0x237be8b0, 0x855bde4a, 0xde114a85
, 0xbb06bd6b, 0xbd6d6bbb, 0xc5bb7e2a, 0x7e912ac5, 0x4f7b34e5, 0x349ee54f, 0xedd73a16, 0x3ac116ed, 0x86d254c5, 0x5417c586, 0x9af862d7, 0x622fd79a, 0x6699ff55, 0xffcc5566, 0x11b6a794, 0xa7229411
, 0x8ac04acf, 0x4a0fcf8a, 0xe9d93010, 0x30c910e9, 0x040e0a06, 0x0a080604, 0xfe669881, 0x98e781fe, 0xa0ab0bf0, 0x0b5bf0a0, 0x78b4cc44, 0xccf04478, 0x25f0d5ba, 0xd54aba25, 0x4b753ee3, 0x3e96e34b
, 0xa2ac0ef3, 0x0e5ff3a2, 0x5d4419fe, 0x19bafe5d, 0x80db5bc0, 0x5b1bc080, 0x0580858a, 0x850a8a05, 0x3fd3ecad, 0xec7ead3f, 0x21fedfbc, 0xdf42bc21, 0x70a8d848, 0xd8e04870, 0xf1fd0c04, 0x0cf904f1
, 0x63197adf, 0x7ac6df63, 0x772f58c1, 0x58eec177, 0xaf309f75, 0x9f4575af, 0x42e7a563, 0xa5846342, 0x20705030, 0x50403020, 0xe5cb2e1a, 0x2ed11ae5, 0xfdef120e, 0x12e10efd, 0xbf08b76d, 0xb7656dbf
, 0x8155d44c, 0xd4194c81, 0x18243c14, 0x3c301418, 0x26795f35, 0x5f4c3526, 0xc3b2712f, 0x719d2fc3, 0xbe8638e1, 0x3867e1be, 0x35c8fda2, 0xfd6aa235, 0x88c74fcc, 0x4f0bcc88, 0x2e654b39, 0x4b5c392e
, 0x936af957, 0xf93d5793, 0x55580df2, 0x0daaf255, 0xfc619d82, 0x9de382fc, 0x7ab3c947, 0xc9f4477a, 0xc827efac, 0xef8bacc8, 0xba8832e7, 0x326fe7ba, 0x324f7d2b, 0x7d642b32, 0xe642a495, 0xa4d795e6
, 0xc03bfba0, 0xfb9ba0c0, 0x19aab398, 0xb3329819, 0x9ef668d1, 0x6827d19e, 0xa322817f, 0x815d7fa3, 0x44eeaa66, 0xaa886644, 0x54d6827e, 0x82a87e54, 0x3bdde6ab, 0xe676ab3b, 0x0b959e83, 0x9e16830b
, 0x8cc945ca, 0x4503ca8c, 0xc7bc7b29, 0x7b9529c7, 0x6b056ed3, 0x6ed6d36b, 0x286c443c, 0x44503c28, 0xa72c8b79, 0x8b5579a7, 0xbc813de2, 0x3d63e2bc, 0x1631271d, 0x272c1d16, 0xad379a76, 0x9a4176ad
, 0xdb964d3b, 0x4dad3bdb, 0x649efa56, 0xfac85664, 0x74a6d24e, 0xd2e84e74, 0x1436221e, 0x22281e14, 0x92e476db, 0x763fdb92, 0x0c121e0a, 0x1e180a0c, 0x48fcb46c, 0xb4906c48, 0xb88f37e4, 0x376be4b8
, 0x9f78e75d, 0xe7255d9f, 0xbd0fb26e, 0xb2616ebd, 0x43692aef, 0x2a86ef43, 0xc435f1a6, 0xf193a6c4, 0x39dae3a8, 0xe372a839, 0x31c6f7a4, 0xf762a431, 0xd38a5937, 0x59bd37d3, 0xf274868b, 0x86ff8bf2
, 0xd5835632, 0x56b132d5, 0x8b4ec543, 0xc50d438b, 0x6e85eb59, 0xebdc596e, 0xda18c2b7, 0xc2afb7da, 0x018e8f8c, 0x8f028c01, 0xb11dac64, 0xac7964b1, 0x9cf16dd2, 0x6d23d29c, 0x49723be0, 0x3b92e049
, 0xd81fc7b4, 0xc7abb4d8, 0xacb915fa, 0x1543faac, 0xf3fa0907, 0x09fd07f3, 0xcfa06f25, 0x6f8525cf, 0xca20eaaf, 0xea8fafca, 0xf47d898e, 0x89f38ef4, 0x476720e9, 0x208ee947, 0x10382818, 0x28201810
, 0x6f0b64d5, 0x64ded56f, 0xf0738388, 0x83fb88f0, 0x4afbb16f, 0xb1946f4a, 0x5cca9672, 0x96b8725c, 0x38546c24, 0x6c702438, 0x575f08f1, 0x08aef157, 0x732152c7, 0x52e6c773, 0x9764f351, 0xf3355197
, 0xcbae6523, 0x658d23cb, 0xa125847c, 0x84597ca1, 0xe857bf9c, 0xbfcb9ce8, 0x3e5d6321, 0x637c213e, 0x96ea7cdd, 0x7c37dd96, 0x611e7fdc, 0x7fc2dc61, 0x0d9c9186, 0x911a860d, 0x0f9b9485, 0x941e850f
, 0xe04bab90, 0xabdb90e0, 0x7cbac642, 0xc6f8427c, 0x712657c4, 0x57e2c471, 0xcc29e5aa, 0xe583aacc, 0x90e373d8, 0x733bd890, 0x06090f05, 0x0f0c0506, 0xf7f40301, 0x03f501f7, 0x1c2a3612, 0x3638121c
, 0xc23cfea3, 0xfe9fa3c2, 0x6a8be15f, 0xe1d45f6a, 0xaebe10f9, 0x1047f9ae, 0x69026bd0, 0x6bd2d069, 0x17bfa891, 0xa82e9117, 0x9971e858, 0xe8295899, 0x3a536927, 0x6974273a, 0x27f7d0b9, 0xd04eb927
, 0xd9914838, 0x48a938d9, 0xebde3513, 0x35cd13eb, 0x2be5ceb3, 0xce56b32b, 0x22775533, 0x55443322, 0xd204d6bb, 0xd6bfbbd2, 0xa9399070, 0x904970a9, 0x07878089, 0x800e8907, 0x33c1f2a7, 0xf266a733
, 0x2decc1b6, 0xc15ab62d, 0x3c5a6622, 0x6678223c, 0x15b8ad92, 0xad2a9215, 0xc9a96020, 0x608920c9, 0x875cdb49, 0xdb154987, 0xaab01aff, 0x1a4fffaa, 0x50d88878, 0x88a07850, 0xa52b8e7a, 0x8e517aa5
, 0x03898a8f, 0x8a068f03, 0x594a13f8, 0x13b2f859, 0x09929b80, 0x9b128009, 0x1a233917, 0x3934171a, 0x651075da, 0x75cada65, 0xd7845331, 0x53b531d7, 0x84d551c6, 0x5113c684, 0xd003d3b8, 0xd3bbb8d0
, 0x82dc5ec3, 0x5e1fc382, 0x29e2cbb0, 0xcb52b029, 0x5ac39977, 0x99b4775a, 0x1e2d3311, 0x333c111e, 0x7b3d46cb, 0x46f6cb7b, 0xa8b71ffc, 0x1f4bfca8, 0x6d0c61d6, 0x61dad66d, 0x2c624e3a, 0x4e583a2c};
#endif
#endif /* __tables_h */

View File

@ -145,7 +145,7 @@ void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md)
#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0)))
#define KECCAK_PROCESS_BLOCK(st, block) { \
for (int i_ = 0; i_ < KECCAK_WORDS; i_++){ \
((st))[i_] ^= ((block))[i_]; \
((st))[i_] ^= swap64le(((block))[i_]); \
}; \
keccakf(st, KECCAK_ROUNDS); }
@ -207,7 +207,8 @@ void keccak_finish(KECCAK_CTX * ctx, uint8_t *md){
}
static_assert(KECCAK_BLOCKLEN > KECCAK_DIGESTSIZE, "");
static_assert(KECCAK_DIGESTSIZE % sizeof(uint64_t) == 0, "");
if (md) {
memcpy(md, ctx->hash, KECCAK_DIGESTSIZE);
memcpy_swap64le(md, ctx->hash, KECCAK_DIGESTSIZE / sizeof(uint64_t));
}
}

View File

@ -140,6 +140,15 @@ DISABLE_VS_WARNINGS(4244 4345)
m_creation_timestamp = 0;
}
//-----------------------------------------------------------------
void account_base::deinit()
{
try{
m_keys.get_device().disconnect();
} catch (const std::exception &e){
MERROR("Device disconnect exception: " << e.what());
}
}
//-----------------------------------------------------------------
void account_base::forget_spend_key()
{
m_keys.m_spend_secret_key = crypto::secret_key();
@ -207,11 +216,16 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_base::create_from_device(hw::device &hwdev)
{
m_keys.set_device(hwdev);
MCDEBUG("ledger", "device type: "<<typeid(hwdev).name());
hwdev.init();
hwdev.connect();
hwdev.get_public_address(m_keys.m_account_address);
hwdev.get_secret_keys(m_keys.m_view_secret_key, m_keys.m_spend_secret_key);
MCDEBUG("device", "device type: "<<typeid(hwdev).name());
CHECK_AND_ASSERT_THROW_MES(hwdev.init(), "Device init failed");
CHECK_AND_ASSERT_THROW_MES(hwdev.connect(), "Device connect failed");
try {
CHECK_AND_ASSERT_THROW_MES(hwdev.get_public_address(m_keys.m_account_address), "Cannot get a device address");
CHECK_AND_ASSERT_THROW_MES(hwdev.get_secret_keys(m_keys.m_view_secret_key, m_keys.m_spend_secret_key), "Cannot get device secret");
} catch (const std::exception &e){
hwdev.disconnect();
throw;
}
struct tm timestamp = {0};
timestamp.tm_year = 2014 - 1900; // year 2014
timestamp.tm_mon = 4 - 1; // month april

View File

@ -89,6 +89,7 @@ namespace cryptonote
hw::device& get_device() const {return m_keys.get_device();}
void set_device( hw::device &hwdev) {m_keys.set_device(hwdev);}
void deinit();
uint64_t get_createtime() const { return m_creation_timestamp; }
void set_createtime(uint64_t val) { m_creation_timestamp = val; }

View File

@ -1861,7 +1861,7 @@ namespace cryptonote
for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n)
{
unsigned int b = 0;
for (time_t ts: timestamps) b += ts >= now - seconds[n];
for (time_t ts: timestamps) b += ts >= now - static_cast<time_t>(seconds[n]);
const double p = probability(b, seconds[n] / DIFFICULTY_TARGET_V2);
MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")");
if (p < threshold)

View File

@ -129,7 +129,7 @@ namespace cryptonote
* @param relayed whether or not the transaction was relayed to us
* @param do_not_relay whether to prevent the transaction from being relayed
*
* @return true if the transaction made it to the transaction pool, otherwise false
* @return true if the transaction was accepted, false otherwise
*/
bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
@ -145,7 +145,7 @@ namespace cryptonote
* @param relayed whether or not the transactions were relayed to us
* @param do_not_relay whether to prevent the transactions from being relayed
*
* @return true if the transactions made it to the transaction pool, otherwise false
* @return true if the transactions were accepted, false otherwise
*/
bool handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);

View File

@ -45,6 +45,7 @@ set(device_headers
device.hpp
device_io.hpp
device_default.hpp
device_cold.hpp
log.hpp
)
@ -73,5 +74,6 @@ target_link_libraries(device
cncrypto
ringct_basic
${OPENSSL_CRYPTO_LIBRARIES}
${Boost_SERIALIZATION_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})

View File

@ -47,6 +47,7 @@
#include "crypto/crypto.h"
#include "crypto/chacha.h"
#include "ringct/rctTypes.h"
#include "cryptonote_config.h"
#ifndef USE_DEVICE_LEDGER
@ -99,10 +100,17 @@ namespace hw {
enum device_type
{
SOFTWARE = 0,
LEDGER = 1
LEDGER = 1,
TREZOR = 2
};
enum device_protocol_t {
PROTOCOL_DEFAULT,
PROTOCOL_PROXY, // Originally defined by Ledger
PROTOCOL_COLD, // Originally defined by Trezor
};
/* ======================================================================= */
/* SETUP/TEARDOWN */
/* ======================================================================= */
@ -120,6 +128,7 @@ namespace hw {
virtual device_type get_type() const = 0;
virtual device_protocol_t device_protocol() const { return PROTOCOL_DEFAULT; };
/* ======================================================================= */
/* LOCKER */
@ -204,6 +213,11 @@ namespace hw {
virtual bool close_tx(void) = 0;
virtual bool has_ki_cold_sync(void) const { return false; }
virtual bool has_tx_cold_sign(void) const { return false; }
virtual void set_network_type(cryptonote::network_type network_type) { }
protected:
device_mode mode;
} ;

View File

@ -0,0 +1,71 @@
// 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_DEVICE_COLD_H
#define MONERO_DEVICE_COLD_H
#include "wallet/wallet2.h"
#include <boost/function.hpp>
namespace hw {
typedef struct wallet_shim {
boost::function<crypto::public_key (const tools::wallet2::transfer_details &td)> get_tx_pub_key_from_received_outs;
} wallet_shim;
class tx_aux_data {
public:
std::vector<std::string> tx_device_aux; // device generated aux data
std::vector<cryptonote::address_parse_info> tx_recipients; // as entered by user
};
class device_cold {
public:
using exported_key_image = std::vector<std::pair<crypto::key_image, crypto::signature>>;
/**
* Key image sync with the cold protocol.
*/
virtual void ki_sync(wallet_shim * wallet,
const std::vector<::tools::wallet2::transfer_details> & transfers,
exported_key_image & ski) =0;
/**
* Signs unsigned transaction with the cold protocol.
*/
virtual void tx_sign(wallet_shim * wallet,
const ::tools::wallet2::unsigned_tx_set & unsigned_tx,
::tools::wallet2::signed_tx_set & signed_tx,
tx_aux_data & aux_data) =0;
};
}
#endif //MONERO_DEVICE_COLD_H

View File

@ -69,17 +69,17 @@ namespace hw {
}
bool device_default::init(void) {
dfns();
return true;
}
bool device_default::release() {
dfns();
return true;
}
bool device_default::connect(void) {
dfns();
return true;
}
bool device_default::disconnect() {
dfns();
return true;
}
bool device_default::set_mode(device_mode mode) {

View File

@ -141,6 +141,7 @@ namespace hw {
bool set_mode(device_mode mode) override;
device_type get_type() const override {return device_type::LEDGER;};
device_protocol_t device_protocol() const override { return PROTOCOL_PROXY; };
/* ======================================================================= */
/* LOCKER */

View File

@ -0,0 +1,123 @@
# Copyright (c) 2014-2017, 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.
set(TREZOR_PROTOB_H
trezor/messages/messages.pb.h
trezor/messages/messages-common.pb.h
trezor/messages/messages-management.pb.h
trezor/messages/messages-monero.pb.h
)
set(TREZOR_PROTOB_CPP
trezor/messages/messages.pb.cc
trezor/messages/messages-common.pb.cc
trezor/messages/messages-management.pb.cc
trezor/messages/messages-monero.pb.cc
)
set(trezor_headers
trezor/exceptions.hpp
trezor/messages_map.hpp
trezor/protocol.hpp
trezor/transport.hpp
device_trezor_base.hpp
device_trezor.hpp
trezor.hpp
${TREZOR_PROTOB_H}
)
set(trezor_sources
trezor/messages_map.cpp
trezor/protocol.cpp
trezor/transport.cpp
device_trezor_base.cpp
device_trezor.cpp
${TREZOR_PROTOB_CPP}
)
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)
message(STATUS "Trezor support enabled")
add_definitions(-DPROTOBUF_INLINE_NOT_IN_HEADERS=0)
monero_private_headers(device_trezor
${device_private_headers}
${PROTOBUF_INCLUDE_DIR})
monero_add_library(device_trezor
${trezor_sources}
${trezor_headers}
${trezor_private_headers})
target_link_libraries(device_trezor
PUBLIC
device
cncrypto
ringct_basic
cryptonote_core
common
${SODIUM_LIBRARY}
${Boost_CHRONO_LIBRARY}
${PROTOBUF_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
# set(WITH_DEVICE_TREZOR 1 PARENT_SCOPE)
# add_definitions(-DWITH_DEVICE_TREZOR=1)
else()
monero_private_headers(device_trezor)
monero_add_library(device_trezor device_trezor.cpp)
target_link_libraries(device_trezor PUBLIC cncrypto)
endif()

View File

@ -0,0 +1,363 @@
// 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 "device_trezor.hpp"
namespace hw {
namespace trezor {
#if 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(){
if (!trezor_device) {
trezor_device = new device_trezor();
trezor_device->set_name(HW_TREZOR_NAME);
}
return trezor_device;
}
void register_all(std::map<std::string, std::unique_ptr<device>> &registry) {
registry.insert(std::make_pair(HW_TREZOR_NAME, std::unique_ptr<device>(ensure_trezor_device())));
}
void register_all() {
hw::register_device(HW_TREZOR_NAME, ensure_trezor_device());
}
device_trezor::device_trezor() {
}
device_trezor::~device_trezor() {
try {
disconnect();
release();
} catch(std::exception const& e){
MWARNING("Could not disconnect and release: " << e.what());
}
}
/* ======================================================================= */
/* WALLET & ADDRESS */
/* ======================================================================= */
bool device_trezor::get_public_address(cryptonote::account_public_address &pubkey) {
try {
auto res = get_address();
cryptonote::address_parse_info info{};
bool r = cryptonote::get_account_address_from_str(info, this->network_type, res->address());
CHECK_AND_ASSERT_MES(r, false, "Could not parse returned address. Address parse failed: " + res->address());
CHECK_AND_ASSERT_MES(!info.is_subaddress, false, "Trezor returned a sub address");
pubkey = info.address;
return true;
} catch(std::exception const& e){
MERROR("Get public address exception: " << e.what());
return false;
}
}
bool device_trezor::get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) {
try {
MDEBUG("Loading view-only key from the Trezor. Please check the Trezor for a confirmation.");
auto res = get_view_key();
CHECK_AND_ASSERT_MES(res->watch_key().size() == 32, false, "Trezor returned invalid view key");
spendkey = crypto::null_skey; // not given
memcpy(viewkey.data, res->watch_key().data(), 32);
return true;
} catch(std::exception const& e){
MERROR("Get secret keys exception: " << e.what());
return false;
}
}
/* ======================================================================= */
/* Helpers */
/* ======================================================================= */
/* ======================================================================= */
/* TREZOR PROTOCOL */
/* ======================================================================= */
std::shared_ptr<messages::monero::MoneroAddress> device_trezor::get_address(
const boost::optional<std::vector<uint32_t>> & path,
const boost::optional<cryptonote::network_type> & network_type){
AUTO_LOCK_CMD();
require_connected();
test_ping();
auto req = std::make_shared<messages::monero::MoneroGetAddress>();
this->set_msg_addr<messages::monero::MoneroGetAddress>(req.get(), path, network_type);
auto response = this->client_exchange<messages::monero::MoneroAddress>(req);
MTRACE("Get address response received");
return response;
}
std::shared_ptr<messages::monero::MoneroWatchKey> device_trezor::get_view_key(
const boost::optional<std::vector<uint32_t>> & path,
const boost::optional<cryptonote::network_type> & network_type){
AUTO_LOCK_CMD();
require_connected();
test_ping();
auto req = std::make_shared<messages::monero::MoneroGetWatchKey>();
this->set_msg_addr<messages::monero::MoneroGetWatchKey>(req.get(), path, network_type);
auto response = this->client_exchange<messages::monero::MoneroWatchKey>(req);
MTRACE("Get watch key response received");
return response;
}
void device_trezor::ki_sync(wallet_shim * wallet,
const std::vector<tools::wallet2::transfer_details> & transfers,
hw::device_cold::exported_key_image & ski)
{
AUTO_LOCK_CMD();
require_connected();
test_ping();
std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> req;
std::vector<protocol::ki::MoneroTransferDetails> mtds;
std::vector<protocol::ki::MoneroExportedKeyImage> kis;
protocol::ki::key_image_data(wallet, transfers, mtds);
protocol::ki::generate_commitment(mtds, transfers, req);
this->set_msg_addr<messages::monero::MoneroKeyImageExportInitRequest>(req.get());
auto ack1 = this->client_exchange<messages::monero::MoneroKeyImageExportInitAck>(req);
const auto batch_size = 10;
const auto num_batches = (mtds.size() + batch_size - 1) / batch_size;
for(uint64_t cur = 0; cur < num_batches; ++cur){
auto step_req = std::make_shared<messages::monero::MoneroKeyImageSyncStepRequest>();
auto idx_finish = std::min(static_cast<uint64_t>((cur + 1) * batch_size), static_cast<uint64_t>(mtds.size()));
for(uint64_t idx = cur * batch_size; idx < idx_finish; ++idx){
auto added_tdis = step_req->add_tdis();
CHECK_AND_ASSERT_THROW_MES(idx < mtds.size(), "Invalid transfer detail index");
*added_tdis = mtds[idx];
}
auto step_ack = this->client_exchange<messages::monero::MoneroKeyImageSyncStepAck>(step_req);
auto kis_size = step_ack->kis_size();
kis.reserve(static_cast<size_t>(kis_size));
for(int i = 0; i < kis_size; ++i){
auto ckis = step_ack->kis(i);
kis.push_back(ckis);
}
MTRACE("Batch " << cur << " / " << num_batches << " batches processed");
}
auto final_req = std::make_shared<messages::monero::MoneroKeyImageSyncFinalRequest>();
auto final_ack = this->client_exchange<messages::monero::MoneroKeyImageSyncFinalAck>(final_req);
ski.reserve(kis.size());
for(auto & sub : kis){
char buff[32*3];
protocol::crypto::chacha::decrypt(sub.blob().data(), sub.blob().size(),
reinterpret_cast<const uint8_t *>(final_ack->enc_key().data()),
reinterpret_cast<const uint8_t *>(sub.iv().data()), buff);
::crypto::signature sig{};
::crypto::key_image ki;
memcpy(ki.data, buff, 32);
memcpy(sig.c.data, buff + 32, 32);
memcpy(sig.r.data, buff + 64, 32);
ski.push_back(std::make_pair(ki, sig));
}
}
void device_trezor::tx_sign(wallet_shim * wallet,
const tools::wallet2::unsigned_tx_set & unsigned_tx,
tools::wallet2::signed_tx_set & signed_tx,
hw::tx_aux_data & aux_data)
{
size_t num_tx = unsigned_tx.txes.size();
signed_tx.key_images.clear();
signed_tx.key_images.resize(unsigned_tx.transfers.size());
for(size_t tx_idx = 0; tx_idx < num_tx; ++tx_idx) {
std::shared_ptr<protocol::tx::Signer> signer;
tx_sign(wallet, unsigned_tx, tx_idx, aux_data, signer);
auto & cdata = signer->tdata();
auto aux_info_cur = signer->store_tx_aux_info();
aux_data.tx_device_aux.emplace_back(aux_info_cur);
// Pending tx reconstruction
signed_tx.ptx.emplace_back();
auto & cpend = signed_tx.ptx.back();
cpend.tx = cdata.tx;
cpend.dust = 0;
cpend.fee = 0;
cpend.dust_added_to_fee = false;
cpend.change_dts = cdata.tx_data.change_dts;
cpend.selected_transfers = cdata.tx_data.selected_transfers;
cpend.key_images = "";
cpend.dests = cdata.tx_data.dests;
cpend.construction_data = cdata.tx_data;
// Transaction check
cryptonote::blobdata tx_blob;
cryptonote::transaction tx_deserialized;
bool r = cryptonote::t_serializable_object_to_blob(cpend.tx, tx_blob);
CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed");
r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized);
CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed");
std::string key_images;
bool all_are_txin_to_key = std::all_of(cdata.tx.vin.begin(), cdata.tx.vin.end(), [&](const cryptonote::txin_v& s_e) -> bool
{
CHECKED_GET_SPECIFIC_VARIANT(s_e, const cryptonote::txin_to_key, in, false);
key_images += boost::to_string(in.k_image) + " ";
return true;
});
if(!all_are_txin_to_key) {
throw std::invalid_argument("Not all are txin_to_key");
}
cpend.key_images = key_images;
// KI sync
size_t num_sources = cdata.tx_data.sources.size();
CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.source_permutation.size(), "Invalid permutation size");
CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.tx.vin.size(), "Invalid tx.vin size");
for(size_t src_idx = 0; src_idx < num_sources; ++src_idx){
size_t idx_mapped = cdata.source_permutation[src_idx];
CHECK_AND_ASSERT_THROW_MES(idx_mapped < cdata.tx_data.selected_transfers.size(), "Invalid idx_mapped");
CHECK_AND_ASSERT_THROW_MES(src_idx < cdata.tx.vin.size(), "Invalid idx_mapped");
size_t idx_map_src = cdata.tx_data.selected_transfers[idx_mapped];
auto vini = boost::get<cryptonote::txin_to_key>(cdata.tx.vin[src_idx]);
CHECK_AND_ASSERT_THROW_MES(idx_map_src < signed_tx.key_images.size(), "Invalid key image index");
signed_tx.key_images[idx_map_src] = vini.k_image;
}
}
}
void device_trezor::tx_sign(wallet_shim * wallet,
const tools::wallet2::unsigned_tx_set & unsigned_tx,
size_t idx,
hw::tx_aux_data & aux_data,
std::shared_ptr<protocol::tx::Signer> & signer)
{
AUTO_LOCK_CMD();
require_connected();
test_ping();
CHECK_AND_ASSERT_THROW_MES(idx < unsigned_tx.txes.size(), "Invalid transaction index");
signer = std::make_shared<protocol::tx::Signer>(wallet, &unsigned_tx, idx, &aux_data);
const tools::wallet2::tx_construction_data & cur_tx = unsigned_tx.txes[idx];
unsigned long num_sources = cur_tx.sources.size();
unsigned long num_outputs = cur_tx.splitted_dsts.size();
// Step: Init
auto init_msg = signer->step_init();
this->set_msg_addr(init_msg.get());
auto response = this->client_exchange<messages::monero::MoneroTransactionInitAck>(init_msg);
signer->step_init_ack(response);
// Step: Set transaction inputs
for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){
auto src = signer->step_set_input(cur_src);
auto ack = this->client_exchange<messages::monero::MoneroTransactionSetInputAck>(src);
signer->step_set_input_ack(ack);
}
// Step: sort
auto perm_req = signer->step_permutation();
if (perm_req){
auto perm_ack = this->client_exchange<messages::monero::MoneroTransactionInputsPermutationAck>(perm_req);
signer->step_permutation_ack(perm_ack);
}
// Step: input_vini
if (!signer->in_memory()){
for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){
auto src = signer->step_set_vini_input(cur_src);
auto ack = this->client_exchange<messages::monero::MoneroTransactionInputViniAck>(src);
signer->step_set_vini_input_ack(ack);
}
}
// Step: all inputs set
auto all_inputs_set = signer->step_all_inputs_set();
auto ack_all_inputs = this->client_exchange<messages::monero::MoneroTransactionAllInputsSetAck>(all_inputs_set);
signer->step_all_inputs_set_ack(ack_all_inputs);
// Step: outputs
for(size_t cur_dst = 0; cur_dst < num_outputs; ++cur_dst){
auto src = signer->step_set_output(cur_dst);
auto ack = this->client_exchange<messages::monero::MoneroTransactionSetOutputAck>(src);
signer->step_set_output_ack(ack);
}
// Step: all outs set
auto all_out_set = signer->step_all_outs_set();
auto ack_all_out_set = this->client_exchange<messages::monero::MoneroTransactionAllOutSetAck>(all_out_set);
signer->step_all_outs_set_ack(ack_all_out_set, *this);
// Step: sign each input
for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){
auto src = signer->step_sign_input(cur_src);
auto ack_sign = this->client_exchange<messages::monero::MoneroTransactionSignInputAck>(src);
signer->step_sign_input_ack(ack_sign);
}
// Step: final
auto final_msg = signer->step_final();
auto ack_final = this->client_exchange<messages::monero::MoneroTransactionFinalAck>(final_msg);
signer->step_final_ack(ack_final);
}
#else //WITH_DEVICE_TREZOR
void register_all(std::map<std::string, std::unique_ptr<device>> &registry) {
}
void register_all() {
}
#endif //WITH_DEVICE_TREZOR
}}

View File

@ -0,0 +1,132 @@
// 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_DEVICE_TREZOR_H
#define MONERO_DEVICE_TREZOR_H
#include <cstddef>
#include <string>
#include "device/device.hpp"
#include "device/device_default.hpp"
#include "device/device_cold.hpp"
#include <boost/scope_exit.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include "cryptonote_config.h"
#include "trezor.hpp"
#include "device_trezor_base.hpp"
namespace hw {
namespace trezor {
void register_all();
void register_all(std::map<std::string, std::unique_ptr<device>> &registry);
#if WITH_DEVICE_TREZOR
class device_trezor;
/**
* Main device
*/
class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold {
protected:
// To speed up blockchain parsing the view key maybe handle here.
crypto::secret_key viewkey;
bool has_view_key;
public:
device_trezor();
virtual ~device_trezor() override;
device_trezor(const device_trezor &device) = delete ;
device_trezor& operator=(const device_trezor &device) = delete;
explicit operator bool() const override {return true;}
device_protocol_t device_protocol() const override { return PROTOCOL_COLD; };
bool has_ki_cold_sync() const override { return true; }
bool has_tx_cold_sign() const override { return true; }
void set_network_type(cryptonote::network_type network_type) override { this->network_type = network_type; }
/* ======================================================================= */
/* WALLET & ADDRESS */
/* ======================================================================= */
bool get_public_address(cryptonote::account_public_address &pubkey) override;
bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override;
/* ======================================================================= */
/* TREZOR PROTOCOL */
/* ======================================================================= */
/**
* Get address. Throws.
*/
std::shared_ptr<messages::monero::MoneroAddress> get_address(
const boost::optional<std::vector<uint32_t>> & path = boost::none,
const boost::optional<cryptonote::network_type> & network_type = boost::none);
/**
* Get watch key from device. Throws.
*/
std::shared_ptr<messages::monero::MoneroWatchKey> get_view_key(
const boost::optional<std::vector<uint32_t>> & path = boost::none,
const boost::optional<cryptonote::network_type> & network_type = boost::none);
/**
* Key image sync with the Trezor.
*/
void ki_sync(wallet_shim * wallet,
const std::vector<::tools::wallet2::transfer_details> & transfers,
hw::device_cold::exported_key_image & ski) override;
/**
* Signs particular transaction idx in the unsigned set, keeps state in the signer
*/
void tx_sign(wallet_shim * wallet,
const ::tools::wallet2::unsigned_tx_set & unsigned_tx,
size_t idx,
hw::tx_aux_data & aux_data,
std::shared_ptr<protocol::tx::Signer> & signer);
/**
* Signs unsigned transaction with the Trezor.
*/
void tx_sign(wallet_shim * wallet,
const ::tools::wallet2::unsigned_tx_set & unsigned_tx,
::tools::wallet2::signed_tx_set & signed_tx,
hw::tx_aux_data & aux_data) override;
};
#endif
}
}
#endif //MONERO_DEVICE_TREZOR_H

View File

@ -0,0 +1,301 @@
// 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 "device_trezor_base.hpp"
namespace hw {
namespace trezor {
#if 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() {
}
device_trezor_base::~device_trezor_base() {
try {
disconnect();
release();
} catch(std::exception const& e){
MERROR("Could not disconnect and release: " << e.what());
}
}
/* ======================================================================= */
/* SETUP/TEARDOWN */
/* ======================================================================= */
bool device_trezor_base::reset() {
return false;
}
bool device_trezor_base::set_name(const std::string & name) {
this->full_name = name;
this->name = "";
auto delim = name.find(':');
if (delim != std::string::npos && delim + 1 < name.length()) {
this->name = name.substr(delim + 1);
}
return true;
}
const std::string device_trezor_base::get_name() const {
if (this->full_name.empty()) {
return std::string("<disconnected:").append(this->name).append(">");
}
return this->full_name;
}
bool device_trezor_base::init() {
if (!release()){
MERROR("Release failed");
return false;
}
if (!m_protocol_callback){
m_protocol_callback = std::make_shared<trezor_protocol_callback>(*this);
}
return true;
}
bool device_trezor_base::release() {
try {
disconnect();
return true;
} catch(std::exception const& e){
MERROR("Release exception: " << e.what());
return false;
}
}
bool device_trezor_base::connect() {
disconnect();
// Enumerate all available devices
try {
hw::trezor::t_transport_vect trans;
MDEBUG("Enumerating Trezor devices...");
enumerate(trans);
MDEBUG("Enumeration yielded " << trans.size() << " devices");
for (auto &cur : trans) {
MDEBUG(" device: " << *(cur.get()));
std::string cur_path = cur->get_path();
if (boost::starts_with(cur_path, this->name)) {
MDEBUG("Device Match: " << cur_path);
m_transport = cur;
break;
}
}
if (!m_transport) {
MERROR("No matching Trezor device found. Device specifier: \"" + this->name + "\"");
return false;
}
m_transport->open();
return true;
} catch(std::exception const& e){
MERROR("Open exception: " << e.what());
return false;
}
}
bool device_trezor_base::disconnect() {
if (m_transport){
try {
m_transport->close();
m_transport = nullptr;
} catch(std::exception const& e){
MERROR("Disconnect exception: " << e.what());
m_transport = nullptr;
return false;
}
}
return true;
}
/* ======================================================================= */
/* LOCKER */
/* ======================================================================= */
//lock the device for a long sequence
void device_trezor_base::lock() {
MTRACE("Ask for LOCKING for device " << this->name << " in thread ");
device_locker.lock();
MTRACE("Device " << this->name << " LOCKed");
}
//lock the device for a long sequence
bool device_trezor_base::try_lock() {
MTRACE("Ask for LOCKING(try) for device " << this->name << " in thread ");
bool r = device_locker.try_lock();
if (r) {
MTRACE("Device " << this->name << " LOCKed(try)");
} else {
MDEBUG("Device " << this->name << " not LOCKed(try)");
}
return r;
}
//unlock the device
void device_trezor_base::unlock() {
MTRACE("Ask for UNLOCKING for device " << this->name << " in thread ");
device_locker.unlock();
MTRACE("Device " << this->name << " UNLOCKed");
}
/* ======================================================================= */
/* Helpers */
/* ======================================================================= */
void device_trezor_base::require_connected(){
if (!m_transport){
throw exc::NotConnectedException();
}
}
void device_trezor_base::call_ping_unsafe(){
auto pingMsg = std::make_shared<messages::management::Ping>();
pingMsg->set_message("PING");
auto success = this->client_exchange<messages::common::Success>(pingMsg); // messages::MessageType_Success
MDEBUG("Ping response " << success->message());
(void)success;
}
void device_trezor_base::test_ping(){
require_connected();
try {
this->call_ping_unsafe();
} catch(exc::TrezorException const& e){
MINFO("Trezor does not respond: " << e.what());
throw exc::DeviceNotResponsiveException(std::string("Trezor not responding: ") + e.what());
}
}
/* ======================================================================= */
/* TREZOR PROTOCOL */
/* ======================================================================= */
bool device_trezor_base::ping() {
AUTO_LOCK_CMD();
if (!m_transport){
MINFO("Ping failed, device not connected");
return false;
}
try {
this->call_ping_unsafe();
return true;
} catch(std::exception const& e) {
MERROR("Ping failed, exception thrown " << e.what());
} catch(...){
MERROR("Ping failed, general exception thrown" << boost::current_exception_diagnostic_information());
}
return false;
}
void device_trezor_base::on_button_request()
{
if (m_callback){
m_callback->on_button_request();
}
}
void device_trezor_base::on_pin_request(epee::wipeable_string & pin)
{
if (m_callback){
m_callback->on_pin_request(pin);
}
}
void device_trezor_base::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase)
{
if (m_callback){
m_callback->on_passphrase_request(on_device, passphrase);
}
}
void device_trezor_base::on_passphrase_state_request(const std::string & state)
{
if (m_callback){
m_callback->on_passphrase_state_request(state);
}
}
#endif //WITH_DEVICE_TREZOR
}}

View File

@ -0,0 +1,301 @@
// 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_DEVICE_TREZOR_BASE_H
#define MONERO_DEVICE_TREZOR_BASE_H
#include <cstddef>
#include <string>
#include "device/device.hpp"
#include "device/device_default.hpp"
#include "device/device_cold.hpp"
#include <boost/scope_exit.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include "cryptonote_config.h"
#include "trezor.hpp"
//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*/ \
boost::lock(device_locker, command_locker); \
/* make sure both already-locked mutexes are unlocked at the end of scope */ \
boost::lock_guard<boost::recursive_mutex> lock1(device_locker, boost::adopt_lock); \
boost::lock_guard<boost::mutex> lock2(command_locker, boost::adopt_lock)
namespace hw {
namespace trezor {
#if WITH_DEVICE_TREZOR
class device_trezor_base;
/**
* Trezor device callbacks
*/
class trezor_callback {
public:
virtual void on_button_request() {};
virtual void on_pin_request(epee::wipeable_string & pin) {};
virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {};
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
*/
class device_trezor_base : public hw::core::device_default {
protected:
// Locker for concurrent access
mutable boost::recursive_mutex device_locker;
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;
cryptonote::network_type network_type;
//
// Internal methods
//
void require_connected();
void call_ping_unsafe();
void test_ping();
/**
* Client communication wrapper, handles specific Trezor protocol.
*
* @throws UnexpectedMessageException if the response message type is different than expected.
* Exception contains message type and the message itself.
*/
template<class t_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,
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)
{
// Require strictly protocol buffers response in the template.
BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
const bool accepting_base = boost::is_same<google::protobuf::Message, t_message>::value;
if (resp_types && !accepting_base){
throw std::invalid_argument("Cannot specify list of accepted types and not using generic response");
}
// Open session if required
if (open_session && depth == 0){
try {
m_transport->open();
} catch (const std::exception& e) {
std::throw_with_nested(exc::SessionException("Could not open session"));
}
}
// Scoped session closer
BOOST_SCOPE_EXIT_ALL(&, this) {
if (open_session && depth == 0){
this->getTransport()->close();
}
};
// Write the request
CHECK_AND_ASSERT_THROW_MES(req, "Request is null");
this->getTransport()->write(*req);
// Read the response
std::shared_ptr<google::protobuf::Message> msg_resp;
hw::trezor::messages::MessageType msg_resp_type;
// We may have several roundtrips with the handler
this->getTransport()->read(msg_resp, &msg_resp_type);
if (resp_type_ptr){
*(resp_type_ptr.get()) = msg_resp_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_type == messages::MessageType_Failure) {
throw_failure_exception(dynamic_cast<messages::common::Failure *>(msg_resp.get()));
} else if (!accepting_base && msg_resp_type == required_type) {
return message_ptr_retype<t_message>(msg_resp);
} 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);
}
}
}
/**
* Utility method to set address_n and network type to the message requets.
*/
template<class t_message>
void set_msg_addr(t_message * msg,
const boost::optional<std::vector<uint32_t>> & path = boost::none,
const boost::optional<cryptonote::network_type> & network_type = boost::none)
{
CHECK_AND_ASSERT_THROW_MES(msg, "Message is null");
msg->clear_address_n();
if (path){
for(auto x : path.get()){
msg->add_address_n(x);
}
} else {
for (unsigned int i : DEFAULT_BIP44_PATH) {
msg->add_address_n(i);
}
}
if (network_type){
msg->set_network_type(static_cast<uint32_t>(network_type.get()));
} else {
msg->set_network_type(static_cast<uint32_t>(this->network_type));
}
}
public:
device_trezor_base();
~device_trezor_base() override;
device_trezor_base(const device_trezor_base &device) = delete ;
device_trezor_base& operator=(const device_trezor_base &device) = delete;
explicit operator bool() const override {return true;}
device_type get_type() const override {return device_type::TREZOR;};
bool reset();
// Default derivation path for Monero
static const uint32_t DEFAULT_BIP44_PATH[3];
std::shared_ptr<Transport> getTransport(){
return m_transport;
}
std::shared_ptr<trezor_protocol_callback> getProtocolCallback(){
return m_protocol_callback;
}
std::shared_ptr<trezor_callback> getCallback(){
return m_callback;
}
/* ======================================================================= */
/* SETUP/TEARDOWN */
/* ======================================================================= */
bool set_name(const std::string &name) override;
const std::string get_name() const override;
bool init() override;
bool release() override;
bool connect() override;
bool disconnect() override;
/* ======================================================================= */
/* LOCKER */
/* ======================================================================= */
void lock() override;
void unlock() override;
bool try_lock() override;
/* ======================================================================= */
/* TREZOR PROTOCOL */
/* ======================================================================= */
/**
* Device ping, no-throw
*/
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);
};
#endif
}
}
#endif //MONERO_DEVICE_TREZOR_BASE_H

View File

@ -0,0 +1,44 @@
// 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_TREZOR_HPP
#define MONERO_TREZOR_HPP
#include "trezor/trezor_defs.hpp"
#ifdef HAVE_PROTOBUF
#include "trezor/transport.hpp"
#include "trezor/messages/messages.pb.h"
#include "trezor/messages/messages-common.pb.h"
#include "trezor/messages/messages-management.pb.h"
#include "trezor/messages/messages-monero.pb.h"
#include "trezor/protocol.hpp"
#endif
#endif //MONERO_TREZOR_HPP

View File

@ -0,0 +1,193 @@
// 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_EXCEPTIONS_H
#define MONERO_EXCEPTIONS_H
#include <exception>
#include <string>
#include <boost/optional.hpp>
namespace hw {
namespace trezor {
namespace exc {
class SecurityException : public std::exception {
protected:
boost::optional<std::string> reason;
public:
SecurityException(): reason("General Security exception"){}
explicit SecurityException(std::string what): reason(what){}
virtual const char* what() const throw() {
return reason.get().c_str();
}
};
class Poly1305TagInvalid: public SecurityException {
public:
using SecurityException::SecurityException;
Poly1305TagInvalid(): SecurityException("Poly1305 authentication tag invalid"){}
};
class TrezorException : public std::exception {
protected:
boost::optional<std::string> reason;
public:
TrezorException(): reason("General Trezor exception"){}
explicit TrezorException(std::string what): reason(what){}
virtual const char* what() const throw() {
return reason.get().c_str();
}
};
class CommunicationException: public TrezorException {
public:
using TrezorException::TrezorException;
CommunicationException(): TrezorException("Trezor communication error"){}
};
class EncodingException: public CommunicationException {
public:
using CommunicationException::CommunicationException;
EncodingException(): CommunicationException("Trezor message encoding error"){}
};
class NotConnectedException : public CommunicationException {
public:
using CommunicationException::CommunicationException;
NotConnectedException(): CommunicationException("Trezor not connected"){}
};
class DeviceNotResponsiveException : public CommunicationException {
public:
using CommunicationException::CommunicationException;
DeviceNotResponsiveException(): CommunicationException("Trezor does not respond to ping"){}
};
class DeviceAcquireException : public CommunicationException {
public:
using CommunicationException::CommunicationException;
DeviceAcquireException(): CommunicationException("Trezor could not be acquired"){}
};
class SessionException: public CommunicationException {
public:
using CommunicationException::CommunicationException;
SessionException(): CommunicationException("Trezor session expired"){}
};
class TimeoutException: public CommunicationException {
public:
using CommunicationException::CommunicationException;
TimeoutException(): CommunicationException("Trezor communication timeout"){}
};
class ProtocolException: public CommunicationException {
public:
using CommunicationException::CommunicationException;
ProtocolException(): CommunicationException("Trezor protocol error"){}
};
// Communication protocol namespace
// Separated to distinguish between client and Trezor side exceptions.
namespace proto {
class SecurityException : public ProtocolException {
public:
using ProtocolException::ProtocolException;
SecurityException(): ProtocolException("Security assertion violated in the protocol"){}
};
class FailureException : public ProtocolException {
private:
boost::optional<uint32_t> code;
boost::optional<std::string> message;
public:
using ProtocolException::ProtocolException;
FailureException(): ProtocolException("Trezor returned failure"){}
FailureException(boost::optional<uint32_t> code,
boost::optional<std::string> message)
: code(code), message(message) {
reason = "Trezor returned failure: code="
+ (code ? std::to_string(code.get()) : "")
+ ", message=" + (message ? message.get() : "");
};
};
class UnexpectedMessageException : public FailureException {
public:
using FailureException::FailureException;
UnexpectedMessageException(): FailureException("Trezor claims unexpected message received"){}
};
class CancelledException : public FailureException {
public:
using FailureException::FailureException;
CancelledException(): FailureException("Trezor returned: cancelled operation"){}
};
class PinExpectedException : public FailureException {
public:
using FailureException::FailureException;
PinExpectedException(): FailureException("Trezor claims PIN is expected"){}
};
class InvalidPinException : public FailureException {
public:
using FailureException::FailureException;
InvalidPinException(): FailureException("Trezor claims PIN is invalid"){}
};
class NotEnoughFundsException : public FailureException {
public:
using FailureException::FailureException;
NotEnoughFundsException(): FailureException("Trezor claims not enough funds"){}
};
class NotInitializedException : public FailureException {
public:
using FailureException::FailureException;
NotInitializedException(): FailureException("Trezor claims not initialized"){}
};
class FirmwareErrorException : public FailureException {
public:
using FailureException::FailureException;
FirmwareErrorException(): FailureException("Trezor returned firmware error"){}
};
}
}
}
}
#endif //MONERO_EXCEPTIONS_H

View File

@ -0,0 +1,2 @@
# protobuf generated code
*

View File

@ -0,0 +1,125 @@
// 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 "messages_map.hpp"
#include "messages/messages.pb.h"
#include "messages/messages-common.pb.h"
#include "messages/messages-management.pb.h"
#include "messages/messages-monero.pb.h"
using namespace std;
using namespace hw::trezor;
namespace hw{
namespace trezor
{
const char * TYPE_PREFIX = "MessageType_";
const char * PACKAGES[] = {
"hw.trezor.messages.",
"hw.trezor.messages.common.",
"hw.trezor.messages.management.",
"hw.trezor.messages.monero."
};
google::protobuf::Message * MessageMapper::get_message(int wire_number) {
return MessageMapper::get_message(static_cast<messages::MessageType>(wire_number));
}
google::protobuf::Message * MessageMapper::get_message(messages::MessageType wire_number) {
const string &messageTypeName = hw::trezor::messages::MessageType_Name(wire_number);
if (messageTypeName.empty()) {
throw exc::EncodingException(std::string("Message descriptor not found: ") + std::to_string(wire_number));
}
string messageName = messageTypeName.substr(strlen(TYPE_PREFIX));
return MessageMapper::get_message(messageName);
}
google::protobuf::Message * MessageMapper::get_message(const std::string & msg_name) {
// Each package instantiation so lookup works
hw::trezor::messages::common::Success::default_instance();
hw::trezor::messages::management::Cancel::default_instance();
hw::trezor::messages::monero::MoneroGetAddress::default_instance();
google::protobuf::Descriptor const * desc = nullptr;
for(const string &text : PACKAGES){
desc = google::protobuf::DescriptorPool::generated_pool()
->FindMessageTypeByName(text + msg_name);
if (desc != nullptr){
break;
}
}
if (desc == nullptr){
throw exc::EncodingException(std::string("Message not found: ") + msg_name);
}
google::protobuf::Message* message =
google::protobuf::MessageFactory::generated_factory()
->GetPrototype(desc)->New();
return message;
// // CODEGEN way, fast
// switch(wire_number){
// case 501:
// return new messages::monero::MoneroTransactionSignRequest();
// default:
// throw std::runtime_error("not implemented");
// }
//
// // CODEGEN message -> number: specification
// // messages::MessageType get_message_wire_number(const messages::monero::MoneroTransactionSignRequest * msg) { return 501; }
// // messages::MessageType get_message_wire_number(const messages::management::ping * msg)
//
}
messages::MessageType MessageMapper::get_message_wire_number(const google::protobuf::Message * msg){
return MessageMapper::get_message_wire_number(msg->GetDescriptor()->name());
}
messages::MessageType MessageMapper::get_message_wire_number(const google::protobuf::Message & msg){
return MessageMapper::get_message_wire_number(msg.GetDescriptor()->name());
}
messages::MessageType MessageMapper::get_message_wire_number(const std::string & msg_name){
string enumMessageName = std::string(TYPE_PREFIX) + msg_name;
messages::MessageType res;
bool r = hw::trezor::messages::MessageType_Parse(enumMessageName, &res);
if (!r){
throw exc::EncodingException(std::string("Message ") + msg_name + " not found");
}
return res;
}
}
}

View File

@ -0,0 +1,94 @@
// 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_MESSAGES_MAP_H
#define MONERO_MESSAGES_MAP_H
#include <string>
#include <type_traits>
#include <memory>
#include "exceptions.hpp"
#include "trezor_defs.hpp"
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/generated_message_util.h>
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/extension_set.h>
#include <google/protobuf/generated_enum_reflection.h>
#include "google/protobuf/descriptor.pb.h"
#include "messages/messages.pb.h"
namespace hw {
namespace trezor {
class MessageMapper{
public:
MessageMapper() {
}
static ::google::protobuf::Message * get_message(int wire_number);
static ::google::protobuf::Message * get_message(messages::MessageType);
static ::google::protobuf::Message * get_message(const std::string & msg_name);
static messages::MessageType get_message_wire_number(const google::protobuf::Message * msg);
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>
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>
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){
return nullptr;
}
return std::dynamic_pointer_cast<t_message>(in);
}
template<class t_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){
return nullptr;
}
return std::static_pointer_cast<t_message>(in);
}
}}
#endif //MONERO_MESSAGES_MAP_H

View File

@ -0,0 +1,891 @@
// 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 "protocol.hpp"
#include <unordered_map>
#include <set>
#include <utility>
#include <boost/endian/conversion.hpp>
#include <common/apply_permutation.h>
#include <ringct/rctSigs.h>
#include <ringct/bulletproofs.h>
#include "cryptonote_config.h"
#include <sodium.h>
#include <sodium/crypto_verify_32.h>
#include <sodium/crypto_aead_chacha20poly1305.h>
namespace hw{
namespace trezor{
namespace protocol{
std::string key_to_string(const ::crypto::ec_point & key){
return std::string(key.data, sizeof(key.data));
}
std::string key_to_string(const ::crypto::ec_scalar & key){
return std::string(key.data, sizeof(key.data));
}
std::string key_to_string(const ::crypto::hash & key){
return std::string(key.data, sizeof(key.data));
}
std::string key_to_string(const ::rct::key & key){
return std::string(reinterpret_cast<const char*>(key.bytes), sizeof(key.bytes));
}
void string_to_key(::crypto::ec_scalar & key, const std::string & str){
if (str.size() != sizeof(key.data)){
throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.data)) + " B");
}
memcpy(key.data, str.data(), sizeof(key.data));
}
void string_to_key(::crypto::ec_point & key, const std::string & str){
if (str.size() != sizeof(key.data)){
throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.data)) + " B");
}
memcpy(key.data, str.data(), sizeof(key.data));
}
void string_to_key(::rct::key & key, const std::string & str){
if (str.size() != sizeof(key.bytes)){
throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.bytes)) + " B");
}
memcpy(key.bytes, str.data(), sizeof(key.bytes));
}
namespace crypto {
namespace chacha {
void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext){
if (length < 16){
throw std::invalid_argument("Ciphertext length too small");
}
unsigned long long int cip_len = length;
auto r = crypto_aead_chacha20poly1305_ietf_decrypt(
reinterpret_cast<unsigned char *>(plaintext), &cip_len, nullptr,
static_cast<const unsigned char *>(ciphertext), length, nullptr, 0, iv, key);
if (r != 0){
throw exc::Poly1305TagInvalid();
}
}
}
}
// Cold Key image sync
namespace ki {
bool key_image_data(wallet_shim * wallet,
const std::vector<tools::wallet2::transfer_details> & transfers,
std::vector<MoneroTransferDetails> & res)
{
for(auto & td : transfers){
::crypto::public_key tx_pub_key = wallet->get_tx_pub_key_from_received_outs(td);
const std::vector<::crypto::public_key> additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(td.m_tx);
res.emplace_back();
auto & cres = res.back();
cres.set_out_key(key_to_string(boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key));
cres.set_tx_pub_key(key_to_string(tx_pub_key));
cres.set_internal_output_index(td.m_internal_output_index);
for(auto & aux : additional_tx_pub_keys){
cres.add_additional_tx_pub_keys(key_to_string(aux));
}
}
return true;
}
std::string compute_hash(const MoneroTransferDetails & rr){
KECCAK_CTX kck;
uint8_t md[32];
CHECK_AND_ASSERT_THROW_MES(rr.out_key().size() == 32, "Invalid out_key size");
CHECK_AND_ASSERT_THROW_MES(rr.tx_pub_key().size() == 32, "Invalid tx_pub_key size");
keccak_init(&kck);
keccak_update(&kck, reinterpret_cast<const uint8_t *>(rr.out_key().data()), 32);
keccak_update(&kck, reinterpret_cast<const uint8_t *>(rr.tx_pub_key().data()), 32);
for (const auto &aux : rr.additional_tx_pub_keys()){
CHECK_AND_ASSERT_THROW_MES(aux.size() == 32, "Invalid aux size");
keccak_update(&kck, reinterpret_cast<const uint8_t *>(aux.data()), 32);
}
auto index_serialized = tools::get_varint_data(rr.internal_output_index());
keccak_update(&kck, reinterpret_cast<const uint8_t *>(index_serialized.data()), index_serialized.size());
keccak_finish(&kck, md);
return std::string(reinterpret_cast<const char*>(md), sizeof(md));
}
void generate_commitment(std::vector<MoneroTransferDetails> & mtds,
const std::vector<tools::wallet2::transfer_details> & transfers,
std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req)
{
req = std::make_shared<messages::monero::MoneroKeyImageExportInitRequest>();
KECCAK_CTX kck;
uint8_t final_hash[32];
keccak_init(&kck);
for(auto &cur : mtds){
auto hash = compute_hash(cur);
keccak_update(&kck, reinterpret_cast<const uint8_t *>(hash.data()), hash.size());
}
keccak_finish(&kck, final_hash);
req = std::make_shared<messages::monero::MoneroKeyImageExportInitRequest>();
req->set_hash(std::string(reinterpret_cast<const char*>(final_hash), 32));
req->set_num(transfers.size());
std::unordered_map<uint32_t, std::set<uint32_t>> sub_indices;
for (auto &cur : transfers){
auto search = sub_indices.emplace(cur.m_subaddr_index.major, std::set<uint32_t>());
auto & st = search.first->second;
st.insert(cur.m_subaddr_index.minor);
}
for (auto& x: sub_indices){
auto subs = req->add_subs();
subs->set_account(x.first);
for(auto minor : x.second){
subs->add_minor_indices(minor);
}
}
}
}
// Cold transaction signing
namespace tx {
void translate_address(MoneroAccountPublicAddress * dst, const cryptonote::account_public_address * src){
dst->set_view_public_key(key_to_string(src->m_view_public_key));
dst->set_spend_public_key(key_to_string(src->m_spend_public_key));
}
void translate_dst_entry(MoneroTransactionDestinationEntry * dst, const cryptonote::tx_destination_entry * src){
dst->set_amount(src->amount);
dst->set_is_subaddress(src->is_subaddress);
translate_address(dst->mutable_addr(), &(src->addr));
}
void translate_src_entry(MoneroTransactionSourceEntry * dst, const cryptonote::tx_source_entry * src){
for(auto & cur : src->outputs){
auto out = dst->add_outputs();
out->set_idx(cur.first);
translate_rct_key(out->mutable_key(), &(cur.second));
}
dst->set_real_output(src->real_output);
dst->set_real_out_tx_key(key_to_string(src->real_out_tx_key));
for(auto & cur : src->real_out_additional_tx_keys){
dst->add_real_out_additional_tx_keys(key_to_string(cur));
}
dst->set_real_output_in_tx_index(src->real_output_in_tx_index);
dst->set_amount(src->amount);
dst->set_rct(src->rct);
dst->set_mask(key_to_string(src->mask));
translate_klrki(dst->mutable_multisig_klrki(), &(src->multisig_kLRki));
}
void translate_klrki(MoneroMultisigKLRki * dst, const rct::multisig_kLRki * src){
dst->set_k(key_to_string(src->k));
dst->set_l(key_to_string(src->L));
dst->set_r(key_to_string(src->R));
dst->set_ki(key_to_string(src->ki));
}
void translate_rct_key(MoneroRctKey * dst, const rct::ctkey * src){
dst->set_dest(key_to_string(src->dest));
dst->set_commitment(key_to_string(src->mask));
}
std::string hash_addr(const MoneroAccountPublicAddress * addr, boost::optional<uint64_t> amount, boost::optional<bool> is_subaddr){
return hash_addr(addr->spend_public_key(), addr->view_public_key(), amount, is_subaddr);
}
std::string hash_addr(const std::string & spend_key, const std::string & view_key, boost::optional<uint64_t> amount, boost::optional<bool> is_subaddr){
::crypto::public_key spend{}, view{};
if (spend_key.size() != 32 || view_key.size() != 32){
throw std::invalid_argument("Public keys have invalid sizes");
}
memcpy(spend.data, spend_key.data(), 32);
memcpy(view.data, view_key.data(), 32);
return hash_addr(&spend, &view, amount, is_subaddr);
}
std::string hash_addr(const ::crypto::public_key * spend_key, const ::crypto::public_key * view_key, boost::optional<uint64_t> amount, boost::optional<bool> is_subaddr){
char buff[64+8+1];
size_t offset = 0;
memcpy(buff + offset, spend_key->data, 32); offset += 32;
memcpy(buff + offset, view_key->data, 32); offset += 32;
if (amount){
memcpy(buff + offset, (uint8_t*) &(amount.get()), sizeof(amount.get())); offset += sizeof(amount.get());
}
if (is_subaddr){
buff[offset] = is_subaddr.get();
offset += 1;
}
return std::string(buff, offset);
}
TData::TData() {
in_memory = false;
rsig_type = 0;
cur_input_idx = 0;
cur_output_idx = 0;
cur_batch_idx = 0;
cur_output_in_batch_idx = 0;
}
Signer::Signer(wallet_shim *wallet2, const unsigned_tx_set * unsigned_tx, size_t tx_idx, hw::tx_aux_data * aux_data) {
m_wallet2 = wallet2;
m_unsigned_tx = unsigned_tx;
m_aux_data = aux_data;
m_tx_idx = tx_idx;
m_ct.tx_data = cur_tx();
m_multisig = false;
}
void Signer::extract_payment_id(){
const std::vector<uint8_t>& tx_extra = cur_tx().extra;
m_ct.tsx_data.set_payment_id("");
std::vector<cryptonote::tx_extra_field> tx_extra_fields;
cryptonote::parse_tx_extra(tx_extra, tx_extra_fields); // ok if partially parsed
cryptonote::tx_extra_nonce extra_nonce;
::crypto::hash payment_id{};
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
{
::crypto::hash8 payment_id8{};
if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
{
m_ct.tsx_data.set_payment_id(std::string(payment_id8.data, 8));
}
else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
m_ct.tsx_data.set_payment_id(std::string(payment_id.data, 32));
}
}
}
static unsigned get_rsig_type(bool use_bulletproof, size_t num_outputs){
if (!use_bulletproof){
return rct::RangeProofBorromean;
} else if (num_outputs > BULLETPROOF_MAX_OUTPUTS){
return rct::RangeProofMultiOutputBulletproof;
} else {
return rct::RangeProofPaddedBulletproof;
}
}
static void generate_rsig_batch_sizes(std::vector<uint64_t> &batches, unsigned rsig_type, size_t num_outputs){
size_t amount_batched = 0;
while(amount_batched < num_outputs){
if (rsig_type == rct::RangeProofBorromean || rsig_type == rct::RangeProofBulletproof) {
batches.push_back(1);
amount_batched += 1;
} else if (rsig_type == rct::RangeProofPaddedBulletproof){
if (num_outputs > BULLETPROOF_MAX_OUTPUTS){
throw std::invalid_argument("BP padded can support only BULLETPROOF_MAX_OUTPUTS statements");
}
batches.push_back(num_outputs);
amount_batched += num_outputs;
} else if (rsig_type == rct::RangeProofMultiOutputBulletproof){
size_t batch_size = 1;
while (batch_size * 2 + amount_batched <= num_outputs && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS){
batch_size *= 2;
}
batch_size = std::min(batch_size, num_outputs - amount_batched);
batches.push_back(batch_size);
amount_batched += batch_size;
} else {
throw std::invalid_argument("Unknown rsig type");
}
}
}
void Signer::compute_integrated_indices(TsxData * tsx_data){
if (m_aux_data == nullptr || m_aux_data->tx_recipients.empty()){
return;
}
auto & chg = tsx_data->change_dts();
std::string change_hash = hash_addr(&chg.addr(), chg.amount(), chg.is_subaddress());
std::vector<uint32_t> integrated_indices;
std::set<std::string> integrated_hashes;
for (auto & cur : m_aux_data->tx_recipients){
if (!cur.has_payment_id){
continue;
}
integrated_hashes.emplace(hash_addr(&cur.address.m_spend_public_key, &cur.address.m_view_public_key));
}
ssize_t idx = -1;
for (auto & cur : tsx_data->outputs()){
idx += 1;
std::string c_hash = hash_addr(&cur.addr(), cur.amount(), cur.is_subaddress());
if (c_hash == change_hash || cur.is_subaddress()){
continue;
}
c_hash = hash_addr(&cur.addr());
if (integrated_hashes.find(c_hash) != integrated_hashes.end()){
integrated_indices.push_back((uint32_t)idx);
}
}
if (!integrated_indices.empty()){
assign_to_repeatable(tsx_data->mutable_integrated_indices(), integrated_indices.begin(), integrated_indices.end());
}
}
std::shared_ptr<messages::monero::MoneroTransactionInitRequest> Signer::step_init(){
// extract payment ID from construction data
auto & tsx_data = m_ct.tsx_data;
auto & tx = cur_tx();
m_ct.tx.version = 2;
m_ct.tx.unlock_time = tx.unlock_time;
tsx_data.set_version(1);
tsx_data.set_unlock_time(tx.unlock_time);
tsx_data.set_num_inputs(static_cast<google::protobuf::uint32>(tx.sources.size()));
tsx_data.set_mixin(static_cast<google::protobuf::uint32>(tx.sources[0].outputs.size() - 1));
tsx_data.set_account(tx.subaddr_account);
assign_to_repeatable(tsx_data.mutable_minor_indices(), tx.subaddr_indices.begin(), tx.subaddr_indices.end());
// Rsig decision
auto rsig_data = tsx_data.mutable_rsig_data();
m_ct.rsig_type = get_rsig_type(tx.use_bulletproofs, tx.splitted_dsts.size());
rsig_data->set_rsig_type(m_ct.rsig_type);
generate_rsig_batch_sizes(m_ct.grouping_vct, m_ct.rsig_type, tx.splitted_dsts.size());
assign_to_repeatable(rsig_data->mutable_grouping(), m_ct.grouping_vct.begin(), m_ct.grouping_vct.end());
translate_dst_entry(tsx_data.mutable_change_dts(), &(tx.change_dts));
for(auto & cur : tx.splitted_dsts){
auto dst = tsx_data.mutable_outputs()->Add();
translate_dst_entry(dst, &cur);
}
compute_integrated_indices(&tsx_data);
int64_t fee = 0;
for(auto & cur_in : tx.sources){
fee += cur_in.amount;
}
for(auto & cur_out : tx.splitted_dsts){
fee -= cur_out.amount;
}
if (fee < 0){
throw std::invalid_argument("Fee cannot be negative");
}
tsx_data.set_fee(static_cast<google::protobuf::uint64>(fee));
this->extract_payment_id();
auto init_req = std::make_shared<messages::monero::MoneroTransactionInitRequest>();
init_req->set_version(0);
init_req->mutable_tsx_data()->CopyFrom(tsx_data);
return init_req;
}
void Signer::step_init_ack(std::shared_ptr<const messages::monero::MoneroTransactionInitAck> ack){
m_ct.in_memory = false;
if (ack->has_rsig_data()){
m_ct.rsig_param = std::make_shared<MoneroRsigData>(ack->rsig_data());
}
assign_from_repeatable(&(m_ct.tx_out_entr_hmacs), ack->hmacs().begin(), ack->hmacs().end());
}
std::shared_ptr<messages::monero::MoneroTransactionSetInputRequest> Signer::step_set_input(size_t idx){
CHECK_AND_ASSERT_THROW_MES(idx < cur_tx().sources.size(), "Invalid source index");
m_ct.cur_input_idx = idx;
auto res = std::make_shared<messages::monero::MoneroTransactionSetInputRequest>();
translate_src_entry(res->mutable_src_entr(), &(cur_tx().sources[idx]));
return res;
}
void Signer::step_set_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetInputAck> ack){
auto & vini_str = ack->vini();
cryptonote::txin_v vini;
if (!cn_deserialize(vini_str.data(), vini_str.size(), vini)){
throw exc::ProtocolException("Cannot deserialize vin[i]");
}
m_ct.tx.vin.emplace_back(vini);
m_ct.tx_in_hmacs.push_back(ack->vini_hmac());
m_ct.pseudo_outs.push_back(ack->pseudo_out());
m_ct.pseudo_outs_hmac.push_back(ack->pseudo_out_hmac());
m_ct.alphas.push_back(ack->pseudo_out_alpha());
m_ct.spend_encs.push_back(ack->spend_key());
}
void Signer::sort_ki(){
const size_t input_size = cur_tx().sources.size();
m_ct.source_permutation.clear();
for (size_t n = 0; n < input_size; ++n){
m_ct.source_permutation.push_back(n);
}
CHECK_AND_ASSERT_THROW_MES(m_ct.tx.vin.size() == input_size, "Invalid vector size");
std::sort(m_ct.source_permutation.begin(), m_ct.source_permutation.end(), [&](const size_t i0, const size_t i1) {
const cryptonote::txin_to_key &tk0 = boost::get<cryptonote::txin_to_key>(m_ct.tx.vin[i0]);
const cryptonote::txin_to_key &tk1 = boost::get<cryptonote::txin_to_key>(m_ct.tx.vin[i1]);
return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) > 0;
});
CHECK_AND_ASSERT_THROW_MES(m_ct.tx_in_hmacs.size() == input_size, "Invalid vector size");
CHECK_AND_ASSERT_THROW_MES(m_ct.pseudo_outs.size() == input_size, "Invalid vector size");
CHECK_AND_ASSERT_THROW_MES(m_ct.pseudo_outs_hmac.size() == input_size, "Invalid vector size");
CHECK_AND_ASSERT_THROW_MES(m_ct.alphas.size() == input_size, "Invalid vector size");
CHECK_AND_ASSERT_THROW_MES(m_ct.spend_encs.size() == input_size, "Invalid vector size");
CHECK_AND_ASSERT_THROW_MES(m_ct.tx_data.sources.size() == input_size, "Invalid vector size");
tools::apply_permutation(m_ct.source_permutation, [&](size_t i0, size_t i1){
std::swap(m_ct.tx.vin[i0], m_ct.tx.vin[i1]);
std::swap(m_ct.tx_in_hmacs[i0], m_ct.tx_in_hmacs[i1]);
std::swap(m_ct.pseudo_outs[i0], m_ct.pseudo_outs[i1]);
std::swap(m_ct.pseudo_outs_hmac[i0], m_ct.pseudo_outs_hmac[i1]);
std::swap(m_ct.alphas[i0], m_ct.alphas[i1]);
std::swap(m_ct.spend_encs[i0], m_ct.spend_encs[i1]);
std::swap(m_ct.tx_data.sources[i0], m_ct.tx_data.sources[i1]);
});
}
std::shared_ptr<messages::monero::MoneroTransactionInputsPermutationRequest> Signer::step_permutation(){
sort_ki();
if (in_memory()){
return nullptr;
}
auto res = std::make_shared<messages::monero::MoneroTransactionInputsPermutationRequest>();
assign_to_repeatable(res->mutable_perm(), m_ct.source_permutation.begin(), m_ct.source_permutation.end());
return res;
}
void Signer::step_permutation_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputsPermutationAck> ack){
if (in_memory()){
return;
}
}
std::shared_ptr<messages::monero::MoneroTransactionInputViniRequest> Signer::step_set_vini_input(size_t idx){
if (in_memory()){
return nullptr;
}
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.sources.size(), "Invalid transaction index");
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx.vin.size(), "Invalid transaction index");
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_in_hmacs.size(), "Invalid transaction index");
m_ct.cur_input_idx = idx;
auto tx = m_ct.tx_data;
auto res = std::make_shared<messages::monero::MoneroTransactionInputViniRequest>();
auto & vini = m_ct.tx.vin[idx];
translate_src_entry(res->mutable_src_entr(), &(tx.sources[idx]));
res->set_vini(cryptonote::t_serializable_object_to_blob(vini));
res->set_vini_hmac(m_ct.tx_in_hmacs[idx]);
if (!in_memory()) {
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index");
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index");
res->set_pseudo_out(m_ct.pseudo_outs[idx]);
res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]);
}
return res;
}
void Signer::step_set_vini_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputViniAck> ack){
if (in_memory()){
return;
}
}
std::shared_ptr<messages::monero::MoneroTransactionAllInputsSetRequest> Signer::step_all_inputs_set(){
return std::make_shared<messages::monero::MoneroTransactionAllInputsSetRequest>();
}
void Signer::step_all_inputs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllInputsSetAck> ack){
if (is_offloading()){
// If offloading, expect rsig configuration.
if (!ack->has_rsig_data()){
throw exc::ProtocolException("Rsig offloading requires rsig param");
}
auto & rsig_data = ack->rsig_data();
if (!rsig_data.has_mask()){
throw exc::ProtocolException("Gamma masks not present in offloaded version");
}
auto & mask = rsig_data.mask();
if (mask.size() != 32 * num_outputs()){
throw exc::ProtocolException("Invalid number of gamma masks");
}
m_ct.rsig_gamma.reserve(num_outputs());
for(size_t c=0; c < num_outputs(); ++c){
rct::key cmask{};
memcpy(cmask.bytes, mask.data() + c * 32, 32);
m_ct.rsig_gamma.emplace_back(cmask);
}
}
}
std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> Signer::step_set_output(size_t idx){
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.splitted_dsts.size(), "Invalid transaction index");
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_out_entr_hmacs.size(), "Invalid transaction index");
m_ct.cur_output_idx = idx;
m_ct.cur_output_in_batch_idx += 1; // assumes sequential call to step_set_output()
auto res = std::make_shared<messages::monero::MoneroTransactionSetOutputRequest>();
auto & cur_dst = m_ct.tx_data.splitted_dsts[idx];
translate_dst_entry(res->mutable_dst_entr(), &cur_dst);
res->set_dst_entr_hmac(m_ct.tx_out_entr_hmacs[idx]);
// Range sig offloading to the host
if (!is_offloading()) {
return res;
}
CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index");
if (m_ct.grouping_vct[m_ct.cur_batch_idx] > m_ct.cur_output_in_batch_idx) {
return res;
}
auto rsig_data = res->mutable_rsig_data();
auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx];
if (!is_req_bulletproof()){
if (batch_size > 1){
throw std::invalid_argument("Borromean cannot batch outputs");
}
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.rsig_gamma.size(), "Invalid gamma index");
rct::key C{}, mask = m_ct.rsig_gamma[idx];
auto genRsig = rct::proveRange(C, mask, cur_dst.amount); // TODO: rsig with given mask
auto serRsig = cn_serialize(genRsig);
m_ct.tx_out_rsigs.emplace_back(genRsig);
rsig_data->set_rsig(serRsig);
} else {
std::vector<uint64_t> amounts;
rct::keyV masks;
CHECK_AND_ASSERT_THROW_MES(idx + 1 >= batch_size, "Invalid index for batching");
for(size_t i = 0; i < batch_size; ++i){
const size_t bidx = 1 + idx - batch_size + i;
CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_data.splitted_dsts.size(), "Invalid gamma index");
CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.rsig_gamma.size(), "Invalid gamma index");
amounts.push_back(m_ct.tx_data.splitted_dsts[bidx].amount);
masks.push_back(m_ct.rsig_gamma[bidx]);
}
auto bp = bulletproof_PROVE(amounts, masks);
auto serRsig = cn_serialize(bp);
m_ct.tx_out_rsigs.emplace_back(bp);
rsig_data->set_rsig(serRsig);
}
return res;
}
void Signer::step_set_output_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack){
cryptonote::tx_out tx_out;
rct::rangeSig range_sig{};
rct::Bulletproof bproof{};
rct::ctkey out_pk{};
rct::ecdhTuple ecdh{};
bool has_rsig = false;
std::string rsig_buff;
if (ack->has_rsig_data()){
auto & rsig_data = ack->rsig_data();
if (rsig_data.has_rsig() && !rsig_data.rsig().empty()){
has_rsig = true;
rsig_buff = rsig_data.rsig();
} else if (rsig_data.rsig_parts_size() > 0){
has_rsig = true;
for (const auto &it : rsig_data.rsig_parts()) {
rsig_buff += it;
}
}
}
if (!cn_deserialize(ack->tx_out(), tx_out)){
throw exc::ProtocolException("Cannot deserialize vout[i]");
}
if (!cn_deserialize(ack->out_pk(), out_pk)){
throw exc::ProtocolException("Cannot deserialize out_pk");
}
if (!cn_deserialize(ack->ecdh_info(), ecdh)){
throw exc::ProtocolException("Cannot deserialize ecdhtuple");
}
if (has_rsig && !is_req_bulletproof() && !cn_deserialize(rsig_buff, range_sig)){
throw exc::ProtocolException("Cannot deserialize rangesig");
}
if (has_rsig && is_req_bulletproof() && !cn_deserialize(rsig_buff, bproof)){
throw exc::ProtocolException("Cannot deserialize bulletproof rangesig");
}
m_ct.tx.vout.emplace_back(tx_out);
m_ct.tx_out_hmacs.push_back(ack->vouti_hmac());
m_ct.tx_out_pk.emplace_back(out_pk);
m_ct.tx_out_ecdh.emplace_back(ecdh);
if (!has_rsig){
return;
}
if (is_req_bulletproof()){
CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index");
auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx];
for (size_t i = 0; i < batch_size; ++i){
const size_t bidx = 1 + m_ct.cur_output_idx - batch_size + i;
CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_out_pk.size(), "Invalid out index");
rct::key commitment = m_ct.tx_out_pk[bidx].mask;
commitment = rct::scalarmultKey(commitment, rct::INV_EIGHT);
bproof.V.push_back(commitment);
}
m_ct.tx_out_rsigs.emplace_back(bproof);
if (!rct::bulletproof_VERIFY(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs.back()))) {
throw exc::ProtocolException("Returned range signature is invalid");
}
} else {
m_ct.tx_out_rsigs.emplace_back(range_sig);
if (!rct::verRange(out_pk.mask, boost::get<rct::rangeSig>(m_ct.tx_out_rsigs.back()))) {
throw exc::ProtocolException("Returned range signature is invalid");
}
}
m_ct.cur_batch_idx += 1;
m_ct.cur_output_in_batch_idx = 0;
}
std::shared_ptr<messages::monero::MoneroTransactionAllOutSetRequest> Signer::step_all_outs_set(){
return std::make_shared<messages::monero::MoneroTransactionAllOutSetRequest>();
}
void Signer::step_all_outs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllOutSetAck> ack, hw::device &hwdev){
m_ct.rv = std::make_shared<rct::rctSig>();
m_ct.rv->txnFee = ack->rv().txn_fee();
m_ct.rv->type = static_cast<uint8_t>(ack->rv().rv_type());
string_to_key(m_ct.rv->message, ack->rv().message());
// Extra copy
m_ct.tx.extra.clear();
auto extra = ack->extra();
auto extra_data = extra.data();
m_ct.tx.extra.reserve(extra.size());
for(size_t i = 0; i < extra.size(); ++i){
m_ct.tx.extra.push_back(static_cast<uint8_t>(extra_data[i]));
}
::crypto::hash tx_prefix_hash{};
cryptonote::get_transaction_prefix_hash(m_ct.tx, tx_prefix_hash);
m_ct.tx_prefix_hash = key_to_string(tx_prefix_hash);
if (crypto_verify_32(reinterpret_cast<const unsigned char *>(tx_prefix_hash.data),
reinterpret_cast<const unsigned char *>(ack->tx_prefix_hash().data()))){
throw exc::proto::SecurityException("Transaction prefix has does not match to the computed value");
}
// RctSig
auto num_sources = m_ct.tx_data.sources.size();
if (is_simple() || is_req_bulletproof()){
auto dst = &m_ct.rv->pseudoOuts;
if (is_bulletproof()){
dst = &m_ct.rv->p.pseudoOuts;
}
dst->clear();
for (const auto &pseudo_out : m_ct.pseudo_outs) {
dst->emplace_back();
string_to_key(dst->back(), pseudo_out);
}
m_ct.rv->mixRing.resize(num_sources);
} else {
m_ct.rv->mixRing.resize(m_ct.tsx_data.mixin());
m_ct.rv->mixRing[0].resize(num_sources);
}
CHECK_AND_ASSERT_THROW_MES(m_ct.tx_out_pk.size() == m_ct.tx_out_ecdh.size(), "Invalid vector sizes");
for(size_t i = 0; i < m_ct.tx_out_ecdh.size(); ++i){
m_ct.rv->outPk.push_back(m_ct.tx_out_pk[i]);
m_ct.rv->ecdhInfo.push_back(m_ct.tx_out_ecdh[i]);
}
for(size_t i = 0; i < m_ct.tx_out_rsigs.size(); ++i){
if (is_bulletproof()){
m_ct.rv->p.bulletproofs.push_back(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs[i]));
} else {
m_ct.rv->p.rangeSigs.push_back(boost::get<rct::rangeSig>(m_ct.tx_out_rsigs[i]));
}
}
rct::key hash_computed = rct::get_pre_mlsag_hash(*(m_ct.rv), hwdev);
auto & hash = ack->full_message_hash();
if (hash.size() != 32){
throw exc::ProtocolException("Returned mlsag hash has invalid size");
}
if (crypto_verify_32(reinterpret_cast<const unsigned char *>(hash_computed.bytes),
reinterpret_cast<const unsigned char *>(hash.data()))){
throw exc::proto::SecurityException("Computed MLSAG does not match");
}
}
std::shared_ptr<messages::monero::MoneroTransactionSignInputRequest> Signer::step_sign_input(size_t idx){
m_ct.cur_input_idx = idx;
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.sources.size(), "Invalid transaction index");
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx.vin.size(), "Invalid transaction index");
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_in_hmacs.size(), "Invalid transaction index");
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.alphas.size(), "Invalid transaction index");
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.spend_encs.size(), "Invalid transaction index");
auto res = std::make_shared<messages::monero::MoneroTransactionSignInputRequest>();
translate_src_entry(res->mutable_src_entr(), &(m_ct.tx_data.sources[idx]));
res->set_vini(cryptonote::t_serializable_object_to_blob(m_ct.tx.vin[idx]));
res->set_vini_hmac(m_ct.tx_in_hmacs[idx]);
res->set_pseudo_out_alpha(m_ct.alphas[idx]);
res->set_spend_key(m_ct.spend_encs[idx]);
if (!in_memory()){
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index");
CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index");
res->set_pseudo_out(m_ct.pseudo_outs[idx]);
res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]);
}
return res;
}
void Signer::step_sign_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionSignInputAck> ack){
rct::mgSig mg;
if (!cn_deserialize(ack->signature(), mg)){
throw exc::ProtocolException("Cannot deserialize mg[i]");
}
m_ct.rv->p.MGs.push_back(mg);
}
std::shared_ptr<messages::monero::MoneroTransactionFinalRequest> Signer::step_final(){
m_ct.tx.rct_signatures = *(m_ct.rv);
return std::make_shared<messages::monero::MoneroTransactionFinalRequest>();
}
void Signer::step_final_ack(std::shared_ptr<const messages::monero::MoneroTransactionFinalAck> ack){
if (m_multisig){
auto & cout_key = ack->cout_key();
for(auto & cur : m_ct.couts){
if (cur.size() != 12 + 32){
throw std::invalid_argument("Encrypted cout has invalid length");
}
char buff[32];
auto data = cur.data();
crypto::chacha::decrypt(data + 12, 32, reinterpret_cast<const uint8_t *>(cout_key.data()), reinterpret_cast<const uint8_t *>(data), buff);
m_ct.couts_dec.emplace_back(buff, 32);
}
}
m_ct.enc_salt1 = ack->salt();
m_ct.enc_salt2 = ack->rand_mult();
m_ct.enc_keys = ack->tx_enc_keys();
}
std::string Signer::store_tx_aux_info(){
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
rapidjson::Document json;
json.SetObject();
rapidjson::Value valueS(rapidjson::kStringType);
rapidjson::Value valueI(rapidjson::kNumberType);
valueI.SetInt(1);
json.AddMember("version", valueI, json.GetAllocator());
valueS.SetString(m_ct.enc_salt1.c_str(), m_ct.enc_salt1.size());
json.AddMember("salt1", valueS, json.GetAllocator());
valueS.SetString(m_ct.enc_salt2.c_str(), m_ct.enc_salt2.size());
json.AddMember("salt2", valueS, json.GetAllocator());
valueS.SetString(m_ct.enc_keys.c_str(), m_ct.enc_keys.size());
json.AddMember("enc_keys", valueS, json.GetAllocator());
json.Accept(writer);
return sb.GetString();
}
}
}
}
}

View File

@ -0,0 +1,300 @@
// 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_PROTOCOL_H
#define MONERO_PROTOCOL_H
#include "trezor_defs.hpp"
#include "device/device_cold.hpp"
#include "messages_map.hpp"
#include "transport.hpp"
#include "wallet/wallet2.h"
namespace hw{
namespace trezor{
namespace protocol{
std::string key_to_string(const ::crypto::ec_point & key);
std::string key_to_string(const ::crypto::ec_scalar & key);
std::string key_to_string(const ::crypto::hash & key);
std::string key_to_string(const ::rct::key & key);
void string_to_key(::crypto::ec_scalar & key, const std::string & str);
void string_to_key(::crypto::ec_point & key, const std::string & str);
void string_to_key(::rct::key & key, const std::string & str);
template<class sub_t, class InputIterator>
void assign_to_repeatable(::google::protobuf::RepeatedField<sub_t> * dst, const InputIterator begin, const InputIterator end){
for (InputIterator it = begin; it != end; it++) {
auto s = dst->Add();
*s = *it;
}
}
template<class sub_t, class InputIterator>
void assign_from_repeatable(std::vector<sub_t> * dst, const InputIterator begin, const InputIterator end){
for (InputIterator it = begin; it != end; it++) {
dst->push_back(*it);
}
};
template<typename T>
bool cn_deserialize(const void * buff, size_t len, T & dst){
std::stringstream ss;
ss.write(static_cast<const char *>(buff), len); //ss << tx_blob;
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, dst);
return r;
}
template<typename T>
bool cn_deserialize(const std::string & str, T & dst){
return cn_deserialize(str.data(), str.size(), dst);
}
template<typename T>
std::string cn_serialize(T & obj){
std::ostringstream oss;
binary_archive<true> oar(oss);
bool success = ::serialization::serialize(oar, obj);
if (!success){
throw exc::EncodingException("Could not CN serialize given object");
}
return oss.str();
}
// Crypto / encryption
namespace crypto {
namespace chacha {
/**
* Chacha20Poly1305 decryption with tag verification. RFC 7539.
*/
void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext);
}
}
// Cold Key image sync
namespace ki {
using MoneroTransferDetails = messages::monero::MoneroKeyImageSyncStepRequest_MoneroTransferDetails;
using MoneroSubAddressIndicesList = messages::monero::MoneroKeyImageExportInitRequest_MoneroSubAddressIndicesList;
using MoneroExportedKeyImage = messages::monero::MoneroKeyImageSyncStepAck_MoneroExportedKeyImage;
using exported_key_image = hw::device_cold::exported_key_image;
/**
* Converts transfer details to the MoneroTransferDetails required for KI sync
*/
bool key_image_data(wallet_shim * wallet,
const std::vector<tools::wallet2::transfer_details> & transfers,
std::vector<MoneroTransferDetails> & res);
/**
* Computes a hash over MoneroTransferDetails. Commitment used in the KI sync.
*/
std::string compute_hash(const MoneroTransferDetails & rr);
/**
* Generates KI sync request with commitments computed.
*/
void generate_commitment(std::vector<MoneroTransferDetails> & mtds,
const std::vector<tools::wallet2::transfer_details> & transfers,
std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req);
}
// Cold transaction signing
namespace tx {
using TsxData = messages::monero::MoneroTransactionInitRequest_MoneroTransactionData;
using MoneroTransactionDestinationEntry = messages::monero::MoneroTransactionDestinationEntry;
using MoneroAccountPublicAddress = messages::monero::MoneroTransactionDestinationEntry_MoneroAccountPublicAddress;
using MoneroTransactionSourceEntry = messages::monero::MoneroTransactionSourceEntry;
using MoneroMultisigKLRki = messages::monero::MoneroTransactionSourceEntry_MoneroMultisigKLRki;
using MoneroOutputEntry = messages::monero::MoneroTransactionSourceEntry_MoneroOutputEntry;
using MoneroRctKey = messages::monero::MoneroTransactionSourceEntry_MoneroOutputEntry_MoneroRctKeyPublic;
using MoneroRsigData = messages::monero::MoneroTransactionRsigData;
using tx_construction_data = tools::wallet2::tx_construction_data;
using unsigned_tx_set = tools::wallet2::unsigned_tx_set;
void translate_address(MoneroAccountPublicAddress * dst, const cryptonote::account_public_address * src);
void translate_dst_entry(MoneroTransactionDestinationEntry * dst, const cryptonote::tx_destination_entry * src);
void translate_src_entry(MoneroTransactionSourceEntry * dst, const cryptonote::tx_source_entry * src);
void translate_klrki(MoneroMultisigKLRki * dst, const rct::multisig_kLRki * src);
void translate_rct_key(MoneroRctKey * dst, const rct::ctkey * src);
std::string hash_addr(const MoneroAccountPublicAddress * addr, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none);
std::string hash_addr(const std::string & spend_key, const std::string & view_key, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none);
std::string hash_addr(const ::crypto::public_key * spend_key, const ::crypto::public_key * view_key, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none);
typedef boost::variant<rct::rangeSig, rct::Bulletproof> rsig_v;
/**
* Transaction signer state holder.
*/
class TData {
public:
TsxData tsx_data;
tx_construction_data tx_data;
cryptonote::transaction tx;
bool in_memory;
unsigned rsig_type;
std::vector<uint64_t> grouping_vct;
std::shared_ptr<MoneroRsigData> rsig_param;
size_t cur_input_idx;
size_t cur_output_idx;
size_t cur_batch_idx;
size_t cur_output_in_batch_idx;
std::vector<std::string> tx_in_hmacs;
std::vector<std::string> tx_out_entr_hmacs;
std::vector<std::string> tx_out_hmacs;
std::vector<rsig_v> tx_out_rsigs;
std::vector<rct::ctkey> tx_out_pk;
std::vector<rct::ecdhTuple> tx_out_ecdh;
std::vector<size_t> source_permutation;
std::vector<std::string> alphas;
std::vector<std::string> spend_encs;
std::vector<std::string> pseudo_outs;
std::vector<std::string> pseudo_outs_hmac;
std::vector<std::string> couts;
std::vector<std::string> couts_dec;
std::vector<rct::key> rsig_gamma;
std::string tx_prefix_hash;
std::string enc_salt1;
std::string enc_salt2;
std::string enc_keys;
std::shared_ptr<rct::rctSig> rv;
TData();
};
class Signer {
private:
TData m_ct;
wallet_shim * m_wallet2;
size_t m_tx_idx;
const unsigned_tx_set * m_unsigned_tx;
hw::tx_aux_data * m_aux_data;
bool m_multisig;
const tx_construction_data & cur_tx(){
CHECK_AND_ASSERT_THROW_MES(m_tx_idx < m_unsigned_tx->txes.size(), "Invalid transaction index");
return m_unsigned_tx->txes[m_tx_idx];
}
void extract_payment_id();
void compute_integrated_indices(TsxData * tsx_data);
public:
Signer(wallet_shim * wallet2, const unsigned_tx_set * unsigned_tx, size_t tx_idx = 0, hw::tx_aux_data * aux_data = nullptr);
std::shared_ptr<messages::monero::MoneroTransactionInitRequest> step_init();
void step_init_ack(std::shared_ptr<const messages::monero::MoneroTransactionInitAck> ack);
std::shared_ptr<messages::monero::MoneroTransactionSetInputRequest> step_set_input(size_t idx);
void step_set_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetInputAck> ack);
void sort_ki();
std::shared_ptr<messages::monero::MoneroTransactionInputsPermutationRequest> step_permutation();
void step_permutation_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputsPermutationAck> ack);
std::shared_ptr<messages::monero::MoneroTransactionInputViniRequest> step_set_vini_input(size_t idx);
void step_set_vini_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputViniAck> ack);
std::shared_ptr<messages::monero::MoneroTransactionAllInputsSetRequest> step_all_inputs_set();
void step_all_inputs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllInputsSetAck> ack);
std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> step_set_output(size_t idx);
void step_set_output_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack);
std::shared_ptr<messages::monero::MoneroTransactionAllOutSetRequest> step_all_outs_set();
void step_all_outs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllOutSetAck> ack, hw::device &hwdev);
std::shared_ptr<messages::monero::MoneroTransactionSignInputRequest> step_sign_input(size_t idx);
void step_sign_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionSignInputAck> ack);
std::shared_ptr<messages::monero::MoneroTransactionFinalRequest> step_final();
void step_final_ack(std::shared_ptr<const messages::monero::MoneroTransactionFinalAck> ack);
std::string store_tx_aux_info();
bool in_memory() const {
return m_ct.in_memory;
}
bool is_simple() const {
if (!m_ct.rv){
throw std::invalid_argument("RV not initialized");
}
auto tp = m_ct.rv->type;
return tp == rct::RCTTypeSimple;
}
bool is_req_bulletproof() const {
return m_ct.tx_data.use_bulletproofs;
}
bool is_bulletproof() const {
if (!m_ct.rv){
throw std::invalid_argument("RV not initialized");
}
auto tp = m_ct.rv->type;
return tp == rct::RCTTypeBulletproof;
}
bool is_offloading() const {
return m_ct.rsig_param && m_ct.rsig_param->offload_type() != 0;
}
size_t num_outputs() const {
return m_ct.tx_data.splitted_dsts.size();
}
size_t num_inputs() const {
return m_ct.tx_data.sources.size();
}
const TData & tdata() const {
return m_ct;
}
};
}
}
}
}
#endif //MONERO_PROTOCOL_H

View File

@ -0,0 +1,36 @@
# Trezor
## Messages rebuild
Install `protoc` for your distribution.
- `protobuf-compiler`
- `libprotobuf-dev`
- `libprotoc-dev`
- `python-protobuf`
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.
```bash
python -m venv /
```
Make sure your python has `protobuf` package installed
```bash
pip install protobuf
```
Regenerate messages:
```
./venv/bin/python3 src/device_trezor/trezor/tools/build_protob.py
```
The messages regeneration is done also automatically via cmake.
[pyenv]: https://github.com/pyenv/pyenv

View File

@ -0,0 +1,38 @@
#!/usr/bin/env python
import os
import subprocess
import sys
CWD = os.path.dirname(os.path.realpath(__file__))
ROOT_DIR = os.path.abspath(os.path.join(CWD, "..", "..", "..", ".."))
TREZOR_COMMON = os.path.join(ROOT_DIR, "external", "trezor-common")
TREZOR_MESSAGES = os.path.join(CWD, "..", "messages")
# check for existence of the submodule directory
common_defs = os.path.join(TREZOR_COMMON, "defs")
if not os.path.exists(common_defs):
raise ValueError(
"trezor-common submodule seems to be missing.\n"
+ 'Use "git submodule update --init --recursive" to retrieve it.'
)
# regenerate messages
try:
selected = [
"messages.proto",
"messages-common.proto",
"messages-management.proto",
"messages-monero.proto",
]
proto_srcs = [os.path.join(TREZOR_COMMON, "protob", x) for x in selected]
exec_args = [
sys.executable,
os.path.join(CWD, "pb2cpp.py"),
"-o",
TREZOR_MESSAGES,
] + proto_srcs
subprocess.check_call(exec_args)
except Exception as e:
raise

View File

@ -0,0 +1,186 @@
#!/usr/bin/env python3
# Converts Google's protobuf python definitions of TREZOR wire messages
# to plain-python objects as used in TREZOR Core and python-trezor
import argparse
import logging
import os
import re
import shutil
import subprocess
import sys
import glob
import tempfile
import hashlib
AUTO_HEADER = "# Automatically generated by pb2cpp\n"
# Fixing GCC7 compilation error
UNDEF_STATEMENT = """
#ifdef minor
#undef minor
#endif
"""
def which(pgm):
path = os.getenv('PATH')
for p in path.split(os.path.pathsep):
p = os.path.join(p, pgm)
if os.path.exists(p) and os.access(p, os.X_OK):
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:
fdata = fh.read()
re_syntax = re.compile(r"^syntax\s*=")
re_package = re.compile(r"^package\s+([^;]+?)\s*;\s*$")
lines = fdata.split("\n")
line_syntax = None
line_package = None
for idx, line in enumerate(lines):
if line_syntax is None and re_syntax.match(line):
line_syntax = idx
if line_package is None and re_package.match(line):
line_package = idx
if package is None:
if line_package is None:
return
else:
lines.pop(line_package)
else:
new_package = "package %s;" % package
if line_package is None:
lines.insert(line_syntax + 1 if line_syntax is not None else 0, new_package)
else:
lines[line_package] = new_package
new_fdat = "\n".join(lines)
with open(fpath, "w+") as fh:
fh.write(new_fdat)
return new_fdat
def protoc(files, out_dir, additional_includes=(), package=None, force=False):
"""Compile code with protoc and return the data."""
include_dirs = set()
include_dirs.add(PROTOC_INCLUDE)
include_dirs.update(additional_includes)
with tempfile.TemporaryDirectory() as tmpdir_protob, tempfile.TemporaryDirectory() as tmpdir_out:
include_dirs.add(tmpdir_protob)
new_files = []
for file in files:
bname = os.path.basename(file)
tmp_file = os.path.join(tmpdir_protob, bname)
shutil.copy(file, tmp_file)
if package is not None:
namespace_file(tmp_file, package)
new_files.append(tmp_file)
protoc_includes = ["-I" + dir for dir in include_dirs if dir]
exec_args = (
[
PROTOC,
"--cpp_out",
tmpdir_out,
]
+ protoc_includes
+ new_files
)
subprocess.check_call(exec_args)
# Fixing gcc compilation and clashes with "minor" field name
add_undef(tmpdir_out)
# Scan output dir, check file differences
update_message_files(tmpdir_out, out_dir, force)
def update_message_files(tmpdir_out, out_dir, force=False):
files = glob.glob(os.path.join(tmpdir_out, '*.pb.*'))
for fname in files:
bname = os.path.basename(fname)
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_dest = open(dest_file, 'rb').read()
data_dest_hash = hashlib.sha3_256(data_dest).digest()
if data_hash == data_dest_hash:
continue
shutil.copy(fname, dest_file)
def add_undef(out_dir):
files = glob.glob(os.path.join(out_dir, '*.pb.*'))
for fname in files:
with open(fname) as fh:
lines = fh.readlines()
idx_insertion = None
for idx in range(len(lines)):
if '@@protoc_insertion_point(includes)' in lines[idx]:
idx_insertion = idx
break
if idx_insertion is None:
pass
lines.insert(idx_insertion + 1, UNDEF_STATEMENT)
with open(fname, 'w') as fh:
fh.write("".join(lines))
def strip_leader(s, prefix):
"""Remove given prefix from underscored name."""
leader = prefix + "_"
if s.startswith(leader):
return s[len(leader) :]
else:
return s
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
parser = argparse.ArgumentParser()
# fmt: off
parser.add_argument("proto", nargs="+", help="Protobuf definition files")
parser.add_argument("-o", "--out-dir", help="Directory for generated source code")
parser.add_argument("-n", "--namespace", default=None, help="Message namespace")
parser.add_argument("-I", "--protoc-include", action="append", help="protoc include path")
parser.add_argument("-P", "--protobuf-module", default="protobuf", help="Name of protobuf module")
parser.add_argument("-f", "--force", default=False, help="Overwrite existing files")
# fmt: on
args = parser.parse_args()
protoc_includes = args.protoc_include or (os.environ.get("PROTOC_INCLUDE"),)
protoc(
args.proto, args.out_dir, protoc_includes, package=args.namespace, force=args.force
)

View File

@ -0,0 +1,651 @@
// 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 <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 "transport.hpp"
#include "messages/messages-common.pb.h"
using namespace std;
using json = rapidjson::Document;
namespace hw{
namespace trezor{
bool t_serialize(const std::string & in, std::string & out){
out = in;
return true;
}
bool t_serialize(const json_val & in, std::string & out){
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
in.Accept(writer);
out = sb.GetString();
return true;
}
std::string t_serialize(const json_val & in){
std::string ret;
t_serialize(in, ret);
return ret;
}
bool t_deserialize(const std::string & in, std::string & out){
out = in;
return true;
}
bool t_deserialize(const std::string & in, json & out){
if (out.Parse(in.c_str()).HasParseError()) {
throw exc::CommunicationException("JSON parse error");
}
return true;
}
static std::string json_get_string(const rapidjson::Value & in){
return std::string(in.GetString());
}
//
// Helpers
//
#define PROTO_HEADER_SIZE 6
static size_t message_size(const google::protobuf::Message &req){
return static_cast<size_t>(req.ByteSize());
}
static size_t serialize_message_buffer_size(size_t msg_size) {
return PROTO_HEADER_SIZE + msg_size; // tag 2B + len 4B
}
static void serialize_message_header(void * buff, uint16_t tag, uint32_t len){
uint16_t wire_tag = boost::endian::native_to_big(static_cast<uint16_t>(tag));
uint32_t wire_len = boost::endian::native_to_big(static_cast<uint32_t>(len));
memcpy(buff, (void *) &wire_tag, 2);
memcpy((uint8_t*)buff + 2, (void *) &wire_len, 4);
}
static void deserialize_message_header(const void * buff, uint16_t & tag, uint32_t & len){
uint16_t wire_tag;
uint32_t wire_len;
memcpy(&wire_tag, buff, 2);
memcpy(&wire_len, (uint8_t*)buff + 2, 4);
tag = boost::endian::big_to_native(wire_tag);
len = boost::endian::big_to_native(wire_len);
}
static void serialize_message(const google::protobuf::Message &req, size_t msg_size, uint8_t * buff, size_t buff_size) {
auto msg_wire_num = MessageMapper::get_message_wire_number(req);
const auto req_buffer_size = serialize_message_buffer_size(msg_size);
if (req_buffer_size > buff_size){
throw std::invalid_argument("Buffer too small");
}
serialize_message_header(buff, msg_wire_num, msg_size);
if (!req.SerializeToArray(buff + 6, msg_size)){
throw exc::EncodingException("Message serialization error");
}
}
//
// Communication protocol
//
#define REPLEN 64
void ProtocolV1::write(Transport & transport, const google::protobuf::Message & req){
const auto msg_size = message_size(req);
const auto buff_size = serialize_message_buffer_size(msg_size) + 2;
std::unique_ptr<uint8_t[]> req_buff(new uint8_t[buff_size]);
uint8_t * req_buff_raw = req_buff.get();
req_buff_raw[0] = '#';
req_buff_raw[1] = '#';
serialize_message(req, msg_size, req_buff_raw + 2, buff_size - 2);
size_t offset = 0;
uint8_t chunk_buff[REPLEN];
// Chunk by chunk upload
while(offset < buff_size){
auto to_copy = std::min((size_t)(buff_size - offset), (size_t)(REPLEN - 1));
chunk_buff[0] = '?';
memcpy(chunk_buff + 1, req_buff_raw + offset, to_copy);
// Pad with zeros
if (to_copy < REPLEN - 1){
memset(chunk_buff + 1 + to_copy, 0, REPLEN - 1 - to_copy);
}
transport.write_chunk(chunk_buff, REPLEN);
offset += REPLEN - 1;
}
}
void ProtocolV1::read(Transport & transport, std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type){
char chunk[REPLEN];
// Initial chunk read
size_t nread = transport.read_chunk(chunk, REPLEN);
if (nread != REPLEN){
throw exc::CommunicationException("Read chunk has invalid size");
}
if (strncmp(chunk, "?##", 3) != 0){
throw exc::CommunicationException("Malformed chunk");
}
uint16_t tag;
uint32_t len;
nread -= 3 + 6;
deserialize_message_header(chunk + 3, tag, len);
std::string data_acc(chunk + 3 + 6, nread);
data_acc.reserve(len);
while(nread < len){
const size_t cur = transport.read_chunk(chunk, REPLEN);
if (chunk[0] != '?'){
throw exc::CommunicationException("Chunk malformed");
}
data_acc.append(chunk + 1, cur - 1);
nread += cur - 1;
}
if (msg_type){
*msg_type = static_cast<messages::MessageType>(tag);
}
if (nread < len){
throw exc::CommunicationException("Response incomplete");
}
std::shared_ptr<google::protobuf::Message> msg_wrap(MessageMapper::get_message(tag));
if (!msg_wrap->ParseFromArray(data_acc.c_str(), len)){
throw exc::CommunicationException("Message could not be parsed");
}
msg = msg_wrap;
}
//
// Bridge transport
//
const char * BridgeTransport::PATH_PREFIX = "bridge:";
std::string BridgeTransport::get_path() const {
if (!m_device_path){
return "";
}
std::string path(PATH_PREFIX);
return path + m_device_path.get();
}
void BridgeTransport::enumerate(t_transport_vect & res) {
json bridge_res;
std::string req;
bool req_status = invoke_bridge_http("/enumerate", req, bridge_res, m_http_client);
if (!req_status){
throw exc::CommunicationException("Bridge enumeration failed");
}
for(rapidjson::Value::ConstValueIterator itr = bridge_res.Begin(); itr != bridge_res.End(); ++itr){
auto element = itr->GetObject();
auto t = std::make_shared<BridgeTransport>(boost::make_optional(json_get_string(element["path"])));
t->m_device_info.emplace();
t->m_device_info->CopyFrom(*itr, t->m_device_info->GetAllocator());
res.push_back(t);
}
}
void BridgeTransport::open() {
if (!m_device_path){
throw exc::CommunicationException("Coud not open, empty device path");
}
std::string uri = "/acquire/" + m_device_path.get() + "/null";
std::string req;
json bridge_res;
bool req_status = invoke_bridge_http(uri, req, bridge_res, m_http_client);
if (!req_status){
throw exc::CommunicationException("Failed to acquire device");
}
m_session = boost::make_optional(json_get_string(bridge_res["session"]));
}
void BridgeTransport::close() {
if (!m_device_path || !m_session){
throw exc::CommunicationException("Device not open");
}
std::string uri = "/release/" + m_session.get();
std::string req;
json bridge_res;
bool req_status = invoke_bridge_http(uri, req, bridge_res, m_http_client);
if (!req_status){
throw exc::CommunicationException("Failed to release device");
}
m_session = boost::none;
}
void BridgeTransport::write(const google::protobuf::Message &req) {
m_response = boost::none;
const auto msg_size = message_size(req);
const auto buff_size = serialize_message_buffer_size(msg_size);
std::unique_ptr<uint8_t[]> req_buff(new uint8_t[buff_size]);
uint8_t * req_buff_raw = req_buff.get();
serialize_message(req, msg_size, req_buff_raw, buff_size);
std::string uri = "/call/" + m_session.get();
std::string req_hex = epee::to_hex::string(epee::span<const std::uint8_t>(req_buff_raw, buff_size));
std::string res_hex;
bool req_status = invoke_bridge_http(uri, req_hex, res_hex, m_http_client);
if (!req_status){
throw exc::CommunicationException("Call method failed");
}
m_response = res_hex;
}
void BridgeTransport::read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type) {
if (!m_response){
throw exc::CommunicationException("Could not read, no response stored");
}
std::string bin_data;
if (!epee::string_tools::parse_hexstr_to_binbuff(m_response.get(), bin_data)){
throw exc::CommunicationException("Response is not well hexcoded");
}
uint16_t msg_tag;
uint32_t msg_len;
deserialize_message_header(bin_data.c_str(), msg_tag, msg_len);
if (bin_data.size() != msg_len + 6){
throw exc::CommunicationException("Response is not well hexcoded");
}
if (msg_type){
*msg_type = static_cast<messages::MessageType>(msg_tag);
}
std::shared_ptr<google::protobuf::Message> msg_wrap(MessageMapper::get_message(msg_tag));
if (!msg_wrap->ParseFromArray(bin_data.c_str() + 6, msg_len)){
throw exc::EncodingException("Response is not well hexcoded");
}
msg = msg_wrap;
}
const boost::optional<json> & BridgeTransport::device_info() const {
return m_device_info;
}
std::ostream& BridgeTransport::dump(std::ostream& o) const {
return o << "BridgeTransport<path=" << (m_device_path ? get_path() : "None")
<< ", info=" << (m_device_info ? t_serialize(m_device_info.get()) : "None")
<< ", session=" << (m_session ? m_session.get() : "None")
<< ">";
}
//
// UdpTransport
//
const char * UdpTransport::PATH_PREFIX = "udp:";
const char * UdpTransport::DEFAULT_HOST = "127.0.0.1";
const int UdpTransport::DEFAULT_PORT = 21324;
UdpTransport::UdpTransport(boost::optional<std::string> device_path,
boost::optional<std::shared_ptr<Protocol>> proto) :
m_io_service(), m_deadline(m_io_service)
{
m_device_port = DEFAULT_PORT;
if (device_path) {
const std::string device_str = device_path.get();
auto delim = device_str.find(':');
if (delim == std::string::npos) {
m_device_host = device_str;
} else {
m_device_host = device_str.substr(0, delim);
m_device_port = std::stoi(device_str.substr(delim + 1));
}
} else {
m_device_host = DEFAULT_HOST;
}
if (m_device_port <= 1024 || m_device_port > 65535){
throw std::invalid_argument("Port number invalid");
}
if (m_device_host != "localhost" && m_device_host != DEFAULT_HOST){
throw std::invalid_argument("Local endpoint allowed only");
}
m_proto = proto ? proto.get() : std::make_shared<ProtocolV1>();
}
std::string UdpTransport::get_path() const {
std::string path(PATH_PREFIX);
return path + m_device_host + ":" + std::to_string(m_device_port);
}
void UdpTransport::require_socket(){
if (!m_socket){
throw exc::NotConnectedException("Socket not connected");
}
}
bool UdpTransport::ping(){
return ping_int();
}
bool UdpTransport::ping_int(boost::posix_time::time_duration timeout){
require_socket();
try {
std::string req = "PINGPING";
char res[8];
m_socket->send_to(boost::asio::buffer(req.c_str(), req.size()), m_endpoint);
receive(res, 8, nullptr, false, timeout);
return memcmp(res, "PONGPONG", 8) == 0;
} catch(...){
return false;
}
}
void UdpTransport::enumerate(t_transport_vect & res) {
std::shared_ptr<UdpTransport> t = std::make_shared<UdpTransport>();
bool t_works = false;
try{
t->open();
t_works = t->ping();
} catch(...) {
}
t->close();
if (t_works){
res.push_back(t);
}
}
void UdpTransport::open() {
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);
m_socket.reset(new udp::socket(m_io_service));
m_socket->open(udp::v4());
m_deadline.expires_at(boost::posix_time::pos_infin);
check_deadline();
m_proto->session_begin(*this);
}
void UdpTransport::close() {
if (!m_socket){
throw exc::CommunicationException("Socket is already closed");
}
m_proto->session_end(*this);
m_socket->close();
m_socket = nullptr;
}
void UdpTransport::write_chunk(const void * buff, size_t size){
require_socket();
if (size != 64){
throw exc::CommunicationException("Invalid chunk size");
}
auto written = m_socket->send_to(boost::asio::buffer(buff, size), m_endpoint);
if (size != written){
throw exc::CommunicationException("Could not send the whole chunk");
}
}
size_t UdpTransport::read_chunk(void * buff, size_t size){
require_socket();
if (size < 64){
throw std::invalid_argument("Buffer too small");
}
ssize_t len;
while(true) {
try {
boost::system::error_code ec;
len = receive(buff, size, &ec, true);
if (ec == boost::asio::error::operation_aborted) {
continue;
} else if (ec) {
throw exc::CommunicationException(std::string("Comm error: ") + ec.message());
}
if (len != 64) {
throw exc::CommunicationException("Invalid chunk size");
}
break;
} catch(exc::CommunicationException const& e){
throw;
} catch(std::exception const& e){
MWARNING("Error reading chunk, reason: " << e.what());
throw exc::CommunicationException(std::string("Chunk read error: ") + std::string(e.what()));
}
}
return static_cast<size_t>(len);
}
ssize_t UdpTransport::receive(void * buff, size_t size, boost::system::error_code * error_code, bool no_throw, boost::posix_time::time_duration timeout){
boost::system::error_code ec;
boost::asio::mutable_buffer buffer = boost::asio::buffer(buff, size);
require_socket();
// Set a deadline for the asynchronous operation.
m_deadline.expires_from_now(timeout);
// Set up the variables that receive the result of the asynchronous
// operation. The error code is set to would_block to signal that the
// operation is incomplete. Asio guarantees that its asynchronous
// operations will never fail with would_block, so any other value in
// ec indicates completion.
ec = boost::asio::error::would_block;
std::size_t length = 0;
// Start the asynchronous operation itself. The handle_receive function
// used as a callback will update the ec and length variables.
m_socket->async_receive_from(boost::asio::buffer(buffer), m_endpoint,
boost::bind(&UdpTransport::handle_receive, _1, _2, &ec, &length));
// Block until the asynchronous operation has completed.
do {
m_io_service.run_one();
}
while (ec == boost::asio::error::would_block);
if (error_code){
*error_code = ec;
}
if (no_throw){
return length;
}
// Operation result
if (ec == boost::asio::error::operation_aborted){
throw exc::TimeoutException();
} else if (ec) {
MWARNING("Reading from UDP socket failed: " << ec.message());
throw exc::CommunicationException();
}
return length;
}
void UdpTransport::write(const google::protobuf::Message &req) {
m_proto->write(*this, req);
}
void UdpTransport::read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type) {
m_proto->read(*this, msg, msg_type);
}
void UdpTransport::check_deadline(){
if (!m_socket){
return; // no active socket.
}
// Check whether the deadline has passed. We compare the deadline against
// the current time since a new asynchronous operation may have moved the
// deadline before this actor had a chance to run.
if (m_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
{
// The deadline has passed. The outstanding asynchronous operation needs
// to be cancelled so that the blocked receive() function will return.
//
// Please note that cancel() has portability issues on some versions of
// Microsoft Windows, and it may be necessary to use close() instead.
// Consult the documentation for cancel() for further information.
m_socket->cancel();
// There is no longer an active deadline. The expiry is set to positive
// infinity so that the actor takes no action until a new deadline is set.
m_deadline.expires_at(boost::posix_time::pos_infin);
}
// Put the actor back to sleep.
m_deadline.async_wait(boost::bind(&UdpTransport::check_deadline, this));
}
void UdpTransport::handle_receive(const boost::system::error_code &ec, std::size_t length,
boost::system::error_code *out_ec, std::size_t *out_length) {
*out_ec = ec;
*out_length = length;
}
std::ostream& UdpTransport::dump(std::ostream& o) const {
return o << "UdpTransport<path=" << get_path()
<< ", socket_alive=" << (m_socket ? "true" : "false")
<< ">";
}
void enumerate(t_transport_vect & res){
BridgeTransport bt;
bt.enumerate(res);
hw::trezor::UdpTransport btu;
btu.enumerate(res);
}
std::shared_ptr<Transport> transport(const std::string & path){
if (boost::starts_with(path, BridgeTransport::PATH_PREFIX)){
return std::make_shared<BridgeTransport>(path.substr(strlen(BridgeTransport::PATH_PREFIX)));
} else if (boost::starts_with(path, UdpTransport::PATH_PREFIX)){
return std::make_shared<UdpTransport>(path.substr(strlen(UdpTransport::PATH_PREFIX)));
} else {
throw std::invalid_argument("Unknown Trezor device path: " + path);
}
}
void throw_failure_exception(const messages::common::Failure * failure) {
if (failure == nullptr){
throw std::invalid_argument("Failure message cannot be null");
}
boost::optional<std::string> message = failure->has_message() ? boost::make_optional(failure->message()) : boost::none;
boost::optional<uint32_t> code = failure->has_code() ? boost::make_optional(static_cast<uint32_t>(failure->code())) : boost::none;
if (!code){
throw exc::proto::FailureException(code, message);
}
auto ecode = failure->code();
if (ecode == messages::common::Failure_FailureType_Failure_UnexpectedMessage){
throw exc::proto::UnexpectedMessageException(code, message);
} else if (ecode == messages::common::Failure_FailureType_Failure_ActionCancelled){
throw exc::proto::CancelledException(code, message);
} else if (ecode == messages::common::Failure_FailureType_Failure_PinExpected){
throw exc::proto::PinExpectedException(code, message);
} else if (ecode == messages::common::Failure_FailureType_Failure_PinInvalid){
throw exc::proto::InvalidPinException(code, message);
} else if (ecode == messages::common::Failure_FailureType_Failure_NotEnoughFunds){
throw exc::proto::NotEnoughFundsException(code, message);
} else if (ecode == messages::common::Failure_FailureType_Failure_NotInitialized){
throw exc::proto::NotInitializedException(code, message);
} else if (ecode == messages::common::Failure_FailureType_Failure_FirmwareError){
throw exc::proto::FirmwareErrorException(code, message);
} else {
throw exc::proto::FailureException(code, message);
}
}
std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t){
return t.dump(o);
}
std::ostream& operator<<(std::ostream& o, std::shared_ptr<hw::trezor::Transport> const& t){
if (!t){
return o << "None";
}
return t->dump(o);
}
}
}

View File

@ -0,0 +1,331 @@
// 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_TRANSPORT_H
#define MONERO_TRANSPORT_H
#include <boost/asio.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/array.hpp>
#include <boost/utility/string_ref.hpp>
#include <typeinfo>
#include <type_traits>
#include "net/http_client.h"
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "exceptions.hpp"
#include "trezor_defs.hpp"
#include "messages_map.hpp"
#include "messages/messages.pb.h"
#include "messages/messages-common.pb.h"
#include "messages/messages-management.pb.h"
#include "messages/messages-monero.pb.h"
namespace hw {
namespace trezor {
using json = rapidjson::Document;
using json_val = rapidjson::Value;
namespace http = epee::net_utils::http;
const std::string DEFAULT_BRIDGE = "127.0.0.1:21325";
// Base HTTP comm serialization.
bool t_serialize(const std::string & in, std::string & out);
bool t_serialize(const json_val & in, std::string & out);
std::string t_serialize(const json_val & in);
bool t_deserialize(const std::string & in, std::string & out);
bool t_deserialize(const std::string & in, json & out);
// Flexible json serialization. HTTP client tailored for bridge API
template<class t_req, class t_res, class t_transport>
bool invoke_bridge_http(const boost::string_ref uri, const t_req & out_struct, t_res & result_struct, t_transport& transport, const boost::string_ref method = "POST", std::chrono::milliseconds timeout = std::chrono::seconds(180))
{
std::string req_param;
t_serialize(out_struct, req_param);
http::fields_list additional_params;
additional_params.push_back(std::make_pair("Origin","https://monero.trezor.io"));
additional_params.push_back(std::make_pair("Content-Type","application/json; charset=utf-8"));
const http::http_response_info* pri = nullptr;
if(!transport.invoke(uri, method, req_param, timeout, &pri, std::move(additional_params)))
{
MERROR("Failed to invoke http request to " << uri);
return false;
}
if(!pri)
{
MERROR("Failed to invoke http request to " << uri << ", internal error (null response ptr)");
return false;
}
if(pri->m_response_code != 200)
{
MERROR("Failed to invoke http request to " << uri << ", wrong response code: " << pri->m_response_code
<< " Response Body: " << pri->m_body);
return false;
}
return t_deserialize(pri->m_body, result_struct);
}
// Forward decl
class Transport;
class Protocol;
// Communication protocol
class Protocol {
public:
Protocol() = default;
virtual ~Protocol() = default;
virtual void session_begin(Transport & transport){ };
virtual void session_end(Transport & transport){ };
virtual void write(Transport & transport, const google::protobuf::Message & req)= 0;
virtual void read(Transport & transport, std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr)= 0;
};
class ProtocolV1 : public Protocol {
public:
ProtocolV1() = default;
virtual ~ProtocolV1() = default;
void write(Transport & transport, const google::protobuf::Message & req) override;
void read(Transport & transport, std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override;
};
// Base transport
typedef std::vector<std::shared_ptr<Transport>> t_transport_vect;
class Transport {
public:
Transport() = default;
virtual ~Transport() = default;
virtual bool ping() { return false; };
virtual std::string get_path() const { return ""; };
virtual void enumerate(t_transport_vect & res){};
virtual void open(){};
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 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<>"; }
};
// Bridge transport
class BridgeTransport : public Transport {
public:
BridgeTransport(
boost::optional<std::string> device_path = boost::none,
boost::optional<std::string> bridge_host = boost::none):
m_device_path(device_path),
m_bridge_host(bridge_host ? bridge_host.get() : DEFAULT_BRIDGE),
m_response(boost::none),
m_session(boost::none),
m_device_info(boost::none)
{
m_http_client.set_server(m_bridge_host, boost::none, false);
}
virtual ~BridgeTransport() = default;
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;
const boost::optional<json> & device_info() const;
std::ostream& dump(std::ostream& o) const override;
private:
epee::net_utils::http::http_simple_client m_http_client;
std::string m_bridge_host;
boost::optional<std::string> m_device_path;
boost::optional<std::string> m_session;
boost::optional<std::string> m_response;
boost::optional<json> m_device_info;
};
// UdpTransport transport
using boost::asio::ip::udp;
class UdpTransport : public Transport {
public:
explicit UdpTransport(
boost::optional<std::string> device_path=boost::none,
boost::optional<std::shared_ptr<Protocol>> proto=boost::none);
virtual ~UdpTransport() = default;
static const char * PATH_PREFIX;
static const char * DEFAULT_HOST;
static const int DEFAULT_PORT;
bool ping() override;
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_socket();
ssize_t receive(void * buff, size_t size, boost::system::error_code * error_code=nullptr, bool no_throw=false, boost::posix_time::time_duration timeout=boost::posix_time::seconds(10));
void check_deadline();
static void handle_receive(const boost::system::error_code& ec, std::size_t length,
boost::system::error_code* out_ec, std::size_t* out_length);
bool ping_int(boost::posix_time::time_duration timeout=boost::posix_time::milliseconds(1500));
std::shared_ptr<Protocol> m_proto;
std::string m_device_host;
int m_device_port;
std::unique_ptr<udp::socket> m_socket;
boost::asio::io_service m_io_service;
boost::asio::deadline_timer m_deadline;
udp::endpoint m_endpoint;
};
//
// General helpers
//
/**
* Enumerates all transports
*/
void enumerate(t_transport_vect & res);
/**
* Transforms path to the transport
*/
std::shared_ptr<Transport> transport(const std::string & path);
/**
* Transforms path to the particular transport
*/
template<class t_transport>
std::shared_ptr<t_transport> transport_typed(const std::string & path){
auto t = transport(path);
if (!t){
return nullptr;
}
return std::dynamic_pointer_cast<t_transport>(t);
}
// Exception carries unexpected message being received
namespace exc {
class UnexpectedMessageException: public ProtocolException {
protected:
hw::trezor::messages::MessageType recvType;
std::shared_ptr<google::protobuf::Message> recvMsg;
public:
using ProtocolException::ProtocolException;
UnexpectedMessageException(): ProtocolException("Trezor returned unexpected message") {};
UnexpectedMessageException(hw::trezor::messages::MessageType recvType,
const std::shared_ptr<google::protobuf::Message> & recvMsg)
: recvType(recvType), recvMsg(recvMsg) {
reason = std::string("Trezor returned unexpected message: ") + std::to_string(recvType);
}
};
}
/**
* Throws corresponding failure exception.
*/
[[ noreturn ]] void throw_failure_exception(const messages::common::Failure * failure);
/**
* Simple wrapper for write-read message exchange with expected message response type.
*
* @throws UnexpectedMessageException if the response message type is different than expected.
* Exception contains message type and the message itself.
*/
template<class t_message>
std::shared_ptr<t_message>
exchange_message(Transport & transport, const google::protobuf::Message & req,
boost::optional<messages::MessageType> resp_type = boost::none)
{
// Require strictly protocol buffers response in the template.
BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
// Write the request
transport.write(req);
// Read the response
std::shared_ptr<google::protobuf::Message> msg_resp;
hw::trezor::messages::MessageType msg_resp_type;
transport.read(msg_resp, &msg_resp_type);
// Determine type of expected message response
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);
}
}
std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t);
std::ostream& operator<<(std::ostream& o, std::shared_ptr<hw::trezor::Transport> const& t);
}}
#endif //MONERO_TRANSPORT_H

View File

@ -0,0 +1,48 @@
// 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.
//
#if defined(HAVE_PROTOBUF) && !defined(WITHOUT_TREZOR)
#define WITH_DEVICE_TREZOR 1
#else
#define WITH_DEVICE_TREZOR 0
#endif
#ifndef WITH_DEVICE_TREZOR_LITE
#define WITH_DEVICE_TREZOR_LITE 0
#endif
// Avoids protobuf undefined macro warning
#ifndef PROTOBUF_INLINE_NOT_IN_HEADERS
#define PROTOBUF_INLINE_NOT_IN_HEADERS 0
#endif
// Fixes gcc7 problem with minor macro defined clashing with minor() field.
#ifdef minor
#undef minor
#endif

View File

@ -215,7 +215,7 @@ namespace
}
boost::crc_32_type result;
result.process_bytes(trimmed_words.data(), trimmed_words.length());
return result.checksum() % crypto::ElectrumWords::seed_length;
return result.checksum() % word_list.size();
}
/*!

File diff suppressed because it is too large Load Diff

View File

@ -79,6 +79,25 @@ extern "C"
// Best/cached Straus Straus Straus Straus Straus Straus Straus Straus Pip Pip Pip Pip
// Best/uncached Straus Straus Straus Straus Straus Straus Pip Pip Pip Pip Pip Pip
// New timings:
// Pippenger:
// 2/1 always
// 3/2 at ~13
// 4/3 at ~29
// 5/4 at ~83
// 6/5 < 200
// 7/6 at ~470
// 8/7 at ~1180
// 9/8 at ~2290
// Cached Pippenger:
// 6/5 < 200
// 7/6 at 460
// 8/7 at 1180
// 9/8 at 2300
//
// Cached Straus/Pippenger cross at 232
//
namespace rct
{
@ -320,7 +339,7 @@ rct::key bos_coster_heap_conv_robust(std::vector<MultiexpData> data)
return res;
}
static constexpr unsigned int STRAUS_C = 4;
#define STRAUS_C 4
struct straus_cached_data
{
@ -447,28 +466,26 @@ rct::key straus(const std::vector<MultiexpData> &data, const std::shared_ptr<str
#endif
MULTIEXP_PERF(PERF_TIMER_START_UNIT(digits, 1000000));
#if STRAUS_C==4
std::unique_ptr<uint8_t[]> digits{new uint8_t[64 * data.size()]};
#else
std::unique_ptr<uint8_t[]> digits{new uint8_t[256 * data.size()]};
#endif
for (size_t j = 0; j < data.size(); ++j)
{
const unsigned char *bytes = data[j].scalar.bytes;
#if STRAUS_C==4
unsigned int i;
for (i = 0; i < 64; i += 2, bytes++)
{
digits[j*64+i] = bytes[0] & 0xf;
digits[j*64+i+1] = bytes[0] >> 4;
}
#elif 1
unsigned char bytes33[33];
memcpy(bytes33, data[j].scalar.bytes, 32);
bytes33[32] = 0;
const unsigned char *bytes = bytes33;
#if 1
static_assert(STRAUS_C == 4, "optimized version needs STRAUS_C == 4");
unsigned int i;
for (i = 0; i < 256; i += 8, bytes++)
{
digits[j*256+i] = bytes[0] & 0xf;
digits[j*256+i+1] = (bytes[0] >> 1) & 0xf;
digits[j*256+i+2] = (bytes[0] >> 2) & 0xf;
digits[j*256+i+3] = (bytes[0] >> 3) & 0xf;
digits[j*256+i+4] = ((bytes[0] >> 4) | (bytes[1]<<4)) & 0xf;
digits[j*256+i+5] = ((bytes[0] >> 5) | (bytes[1]<<3)) & 0xf;
digits[j*256+i+6] = ((bytes[0] >> 6) | (bytes[1]<<2)) & 0xf;
digits[j*256+i+7] = ((bytes[0] >> 7) | (bytes[1]<<1)) & 0xf;
}
#elif 1
bytes = bytes33;
for (size_t i = 0; i < 256; ++i)
digits[j*256+i] = ((bytes[i>>3] | (bytes[(i>>3)+1]<<8)) >> (i&7)) & mask;
#else
@ -521,7 +538,11 @@ skipfirst:
if (skip[j])
continue;
#endif
#if STRAUS_C==4
const uint8_t digit = digits[j*64+i/4];
#else
const uint8_t digit = digits[j*256+i];
#endif
if (digit)
{
ge_add(&p1, &band_p3, &CACHE_OFFSET(local_cache, j, digit));
@ -542,16 +563,13 @@ skipfirst:
size_t get_pippenger_c(size_t N)
{
// uncached: 2:1, 4:2, 8:2, 16:3, 32:4, 64:4, 128:5, 256:6, 512:7, 1024:7, 2048:8, 4096:9
// cached: 2:1, 4:2, 8:2, 16:3, 32:4, 64:4, 128:5, 256:6, 512:7, 1024:7, 2048:8, 4096:9
if (N <= 2) return 1;
if (N <= 8) return 2;
if (N <= 16) return 3;
if (N <= 64) return 4;
if (N <= 128) return 5;
if (N <= 256) return 6;
if (N <= 1024) return 7;
if (N <= 2048) return 8;
if (N <= 13) return 2;
if (N <= 29) return 3;
if (N <= 83) return 4;
if (N <= 185) return 5;
if (N <= 465) return 6;
if (N <= 1180) return 7;
if (N <= 2295) return 8;
return 9;
}
@ -563,12 +581,13 @@ struct pippenger_cached_data
~pippenger_cached_data() { aligned_free(cached); }
};
std::shared_ptr<pippenger_cached_data> pippenger_init_cache(const std::vector<MultiexpData> &data, size_t N)
std::shared_ptr<pippenger_cached_data> pippenger_init_cache(const std::vector<MultiexpData> &data, size_t start_offset, size_t N)
{
MULTIEXP_PERF(PERF_TIMER_START_UNIT(pippenger_init_cache, 1000000));
CHECK_AND_ASSERT_THROW_MES(start_offset <= data.size(), "Bad cache base data");
if (N == 0)
N = data.size();
CHECK_AND_ASSERT_THROW_MES(N <= data.size(), "Bad cache base data");
N = data.size() - start_offset;
CHECK_AND_ASSERT_THROW_MES(N <= data.size() - start_offset, "Bad cache base data");
ge_cached cached;
std::shared_ptr<pippenger_cached_data> cache(new pippenger_cached_data());
@ -576,7 +595,7 @@ std::shared_ptr<pippenger_cached_data> pippenger_init_cache(const std::vector<Mu
cache->cached = (ge_cached*)aligned_realloc(cache->cached, N * sizeof(ge_cached), 4096);
CHECK_AND_ASSERT_THROW_MES(cache->cached, "Out of memory");
for (size_t i = 0; i < N; ++i)
ge_p3_to_cached(&cache->cached[i], &data[i].point);
ge_p3_to_cached(&cache->cached[i], &data[i+start_offset].point);
MULTIEXP_PERF(PERF_TIMER_STOP(pippenger_init_cache));
return cache;
@ -587,16 +606,21 @@ size_t pippenger_get_cache_size(const std::shared_ptr<pippenger_cached_data> &ca
return cache->size * sizeof(*cache->cached);
}
rct::key pippenger(const std::vector<MultiexpData> &data, const std::shared_ptr<pippenger_cached_data> &cache, size_t c)
rct::key pippenger(const std::vector<MultiexpData> &data, const std::shared_ptr<pippenger_cached_data> &cache, size_t cache_size, size_t c)
{
CHECK_AND_ASSERT_THROW_MES(cache == NULL || cache->size >= data.size(), "Cache is too small");
if (cache != NULL && cache_size == 0)
cache_size = cache->size;
CHECK_AND_ASSERT_THROW_MES(cache == NULL || cache_size <= cache->size, "Cache is too small");
if (c == 0)
c = get_pippenger_c(data.size());
CHECK_AND_ASSERT_THROW_MES(c <= 9, "c is too large");
ge_p3 result = ge_p3_identity;
bool result_init = false;
std::unique_ptr<ge_p3[]> buckets{new ge_p3[1<<c]};
bool buckets_init[1<<9];
std::shared_ptr<pippenger_cached_data> local_cache = cache == NULL ? pippenger_init_cache(data) : cache;
std::shared_ptr<pippenger_cached_data> local_cache_2 = data.size() > cache_size ? pippenger_init_cache(data, cache_size) : NULL;
rct::key maxscalar = rct::zero();
for (size_t i = 0; i < data.size(); ++i)
@ -611,7 +635,7 @@ rct::key pippenger(const std::vector<MultiexpData> &data, const std::shared_ptr<
for (size_t k = groups; k-- > 0; )
{
if (!ge_p3_is_point_at_infinity(&result))
if (result_init)
{
ge_p2 p2;
ge_p3_to_p2(&p2, &result);
@ -625,8 +649,7 @@ rct::key pippenger(const std::vector<MultiexpData> &data, const std::shared_ptr<
ge_p1p1_to_p2(&p2, &p1);
}
}
for (size_t i = 0; i < (1u<<c); ++i)
buckets[i] = ge_p3_identity;
memset(buckets_init, 0, 1u<<c);
// partition scalars into buckets
for (size_t i = 0; i < data.size(); ++i)
@ -638,22 +661,45 @@ rct::key pippenger(const std::vector<MultiexpData> &data, const std::shared_ptr<
if (bucket == 0)
continue;
CHECK_AND_ASSERT_THROW_MES(bucket < (1u<<c), "bucket overflow");
if (!ge_p3_is_point_at_infinity(&buckets[bucket]))
if (buckets_init[bucket])
{
add(buckets[bucket], local_cache->cached[i]);
if (i < cache_size)
add(buckets[bucket], local_cache->cached[i]);
else
add(buckets[bucket], local_cache_2->cached[i - cache_size]);
}
else
{
buckets[bucket] = data[i].point;
buckets_init[bucket] = true;
}
}
// sum the buckets
ge_p3 pail = ge_p3_identity;
ge_p3 pail;
bool pail_init = false;
for (size_t i = (1<<c)-1; i > 0; --i)
{
if (!ge_p3_is_point_at_infinity(&buckets[i]))
add(pail, buckets[i]);
if (!ge_p3_is_point_at_infinity(&pail))
add(result, pail);
if (buckets_init[i])
{
if (pail_init)
add(pail, buckets[i]);
else
{
pail = buckets[i];
pail_init = true;
}
}
if (pail_init)
{
if (result_init)
add(result, pail);
else
{
result = pail;
result_init = true;
}
}
}
}

View File

@ -61,10 +61,10 @@ rct::key bos_coster_heap_conv_robust(std::vector<MultiexpData> data);
std::shared_ptr<straus_cached_data> straus_init_cache(const std::vector<MultiexpData> &data, size_t N =0);
size_t straus_get_cache_size(const std::shared_ptr<straus_cached_data> &cache);
rct::key straus(const std::vector<MultiexpData> &data, const std::shared_ptr<straus_cached_data> &cache = NULL, size_t STEP = 0);
std::shared_ptr<pippenger_cached_data> pippenger_init_cache(const std::vector<MultiexpData> &data, size_t N =0);
std::shared_ptr<pippenger_cached_data> pippenger_init_cache(const std::vector<MultiexpData> &data, size_t start_offset = 0, size_t N =0);
size_t pippenger_get_cache_size(const std::shared_ptr<pippenger_cached_data> &cache);
size_t get_pippenger_c(size_t N);
rct::key pippenger(const std::vector<MultiexpData> &data, const std::shared_ptr<pippenger_cached_data> &cache = NULL, size_t c = 0);
rct::key pippenger(const std::vector<MultiexpData> &data, const std::shared_ptr<pippenger_cached_data> &cache = NULL, size_t cache_size = 0, size_t c = 0);
}

View File

@ -58,15 +58,6 @@ namespace
}
namespace rct {
Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount)
{
mask = rct::skGen();
Bulletproof proof = bulletproof_PROVE(amount, mask);
CHECK_AND_ASSERT_THROW_MES(proof.V.size() == 1, "V has not exactly one element");
C = proof.V[0];
return proof;
}
Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts)
{
masks = rct::skvGen(amounts.size());

View File

@ -133,7 +133,7 @@ namespace rct {
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);
key get_pre_mlsag_hash(const rctSig &rv, hw::device &hwdev);
bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key);
}
#endif /* RCTSIGS_H */

View File

@ -28,7 +28,8 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(rpc_base_sources
rpc_args.cpp)
rpc_args.cpp
rpc_handler.cpp)
set(rpc_sources
core_rpc_server.cpp
@ -44,7 +45,8 @@ set(daemon_rpc_server_sources
set(rpc_base_headers
rpc_args.h)
rpc_args.h
rpc_handler.h)
set(rpc_headers)
@ -64,7 +66,6 @@ set(daemon_rpc_server_private_headers
message.h
daemon_messages.h
daemon_handler.h
rpc_handler.h
zmq_server.h)

View File

@ -46,6 +46,7 @@ using namespace epee;
#include "storages/http_abstract_invoke.h"
#include "crypto/hash.h"
#include "rpc/rpc_args.h"
#include "rpc/rpc_handler.h"
#include "core_rpc_server_error_codes.h"
#include "p2p/net_node.h"
#include "version.h"
@ -2163,62 +2164,15 @@ namespace cryptonote
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)
{
static struct D
{
boost::mutex mutex;
std::vector<uint64_t> cached_distribution;
uint64_t cached_from, cached_to, cached_start_height, cached_base;
bool cached;
D(): cached_from(0), cached_to(0), cached_start_height(0), cached_base(0), cached(false) {}
} d;
boost::unique_lock<boost::mutex> lock(d.mutex);
if (d.cached && amount == 0 && d.cached_from == req.from_height && d.cached_to == req_to_height)
{
res.distributions.push_back({amount, d.cached_start_height, req.binary, d.cached_distribution, d.cached_base});
if (!req.cumulative)
{
auto &distribution = res.distributions.back().distribution;
for (size_t n = distribution.size() - 1; n > 0; --n)
distribution[n] -= distribution[n-1];
distribution[0] -= d.cached_base;
}
continue;
}
std::vector<uint64_t> distribution;
uint64_t start_height, base;
if (!m_core.get_output_distribution(amount, req.from_height, req_to_height, start_height, distribution, base))
auto data = rpc::RpcHandler::get_output_distribution(m_core, amount, req.from_height, req_to_height, req.cumulative);
if (!data)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Failed to get rct distribution";
error_resp.message = "Failed to get output distribution";
return false;
}
if (req_to_height > 0 && req_to_height >= req.from_height)
{
uint64_t offset = std::max(req.from_height, start_height);
if (offset <= req_to_height && req_to_height - offset + 1 < distribution.size())
distribution.resize(req_to_height - offset + 1);
}
if (amount == 0)
{
d.cached_from = req.from_height;
d.cached_to = req_to_height;
d.cached_distribution = distribution;
d.cached_start_height = start_height;
d.cached_base = base;
d.cached = true;
}
if (!req.cumulative)
{
for (size_t n = distribution.size() - 1; n > 0; --n)
distribution[n] -= distribution[n-1];
distribution[0] -= base;
}
res.distributions.push_back({amount, start_height, req.binary, std::move(distribution), base});
res.distributions.push_back({std::move(*data), amount, req.binary});
}
}
catch (const std::exception &e)

View File

@ -36,6 +36,7 @@
#include "crypto/hash.h"
#include "cryptonote_config.h"
#include "cryptonote_core/service_node_deregister.h"
#include "rpc/rpc_handler.h"
namespace cryptonote
{
@ -2250,21 +2251,19 @@ namespace cryptonote
struct distribution
{
rpc::output_distribution_data data;
uint64_t amount;
uint64_t start_height;
bool binary;
std::vector<uint64_t> distribution;
uint64_t base;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(start_height)
KV_SERIALIZE_N(data.start_height, "start_height")
KV_SERIALIZE(binary)
if (this_ref.binary)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(distribution)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution")
else
KV_SERIALIZE(distribution)
KV_SERIALIZE(base)
KV_SERIALIZE_N(data.distribution, "distribution")
KV_SERIALIZE_N(data.base, "base")
END_KV_SERIALIZE_MAP()
};

View File

@ -729,12 +729,53 @@ namespace rpc
res.status = Message::STATUS_OK;
}
void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res)
void DaemonHandler::handle(const GetFeeEstimate::Request& req, GetFeeEstimate::Response& res)
{
res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.num_grace_blocks);
res.hard_fork_version = m_core.get_blockchain_storage().get_current_hard_fork_version();
res.estimated_base_fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.num_grace_blocks);
if (res.hard_fork_version < HF_VERSION_PER_BYTE_FEE)
{
res.size_scale = 1024; // per KiB fee
res.fee_mask = 1;
}
else
{
res.size_scale = 1; // per byte fee
res.fee_mask = Blockchain::get_fee_quantization_mask();
}
res.status = Message::STATUS_OK;
}
void DaemonHandler::handle(const GetOutputDistribution::Request& req, GetOutputDistribution::Response& res)
{
try
{
res.distributions.reserve(req.amounts.size());
const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
for (std::uint64_t amount : req.amounts)
{
auto data = get_output_distribution(m_core, amount, req.from_height, req_to_height, req.cumulative);
if (!data)
{
res.distributions.clear();
res.status = Message::STATUS_FAILED;
res.error_details = "Failed to get output distribution";
return;
}
res.distributions.push_back(output_distribution{std::move(*data), amount, req.cumulative});
}
res.status = Message::STATUS_OK;
}
catch (const std::exception& e)
{
res.distributions.clear();
res.status = Message::STATUS_FAILED;
res.error_details = e.what();
}
}
bool DaemonHandler::getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& header)
{
block b;
@ -809,7 +850,8 @@ namespace rpc
REQ_RESP_TYPES_MACRO(request_type, GetOutputHistogram, req_json, resp_message, handle);
REQ_RESP_TYPES_MACRO(request_type, GetOutputKeys, req_json, resp_message, handle);
REQ_RESP_TYPES_MACRO(request_type, GetRPCVersion, req_json, resp_message, handle);
REQ_RESP_TYPES_MACRO(request_type, GetPerKBFeeEstimate, req_json, resp_message, handle);
REQ_RESP_TYPES_MACRO(request_type, GetFeeEstimate, req_json, resp_message, handle);
REQ_RESP_TYPES_MACRO(request_type, GetOutputDistribution, req_json, resp_message, handle);
// if none of the request types matches
if (resp_message == NULL)

View File

@ -126,7 +126,9 @@ class DaemonHandler : public RpcHandler
void handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res);
void handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res);
void handle(const GetFeeEstimate::Request& req, GetFeeEstimate::Response& res);
void handle(const GetOutputDistribution::Request& req, GetOutputDistribution::Response& res);
std::string handle(const std::string& request);

View File

@ -59,7 +59,8 @@ const char* const HardForkInfo::name = "hard_fork_info";
const char* const GetOutputHistogram::name = "get_output_histogram";
const char* const GetOutputKeys::name = "get_output_keys";
const char* const GetRPCVersion::name = "get_rpc_version";
const char* const GetPerKBFeeEstimate::name = "get_dynamic_per_kb_fee_estimate";
const char* const GetFeeEstimate::name = "get_dynamic_fee_estimate";
const char* const GetOutputDistribution::name = "get_output_distribution";
@ -825,7 +826,7 @@ void GetRPCVersion::Response::fromJson(rapidjson::Value& val)
GET_FROM_JSON_OBJECT(val, version, version);
}
rapidjson::Value GetPerKBFeeEstimate::Request::toJson(rapidjson::Document& doc) const
rapidjson::Value GetFeeEstimate::Request::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
@ -836,27 +837,70 @@ rapidjson::Value GetPerKBFeeEstimate::Request::toJson(rapidjson::Document& doc)
return val;
}
void GetPerKBFeeEstimate::Request::fromJson(rapidjson::Value& val)
void GetFeeEstimate::Request::fromJson(rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, num_grace_blocks, num_grace_blocks);
}
rapidjson::Value GetPerKBFeeEstimate::Response::toJson(rapidjson::Document& doc) const
rapidjson::Value GetFeeEstimate::Response::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
auto& al = doc.GetAllocator();
INSERT_INTO_JSON_OBJECT(val, doc, estimated_fee_per_kb, estimated_fee_per_kb);
INSERT_INTO_JSON_OBJECT(val, doc, estimated_base_fee, estimated_base_fee);
INSERT_INTO_JSON_OBJECT(val, doc, fee_mask, fee_mask);
INSERT_INTO_JSON_OBJECT(val, doc, size_scale, size_scale);
INSERT_INTO_JSON_OBJECT(val, doc, hard_fork_version, hard_fork_version);
return val;
}
void GetPerKBFeeEstimate::Response::fromJson(rapidjson::Value& val)
void GetFeeEstimate::Response::fromJson(rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, estimated_fee_per_kb, estimated_fee_per_kb);
GET_FROM_JSON_OBJECT(val, estimated_base_fee, estimated_base_fee);
GET_FROM_JSON_OBJECT(val, fee_mask, fee_mask);
GET_FROM_JSON_OBJECT(val, size_scale, size_scale);
GET_FROM_JSON_OBJECT(val, hard_fork_version, hard_fork_version);
}
rapidjson::Value GetOutputDistribution::Request::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
auto& al = doc.GetAllocator();
INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
INSERT_INTO_JSON_OBJECT(val, doc, from_height, from_height);
INSERT_INTO_JSON_OBJECT(val, doc, to_height, to_height);
INSERT_INTO_JSON_OBJECT(val, doc, cumulative, cumulative);
return val;
}
void GetOutputDistribution::Request::fromJson(rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, amounts, amounts);
GET_FROM_JSON_OBJECT(val, from_height, from_height);
GET_FROM_JSON_OBJECT(val, to_height, to_height);
GET_FROM_JSON_OBJECT(val, cumulative, cumulative);
}
rapidjson::Value GetOutputDistribution::Response::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
auto& al = doc.GetAllocator();
INSERT_INTO_JSON_OBJECT(val, doc, status, status);
INSERT_INTO_JSON_OBJECT(val, doc, distributions, distributions);
return val;
}
void GetOutputDistribution::Response::fromJson(rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, status, status);
GET_FROM_JSON_OBJECT(val, distributions, distributions);
}
} // namespace rpc

View File

@ -28,6 +28,9 @@
#pragma once
#include <unordered_map>
#include <vector>
#include "message.h"
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "rpc/message_data_structs.h"
@ -110,7 +113,8 @@ BEGIN_RPC_MESSAGE_CLASS(GetTransactions);
std::vector<crypto::hash> tx_hashes;
END_RPC_MESSAGE_REQUEST;
BEGIN_RPC_MESSAGE_RESPONSE;
std::unordered_map<crypto::hash, cryptonote::rpc::transaction_info> txs;
using txes_map = std::unordered_map<crypto::hash, transaction_info>;
txes_map txs;
std::vector<crypto::hash> missed_hashes;
END_RPC_MESSAGE_RESPONSE;
END_RPC_MESSAGE_CLASS;
@ -399,12 +403,27 @@ BEGIN_RPC_MESSAGE_CLASS(GetRPCVersion);
END_RPC_MESSAGE_RESPONSE;
END_RPC_MESSAGE_CLASS;
BEGIN_RPC_MESSAGE_CLASS(GetPerKBFeeEstimate);
BEGIN_RPC_MESSAGE_CLASS(GetFeeEstimate);
BEGIN_RPC_MESSAGE_REQUEST;
uint64_t num_grace_blocks;
END_RPC_MESSAGE_REQUEST;
BEGIN_RPC_MESSAGE_RESPONSE;
uint64_t estimated_fee_per_kb;
uint64_t estimated_base_fee;
uint64_t fee_mask;
uint32_t size_scale;
uint8_t hard_fork_version;
END_RPC_MESSAGE_RESPONSE;
END_RPC_MESSAGE_CLASS;
BEGIN_RPC_MESSAGE_CLASS(GetOutputDistribution);
BEGIN_RPC_MESSAGE_REQUEST;
std::vector<uint64_t> amounts;
uint64_t from_height;
uint64_t to_height;
bool cumulative;
END_RPC_MESSAGE_REQUEST;
BEGIN_RPC_MESSAGE_RESPONSE;
std::vector<output_distribution> distributions;
END_RPC_MESSAGE_RESPONSE;
END_RPC_MESSAGE_CLASS;

View File

@ -31,6 +31,7 @@
#include "crypto/hash.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "ringct/rctSigs.h"
#include "rpc/rpc_handler.h"
#include <unordered_map>
#include <vector>
@ -192,6 +193,12 @@ namespace rpc
uint64_t start_time;
};
struct output_distribution
{
output_distribution_data data;
uint64_t amount;
bool cumulative;
};
} // namespace rpc
} // namespace cryptonote

69
src/rpc/rpc_handler.cpp Normal file
View File

@ -0,0 +1,69 @@
#include <algorithm>
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>
#include "cryptonote_core/cryptonote_core.h"
namespace cryptonote
{
namespace rpc
{
namespace
{
output_distribution_data
process_distribution(bool cumulative, std::uint64_t start_height, std::vector<std::uint64_t> distribution, std::uint64_t base)
{
if (!cumulative && !distribution.empty())
{
for (std::size_t n = distribution.size() - 1; 0 < n; --n)
distribution[n] -= distribution[n - 1];
distribution[0] -= base;
}
return {std::move(distribution), start_height, base};
}
}
boost::optional<output_distribution_data>
RpcHandler::get_output_distribution(core& src, std::uint64_t amount, std::uint64_t from_height, std::uint64_t to_height, bool cumulative)
{
static struct D
{
boost::mutex mutex;
std::vector<std::uint64_t> cached_distribution;
std::uint64_t cached_from, cached_to, cached_start_height, cached_base;
bool cached;
D(): cached_from(0), cached_to(0), cached_start_height(0), cached_base(0), cached(false) {}
} d;
const boost::unique_lock<boost::mutex> lock(d.mutex);
if (d.cached && amount == 0 && d.cached_from == from_height && d.cached_to == to_height)
return process_distribution(cumulative, d.cached_start_height, d.cached_distribution, d.cached_base);
std::vector<std::uint64_t> distribution;
std::uint64_t start_height, base;
if (!src.get_output_distribution(amount, from_height, to_height, start_height, distribution, base))
return boost::none;
if (to_height > 0 && to_height >= from_height)
{
const std::uint64_t offset = std::max(from_height, start_height);
if (offset <= to_height && to_height - offset + 1 < distribution.size())
distribution.resize(to_height - offset + 1);
}
if (amount == 0)
{
d.cached_from = from_height;
d.cached_to = to_height;
d.cached_distribution = distribution;
d.cached_start_height = start_height;
d.cached_base = base;
d.cached = true;
}
return process_distribution(cumulative, start_height, std::move(distribution), base);
}
} // rpc
} // cryptonote

View File

@ -28,24 +28,35 @@
#pragma once
#include <boost/optional/optional.hpp>
#include <cstdint>
#include <string>
#include <vector>
namespace cryptonote
{
class core;
namespace rpc
{
struct output_distribution_data
{
std::vector<std::uint64_t> distribution;
std::uint64_t start_height;
std::uint64_t base;
};
class RpcHandler
{
public:
RpcHandler() { }
virtual ~RpcHandler() { }
virtual std::string handle(const std::string& request) = 0;
RpcHandler() { }
virtual ~RpcHandler() { }
static boost::optional<output_distribution_data>
get_output_distribution(core& src, std::uint64_t amount, std::uint64_t from_height, std::uint64_t to_height, bool cumulative);
};

View File

@ -1236,6 +1236,29 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
GET_FROM_JSON_OBJECT(val, info.start_time, start_time);
}
void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_distribution& dist, rapidjson::Value& val)
{
val.SetObject();
INSERT_INTO_JSON_OBJECT(val, doc, distribution, dist.data.distribution);
INSERT_INTO_JSON_OBJECT(val, doc, amount, dist.amount);
INSERT_INTO_JSON_OBJECT(val, doc, start_height, dist.data.start_height);
INSERT_INTO_JSON_OBJECT(val, doc, base, dist.data.base);
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribution& dist)
{
if (!val.IsObject())
{
throw WRONG_TYPE("json object");
}
GET_FROM_JSON_OBJECT(val, dist.data.distribution, distribution);
GET_FROM_JSON_OBJECT(val, dist.amount, amount);
GET_FROM_JSON_OBJECT(val, dist.data.start_height, start_height);
GET_FROM_JSON_OBJECT(val, dist.data.base, base);
}
} // namespace json
} // namespace cryptonote

View File

@ -281,6 +281,9 @@ void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig);
void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& info, rapidjson::Value& val);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info);
void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_distribution& dist, rapidjson::Value& val);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribution& dist);
template <typename Map>
typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Document& doc, const Map& map, rapidjson::Value& val);

View File

@ -1830,6 +1830,27 @@ bool simple_wallet::version(const std::vector<std::string> &args)
return true;
}
bool simple_wallet::cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func)
{
std::vector<std::string> tx_aux;
message_writer(console_color_white, false) << tr("Please confirm the transaction on the device");
m_wallet->cold_sign_tx(ptx_vector, exported_txs, dsts_info, tx_aux);
if (accept_func && !accept_func(exported_txs))
{
MERROR("Transactions rejected by callback");
return false;
}
// aux info
m_wallet->cold_tx_aux_import(exported_txs.ptx, tx_aux);
// import key images
return m_wallet->import_key_images(exported_txs.key_images);
}
bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
@ -2234,6 +2255,33 @@ bool simple_wallet::set_ignore_fractional_outputs(const std::vector<std::string>
return true;
}
bool simple_wallet::set_device_name(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
if (args.size() == 0){
fail_msg_writer() << tr("Device name not specified");
return true;
}
m_wallet->device_name(args[0]);
bool r = false;
try {
r = m_wallet->reconnect_device();
if (!r){
fail_msg_writer() << tr("Device reconnect failed");
}
} catch(const std::exception & e){
MWARNING("Device reconnect failed: " << e.what());
fail_msg_writer() << tr("Device reconnect failed: ") << e.what();
}
}
return true;
}
bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
if(args.empty())
@ -2528,6 +2576,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::import_key_images, this, _1),
tr("import_key_images <file>"),
tr("Import a signed key images list and verify their spent status."));
m_cmd_binder.set_handler("hw_key_images_sync",
boost::bind(&simple_wallet::hw_key_images_sync, this, _1),
tr("hw_key_images_sync"),
tr("Synchronizes key images with the hw wallet."));
m_cmd_binder.set_handler("hw_reconnect",
boost::bind(&simple_wallet::hw_reconnect, this, _1),
tr("hw_reconnect"),
@ -2717,6 +2769,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
CHECK_SIMPLE_VARIABLE("subaddress-lookahead", set_subaddress_lookahead, tr("<major>:<minor>"));
CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer"));
CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>"));
}
fail_msg_writer() << tr("set: unrecognized argument(s)");
return true;
@ -4803,12 +4856,14 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
local_args.pop_back();
}
vector<cryptonote::address_parse_info> dsts_info;
vector<cryptonote::tx_destination_entry> dsts;
size_t num_subaddresses = 0;
for (size_t i = 0; i < local_args.size(); )
{
dsts_info.emplace_back();
cryptonote::address_parse_info & info = dsts_info.back();
cryptonote::tx_destination_entry de;
cryptonote::address_parse_info info;
bool r = true;
// check for a URI
@ -5092,6 +5147,28 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_loki_tx";
}
}
else if (m_wallet->get_account().get_device().has_tx_cold_sign())
{
try
{
tools::wallet2::signed_tx_set signed_tx;
if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){
fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet");
return true;
}
commit_or_save(signed_tx.ptx, m_do_not_relay);
}
catch (const std::exception& e)
{
handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
}
catch (...)
{
LOG_ERROR("Unknown error");
fail_msg_writer() << tr("unknown error");
}
}
else if (m_wallet->watch_only())
{
bool r = m_wallet->save_tx(ptx_vector, "unsigned_loki_tx");
@ -6389,6 +6466,31 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_loki_tx";
}
}
else if (m_wallet->get_account().get_device().has_tx_cold_sign())
{
try
{
tools::wallet2::signed_tx_set signed_tx;
std::vector<cryptonote::address_parse_info> dsts_info;
dsts_info.push_back(info);
if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){
fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet");
return true;
}
commit_or_save(signed_tx.ptx, m_do_not_relay);
}
catch (const std::exception& e)
{
handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
}
catch (...)
{
LOG_ERROR("Unknown error");
fail_msg_writer() << tr("unknown error");
}
}
else if (m_wallet->watch_only())
{
bool r = m_wallet->save_tx(ptx_vector, "unsigned_loki_tx");
@ -8792,6 +8894,48 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args)
{
if (!m_wallet->key_on_device())
{
fail_msg_writer() << tr("command only supported by HW wallet");
return true;
}
if (!m_wallet->get_account().get_device().has_ki_cold_sync())
{
fail_msg_writer() << tr("hw wallet does not support cold KI sync");
return true;
}
if (!m_wallet->is_trusted_daemon())
{
fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
return true;
}
LOCK_IDLE_SCOPE();
try
{
message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device");
uint64_t spent = 0, unspent = 0;
uint64_t height = m_wallet->cold_key_image_sync(spent, unspent);
if (height > 0)
{
success_msg_writer() << tr("Signed key images imported to height ") << height << ", "
<< print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent");
} else {
fail_msg_writer() << tr("Failed to import key images");
}
}
catch (const std::exception &e)
{
fail_msg_writer() << tr("Failed to import key images: ") << e.what();
return true;
}
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
{
if (!m_wallet->key_on_device())

View File

@ -142,6 +142,7 @@ namespace cryptonote
bool set_subaddress_lookahead(const std::vector<std::string> &args = std::vector<std::string>());
bool set_segregation_height(const std::vector<std::string> &args = std::vector<std::string>());
bool set_ignore_fractional_outputs(const std::vector<std::string> &args = std::vector<std::string>());
bool set_device_name(const std::vector<std::string> &args = std::vector<std::string>());
bool help(const std::vector<std::string> &args = std::vector<std::string>());
bool start_mining(const std::vector<std::string> &args);
bool stop_mining(const std::vector<std::string> &args);
@ -206,6 +207,7 @@ namespace cryptonote
bool verify(const std::vector<std::string> &args);
bool export_key_images(const std::vector<std::string> &args);
bool import_key_images(const std::vector<std::string> &args);
bool hw_key_images_sync(const std::vector<std::string> &args);
bool hw_reconnect(const std::vector<std::string> &args);
bool export_outputs(const std::vector<std::string> &args);
bool import_outputs(const std::vector<std::string> &args);
@ -230,6 +232,7 @@ namespace cryptonote
bool unblackball(const std::vector<std::string>& args);
bool blackballed(const std::vector<std::string>& args);
bool version(const std::vector<std::string>& args);
bool cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func);
bool register_service_node_main(const std::vector<std::string>& service_node_key_as_str, uint64_t expiration_timestamp, const cryptonote::account_public_address& address, uint32_t priority, const std::vector<uint64_t>& portions, const std::vector<uint8_t>& extra, std::set<uint32_t>& subaddr_indices, bool autostake);
bool stake_main(const crypto::public_key& service_node_key, const cryptonote::address_parse_info& parse_info, uint32_t priority, std::set<uint32_t>& subaddr_indices, uint64_t amount, double amount_fraction, bool autostake);

View File

@ -58,6 +58,7 @@ target_link_libraries(wallet
common
cryptonote_core
mnemonics
device_trezor
${LMDB_LIBRARY}
${Boost_CHRONO_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}

View File

@ -73,6 +73,8 @@ using namespace epee;
#include "common/notify.h"
#include "ringct/rctSigs.h"
#include "ringdb.h"
#include "device/device_cold.hpp"
#include "device_trezor/device_trezor.hpp"
extern "C"
{
@ -767,6 +769,11 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra)
return idx + extra;
}
static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet)
{
shim->get_tx_pub_key_from_received_outs = boost::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, _1);
}
//-----------------------------------------------------------------
} //namespace
@ -1059,8 +1066,9 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl
bool wallet2::reconnect_device()
{
bool r = true;
hw::device &hwdev = hw::get_device(m_device_name);
hw::device &hwdev = lookup_device(m_device_name);
hwdev.set_name(m_device_name);
hwdev.set_network_type(m_nettype);
r = hwdev.init();
if (!r){
LOG_PRINT_L2("Could not init device");
@ -2931,8 +2939,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;
}
start_height = res.distributions[0].start_height;
distribution = std::move(res.distributions[0].distribution);
start_height = res.distributions[0].data.start_height;
distribution = std::move(res.distributions[0].data.distribution);
return true;
}
//----------------------------------------------------------------------------------------------------
@ -3004,6 +3012,7 @@ bool wallet2::deinit()
{
m_is_initialized=false;
unlock_keys_file();
m_account.deinit();
return true;
}
//----------------------------------------------------------------------------------------------------
@ -3473,13 +3482,20 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
r = epee::serialization::load_t_from_binary(m_account, account_data);
THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
if (m_key_device_type == hw::device::device_type::LEDGER) {
if (m_key_device_type == hw::device::device_type::LEDGER || m_key_device_type == hw::device::device_type::TREZOR) {
LOG_PRINT_L0("Account on device. Initing device...");
hw::device &hwdev = hw::get_device(m_device_name);
hwdev.set_name(m_device_name);
hwdev.init();
hwdev.connect();
hw::device &hwdev = lookup_device(m_device_name);
THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name);
hwdev.set_network_type(m_nettype);
THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name);
THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name);
m_account.set_device(hwdev);
account_public_address device_account_public_address;
THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address");
THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. "
"Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) +
", wallet address: " + m_account.get_public_address_str(m_nettype));
LOG_PRINT_L0("Device inited...");
} else if (key_on_device()) {
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "hardware device not supported");
@ -3510,7 +3526,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
const cryptonote::account_keys& keys = m_account.get_keys();
hw::device &hwdev = m_account.get_device();
r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
if(!m_watch_only && !m_multisig)
if(!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD)
r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
@ -3534,7 +3550,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password)
{
// this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
unlock_keys_file();
bool r = verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds);
bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds);
lock_keys_file();
return r;
}
@ -3974,8 +3990,9 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
}
auto &hwdev = hw::get_device(device_name);
auto &hwdev = lookup_device(device_name);
hwdev.set_name(device_name);
hwdev.set_network_type(m_nettype);
m_account.create_from_device(hwdev);
m_key_device_type = m_account.get_device().get_type();
@ -5821,22 +5838,8 @@ bool wallet2::parse_tx_from_str(const std::string &signed_tx_st, std::vector<too
}
// import key images
if (signed_txs.key_images.size() > m_transfers.size())
{
LOG_PRINT_L1("More key images returned that we know outputs for");
return false;
}
for (size_t i = 0; i < signed_txs.key_images.size(); ++i)
{
transfer_details &td = m_transfers[i];
if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != signed_txs.key_images[i])
LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << i << ": trusting imported one");
td.m_key_image = signed_txs.key_images[i];
m_key_images[m_transfers[i].m_key_image] = i;
td.m_key_image_known = true;
td.m_key_image_partial = false;
m_pub_keys[m_transfers[i].get_public_key()] = i;
}
bool r = import_key_images(signed_txs.key_images);
if (!r) return false;
ptx = signed_txs.ptx;
@ -6359,6 +6362,19 @@ crypto::chacha_key wallet2::get_ringdb_key()
return *m_ringdb_key;
}
void wallet2::register_devices(){
hw::trezor::register_all();
}
hw::device& wallet2::lookup_device(const std::string & device_descriptor){
if (!m_devices_registered){
m_devices_registered = true;
register_devices();
}
return hw::get_device(device_descriptor);
}
bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx)
{
if (!m_ringdb)
@ -6706,15 +6722,21 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
uint64_t rct_start_height;
std::vector<uint64_t> rct_offsets;
bool has_rct = false;
uint64_t max_rct_index = 0;
for (size_t idx: selected_transfers)
if (m_transfers[idx].is_rct())
{ has_rct = true; break; }
{
has_rct = true;
max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
}
const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
if (has_rct_distribution)
{
// check we're clear enough of rct start, to avoid corner cases below
THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE,
error::get_output_distribution, "Not enough rct outputs");
THROW_WALLET_EXCEPTION_IF(rct_offsets.back() <= max_rct_index,
error::get_output_distribution, "Daemon reports suspicious number of rct outputs");
}
// get histogram for the amounts we need
@ -6770,13 +6792,13 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
{
if (d.amount == amount)
{
THROW_WALLET_EXCEPTION_IF(d.start_height > segregation_fork_height, error::get_output_distribution, "Distribution start_height too high");
THROW_WALLET_EXCEPTION_IF(segregation_fork_height - d.start_height >= d.distribution.size(), error::get_output_distribution, "Distribution size too small");
THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.start_height >= d.distribution.size(), error::get_output_distribution, "Distribution size too small");
THROW_WALLET_EXCEPTION_IF(d.data.start_height > segregation_fork_height, error::get_output_distribution, "Distribution start_height too high");
THROW_WALLET_EXCEPTION_IF(segregation_fork_height - d.data.start_height >= d.data.distribution.size(), error::get_output_distribution, "Distribution size too small");
THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.data.start_height >= d.data.distribution.size(), error::get_output_distribution, "Distribution size too small");
THROW_WALLET_EXCEPTION_IF(segregation_fork_height <= RECENT_OUTPUT_BLOCKS, error::wallet_internal_error, "Fork height too low");
THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS < d.start_height, error::get_output_distribution, "Bad start height");
uint64_t till_fork = d.distribution[segregation_fork_height - d.start_height];
uint64_t recent = till_fork - d.distribution[segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.start_height];
THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS < d.data.start_height, error::get_output_distribution, "Bad start height");
uint64_t till_fork = d.data.distribution[segregation_fork_height - d.data.start_height];
uint64_t recent = till_fork - d.data.distribution[segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.data.start_height];
segregation_limit[amount] = std::make_pair(till_fork, recent);
found = true;
break;
@ -8386,7 +8408,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// early out if we know we can't make it anyway
// we could also check for being within FEE_PER_KB, but if the fee calculation
// ever changes, this might be missed, so let this go through
const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof)) / 1024;
const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof));
uint64_t balance_subtotal = 0;
uint64_t unlocked_balance_subtotal = 0;
for (uint32_t index_minor : subaddr_indices)
@ -8454,12 +8476,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
}
// shuffle & sort output indices
// sort output indices
{
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), g);
std::shuffle(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), g);
auto sort_predicate = [&unlocked_balance_per_subaddr] (const std::pair<uint32_t, std::vector<size_t>>& x, const std::pair<uint32_t, std::vector<size_t>>& y)
{
return unlocked_balance_per_subaddr[x.first] > unlocked_balance_per_subaddr[y.first];
@ -8651,7 +8669,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
cryptonote::transaction test_tx;
pending_tx test_ptx;
needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_multiplier);
needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
uint64_t inputs = 0, outputs = needed_fee;
for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
@ -9105,6 +9123,62 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
return ptx_vector;
}
//----------------------------------------------------------------------------------------------------
void wallet2::cold_tx_aux_import(const std::vector<pending_tx> & ptx, const std::vector<std::string> & tx_device_aux)
{
CHECK_AND_ASSERT_THROW_MES(ptx.size() == tx_device_aux.size(), "TX aux has invalid size");
for (size_t i = 0; i < ptx.size(); ++i){
crypto::hash txid;
txid = get_transaction_hash(ptx[i].tx);
set_tx_device_aux(txid, tx_device_aux[i]);
}
}
//----------------------------------------------------------------------------------------------------
void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux)
{
auto & hwdev = get_account().get_device();
if (!hwdev.has_tx_cold_sign()){
throw std::invalid_argument("Device does not support cold sign protocol");
}
unsigned_tx_set txs;
for (auto &tx: ptx_vector)
{
txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
}
txs.transfers = m_transfers;
auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
hw::tx_aux_data aux_data;
hw::wallet_shim wallet_shim;
setup_shim(&wallet_shim, this);
aux_data.tx_recipients = dsts_info;
dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data);
tx_device_aux = aux_data.tx_device_aux;
MDEBUG("Signed tx data from hw: " << exported_txs.ptx.size() << " transactions");
for (auto &c_ptx: exported_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx));
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
auto & hwdev = get_account().get_device();
if (!hwdev.has_ki_cold_sync()){
throw std::invalid_argument("Device does not support cold ki sync protocol");
}
auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
hw::wallet_shim wallet_shim;
setup_shim(&wallet_shim, this);
dev_cold->ki_sync(&wallet_shim, m_transfers, ski);
return import_key_images(ski, spent, unspent);
}
//----------------------------------------------------------------------------------------------------
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
{
boost::optional<std::string> result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
@ -10259,6 +10333,19 @@ std::string wallet2::get_tx_note(const crypto::hash &txid) const
return i->second;
}
void wallet2::set_tx_device_aux(const crypto::hash &txid, const std::string &aux)
{
m_tx_device[txid] = aux;
}
std::string wallet2::get_tx_device_aux(const crypto::hash &txid) const
{
std::unordered_map<crypto::hash, std::string>::const_iterator i = m_tx_device.find(txid);
if (i == m_tx_device.end())
return std::string();
return i->second;
}
void wallet2::set_attribute(const std::string &key, const std::string &value)
{
m_attributes[key] = value;
@ -10804,6 +10891,29 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
return m_transfers[signed_key_images.size() - 1].m_block_height;
}
bool wallet2::import_key_images(std::vector<crypto::key_image> key_images)
{
if (key_images.size() > m_transfers.size())
{
LOG_PRINT_L1("More key images returned that we know outputs for");
return false;
}
for (size_t i = 0; i < key_images.size(); ++i)
{
transfer_details &td = m_transfers[i];
if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[i])
LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << i << ": trusting imported one");
td.m_key_image = key_images[i];
m_key_images[m_transfers[i].m_key_image] = i;
td.m_key_image_known = true;
td.m_key_image_partial = false;
m_pub_keys[m_transfers[i].get_public_key()] = i;
}
return true;
}
wallet2::payment_container wallet2::export_payments() const
{
payment_container payments;

View File

@ -55,6 +55,7 @@
#include "ringct/rctTypes.h"
#include "ringct/rctOps.h"
#include "checkpoints/checkpoints.h"
#include "serialization/pair.h"
#include "wallet_errors.h"
#include "common/password.h"
@ -777,6 +778,9 @@ namespace tools
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool is_staking_tx=false);
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool is_staking_tx=false);
void cold_tx_aux_import(const std::vector<pending_tx>& ptx, const std::vector<std::string>& tx_device_aux);
void cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux);
uint64_t cold_key_image_sync(uint64_t &spent, uint64_t &unspent);
bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func);
@ -906,6 +910,9 @@ namespace tools
if(ver < 25)
return;
a & m_last_block_reward;
if(ver < 26)
return;
a & m_tx_device;
}
/*!
@ -1034,6 +1041,9 @@ namespace tools
void set_tx_note(const crypto::hash &txid, const std::string &note);
std::string get_tx_note(const crypto::hash &txid) const;
void set_tx_device_aux(const crypto::hash &txid, const std::string &aux);
std::string get_tx_device_aux(const crypto::hash &txid) const;
void set_description(const std::string &description);
std::string get_description() const;
@ -1088,6 +1098,8 @@ namespace tools
std::vector<std::pair<crypto::key_image, crypto::signature>> export_key_images() const;
uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent = true);
uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent);
bool import_key_images(std::vector<crypto::key_image> key_images);
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
void update_pool_state(bool refreshed = false);
void remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes);
@ -1248,7 +1260,6 @@ namespace tools
void set_unspent(size_t idx);
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const;
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, std::vector<tx_money_got_in_out> &tx_money_got_in_outs, std::vector<size_t> &outs);
@ -1265,6 +1276,9 @@ namespace tools
crypto::chacha_key get_ringdb_key();
void setup_keys(const epee::wipeable_string &password);
void register_devices();
hw::device& lookup_device(const std::string & device_descriptor);
bool get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
uint64_t get_segregation_fork_height() const;
@ -1359,6 +1373,9 @@ namespace tools
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
std::string m_device_name;
// Aux transaction data from device
std::unordered_map<crypto::hash, std::string> m_tx_device;
// Light wallet
bool m_light_wallet; /* sends view key to daemon for scanning */
uint64_t m_light_wallet_scanned_block_height;
@ -1385,11 +1402,12 @@ namespace tools
boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh;
bool m_unattended;
bool m_devices_registered;
std::shared_ptr<tools::Notify> m_tx_notify;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 25)
BOOST_CLASS_VERSION(tools::wallet2, 26)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)

View File

@ -46,6 +46,8 @@ set(performance_tests_headers
range_proof.h
bulletproof.h
crypto_ops.h
sc_reduce32.h
sc_check.h
multiexp.h
multi_tx_test_base.h
performance_tests.h

View File

@ -47,6 +47,7 @@ enum test_op
op_scalarmultKey,
op_scalarmultH,
op_scalarmult8,
op_ge_dsm_precomp,
op_ge_double_scalarmult_base_vartime,
op_ge_double_scalarmult_precomp_vartime,
op_ge_double_scalarmult_precomp_vartime2,
@ -84,6 +85,7 @@ public:
ge_cached tmp_cached;
ge_p1p1 tmp_p1p1;
ge_p2 tmp_p2;
ge_dsmp dsmp;
switch (op)
{
case op_sc_add: sc_add(key.bytes, scalar0.bytes, scalar1.bytes); break;
@ -101,6 +103,7 @@ public:
case op_scalarmultKey: rct::scalarmultKey(point0, scalar0); break;
case op_scalarmultH: rct::scalarmultH(scalar0); break;
case op_scalarmult8: rct::scalarmult8(point0); break;
case op_ge_dsm_precomp: ge_dsm_precomp(dsmp, &p3_0); break;
case op_ge_double_scalarmult_base_vartime: ge_double_scalarmult_base_vartime(&tmp_p2, scalar0.bytes, &p3_0, scalar1.bytes); break;
case op_ge_double_scalarmult_precomp_vartime: ge_double_scalarmult_precomp_vartime(&tmp_p2, scalar0.bytes, &p3_0, scalar1.bytes, precomp0); break;
case op_ge_double_scalarmult_precomp_vartime2: ge_double_scalarmult_precomp_vartime2(&tmp_p2, scalar0.bytes, precomp0, scalar1.bytes, precomp1); break;

View File

@ -50,6 +50,7 @@
#include "is_out_to_acc.h"
#include "subaddress_expand.h"
#include "sc_reduce32.h"
#include "sc_check.h"
#include "cn_fast_hash.h"
#include "rct_mlsag.h"
#include "equality.h"
@ -184,6 +185,7 @@ int main(int argc, char** argv)
TEST_PERFORMANCE0(filter, p, test_ge_frombytes_vartime);
TEST_PERFORMANCE0(filter, p, test_generate_keypair);
TEST_PERFORMANCE0(filter, p, test_sc_reduce32);
TEST_PERFORMANCE0(filter, p, test_sc_check);
TEST_PERFORMANCE1(filter, p, test_signature, false);
TEST_PERFORMANCE1(filter, p, test_signature, true);
@ -249,6 +251,7 @@ int main(int argc, char** argv)
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultKey);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultH);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmult8);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_dsm_precomp);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_base_vartime);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime);
TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime2);

View File

@ -78,9 +78,9 @@ public:
case multiexp_straus_cached:
return res == straus(data, straus_cache);
case multiexp_pippenger:
return res == pippenger(data, NULL, c);
return res == pippenger(data, NULL, 0, c);
case multiexp_pippenger_cached:
return res == pippenger(data, pippenger_cache, c);
return res == pippenger(data, pippenger_cache, 0, c);
default:
return false;
}

View File

@ -0,0 +1,52 @@
// Copyright (c) 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.
#pragma once
#include "crypto/crypto.h"
class test_sc_check
{
public:
static const size_t loop_count = 10000000;
bool init()
{
m_scalar = crypto::rand<crypto::ec_scalar>();
return true;
}
bool test()
{
sc_check((unsigned char*)m_scalar.data);
return true;
}
private:
crypto::ec_scalar m_scalar;
};

View File

@ -44,7 +44,14 @@ TEST(notify, works)
#ifdef __GLIBC__
mode_t prevmode = umask(077);
#endif
char name_template[] = "/tmp/monero-notify-unit-test-XXXXXX";
const char *tmp = getenv("TEMP");
if (!tmp)
tmp = "/tmp";
static const char *filename = "monero-notify-unit-test-XXXXXX";
const size_t len = strlen(tmp) + 1 + strlen(filename);
char *name_template = (char*)malloc(len + 1);
ASSERT_TRUE(name_template != NULL);
snprintf(name_template, len + 1, "%s/%s", tmp, filename);
int fd = mkstemp(name_template);
#ifdef __GLIBC__
umask(prevmode);
@ -68,4 +75,5 @@ TEST(notify, works)
ASSERT_TRUE(s == "1111111111111111111111111111111111111111111111111111111111111111");
boost::filesystem::remove(name_template);
free(name_template);
}