Merge pull request 'upstream' (#354) from wowario/wownero:upstream into master
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is passing Details

Reviewed-on: #354
pull/360/head v0.9.2.1
jwinterm 3 years ago
commit 607cb33668

@ -982,10 +982,12 @@ list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
if (APPLE)
if(DEPENDS)
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit")
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework AppKit -framework IOKit")
else()
find_library(COREFOUNDATION CoreFoundation)
find_library(APPKIT AppKit)
find_library(IOKIT IOKit)
list(APPEND EXTRA_LIBRARIES ${APPKIT})
list(APPEND EXTRA_LIBRARIES ${IOKIT})
list(APPEND EXTRA_LIBRARIES ${COREFOUNDATION})
endif()

@ -90,7 +90,7 @@ Dates are provided in the format YYYY-MM-DD.
| 114,969 | 2019-06-14 | F For Fappening | v0.6.1.0 | v0.6.1.2 | RandomWOW, new block weight algorithm, slightly more efficient RingCT format
| 160,777 | 2019-11-20 | Gaping Goatse | v0.7.0.0 | v0.7.1.0 | Only allow >= 2 outputs, change to the block median used to calculate penalty, rct sigs in coinbase forbidden, 4 unlock time as protocol rule
| - | 2020-06-28 | Hallucinogenic Hypnotoad | v0.8.0.0 | v0.8.0.2 | Dandelion++ support
| 253,999 | 2020-10-09 | Illiterate Illuminati | v0.9.0.0 | v0.9.2.0 | Dynamic coinbase unlock (up to 1 mo.), Deterministic unlock times, Enforce maximum coinbase amount, show_qr_code wallet command, CLSAG
| 253,999 | 2020-10-09 | Illiterate Illuminati | v0.9.0.0 | v0.9.2.1 | Dynamic coinbase unlock (up to 1 mo.), Deterministic unlock times, Enforce maximum coinbase amount, show_qr_code wallet command, CLSAG
X's indicate that these details have not been determined as of commit date.

@ -175,7 +175,7 @@ namespace epee
void put_n(const std::uint8_t ch, const std::size_t count)
{
check(count);
std::memset(tellp(), count, ch);
std::memset(tellp(), ch, count);
next_write_ += count;
}

@ -207,7 +207,6 @@ PRAGMA_WARNING_DISABLE_VS(4355)
buffer_ssl_init_fill = 0;
if (is_income && m_ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_disabled)
socket().async_receive(boost::asio::buffer(buffer_),
boost::asio::socket_base::message_peek,
strand_.wrap(
std::bind(&connection<t_protocol_handler>::handle_receive, self,
std::placeholders::_1,
@ -334,6 +333,9 @@ PRAGMA_WARNING_DISABLE_VS(4355)
TRY_ENTRY();
//_info("[sock " << socket().native_handle() << "] Async read calledback.");
if (m_was_shutdown)
return;
if (!e)
{
double current_speed_down;
@ -360,6 +362,9 @@ PRAGMA_WARNING_DISABLE_VS(4355)
CRITICAL_REGION_LOCAL( epee::net_utils::network_throttle_manager::m_lock_get_global_throttle_in );
delay = epee::net_utils::network_throttle_manager::get_global_throttle_in().get_sleep_time_after_tick( bytes_transferred );
}
if (m_was_shutdown)
return;
delay *= 0.5;
long int ms = (long int)(delay * 100);
@ -431,6 +436,9 @@ PRAGMA_WARNING_DISABLE_VS(4355)
std::size_t bytes_transferred)
{
TRY_ENTRY();
if (m_was_shutdown) return;
if (e)
{
// offload the error case
@ -438,13 +446,11 @@ PRAGMA_WARNING_DISABLE_VS(4355)
return;
}
reset_timer(get_timeout_from_bytes_read(bytes_transferred), false);
buffer_ssl_init_fill += bytes_transferred;
if (buffer_ssl_init_fill <= get_ssl_magic_size())
MTRACE("we now have " << buffer_ssl_init_fill << "/" << get_ssl_magic_size() << " bytes needed to detect SSL");
if (buffer_ssl_init_fill < get_ssl_magic_size())
{
socket().async_receive(boost::asio::buffer(buffer_.data() + buffer_ssl_init_fill, buffer_.size() - buffer_ssl_init_fill),
boost::asio::socket_base::message_peek,
strand_.wrap(
boost::bind(&connection<t_protocol_handler>::handle_receive, connection<t_protocol_handler>::shared_from_this(),
boost::asio::placeholders::error,
@ -470,7 +476,7 @@ PRAGMA_WARNING_DISABLE_VS(4355)
if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled)
{
// Handshake
if (!handshake(boost::asio::ssl::stream_base::server))
if (!handshake(boost::asio::ssl::stream_base::server, boost::asio::const_buffer(buffer_.data(), buffer_ssl_init_fill)))
{
MERROR("SSL handshake failed");
boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1);
@ -485,6 +491,11 @@ PRAGMA_WARNING_DISABLE_VS(4355)
return;
}
}
else
{
handle_read(e, buffer_ssl_init_fill);
return;
}
async_read_some(boost::asio::buffer(buffer_),
strand_.wrap(
@ -651,6 +662,8 @@ PRAGMA_WARNING_DISABLE_VS(4355)
boost::this_thread::sleep(boost::posix_time::milliseconds( ms ) );
m_send_que_lock.lock();
_dbg1("sleep for queue: " << ms);
if (m_was_shutdown)
return false;
if (retry > retry_limit) {
MWARNING("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection");
@ -748,7 +761,8 @@ PRAGMA_WARNING_DISABLE_VS(4355)
template<class t_protocol_handler>
void connection<t_protocol_handler>::reset_timer(boost::posix_time::milliseconds ms, bool add)
{
if (ms.total_milliseconds() < 0)
const auto tms = ms.total_milliseconds();
if (tms < 0 || (add && tms == 0))
{
MWARNING("Ignoring negative timeout " << ms);
return;

@ -132,10 +132,10 @@ class connection_basic { // not-templated base class for rapid developmet of som
ssl_support_t get_ssl_support() const { return m_ssl_support; }
void disable_ssl() { m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; }
bool handshake(boost::asio::ssl::stream_base::handshake_type type)
bool handshake(boost::asio::ssl::stream_base::handshake_type type, boost::asio::const_buffer buffer = {})
{
//m_state != nullptr verified in constructor
return m_state->ssl_options().handshake(socket_, type);
return m_state->ssl_options().handshake(socket_, type, buffer);
}
template<typename MutableBufferSequence, typename ReadHandler>

@ -72,7 +72,8 @@ namespace levin
#define LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED 0
#define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default
#define LEVIN_INITIAL_MAX_PACKET_SIZE 256*1024 // 256 KiB before handshake
#define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default after handshake
#define LEVIN_PACKET_REQUEST 0x00000001
#define LEVIN_PACKET_RESPONSE 0x00000002

@ -84,7 +84,8 @@ class async_protocol_handler_config
public:
typedef t_connection_context connection_context;
uint64_t m_max_packet_size;
uint64_t m_initial_max_packet_size;
uint64_t m_max_packet_size;
uint64_t m_invoke_timeout;
int invoke(int command, const epee::span<const uint8_t> in_buff, std::string& buff_out, boost::uuids::uuid connection_id);
@ -105,7 +106,7 @@ public:
size_t get_in_connections_count();
void set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*) = NULL);
async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE), m_invoke_timeout(LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_initial_max_packet_size(LEVIN_INITIAL_MAX_PACKET_SIZE), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE), m_invoke_timeout(LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
{}
~async_protocol_handler_config() { set_handler(NULL, NULL); }
void del_out_connections(size_t count);
@ -162,6 +163,7 @@ public:
net_utils::i_service_endpoint* m_pservice_endpoint;
config_type& m_config;
t_connection_context& m_connection_context;
std::atomic<uint64_t> m_max_packet_size;
net_utils::buffer m_cache_in_buffer;
stream_state m_state;
@ -289,7 +291,8 @@ public:
m_current_head(bucket_head2()),
m_pservice_endpoint(psnd_hndlr),
m_config(config),
m_connection_context(conn_context),
m_connection_context(conn_context),
m_max_packet_size(config.m_initial_max_packet_size),
m_cache_in_buffer(4 * 1024),
m_state(stream_state_head)
{
@ -399,13 +402,14 @@ public:
}
// these should never fail, but do runtime check for safety
CHECK_AND_ASSERT_MES(m_config.m_max_packet_size >= m_cache_in_buffer.size(), false, "Bad m_cache_in_buffer.size()");
CHECK_AND_ASSERT_MES(m_config.m_max_packet_size - m_cache_in_buffer.size() >= m_fragment_buffer.size(), false, "Bad m_cache_in_buffer.size() + m_fragment_buffer.size()");
const uint64_t max_packet_size = m_max_packet_size;
CHECK_AND_ASSERT_MES(max_packet_size >= m_cache_in_buffer.size(), false, "Bad m_cache_in_buffer.size()");
CHECK_AND_ASSERT_MES(max_packet_size - m_cache_in_buffer.size() >= m_fragment_buffer.size(), false, "Bad m_cache_in_buffer.size() + m_fragment_buffer.size()");
// flipped to subtraction; prevent overflow since m_max_packet_size is variable and public
if(cb > m_config.m_max_packet_size - m_cache_in_buffer.size() - m_fragment_buffer.size())
if(cb > max_packet_size - m_cache_in_buffer.size() - m_fragment_buffer.size())
{
MWARNING(m_connection_context << "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size
MWARNING(m_connection_context << "Maximum packet size exceed!, m_max_packet_size = " << max_packet_size
<< ", packet received " << m_cache_in_buffer.size() + cb
<< ", connection will be closed.");
return false;
@ -430,7 +434,7 @@ public:
//async call scenario
boost::shared_ptr<invoke_response_handler_base> response_handler = m_invoke_response_handlers.front();
response_handler->reset_timer();
MDEBUG(m_connection_context << "LEVIN_PACKET partial msg received. len=" << cb);
MDEBUG(m_connection_context << "LEVIN_PACKET partial msg received. len=" << cb << ", current total " << m_cache_in_buffer.size() << "/" << m_current_head.m_cb << " (" << (100.0f * m_cache_in_buffer.size() / (m_current_head.m_cb ? m_current_head.m_cb : 1)) << "%)");
}
}
break;
@ -465,6 +469,14 @@ public:
temp = std::move(m_fragment_buffer);
m_fragment_buffer.clear();
std::memcpy(std::addressof(m_current_head), std::addressof(temp[0]), sizeof(bucket_head2));
const size_t max_bytes = m_connection_context.get_max_bytes(m_current_head.m_command);
if(m_current_head.m_cb > std::min<size_t>(max_packet_size, max_bytes))
{
MERROR(m_connection_context << "Maximum packet size exceed!, m_max_packet_size = " << std::min<size_t>(max_packet_size, max_bytes)
<< ", packet header received " << m_current_head.m_cb << ", command " << m_current_head.m_command
<< ", connection will be closed.");
return false;
}
buff_to_invoke = {reinterpret_cast<const uint8_t*>(temp.data()) + sizeof(bucket_head2), temp.size() - sizeof(bucket_head2)};
}
@ -519,6 +531,10 @@ public:
m_current_head.m_command, buff_to_invoke, return_buff, m_connection_context
);
// peer_id remains unset if dropped
if (m_current_head.m_command == m_connection_context.handshake_command() && m_connection_context.handshake_complete())
m_max_packet_size = m_config.m_max_packet_size;
bucket_head2 head = make_header(m_current_head.m_command, return_buff.size(), LEVIN_PACKET_RESPONSE, false);
head.m_return_code = SWAP32LE(return_code);
@ -576,10 +592,11 @@ public:
m_cache_in_buffer.erase(sizeof(bucket_head2));
m_state = stream_state_body;
m_oponent_protocol_ver = m_current_head.m_protocol_version;
if(m_current_head.m_cb > m_config.m_max_packet_size)
const size_t max_bytes = m_connection_context.get_max_bytes(m_current_head.m_command);
if(m_current_head.m_cb > std::min<size_t>(max_packet_size, max_bytes))
{
LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size
<< ", packet header received " << m_current_head.m_cb
LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << std::min<size_t>(max_packet_size, max_bytes)
<< ", packet header received " << m_current_head.m_cb << ", command " << m_current_head.m_command
<< ", connection will be closed.");
return false;
}
@ -633,6 +650,9 @@ public:
boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0);
CRITICAL_REGION_BEGIN(m_invoke_response_handlers_lock);
if (command == m_connection_context.handshake_command())
m_max_packet_size = m_config.m_max_packet_size;
if(!send_message(command, in_buff, LEVIN_PACKET_REQUEST, true))
{
LOG_ERROR_CC(m_connection_context, "Failed to do_send");
@ -674,6 +694,9 @@ public:
boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0);
if (command == m_connection_context.handshake_command())
m_max_packet_size = m_config.m_max_packet_size;
if (!send_message(command, in_buff, LEVIN_PACKET_REQUEST, true))
{
LOG_ERROR_CC(m_connection_context, "Failed to send request");

@ -179,7 +179,7 @@ namespace net_utils
// SSL Options
if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
{
if (!m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, addr, timeout))
if (!m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout))
{
if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
{

@ -132,6 +132,7 @@ namespace net_utils
bool handshake(
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket,
boost::asio::ssl::stream_base::handshake_type type,
boost::asio::const_buffer buffer = {},
const std::string& host = {},
std::chrono::milliseconds timeout = std::chrono::seconds(15)) const;
};

@ -141,7 +141,6 @@ public:
rolling_median_t(rolling_median_t &&m)
{
free(data);
memcpy(this, &m, sizeof(rolling_median_t));
m.data = NULL;
}

@ -98,7 +98,12 @@ namespace epee
return false;
}
return serialization::load_t_from_binary(result_struct, epee::strspan<uint8_t>(pri->m_body));
static const constexpr epee::serialization::portable_storage::limits_t default_http_bin_limits = {
65536 * 3, // objects
65536 * 3, // fields
65536 * 3, // strings
};
return serialization::load_t_from_binary(result_struct, epee::strspan<uint8_t>(pri->m_body), &default_http_bin_limits);
}
template<class t_request, class t_response, class t_transport>

@ -52,6 +52,11 @@ namespace
snprintf(buf, sizeof(buf), "command-%u", command);
return on_levin_traffic(context, initiator, sent, error, bytes, buf);
}
static const constexpr epee::serialization::portable_storage::limits_t default_levin_limits = {
8192, // objects
16384, // fields
16384, // strings
};
}
namespace epee
@ -77,7 +82,7 @@ namespace epee
return false;
}
serialization::portable_storage stg_ret;
if(!stg_ret.load_from_binary(buff_to_recv))
if(!stg_ret.load_from_binary(buff_to_recv, &default_levin_limits))
{
LOG_ERROR("Failed to load_from_binary on command " << command);
return false;
@ -124,7 +129,7 @@ namespace epee
return false;
}
typename serialization::portable_storage stg_ret;
if(!stg_ret.load_from_binary(buff_to_recv))
if(!stg_ret.load_from_binary(buff_to_recv, &default_levin_limits))
{
on_levin_traffic(context, true, false, true, buff_to_recv.size(), command);
LOG_ERROR("Failed to load_from_binary on command " << command);
@ -155,7 +160,7 @@ namespace epee
return false;
}
serialization::portable_storage stg_ret;
if(!stg_ret.load_from_binary(buff))
if(!stg_ret.load_from_binary(buff, &default_levin_limits))
{
on_levin_traffic(context, true, false, true, buff.size(), command);
LOG_ERROR("Failed to load_from_binary on command " << command);
@ -205,7 +210,7 @@ namespace epee
int buff_to_t_adapter(int command, const epee::span<const uint8_t> in_buff, byte_slice& buff_out, callback_t cb, t_context& context )
{
serialization::portable_storage strg;
if(!strg.load_from_binary(in_buff))
if(!strg.load_from_binary(in_buff, &default_levin_limits))
{
on_levin_traffic(context, false, false, true, in_buff.size(), command);
LOG_ERROR("Failed to load_from_binary in command " << command);
@ -239,7 +244,7 @@ namespace epee
int buff_to_t_adapter(t_owner* powner, int command, const epee::span<const uint8_t> in_buff, callback_t cb, t_context& context)
{
serialization::portable_storage strg;
if(!strg.load_from_binary(in_buff))
if(!strg.load_from_binary(in_buff, &default_levin_limits))
{
on_levin_traffic(context, false, false, true, in_buff.size(), command);
LOG_ERROR("Failed to load_from_binary in notify " << command);

@ -54,6 +54,13 @@ namespace epee
typedef epee::serialization::harray harray;
typedef storage_entry meta_entry;
struct limits_t
{
size_t n_objects;
size_t n_fields;
size_t n_strings; // not counting field names
};
portable_storage(){}
virtual ~portable_storage(){}
hsection open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist = false);
@ -84,8 +91,8 @@ namespace epee
//-------------------------------------------------------------------------------
bool store_to_binary(byte_slice& target, std::size_t initial_buffer_size = 8192);
bool load_from_binary(const epee::span<const uint8_t> target);
bool load_from_binary(const std::string& target) { return load_from_binary(epee::strspan<uint8_t>(target)); }
bool load_from_binary(const epee::span<const uint8_t> target, const limits_t *limits = NULL);
bool load_from_binary(const std::string& target, const limits_t *limits = NULL) { return load_from_binary(epee::strspan<uint8_t>(target), limits); }
template<class trace_policy>
bool dump_as_xml(std::string& targetObj, const std::string& root_name = "");
bool dump_as_json(std::string& targetObj, size_t indent = 0, bool insert_newlines = true);
@ -134,7 +141,7 @@ namespace epee
return false;//TODO: don't think i ever again will use xml - ambiguous and "overtagged" format
}
inline
bool portable_storage::load_from_binary(const epee::span<const uint8_t> source)
bool portable_storage::load_from_binary(const epee::span<const uint8_t> source, const limits_t *limits)
{
m_root.m_entries.clear();
if(source.size() < sizeof(storage_block_header))
@ -157,6 +164,8 @@ namespace epee
}
TRY_ENTRY();
throwable_buffer_reader buf_reader(source.data()+sizeof(storage_block_header), source.size()-sizeof(storage_block_header));
if (limits)
buf_reader.set_limits(limits->n_objects, limits->n_fields, limits->n_strings);
buf_reader.read(m_root);
return true;//TODO:
CATCH_ENTRY("portable_storage::load_from_binary", false);
@ -266,6 +275,7 @@ namespace epee
static_assert(std::is_rvalue_reference<entry_type&&>(), "unexpected copy of value");
TRY_ENTRY();
CHECK_AND_ASSERT(psection, nullptr);
CHECK_AND_ASSERT(!pentry_name.empty(), nullptr);
auto ins_res = psection->m_entries.emplace(pentry_name, std::forward<entry_type>(entry));
return &ins_res.first->second;
CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr);

@ -29,6 +29,7 @@
#pragma once
#include "misc_language.h"
#include "misc_log_ex.h"
#include "portable_storage_base.h"
#include "portable_storage_bin_utils.h"
@ -42,6 +43,24 @@ namespace epee
{
namespace serialization
{
template<typename T>
struct ps_min_bytes {
static constexpr const size_t strict = 4096; // actual low bound
};
template<> struct ps_min_bytes<uint64_t> { static constexpr const size_t strict = 8; };
template<> struct ps_min_bytes<int64_t> { static constexpr const size_t strict = 8; };
template<> struct ps_min_bytes<uint32_t> { static constexpr const size_t strict = 4; };
template<> struct ps_min_bytes<int32_t> { static constexpr const size_t strict = 4; };
template<> struct ps_min_bytes<uint16_t> { static constexpr const size_t strict = 2; };
template<> struct ps_min_bytes<int16_t> { static constexpr const size_t strict = 2; };
template<> struct ps_min_bytes<uint8_t> { static constexpr const size_t strict = 1; };
template<> struct ps_min_bytes<int8_t> { static constexpr const size_t strict = 1; };
template<> struct ps_min_bytes<double> { static constexpr const size_t strict = 8; };
template<> struct ps_min_bytes<bool> { static constexpr const size_t strict = 1; };
template<> struct ps_min_bytes<std::string> { static constexpr const size_t strict = 2; };
template<> struct ps_min_bytes<section> { static constexpr const size_t strict = 1; };
template<> struct ps_min_bytes<array_entry> { static constexpr const size_t strict = 1; };
struct throwable_buffer_reader
{
throwable_buffer_reader(const void* ptr, size_t sz);
@ -61,6 +80,9 @@ namespace epee
void read(section& sec);
void read(std::string& str);
void read(array_entry &ae);
template<class t_type>
size_t min_bytes() const;
void set_limits(size_t objects, size_t fields, size_t strings);
private:
struct recursuion_limitation_guard
{
@ -81,6 +103,13 @@ namespace epee
const uint8_t* m_ptr;
size_t m_count;
size_t m_recursion_count;
size_t m_objects;
size_t m_fields;
size_t m_strings;
size_t max_objects;
size_t max_fields;
size_t max_strings;
};
inline throwable_buffer_reader::throwable_buffer_reader(const void* ptr, size_t sz)
@ -92,6 +121,12 @@ namespace epee
m_ptr = (uint8_t*)ptr;
m_count = sz;
m_recursion_count = 0;
m_objects = 0;
m_fields = 0;
m_strings = 0;
max_objects = std::numeric_limits<size_t>::max();
max_fields = std::numeric_limits<size_t>::max();
max_strings = std::numeric_limits<size_t>::max();
}
inline
void throwable_buffer_reader::read(void* target, size_t count)
@ -108,6 +143,7 @@ namespace epee
RECURSION_LIMITATION();
uint8_t name_len = 0;
read(name_len);
CHECK_AND_ASSERT_THROW_MES(name_len > 0, "Section name is missing");
sce_name.resize(name_len);
read((void*)sce_name.data(), name_len);
}
@ -138,7 +174,18 @@ namespace epee
//for pod types
array_entry_t<type_name> sa;
size_t size = read_varint();
CHECK_AND_ASSERT_THROW_MES(size <= m_count, "Size sanity check failed");
CHECK_AND_ASSERT_THROW_MES(size <= m_count / ps_min_bytes<type_name>::strict, "Size sanity check failed");
if (std::is_same<type_name, section>())
{
CHECK_AND_ASSERT_THROW_MES(size <= max_objects - m_objects, "Too many objects");
m_objects += size;
}
else if (std::is_same<type_name, std::string>())
{
CHECK_AND_ASSERT_THROW_MES(size <= max_strings - m_strings, "Too many strings");
m_strings += size;
}
sa.reserve(size);
//TODO: add some optimization here later
while(size--)
@ -204,6 +251,8 @@ namespace epee
inline storage_entry throwable_buffer_reader::read_se<std::string>()
{
RECURSION_LIMITATION();
CHECK_AND_ASSERT_THROW_MES(m_strings + 1 <= max_strings, "Too many strings");
m_strings += 1;
return storage_entry(read<std::string>());
}
@ -212,6 +261,8 @@ namespace epee
inline storage_entry throwable_buffer_reader::read_se<section>()
{
RECURSION_LIMITATION();
CHECK_AND_ASSERT_THROW_MES(m_objects < max_objects, "Too many objects");
++m_objects;
section s;//use extra variable due to vs bug, line "storage_entry se(section()); " can't be compiled in visual studio
storage_entry se(std::move(s));
section& section_entry = boost::get<section>(se);
@ -263,12 +314,16 @@ namespace epee
RECURSION_LIMITATION();
sec.m_entries.clear();
size_t count = read_varint();
CHECK_AND_ASSERT_THROW_MES(count <= max_fields - m_fields, "Too many object fields");
m_fields += count;
while(count--)
{
//read section name string
std::string sec_name;
read_sec_name(sec_name);
sec.m_entries.emplace(std::move(sec_name), load_storage_entry());
const auto insert_loc = sec.m_entries.lower_bound(sec_name);
CHECK_AND_ASSERT_THROW_MES(insert_loc == sec.m_entries.end() || insert_loc->first != sec_name, "duplicate key: " << sec_name);
sec.m_entries.emplace_hint(insert_loc, std::move(sec_name), load_storage_entry());
}
}
inline
@ -289,5 +344,12 @@ namespace epee
RECURSION_LIMITATION();
CHECK_AND_ASSERT_THROW_MES(false, "Reading array entry is not supported");
}
inline
void throwable_buffer_reader::set_limits(size_t objects, size_t fields, size_t strings)
{
max_objects = objects;
max_fields = fields;
max_strings = strings;
}
}
}

@ -85,10 +85,10 @@ namespace epee
}
//-----------------------------------------------------------------------------------------------------------
template<class t_struct>
bool load_t_from_binary(t_struct& out, const epee::span<const uint8_t> binary_buff)
bool load_t_from_binary(t_struct& out, const epee::span<const uint8_t> binary_buff, const epee::serialization::portable_storage::limits_t *limits = NULL)
{
portable_storage ps;
bool rs = ps.load_from_binary(binary_buff);
bool rs = ps.load_from_binary(binary_buff, limits);
if(!rs)
return false;

@ -211,6 +211,7 @@ namespace epee
for(const section_pair& se: sec.m_entries)
{
CHECK_AND_ASSERT_THROW_MES(se.first.size() < std::numeric_limits<uint8_t>::max(), "storage_entry_name is too long: " << se.first.size() << ", val: " << se.first);
CHECK_AND_ASSERT_THROW_MES(!se.first.empty(), "storage_entry_name is empty");
uint8_t len = static_cast<uint8_t>(se.first.size());
strm.write((const char*)&len, sizeof(len));
strm.write(se.first.data(), size_t(len));

@ -473,6 +473,7 @@ bool ssl_options_t::has_fingerprint(boost::asio::ssl::verify_context &ctx) const
bool ssl_options_t::handshake(
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket,
boost::asio::ssl::stream_base::handshake_type type,
boost::asio::const_buffer buffer,
const std::string& host,
std::chrono::milliseconds timeout) const
{
@ -530,7 +531,7 @@ bool ssl_options_t::handshake(
});
boost::system::error_code ec = boost::asio::error::would_block;
socket.async_handshake(type, boost::lambda::var(ec) = boost::lambda::_1);
socket.async_handshake(type, boost::asio::buffer(buffer), boost::lambda::var(ec) = boost::lambda::_1);
if (io_service.stopped())
{
io_service.reset();

@ -188,6 +188,7 @@ gpg --detach-sign ${VERSION}-linux/${GH_USER}/monero-linux-*-build.assert
gpg --detach-sign ${VERSION}-win/${GH_USER}/monero-win-*-build.assert
gpg --detach-sign ${VERSION}-osx/${GH_USER}/monero-osx-*-build.assert
gpg --detach-sign ${VERSION}-android/${GH_USER}/monero-android-*-build.assert
gpg --detach-sign ${VERSION}-freebsd/${GH_USER}/monero-freebsd-*-build.assert
```
This will create a `.sig` file for each `.assert` file above (2 files for each platform).

@ -1312,17 +1312,20 @@ public:
* height. The number of blocks returned is variable, based on the max_size passed.
*
* @param start_height the height of the first block
* @param min_count the minimum number of blocks to return, if they exist
* @param max_count the maximum number of blocks to return
* @param min_block_count the minimum number of blocks to return, if they exist
* @param max_block_count the maximum number of blocks to return
* @param max_tx_count the maximum number of txes to return
* @param max_size the maximum size of block/transaction data to return (will be exceeded by one blocks's worth at most, if min_count is met)
* @param blocks the returned block/transaction data
* @param pruned whether to return full or pruned tx data
* @param skip_coinbase whether to return or skip coinbase transactions (they're in blocks regardless)
* @param get_miner_tx_hash whether to calculate and return the miner (coinbase) tx hash
*
* The call will return at least min_block_count if possible, even if this contravenes max_tx_count
*
* @return true iff the blocks and transactions were found
*/
virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const = 0;
virtual bool get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const = 0;
/**
* @brief fetches the prunable transaction blob with the given hash

@ -3001,7 +3001,7 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h) const
if (! tx_found)
{
LOG_PRINT_L1("transaction with hash " << epee::string_tools::pod_to_hex(h) << " not found in db");
LOG_PRINT_L3("transaction with hash " << epee::string_tools::pod_to_hex(h) << " not found in db");
return false;
}
@ -3032,7 +3032,7 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h, uint64_t& tx_id) const
bool ret = false;
if (get_result == MDB_NOTFOUND)
{
LOG_PRINT_L1("transaction with hash " << epee::string_tools::pod_to_hex(h) << " not found in db");
LOG_PRINT_L3("transaction with hash " << epee::string_tools::pod_to_hex(h) << " not found in db");
}
else if (get_result)
throw0(DB_ERROR(lmdb_error("DB error attempting to fetch transaction from hash", get_result).c_str()));
@ -3171,7 +3171,7 @@ bool BlockchainLMDB::get_pruned_tx_blobs_from(const crypto::hash& h, size_t coun
return true;
}
bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const
bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@ -3185,14 +3185,15 @@ bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, si
RCURSOR(txs_prunable);
}
blocks.reserve(std::min<size_t>(max_count, 10000)); // guard against very large max count if only checking bytes
blocks.reserve(std::min<size_t>(max_block_count, 10000)); // guard against very large max count if only checking bytes
const uint64_t blockchain_height = height();
uint64_t size = 0;
size_t num_txes = 0;
MDB_val_copy<uint64_t> key(start_height);
MDB_val k, v, val_tx_id;
uint64_t tx_id = ~0;
MDB_cursor_op op = MDB_SET;
for (uint64_t h = start_height; h < blockchain_height && blocks.size() < max_count && (size < max_size || blocks.size() < min_count); ++h)
for (uint64_t h = start_height; h < blockchain_height && blocks.size() < max_block_count && (size < max_size || blocks.size() < min_block_count); ++h)
{
MDB_cursor_op op = h == start_height ? MDB_SET : MDB_NEXT;
int result = mdb_cursor_get(m_cur_blocks, &key, &v, op);
@ -3243,6 +3244,7 @@ bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, si
op = MDB_NEXT;
current_block.second.reserve(b.tx_hashes.size());
num_txes += b.tx_hashes.size() + (skip_coinbase ? 0 : 1);
for (const auto &tx_hash: b.tx_hashes)
{
// get pruned data
@ -3262,6 +3264,9 @@ bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, si
current_block.second.push_back(std::make_pair(tx_hash, std::move(tx_blob)));
size += current_block.second.back().second.size();
}
if (blocks.size() >= min_block_count && num_txes >= max_tx_count)
break;
}
TXN_POSTFIX_RDONLY();

@ -257,7 +257,7 @@ public:
virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const;
virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const;
virtual bool get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const;
virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const;

@ -70,7 +70,7 @@ public:
virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const override { return false; }
virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const override { return false; }
virtual bool get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const override { return false; }
virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override { return false; }
virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; }

Binary file not shown.

@ -200,7 +200,7 @@ namespace cryptonote
return true;
}
// make RPC call to daemon
// curl http://127.0.0.1:34568/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"get_block","params":{"height":271600}}' -H 'Content-Type: application/json'
// curl http://127.0.0.1:34568/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"get_block","params":{"height":278300}}' -H 'Content-Type: application/json'
// "wide_cumulative_difficulty": "0x14eb4d0131fe8",
ADD_CHECKPOINT2(1, "97f4ce4d7879b3bea54dcec738cd2ebb7952b4e9bb9743262310cd5fec749340", "0x2");
ADD_CHECKPOINT2(6969, "aa7b66e8c461065139b55c29538a39c33ceda93e587f84d490ed573d80511c87", "0x118eef693fd"); //Hard fork to v8
@ -215,6 +215,7 @@ namespace cryptonote
ADD_CHECKPOINT2(254287, "b37cb55abe73965b424f8028bf71bef98d069645077ffa52f0c134907b7734e3", "0x1746622f56668"); //Hard fork to v17
ADD_CHECKPOINT2(256700, "389a8ab95a80e84ec74639c1078bc67b33af208ef00f53bd9609cfc40efa7059", "0x185ace3c1bd68");
ADD_CHECKPOINT2(271600, "9597cdbdc52ca57d7dbd8f9c0a23a73194ef2ebbcfdc75c21992672706108d43", "0x1e2d2d6a2a9e8");
ADD_CHECKPOINT2(278300, "b10dcdf7a51651f60fbcc0447409773eef1458d2c706d9a61daf467571ac19c9", "0x20a83a16d3968");
return true;
}

@ -871,10 +871,19 @@ std::string get_nix_version_display_string()
return max_concurrency;
}
bool is_privacy_preserving_network(const std::string &address)
{
if (boost::ends_with(address, ".onion"))
return true;
if (boost::ends_with(address, ".i2p"))
return true;
return false;
}
bool is_local_address(const std::string &address)
{
// always assume Tor/I2P addresses to be untrusted by default
if (boost::ends_with(address, ".onion") || boost::ends_with(address, ".i2p"))
if (is_privacy_preserving_network(address))
{
MDEBUG("Address '" << address << "' is Tor/I2P, non local");
return false;

@ -228,6 +228,7 @@ namespace tools
unsigned get_max_concurrency();
bool is_local_address(const std::string &address);
bool is_privacy_preserving_network(const std::string &address);
int vercmp(const char *v0, const char *v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly than lexical - does not attempt to validate
bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash);

@ -38,6 +38,7 @@ endif()
set(cryptonote_basic_sources
account.cpp
connection_context.cpp
cryptonote_basic_impl.cpp
cryptonote_format_utils.cpp
difficulty.cpp

@ -0,0 +1,71 @@
// Copyright (c) 2020, 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 "connection_context.h"
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "p2p/p2p_protocol_defs.h"
namespace cryptonote
{
std::size_t cryptonote_connection_context::get_max_bytes(const int command) noexcept
{
switch (command)
{
case nodetool::COMMAND_HANDSHAKE_T<cryptonote::CORE_SYNC_DATA>::ID:
return 65536;
case nodetool::COMMAND_TIMED_SYNC_T<cryptonote::CORE_SYNC_DATA>::ID:
return 65536;
case nodetool::COMMAND_PING::ID:
return 4096;
case nodetool::COMMAND_REQUEST_SUPPORT_FLAGS::ID:
return 4096;
case cryptonote::NOTIFY_NEW_BLOCK::ID:
return 1024 * 1024 * 128; // 128 MB (max packet is a bit less than 100 MB though)
case cryptonote::NOTIFY_NEW_TRANSACTIONS::ID:
return 1024 * 1024 * 128; // 128 MB (max packet is a bit less than 100 MB though)
case cryptonote::NOTIFY_REQUEST_GET_OBJECTS::ID:
return 1024 * 1024 * 2; // 2 MB
case cryptonote::NOTIFY_RESPONSE_GET_OBJECTS::ID:
return 1024 * 1024 * 128; // 128 MB (max packet is a bit less than 100 MB though)
case cryptonote::NOTIFY_REQUEST_CHAIN::ID:
return 512 * 1024; // 512 kB
case cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::ID:
return 1024 * 1024 * 4; // 4 MB
case cryptonote::NOTIFY_NEW_FLUFFY_BLOCK::ID:
return 1024 * 1024 * 4; // 4 MB, but it does not includes transaction data
case cryptonote::NOTIFY_REQUEST_FLUFFY_MISSING_TX::ID:
return 1024 * 1024; // 1 MB
case cryptonote::NOTIFY_GET_TXPOOL_COMPLEMENT::ID:
return 1024 * 1024 * 4; // 4 MB
default:
break;
};
return std::numeric_limits<size_t>::max();
}
} // cryptonote

@ -31,6 +31,7 @@
#pragma once
#include <unordered_set>
#include <atomic>
#include <algorithm>
#include <boost/date_time/posix_time/posix_time.hpp>
#include "net/net_utils_base.h"
#include "copyable_atomic.h"
@ -38,13 +39,12 @@
namespace cryptonote
{
struct cryptonote_connection_context: public epee::net_utils::connection_context_base
{
cryptonote_connection_context(): m_state(state_before_handshake), m_remote_blockchain_height(0), m_last_response_height(0),
m_last_request_time(boost::date_time::not_a_date_time), m_callback_request_count(0),
m_last_known_hash(crypto::null_hash), m_pruning_seed(0), m_rpc_port(0), m_rpc_credits_per_hash(0), m_anchor(false), m_score(0),
m_expect_response(0) {}
m_expect_response(0), m_expect_height(0), m_num_requested(0) {}
enum state
{
@ -55,6 +55,12 @@ namespace cryptonote
state_normal
};
static constexpr int handshake_command() noexcept { return 1001; }
bool handshake_complete() const noexcept { return m_state != state_before_handshake; }
//! \return Maximum number of bytes permissible for `command`.
static size_t get_max_bytes(int command) noexcept;
state m_state;
std::vector<std::pair<crypto::hash, uint64_t>> m_needed_objects;
std::unordered_set<crypto::hash> m_requested_objects;
@ -70,6 +76,7 @@ namespace cryptonote
int32_t m_score;
int m_expect_response;
uint64_t m_expect_height;
size_t m_num_requested;
};
inline std::string get_protocol_state_string(cryptonote_connection_context::state s)

@ -128,7 +128,8 @@
#define CRYPTONOTE_MAX_FRAGMENTS 20 // ~20 * NOISE_BYTES max payload size for covert/noise send
#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000
#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT 1000
#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT 20000
#define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000
#define P2P_LOCAL_GRAY_PEERLIST_LIMIT 5000
@ -203,6 +204,8 @@
#define RPC_CREDITS_PER_HASH_SCALE ((float)(1<<24))
#define DNS_BLOCKLIST_LIFETIME (86400 * 8)
// New constants are intended to go here
namespace config
{

@ -2780,7 +2780,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
// find split point between ours and foreign blockchain (or start at
// blockchain height <req_start_block>), and return up to max_count FULL
// blocks by reference.
bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const
bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@ -2805,8 +2805,8 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
db_rtxn_guard rtxn_guard(m_db);
total_height = get_current_blockchain_height();
blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height)));
CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash),
blocks.reserve(std::min(std::min(max_block_count, (size_t)10000), (size_t)(total_height - start_height)));
CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_block_count, max_tx_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash),
false, "Error getting blocks");
return true;
@ -2837,32 +2837,44 @@ void Blockchain::flush_invalid_blocks()
m_invalid_blocks.clear();
}
//------------------------------------------------------------------
bool Blockchain::have_block(const crypto::hash& id) const
bool Blockchain::have_block_unlocked(const crypto::hash& id, int *where) const
{
// WARNING: this function does not take m_blockchain_lock, and thus should only call read only
// m_db functions which do not depend on one another (ie, no getheight + gethash(height-1), as
// well as not accessing class members, even read only (ie, m_invalid_blocks). The caller must
// lock if it is otherwise needed.
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
if(m_db->block_exists(id))
{
LOG_PRINT_L2("block " << id << " found in main chain");
if (where) *where = HAVE_BLOCK_MAIN_CHAIN;
return true;
}
if(m_db->get_alt_block(id, NULL, NULL))
{
LOG_PRINT_L2("block " << id << " found in alternative chains");
if (where) *where = HAVE_BLOCK_ALT_CHAIN;
return true;
}
if(m_invalid_blocks.count(id))
{
LOG_PRINT_L2("block " << id << " found in m_invalid_blocks");
if (where) *where = HAVE_BLOCK_INVALID;
return true;
}
return false;
}
//------------------------------------------------------------------
bool Blockchain::have_block(const crypto::hash& id, int *where) const
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
return have_block_unlocked(id, where);
}
//------------------------------------------------------------------
bool Blockchain::handle_block_to_main_chain(const block& bl, block_verification_context& bvc, bool notify/* = true*/)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@ -4807,6 +4819,8 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector
CHECK_AND_ASSERT_MES(weights.empty() || weights.size() == hashes.size(), 0, "Unexpected weights size");
CRITICAL_REGION_LOCAL(m_blockchain_lock);
// easy case: height >= hashes
if (height >= m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP)
return hashes.size();
@ -5446,7 +5460,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "bd1d32aed063dd71c1355dc950eb0fc22b10e41530029f54d40a0fd6f6b328f1";
static const char expected_block_hashes_hash[] = "44393af098c4738ecbe664292bc345c0acb40573562cac8bb67cea75debe9f1b";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)

@ -377,10 +377,12 @@ namespace cryptonote
* for a block with the given hash
*
* @param id the hash to search for
* @param where the type of block, if non NULL
*
* @return true if the block is known, else false
*/
bool have_block(const crypto::hash& id) const;
bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const;
bool have_block(const crypto::hash& id, int *where = NULL) const;
/**
* @brief gets the total number of transactions on the main chain
@ -465,11 +467,12 @@ namespace cryptonote
* @param total_height return-by-reference our current blockchain height
* @param start_height return-by-reference the height of the first block returned
* @param pruned whether to return full or pruned tx blobs
* @param max_count the max number of blocks to get
* @param max_block_count the max number of blocks to get
* @param max_tx_count the max number of txes to get (it can get overshot by the last block's number of txes minus 1)
*
* @return true if a block found in common or req_start_block specified, else false
*/
bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const;
bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const;
/**
* @brief retrieves a set of blocks and their transactions, and possibly other transactions

@ -1446,9 +1446,9 @@ namespace cryptonote
return m_blockchain_storage.find_blockchain_supplement(qblock_ids, clip_pruned, resp);
}
//-----------------------------------------------------------------------------------------------
bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const
bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const
{
return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_count);
return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_block_count, max_tx_count);
}
//-----------------------------------------------------------------------------------------------
bool core::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const
@ -1665,9 +1665,14 @@ namespace cryptonote
return m_mempool.get_transactions_count(include_sensitive_txes);
}
//-----------------------------------------------------------------------------------------------
bool core::have_block(const crypto::hash& id) const
bool core::have_block_unlocked(const crypto::hash& id, int *where) const
{
return m_blockchain_storage.have_block(id);
return m_blockchain_storage.have_block_unlocked(id, where);
}
//-----------------------------------------------------------------------------------------------
bool core::have_block(const crypto::hash& id, int *where) const
{
return m_blockchain_storage.have_block(id, where);
}
//-----------------------------------------------------------------------------------------------
bool core::parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, const blobdata& blob) const

@ -55,6 +55,8 @@
PUSH_WARNINGS
DISABLE_VS_WARNINGS(4355)
enum { HAVE_BLOCK_MAIN_CHAIN, HAVE_BLOCK_ALT_CHAIN, HAVE_BLOCK_INVALID };
namespace cryptonote
{
struct test_options {
@ -543,7 +545,8 @@ namespace cryptonote
*
* @note see Blockchain::have_block
*/
bool have_block(const crypto::hash& id) const;
bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const;
bool have_block(const crypto::hash& id, int *where = NULL) const;
/**
* @copydoc Blockchain::get_short_chain_history
@ -564,7 +567,7 @@ namespace cryptonote
*
* @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::vector<std::pair<cryptonote::blobdata, std::vector<transaction> > >&, uint64_t&, uint64_t&, size_t) const
*/
bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const;
bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const;
/**
* @copydoc Blockchain::get_tx_outputs_gindexs

@ -150,7 +150,7 @@ namespace cryptonote
bool update_sync_search();
int try_add_next_blocks(cryptonote_connection_context &context);
void notify_new_stripe(cryptonote_connection_context &context, uint32_t stripe);
void skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const;
size_t skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const;
bool request_txpool_complement(cryptonote_connection_context &context);
void hit_score(cryptonote_connection_context &context, int32_t score);

@ -137,7 +137,7 @@ namespace cryptonote
CHECK_AND_ASSERT_MES_CC( context.m_callback_request_count > 0, false, "false callback fired, but context.m_callback_request_count=" << context.m_callback_request_count);
--context.m_callback_request_count;
if(context.m_state == cryptonote_connection_context::state_synchronizing)
if(context.m_state == cryptonote_connection_context::state_synchronizing && context.m_last_request_time == boost::posix_time::not_a_date_time)
{
NOTIFY_REQUEST_CHAIN::request r = {};
context.m_needed_objects.clear();
@ -409,6 +409,7 @@ namespace cryptonote
++context.m_callback_request_count;
m_p2p->request_callback(context);
MLOG_PEER_STATE("requesting callback");
context.m_num_requested = 0;
return true;
}
//------------------------------------------------------------------------------------------------------------------------
@ -1990,7 +1991,7 @@ skip:
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
void t_cryptonote_protocol_handler<t_core>::skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const
size_t t_cryptonote_protocol_handler<t_core>::skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const
{
// take out blocks we already have
size_t skip = 0;
@ -2007,6 +2008,7 @@ skip:
MDEBUG(context << "skipping " << skip << "/" << context.m_needed_objects.size() << " blocks");
context.m_needed_objects = std::vector<std::pair<crypto::hash, uint64_t>>(context.m_needed_objects.begin() + skip, context.m_needed_objects.end());
}
return skip;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
@ -2060,7 +2062,17 @@ skip:
const size_t block_queue_size_threshold = m_block_download_max_size ? m_block_download_max_size : BLOCK_QUEUE_SIZE_THRESHOLD;
bool queue_proceed = nspans < BLOCK_QUEUE_NSPANS_THRESHOLD || size < block_queue_size_threshold;
// get rid of blocks we already requested, or already have
skip_unneeded_hashes(context, true);
if (skip_unneeded_hashes(context, true) && context.m_needed_objects.empty() && context.m_num_requested == 0)
{
if (context.m_remote_blockchain_height > m_block_queue.get_next_needed_height(bc_height))
{
MERROR(context << "Nothing we can request from this peer, and we did not request anything previously");
return false;
}
MDEBUG(context << "Nothing to get from this peer, and it's not ahead of us, all done");
context.m_state = cryptonote_connection_context::state_normal;
return true;
}
uint64_t next_needed_height = m_block_queue.get_next_needed_height(bc_height);
uint64_t next_block_height;
if (context.m_needed_objects.empty())
@ -2196,7 +2208,17 @@ skip:
context.m_last_response_height = 0;
goto skip;
}
skip_unneeded_hashes(context, false);
if (skip_unneeded_hashes(context, false) && context.m_needed_objects.empty() && context.m_num_requested == 0)
{
if (context.m_remote_blockchain_height > m_block_queue.get_next_needed_height(m_core.get_current_blockchain_height()))
{
MERROR(context << "Nothing we can request from this peer, and we did not request anything previously");
return false;
}
MDEBUG(context << "Nothing to get from this peer, and it's not ahead of us, all done");
context.m_state = cryptonote_connection_context::state_normal;
return true;
}
const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1;
static const uint64_t bp_fork_height = m_core.get_earliest_ideal_height_for_version(HF_VERSION_SMALLER_BP);
@ -2293,35 +2315,22 @@ skip:
<< "/" << tools::get_pruning_stripe(span.first + span.second - 1, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES)
<< ", ours " << tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed()) << ", peer stripe " << tools::get_pruning_stripe(context.m_pruning_seed));
context.m_num_requested += req.blocks.size();
post_notify<NOTIFY_REQUEST_GET_OBJECTS>(req, context);
MLOG_PEER_STATE("requesting objects");
return true;
}
// if we're still around, we might be at a point where the peer is pruned, so we could either
// drop it to make space for other peers, or ask for a span further down the line
const uint32_t next_stripe = get_next_needed_pruning_stripe().first;
const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed);
const uint32_t local_stripe = tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed());
if (!(m_sync_pruned_blocks && peer_stripe == local_stripe) && next_stripe && peer_stripe && next_stripe != peer_stripe)
// we can do nothing, so drop this peer to make room for others unless we think we've downloaded all we need
const uint64_t blockchain_height = m_core.get_current_blockchain_height();
if (std::max(blockchain_height, m_block_queue.get_next_needed_height(blockchain_height)) >= m_core.get_target_blockchain_height())
{
// at this point, we have to either close the connection, or start getting blocks past the
// current point, or become dormant
MDEBUG(context << "this peer is pruned at seed " << epee::string_tools::to_string_hex(context.m_pruning_seed) <<
", next stripe needed is " << next_stripe);
if (!context.m_is_income)
{
if (should_drop_connection(context, next_stripe))
{
m_p2p->add_used_stripe_peer(context);
return false; // drop outgoing connections
}
}
// we'll get back stuck waiting for the go ahead
context.m_state = cryptonote_connection_context::state_normal;
MLOG_PEER_STATE("Nothing to do for now, switching to normal state");
return true;
}
MLOG_PEER_STATE("We can download nothing from this peer, dropping");
return false;
}
skip:
@ -2561,17 +2570,6 @@ skip:
return 1;
}
std::unordered_set<crypto::hash> hashes;
for (const auto &h: arg.m_block_ids)
{
if (!hashes.insert(h).second)
{
LOG_ERROR_CCONTEXT("sent duplicate block, dropping connection");
drop_connection(context, true, false);
return 1;
}
}
uint64_t n_use_blocks = m_core.prevalidate_block_hashes(arg.start_height, arg.m_block_ids, arg.m_block_weights);
if (n_use_blocks == 0 || n_use_blocks + HASH_OF_HASHES_STEP <= arg.m_block_ids.size())
{
@ -2583,18 +2581,74 @@ skip:
context.m_needed_objects.clear();
uint64_t added = 0;
std::unordered_set<crypto::hash> blocks_found;
bool first = true;
bool expect_unknown = false;
for (size_t i = 0; i < arg.m_block_ids.size(); ++i)
{
if (!blocks_found.insert(arg.m_block_ids[i]).second)
{
LOG_ERROR_CCONTEXT("Duplicate blocks in chain entry response, dropping connection");
drop_connection(context, true, false);
drop_connection_with_score(context, 5, false);
return 1;
}
int where;
const bool have_block = m_core.have_block_unlocked(arg.m_block_ids[i], &where);
if (first)
{
if (!have_block && !m_block_queue.requested(arg.m_block_ids[i]) && !m_block_queue.have(arg.m_block_ids[i]))
{
LOG_ERROR_CCONTEXT("First block hash is unknown, dropping connection");
drop_connection_with_score(context, 5, false);
return 1;
}
if (!have_block)
expect_unknown = true;
}
if (!first)
{
// after the first, blocks may be known or unknown, but if they are known,
// they should be at the same height if on the main chain
if (have_block)
{
switch (where)
{
default:
case HAVE_BLOCK_INVALID:
LOG_ERROR_CCONTEXT("Block is invalid or known without known type, dropping connection");
drop_connection(context, true, false);
return 1;
case HAVE_BLOCK_MAIN_CHAIN:
if (expect_unknown)
{
LOG_ERROR_CCONTEXT("Block is on the main chain, but we did not expect a known block, dropping connection");
drop_connection_with_score(context, 5, false);
return 1;
}
if (m_core.get_block_id_by_height(arg.start_height + i) != arg.m_block_ids[i])
{
LOG_ERROR_CCONTEXT("Block is on the main chain, but not at the expected height, dropping connection");
drop_connection_with_score(context, 5, false);
return 1;
}
break;
case HAVE_BLOCK_ALT_CHAIN:
if (expect_unknown)
{
LOG_ERROR_CCONTEXT("Block is on the main chain, but we did not expect a known block, dropping connection");
drop_connection_with_score(context, 5, false);
return 1;
}
break;
}
}
else
expect_unknown = true;
}
const uint64_t block_weight = arg.m_block_weights.empty() ? 0 : arg.m_block_weights[i];
context.m_needed_objects.push_back(std::make_pair(arg.m_block_ids[i], block_weight));
if (++added == n_use_blocks)
break;
first = false;
}
context.m_last_response_height -= arg.m_block_ids.size() - n_use_blocks;
@ -2608,6 +2662,7 @@ skip:
if (arg.total_height > m_core.get_target_blockchain_height())
m_core.set_target_blockchain_height(arg.total_height);
context.m_num_requested = 0;
return 1;
}
//------------------------------------------------------------------------------------------------------------------------
@ -2624,6 +2679,7 @@ skip:
std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> fullConnections, fluffyConnections;
m_p2p->for_each_connection([this, &exclude_context, &fullConnections, &fluffyConnections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)
{
// peer_id also filters out connections before handshake
if (peer_id && exclude_context.m_connection_id != context.m_connection_id && context.m_remote_address.get_zone() == epee::net_utils::zone::public_)
{
if(m_core.fluffy_blocks_enabled() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS))
@ -2784,12 +2840,15 @@ skip:
epee::string_tools::to_string_hex(context.m_pruning_seed) <<
"), score " << score << ", flush_all_spans " << flush_all_spans);
if (score > 0)
m_p2p->add_host_fail(context.m_remote_address, score);
m_block_queue.flush_spans(context.m_connection_id, flush_all_spans);
// copy since dropping the connection will invalidate the context, and thus the address
const auto remote_address = context.m_remote_address;
m_p2p->drop_connection(context);
if (score > 0)
m_p2p->add_host_fail(remote_address, score);
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>

@ -287,7 +287,6 @@ namespace levin
connection_count(0),
flush_callbacks(0),
nzone(zone),
is_public(is_public),
pad_txs(pad_txs),
fluffing(false)
{
@ -305,7 +304,6 @@ namespace levin
std::atomic<std::size_t> connection_count; //!< Only update in strand, can be read at any time
std::uint32_t flush_callbacks; //!< Number of active fluff flush callbacks queued
const epee::net_utils::zone nzone; //!< Zone is public ipv4/ipv6 connections, or i2p or tor
const bool is_public; //!< Zone is public ipv4/ipv6 connections
const bool pad_txs; //!< Pad txs to the next boundary for privacy
bool fluffing; //!< Zone is in Dandelion++ fluff epoch
};
@ -445,7 +443,7 @@ namespace levin
zone->p2p->foreach_connection([txs, now, &zone, &source, &in_duration, &out_duration, &next_flush] (detail::p2p_context& context)
{
// When i2p/tor, only fluff to outbound connections
if (source != context.m_connection_id && (zone->nzone == epee::net_utils::zone::public_ || !context.m_is_income))
if (context.handshake_complete() && source != context.m_connection_id && (zone->nzone == epee::net_utils::zone::public_ || !context.m_is_income))
{
if (context.fluff_txs.empty())
context.flush_time = now + (context.m_is_income ? in_duration() : out_duration());

@ -28,6 +28,7 @@
#include "common/dns_utils.h"
#include "common/command_line.h"
#include "net/parse.h"
#include "daemon/command_parser_executor.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@ -657,7 +658,6 @@ bool t_command_parser_executor::ban(const std::vector<std::string>& args)
std::cout << "Invalid syntax: Expects one or two parameters. For more details, use the help command." << std::endl;
return true;
}
std::string ip = args[0];
time_t seconds = P2P_IP_BLOCKTIME;
if (args.size() > 1)
{
@ -676,7 +676,51 @@ bool t_command_parser_executor::ban(const std::vector<std::string>& args)
return true;
}
}
return m_executor.ban(ip, seconds);
if (boost::starts_with(args[0], "@"))
{
const std::string ban_list = args[0].substr(1);
try
{
const boost::filesystem::path ban_list_path(ban_list);
boost::system::error_code ec;
if (!boost::filesystem::exists(ban_list_path, ec))
{
std::cout << "Can't find ban list file " + ban_list + " - " + ec.message() << std::endl;
return true;
}
bool ret = true;
std::ifstream ifs(ban_list_path.string());
for (std::string line; std::getline(ifs, line); )
{
auto subnet = net::get_ipv4_subnet_address(line);
if (subnet)
{
ret &= m_executor.ban(subnet->str(), seconds);
continue;
}
const expect<epee::net_utils::network_address> parsed_addr = net::get_network_address(line, 0);
if (parsed_addr)
{
ret &= m_executor.ban(parsed_addr->host_str(), seconds);
continue;
}
std::cout << "Invalid IP address or IPv4 subnet: " << line << std::endl;
}
return ret;
}
catch (const std::exception &e)
{
std::cout << "Error loading ban list: " << e.what() << std::endl;
return false;
}
}
else
{
const std::string ip = args[0];
return m_executor.ban(ip, seconds);
}
}
bool t_command_parser_executor::unban(const std::vector<std::string>& args)

@ -234,8 +234,8 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"ban"
, std::bind(&t_command_parser_executor::ban, &m_parser, p::_1)
, "ban <IP> [<seconds>]"
, "Ban a given <IP> for a given amount of <seconds>."
, "ban [<IP>|@<filename>] [<seconds>]"
, "Ban a given <IP> or list of IPs from a file for a given amount of <seconds>."
);
m_command_lookup.set_handler(
"unban"

@ -191,6 +191,9 @@ bool t_rpc_command_executor::print_peer_list(bool white, bool gray, size_t limit
cryptonote::COMMAND_RPC_GET_PEER_LIST::response res;
std::string failure_message = "Couldn't retrieve peer list";
req.include_blocked = true;
if (m_is_rpc)
{
if (!m_rpc_client->rpc_request(req, res, "/get_peer_list", failure_message.c_str()))
@ -237,6 +240,7 @@ bool t_rpc_command_executor::print_peer_list_stats() {
std::string failure_message = "Couldn't retrieve peer list";
req.public_only = false;
req.include_blocked = true;
if (m_is_rpc)
{

@ -149,6 +149,7 @@ namespace nodetool
const command_line::arg_descriptor<std::string> arg_ban_list = {"ban-list", "Specify ban list file, one IP address per line"};
const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true};
const command_line::arg_descriptor<bool> arg_no_sync = {"no-sync", "Don't synchronize the blockchain with other peers", false};
const command_line::arg_descriptor<bool> arg_enable_dns_blocklist = {"enable-dns-blocklist", "Apply realtime blocklist from DNS", false};
const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"};
const command_line::arg_descriptor<std::string> arg_igd = {"igd", "UPnP port mapping (disabled, enabled, delayed)", "delayed"};

@ -139,6 +139,7 @@ namespace nodetool
typedef COMMAND_HANDSHAKE_T<typename t_payload_net_handler::payload_type> COMMAND_HANDSHAKE;
typedef COMMAND_TIMED_SYNC_T<typename t_payload_net_handler::payload_type> COMMAND_TIMED_SYNC;
static_assert(p2p_connection_context::handshake_command() == COMMAND_HANDSHAKE::ID, "invalid handshake command id");
typedef epee::net_utils::boosted_tcp_server<epee::levin::async_protocol_handler<p2p_connection_context>> net_server;
@ -278,7 +279,7 @@ namespace nodetool
uint32_t get_max_out_public_peers() const;
void change_max_in_public_peers(size_t count);
uint32_t get_max_in_public_peers() const;
virtual bool block_host(epee::net_utils::network_address address, time_t seconds = P2P_IP_BLOCKTIME);
virtual bool block_host(epee::net_utils::network_address address, time_t seconds = P2P_IP_BLOCKTIME, bool add_only = false);
virtual bool unblock_host(const epee::net_utils::network_address &address);
virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = P2P_IP_BLOCKTIME);
virtual bool unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet);
@ -357,6 +358,7 @@ namespace nodetool
bool peer_sync_idle_maker();
bool do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context, bool just_take_peerlist = false);
bool do_peer_timed_sync(const epee::net_utils::connection_context_base& context, peerid_type peer_id);
bool update_dns_blocklist();
bool make_new_connection_from_anchor_peerlist(const std::vector<anchor_peerlist_entry>& anchor_peerlist);
bool make_new_connection_from_peerlist(network_zone& zone, bool use_white_list);
@ -461,6 +463,7 @@ namespace nodetool
epee::math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval;
epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval;
epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval;
epee::math_helper::once_a_time_seconds<7000> m_dns_blocklist_interval;
std::list<epee::net_utils::network_address> m_priority_peers;
std::vector<epee::net_utils::network_address> m_exclusive_peers;
@ -502,6 +505,8 @@ namespace nodetool
cryptonote::network_type m_nettype;
epee::net_utils::ssl_support_t m_ssl_support;
bool m_enable_dns_blocklist;
};
const int64_t default_limit_up = P2P_DEFAULT_LIMIT_RATE_UP; // kB/s
@ -523,6 +528,7 @@ namespace nodetool
extern const command_line::arg_descriptor<std::string> arg_ban_list;
extern const command_line::arg_descriptor<bool> arg_p2p_hide_my_port;
extern const command_line::arg_descriptor<bool> arg_no_sync;
extern const command_line::arg_descriptor<bool> arg_enable_dns_blocklist;
extern const command_line::arg_descriptor<bool> arg_no_igd;
extern const command_line::arg_descriptor<std::string> arg_igd;

@ -37,6 +37,7 @@
#include <boost/optional/optional.hpp>
#include <boost/thread/thread.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/algorithm/string.hpp>
#include <atomic>
#include <functional>
#include <limits>
@ -120,6 +121,7 @@ namespace nodetool
command_line::add_arg(desc, arg_ban_list);
command_line::add_arg(desc, arg_p2p_hide_my_port);
command_line::add_arg(desc, arg_no_sync);
command_line::add_arg(desc, arg_enable_dns_blocklist);
command_line::add_arg(desc, arg_no_igd);
command_line::add_arg(desc, arg_igd);
command_line::add_arg(desc, arg_out_peers);
@ -226,7 +228,7 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::block_host(epee::net_utils::network_address addr, time_t seconds)
bool node_server<t_payload_net_handler>::block_host(epee::net_utils::network_address addr, time_t seconds, bool add_only)
{
if(!addr.is_blockable())
return false;
@ -240,7 +242,11 @@ namespace nodetool
else
limit = now + seconds;
const std::string host_str = addr.host_str();
m_blocked_hosts[host_str] = limit;
auto it = m_blocked_hosts.find(host_str);
if (it == m_blocked_hosts.end())
m_blocked_hosts[host_str] = limit;
else if (it->second < limit || !add_only)
it->second = limit;
// drop any connection to that address. This should only have to look into
// the zone related to the connection, but really make sure everything is
@ -260,6 +266,8 @@ namespace nodetool
peerlist_entry pe{};
pe.adr = addr;
zone.second.m_peerlist.remove_from_peer_white(pe);
zone.second.m_peerlist.remove_from_peer_gray(pe);
zone.second.m_peerlist.remove_from_peer_anchor(addr);
for (const auto &c: conns)
zone.second.m_net_server.get_config_object().close(c);
@ -481,13 +489,19 @@ namespace nodetool
std::istringstream iss(banned_ips);
for (std::string line; std::getline(iss, line); )
{
auto subnet = net::get_ipv4_subnet_address(line);
if (subnet)
{
block_subnet(*subnet, std::numeric_limits<time_t>::max());
continue;
}
const expect<epee::net_utils::network_address> parsed_addr = net::get_network_address(line, 0);
if (!parsed_addr)
if (parsed_addr)
{
MERROR("Invalid IP address: " << line << " - " << parsed_addr.error());
block_host(*parsed_addr, std::numeric_limits<time_t>::max());
continue;
}
block_host(*parsed_addr, std::numeric_limits<time_t>::max());
MERROR("Invalid IP address or IPv4 subnet: " << line);
}
}
@ -497,6 +511,8 @@ namespace nodetool
if (command_line::has_arg(vm, arg_no_sync))
m_payload_handler.set_no_sync(true);
m_enable_dns_blocklist = command_line::get_arg(vm, arg_enable_dns_blocklist);
if ( !set_max_out_peers(public_zone, command_line::get_arg(vm, arg_out_peers) ) )
return false;
else
@ -1165,8 +1181,9 @@ namespace nodetool
if(!handle_remote_peerlist(rsp.local_peerlist_new, context))
{
LOG_WARNING_CC(context, "COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection.");
const auto remote_address = context.m_remote_address;
m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id );
add_host_fail(context.m_remote_address);
add_host_fail(remote_address);
}
if(!context.m_is_income)
m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port, context.m_rpc_credits_per_hash);
@ -1333,7 +1350,7 @@ namespace nodetool
if(just_take_peerlist)
{
zone.m_net_server.get_config_object().close(con->m_connection_id);
LOG_DEBUG_CC(*con, "CONNECTION HANDSHAKED OK AND CLOSED.");
MDEBUG(na.str() << "CONNECTION HANDSHAKED OK AND CLOSED.");
return true;
}
@ -1395,7 +1412,7 @@ namespace nodetool
zone.m_net_server.get_config_object().close(con->m_connection_id);
LOG_DEBUG_CC(*con, "CONNECTION HANDSHAKED OK AND CLOSED.");
MDEBUG(na.str() << "CONNECTION HANDSHAKED OK AND CLOSED.");
return true;
}
@ -1927,6 +1944,52 @@ namespace nodetool
m_gray_peerlist_housekeeping_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::gray_peerlist_housekeeping, this));
m_peerlist_store_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::store_config, this));
m_incoming_connections_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::check_incoming_connections, this));
m_dns_blocklist_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::update_dns_blocklist, this));
return true;
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::update_dns_blocklist()
{
if (!m_enable_dns_blocklist)
return true;
if (m_nettype != cryptonote::MAINNET)
return true;
static const std::vector<std::string> dns_urls = {
"blocklist.moneropulse.se"
, "blocklist.moneropulse.org"
, "blocklist.moneropulse.net"
, "blocklist.moneropulse.no"
, "blocklist.moneropulse.fr"
, "blocklist.moneropulse.de"
, "blocklist.moneropulse.ch"
};
std::vector<std::string> records;
if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls))
return true;
unsigned good = 0, bad = 0;
for (const auto& record : records)
{
std::vector<std::string> ips;
boost::split(ips, record, boost::is_any_of(";"));
for (const auto &ip: ips)
{
const expect<epee::net_utils::network_address> parsed_addr = net::get_network_address(ip, 0);
if (!parsed_addr)
{
MWARNING("Invalid IP address from DNS blocklist: " << ip << " - " << parsed_addr.error());
++bad;
continue;
}
block_host(*parsed_addr, DNS_BLOCKLIST_LIFETIME, true);
++good;
}
}
if (good > 0)
MINFO(good << " addresses added to the blocklist");
return true;
}
//-----------------------------------------------------------------------------------
@ -2049,7 +2112,10 @@ namespace nodetool
LOG_DEBUG_CC(context, "REMOTE PEERLIST: remote peerlist size=" << peerlist_.size());
LOG_TRACE_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_));
return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_, [this](const peerlist_entry &pe) { return !is_addr_recently_failed(pe.adr); });
CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_, [this](const peerlist_entry &pe) {
return !is_addr_recently_failed(pe.adr) && is_remote_host_allowed(pe.adr);
});
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
@ -2369,12 +2435,14 @@ namespace nodetool
template<class t_payload_net_handler>
int node_server<t_payload_net_handler>::handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context)
{
// copy since dropping the connection will invalidate the context, and thus the address
const auto remote_address = context.m_remote_address;
if(arg.node_data.network_id != m_network_id)
{
LOG_INFO_CC(context, "WRONG NETWORK AGENT CONNECTED! id=" << arg.node_data.network_id);
drop_connection(context);
add_host_fail(context.m_remote_address);
add_host_fail(remote_address);
return 1;
}
@ -2382,7 +2450,7 @@ namespace nodetool
{
LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came not from incoming connection");
drop_connection(context);
add_host_fail(context.m_remote_address);
add_host_fail(remote_address);
return 1;
}
@ -2794,8 +2862,8 @@ namespace nodetool
const uint32_t index = stripe - 1;
CRITICAL_REGION_LOCAL(m_used_stripe_peers_mutex);
MINFO("adding stripe " << stripe << " peer: " << context.m_remote_address.str());
std::remove_if(m_used_stripe_peers[index].begin(), m_used_stripe_peers[index].end(),
[&context](const epee::net_utils::network_address &na){ return context.m_remote_address == na; });
m_used_stripe_peers[index].erase(std::remove_if(m_used_stripe_peers[index].begin(), m_used_stripe_peers[index].end(),
[&context](const epee::net_utils::network_address &na){ return context.m_remote_address == na; }), m_used_stripe_peers[index].end());
m_used_stripe_peers[index].push_back(context.m_remote_address);
}
@ -2808,8 +2876,8 @@ namespace nodetool
const uint32_t index = stripe - 1;
CRITICAL_REGION_LOCAL(m_used_stripe_peers_mutex);
MINFO("removing stripe " << stripe << " peer: " << context.m_remote_address.str());
std::remove_if(m_used_stripe_peers[index].begin(), m_used_stripe_peers[index].end(),
[&context](const epee::net_utils::network_address &na){ return context.m_remote_address == na; });
m_used_stripe_peers[index].erase(std::remove_if(m_used_stripe_peers[index].begin(), m_used_stripe_peers[index].end(),
[&context](const epee::net_utils::network_address &na){ return context.m_remote_address == na; }), m_used_stripe_peers[index].end());
}
template<class t_payload_net_handler>

@ -58,7 +58,7 @@ namespace nodetool
virtual uint64_t get_public_connections_count()=0;
virtual void for_each_connection(std::function<bool(t_connection_context&, peerid_type, uint32_t)> f)=0;
virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(t_connection_context&, peerid_type, uint32_t)> f)=0;
virtual bool block_host(epee::net_utils::network_address address, time_t seconds = 0)=0;
virtual bool block_host(epee::net_utils::network_address address, time_t seconds = 0, bool add_only = false)=0;
virtual bool unblock_host(const epee::net_utils::network_address &address)=0;
virtual std::map<std::string, time_t> get_blocked_hosts()=0;
virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets()=0;
@ -108,7 +108,7 @@ namespace nodetool
{
return false;
}
virtual bool block_host(epee::net_utils::network_address address, time_t seconds)
virtual bool block_host(epee::net_utils::network_address address, time_t seconds, bool add_only)
{
return true;
}

@ -826,7 +826,7 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
proof_data.reserve(proofs.size());
size_t inv_offset = 0;
std::vector<rct::key> to_invert;
to_invert.reserve(11 * sizeof(proofs));
to_invert.reserve(11 * proofs.size());
size_t max_logM = 0;
for (const Bulletproof *p: proofs)
{

@ -190,6 +190,7 @@ namespace cryptonote
request.gray = true;
request.white = true;
request.include_blocked = false;
if (!on_get_public_nodes(request, response) || response.status != CORE_RPC_STATUS_OK)
{
return {};
@ -500,6 +501,7 @@ namespace cryptonote
res.version = restricted ? "" : MONERO_VERSION_FULL;
res.synchronized = check_core_ready();
res.busy_syncing = m_p2p.get_payload_object().is_busy_syncing();
res.synchronized = check_core_ready();
res.status = CORE_RPC_STATUS_OK;
return true;
@ -564,12 +566,12 @@ namespace cryptonote
}
}
size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT;
size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT;
if (m_rpc_payment)
{
max_blocks = res.credits / COST_PER_BLOCK;
if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)
max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT;
if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT)
max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT;
if (max_blocks == 0)
{
res.status = CORE_RPC_STATUS_PAYMENT_REQUIRED;
@ -578,7 +580,7 @@ namespace cryptonote
}
std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > > bs;
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, max_blocks))
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
{
res.status = "Failed";
add_host_fail(ctx);
@ -1383,6 +1385,8 @@ namespace cryptonote
for (auto & entry : white_list)
{
if (!req.include_blocked && m_p2p.is_host_blocked(entry.adr, NULL))
continue;
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
@ -1395,6 +1399,8 @@ namespace cryptonote
for (auto & entry : gray_list)
{
if (!req.include_blocked && m_p2p.is_host_blocked(entry.adr, NULL))
continue;
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.gray_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
@ -1413,8 +1419,10 @@ namespace cryptonote
{
RPC_TRACKER(get_public_nodes);
COMMAND_RPC_GET_PEER_LIST::request peer_list_req;
COMMAND_RPC_GET_PEER_LIST::response peer_list_res;
const bool success = on_get_peer_list(COMMAND_RPC_GET_PEER_LIST::request(), peer_list_res, ctx);
peer_list_req.include_blocked = req.include_blocked;
const bool success = on_get_peer_list(peer_list_req, peer_list_res, ctx);
res.status = peer_list_res.status;
if (!success)
{

@ -88,7 +88,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 3
#define CORE_RPC_VERSION_MINOR 4
#define CORE_RPC_VERSION_MINOR 5
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@ -1197,10 +1197,12 @@ namespace cryptonote
struct request_t: public rpc_request_base
{
bool public_only;
bool include_blocked;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE_OPT(public_only, true)
KV_SERIALIZE_OPT(include_blocked, false)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@ -1246,11 +1248,13 @@ namespace cryptonote
{
bool gray;
bool white;
bool include_blocked;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE_OPT(gray, false)
KV_SERIALIZE_OPT(white, true)
KV_SERIALIZE_OPT(include_blocked, false)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;

@ -128,7 +128,7 @@ namespace rpc
{
std::vector<std::pair<std::pair<blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, blobdata> > > > blocks;
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
{
res.status = Message::STATUS_FAILED;
res.error_details = "core::find_blockchain_supplement() returned false";

@ -143,6 +143,7 @@ typedef cryptonote::simple_wallet sw;
#define CREDITS_TARGET 50000
#define MAX_PAYMENT_DIFF 10000
#define MIN_PAYMENT_RATE 0.01f // per hash
#define MAX_MNEW_ADDRESSES 1000
enum TransferType {
Transfer,
@ -181,7 +182,7 @@ namespace
const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""};
const char* USAGE_START_MINING("start_mining [<number_of_threads>] [bg_mining] [ignore_battery]");
const char* USAGE_SET_DAEMON("set_daemon <host>[:<port>] [trusted|untrusted]");
const char* USAGE_SET_DAEMON("set_daemon <host>[:<port>] [trusted|untrusted|this-is-probably-a-spy-node]");
const char* USAGE_SHOW_BALANCE("balance [detail]");
const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [uses] [index=<N1>[,<N2>[,...]]]");
const char* USAGE_PAYMENTS("payments <PID_1> [<PID_2> ... <PID_N>]");
@ -203,7 +204,7 @@ namespace
" account tag <tag_name> <account_index_1> [<account_index_2> ...]\n"
" account untag <account_index_1> [<account_index_2> ...]\n"
" account tag_description <tag_name> <description>");
const char* USAGE_ADDRESS("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed> | device [<index>] | one-off <account> <subaddress>]");
const char* USAGE_ADDRESS("address [ new <label text with white spaces allowed> | mnew <amount of new addresses> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed> | device [<index>] | one-off <account> <subaddress>]");
const char* USAGE_INTEGRATED_ADDRESS("integrated_address [device] [<payment_id> | <address>]");
const char* USAGE_ADDRESS_BOOK("address_book [(add (<address>|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]");
const char* USAGE_SET_VARIABLE("set <option> [<value>]");
@ -2286,6 +2287,7 @@ bool simple_wallet::public_nodes(const std::vector<std::string> &args)
{
fail_msg_writer() << tr("Error retrieving public node list: ") << e.what();
}
message_writer(console_color_red, true) << tr("Most of these nodes are probably spies. You should not use them unless connecting via Tor or I2P");
return true;
}
@ -3222,7 +3224,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("set_daemon",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::set_daemon, _1),
tr(USAGE_SET_DAEMON),
tr("Set another daemon to connect to."));
tr("Set another daemon to connect to. If it's not yours, it'll probably spy on you."));
m_cmd_binder.set_handler("save_bc",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::save_bc, _1),
tr("Save the current blockchain data."));
@ -3302,7 +3304,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("address",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::print_address, _1),
tr(USAGE_ADDRESS),
tr("If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If \"all\" is specified, the wallet shows all the existing addresses in the currently selected account. If \"new \" is specified, the wallet creates a new address with the provided label text (which can be empty). If \"label\" is specified, the wallet sets the label of the address specified by <index> to the provided label text. If \"one-off\" is specified, the address for the specified index is generated and displayed, and remembered by the wallet"));
tr("If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If \"all\" is specified, the wallet shows all the existing addresses in the currently selected account. If \"new \" is specified, the wallet creates a new address with the provided label text (which can be empty). If \"mnew\" is specified, the wallet creates as many new addresses as specified by the argument; the default label is set for the new addresses. If \"label\" is specified, the wallet sets the label of the address specified by <index> to the provided label text. If \"one-off\" is specified, the address for the specified index is generated and displayed, and remembered by the wallet"));
m_cmd_binder.set_handler("integrated_address",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::print_integrated_address, _1),
tr(USAGE_INTEGRATED_ADDRESS),
@ -5507,20 +5509,50 @@ bool simple_wallet::set_daemon(const std::vector<std::string>& args)
} else {
daemon_url = args[0];
}
LOCK_IDLE_SCOPE();
m_wallet->init(daemon_url);
epee::net_utils::http::url_content parsed{};
const bool r = epee::net_utils::parse_url(daemon_url, parsed);
if (!r)
{
fail_msg_writer() << tr("Failed to parse address");
return true;
}
std::string trusted;
if (args.size() == 2)
{
if (args[1] == "trusted")
m_wallet->set_trusted_daemon(true);
trusted = "trusted";
else if (args[1] == "untrusted")
m_wallet->set_trusted_daemon(false);
trusted = "untrusted";
else if (args[1] == "this-is-probably-a-spy-node")
trusted = "this-is-probably-a-spy-node";
else
{
fail_msg_writer() << tr("Expected trusted or untrusted, got ") << args[1] << ": assuming untrusted";
m_wallet->set_trusted_daemon(false);
fail_msg_writer() << tr("Expected trusted, untrusted or this-is-probably-a-spy-node got ") << args[1];
return true;
}
}
if (!tools::is_privacy_preserving_network(parsed.host) && !tools::is_local_address(parsed.host))
{
if (trusted == "untrusted" || trusted == "")
{
fail_msg_writer() << tr("This is not Tor/I2P address, and is not a trusted daemon.");
fail_msg_writer() << tr("Either use your own trusted node, connect via Tor or I2P, or pass this-is-probably-a-spy-node and be spied on.");
return true;
}
if (parsed.schema != "https")
message_writer(console_color_red) << tr("Warning: connecting to a non-local daemon without SSL, passive adversaries will be able to spy on you.");
}
LOCK_IDLE_SCOPE();
m_wallet->init(daemon_url);
if (!trusted.empty())
{
m_wallet->set_trusted_daemon(trusted == "trusted");
}
else
{
@ -9508,6 +9540,31 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
print_address_sub(m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1);
m_wallet->device_show_address(m_current_subaddress_account, m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1, boost::none);
}
else if (local_args[0] == "mnew")
{
local_args.erase(local_args.begin());
if (local_args.size() != 1)
{
fail_msg_writer() << tr("Expected exactly one argument for the amount of new addresses");
return true;
}
uint32_t n;
if (!epee::string_tools::get_xtype_from_string(n, local_args[0]))
{
fail_msg_writer() << tr("failed to parse the amount of new addresses: ") << local_args[0];
return true;
}
if (n > MAX_MNEW_ADDRESSES)
{
fail_msg_writer() << tr("the amount of new addresses must be lower or equal to ") << MAX_MNEW_ADDRESSES;
return true;
}
for (uint32_t i = 0; i < n; ++i)
{
m_wallet->add_subaddress(m_current_subaddress_account, tr("(Untitled address)"));
print_address_sub(m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1);
}
}
else if (local_args[0] == "one-off")
{
local_args.erase(local_args.begin());

@ -1,5 +1,5 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
#define DEF_MONERO_VERSION "0.9.2.0"
#define DEF_MONERO_VERSION "0.9.2.1"
#define DEF_MONERO_RELEASE_NAME "Illiterate Illuminati"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
#define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@

@ -14149,6 +14149,7 @@ std::vector<cryptonote::public_node> wallet2::get_public_nodes(bool white_only)
req.white = true;
req.gray = !white_only;
req.include_blocked = false;
{
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};

@ -245,12 +245,17 @@ bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/
return true;
}
bool tests::proxy_core::have_block(const crypto::hash& id) {
bool tests::proxy_core::have_block_unlocked(const crypto::hash& id, int *where) {
if (m_hash2blkidx.end() == m_hash2blkidx.find(id))
return false;
if (where) *where = HAVE_BLOCK_MAIN_CHAIN;
return true;
}
bool tests::proxy_core::have_block(const crypto::hash& id, int *where) {
return have_block_unlocked(id, where);
}
void tests::proxy_core::build_short_history(std::list<crypto::hash> &m_history, const crypto::hash &m_start) {
m_history.push_front(get_block_hash(m_genesis));
/*std::unordered_map<crypto::hash, tests::block_index>::const_iterator cit = m_hash2blkidx.find(m_lastblk);

@ -74,7 +74,8 @@ namespace tests
bool init(const boost::program_options::variables_map& vm);
bool deinit(){return true;}
bool get_short_chain_history(std::list<crypto::hash>& ids);
bool have_block(const crypto::hash& id);
bool have_block(const crypto::hash& id, int *where = NULL);
bool have_block_unlocked(const crypto::hash& id, int *where = NULL);
void get_blockchain_top(uint64_t& height, crypto::hash& top_id);
bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed);
bool handle_incoming_txs(const std::vector<cryptonote::tx_blob_entry>& tx_blobs, std::vector<cryptonote::tx_verification_context>& tvc, cryptonote::relay_method tx_relay, bool relayed);
@ -112,5 +113,6 @@ namespace tests
bool prune_blockchain(uint32_t pruning_seed) const { return true; }
bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) { return false; }
bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const { return false; }
crypto::hash get_block_id_by_height(uint64_t height) const { return crypto::null_hash; }
};
}

@ -52,6 +52,9 @@ namespace
struct test_levin_connection_context : public epee::net_utils::connection_context_base
{
static constexpr int handshake_command() noexcept { return 1001; }
static constexpr bool handshake_complete() noexcept { return true; }
size_t get_max_bytes(int command) const { return LEVIN_DEFAULT_MAX_PACKET_SIZE; }
};
typedef epee::levin::async_protocol_handler_config<test_levin_connection_context> test_levin_protocol_handler_config;

@ -48,6 +48,9 @@ namespace net_load_tests
struct test_connection_context : epee::net_utils::connection_context_base
{
test_connection_context(): epee::net_utils::connection_context_base(boost::uuids::nil_uuid(), {}, false, false), m_closed(false) {}
static constexpr int handshake_command() noexcept { return 1001; }
static constexpr bool handshake_complete() noexcept { return true; }
size_t get_max_bytes(int command) const { return LEVIN_DEFAULT_MAX_PACKET_SIZE; }
volatile bool m_closed;
};

@ -47,6 +47,7 @@ set(unit_tests_sources
dns_resolver.cpp
epee_boosted_tcp_server.cpp
epee_levin_protocol_handler_async.cpp
epee_serialization.cpp
epee_utils.cpp
expect.cpp
fee.cpp

@ -43,6 +43,9 @@ namespace
{
struct test_levin_connection_context : public epee::net_utils::connection_context_base
{
static constexpr int handshake_command() noexcept { return 1001; }
static constexpr bool handshake_complete() noexcept { return true; }
size_t get_max_bytes(int command) const { return LEVIN_DEFAULT_MAX_PACKET_SIZE; }
};
typedef epee::levin::async_protocol_handler_config<test_levin_connection_context> test_levin_protocol_handler_config;
@ -193,6 +196,7 @@ namespace
{
m_handler_config.set_handler(m_pcommands_handler, [](epee::levin::levin_commands_handler<test_levin_connection_context> *handler) { delete handler; });
m_handler_config.m_invoke_timeout = invoke_timeout;
m_handler_config.m_initial_max_packet_size = max_packet_size;
m_handler_config.m_max_packet_size = max_packet_size;
}

@ -0,0 +1,54 @@
// Copyright (c) 2020, 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 <cstdint>
#include <gtest/gtest.h>
#include "storages/portable_storage.h"
TEST(epee_binary, two_keys)
{
static constexpr const std::uint8_t data[] = {
0x01, 0x11, 0x01, 0x1, 0x01, 0x01, 0x02, 0x1, 0x1, 0x08, 0x01, 'a',
0x0B, 0x00, 0x01, 'b', 0x0B, 0x00
};
epee::serialization::portable_storage storage{};
EXPECT_TRUE(storage.load_from_binary(data));
}
TEST(epee_binary, duplicate_key)
{
static constexpr const std::uint8_t data[] = {
0x01, 0x11, 0x01, 0x1, 0x01, 0x01, 0x02, 0x1, 0x1, 0x08, 0x01, 'a',
0x0B, 0x00, 0x01, 'a', 0x0B, 0x00
};
epee::serialization::portable_storage storage{};
EXPECT_FALSE(storage.load_from_binary(data));
}

@ -982,6 +982,23 @@ TEST(ByteStream, Put)
EXPECT_TRUE(equal(bytes, byte_span{stream.data(), stream.size()}));
}
TEST(ByteStream, PutN)
{
using boost::range::equal;
using byte_span = epee::span<const std::uint8_t>;
std::vector<std::uint8_t> bytes;
bytes.resize(1000, 'f');
epee::byte_stream stream;
stream.put_n('f', 1000);
EXPECT_EQ(1000u, stream.size());
EXPECT_LE(1000u, stream.capacity());
EXPECT_EQ(stream.available(), stream.capacity() - stream.size());
EXPECT_TRUE(equal(bytes, byte_span{stream.data(), stream.size()}));
}
TEST(ByteStream, Reserve)
{
using boost::range::equal;

@ -178,17 +178,17 @@ namespace
{
using base_type = epee::net_utils::connection_context_base;
static_cast<base_type&>(context_) = base_type{random_generator(), {}, is_incoming, false};
context_.m_state = cryptonote::cryptonote_connection_context::state_normal;
handler_.after_init_connection();
}
//\return Number of messages processed
std::size_t process_send_queue()
std::size_t process_send_queue(const bool valid = true)
{
std::size_t count = 0;
for ( ; !endpoint_.send_queue_.empty(); ++count, endpoint_.send_queue_.pop_front())
{
// invalid messages shoudn't be possible in this test;
EXPECT_TRUE(handler_.handle_recv(endpoint_.send_queue_.front().data(), endpoint_.send_queue_.front().size()));
EXPECT_EQ(valid, handler_.handle_recv(endpoint_.send_queue_.front().data(), endpoint_.send_queue_.front().size()));
}
return count;
}
@ -238,6 +238,13 @@ namespace
return {connection, std::move(request)};
}
static received_message get_raw_message(std::deque<received_message>& queue)
{
received_message out{std::move(queue.front())};
queue.pop_front();
return out;
}
virtual int invoke(int command, const epee::span<const uint8_t> in_buff, epee::byte_slice& buff_out, cryptonote::levin::detail::p2p_context& context) override final
{
buff_out = nullptr;
@ -294,6 +301,11 @@ namespace
{
return get_message<T>(notified_);
}
received_message get_raw_notification()
{
return get_raw_message(notified_);
}
};
class levin_notify : public ::testing::Test
@ -322,6 +334,8 @@ namespace
EXPECT_EQ(0u, events_.relayed_method_size());
}
cryptonote::levin::connections& get_connections() noexcept { return *connections_; }
void add_connection(const bool is_incoming)
{
contexts_.emplace_back(io_service_, *connections_, random_generator_, is_incoming);
@ -2140,3 +2154,27 @@ TEST_F(levin_notify, noise_stem)
}
}
}
TEST_F(levin_notify, command_max_bytes)
{
static constexpr int ping_command = nodetool::COMMAND_PING::ID;
add_connection(true);
std::string bytes(4096, 'h');
EXPECT_EQ(1, get_connections().notify(ping_command, epee::strspan<std::uint8_t>(bytes), contexts_.front().get_id()));
EXPECT_EQ(1u, contexts_.front().process_send_queue(true));
EXPECT_EQ(1u, receiver_.notified_size());
const received_message msg = receiver_.get_raw_notification();
EXPECT_EQ(ping_command, msg.command);
EXPECT_EQ(contexts_.front().get_id(), msg.connection);
EXPECT_EQ(bytes, msg.payload);
bytes.push_back('e');
EXPECT_EQ(1, get_connections().notify(ping_command, epee::strspan<std::uint8_t>(bytes), contexts_.front().get_id()));
EXPECT_EQ(1u, contexts_.front().process_send_queue(false));
EXPECT_EQ(0u, receiver_.notified_size());
}

@ -55,7 +55,8 @@ public:
bool init(const boost::program_options::variables_map& vm) {return true ;}
bool deinit(){return true;}
bool get_short_chain_history(std::list<crypto::hash>& ids) const { return true; }
bool have_block(const crypto::hash& id) const {return true;}
bool have_block(const crypto::hash& id, int *where = NULL) const {return false;}
bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const {return false;}
void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;}
bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed) { return true; }
bool handle_incoming_txs(const std::vector<cryptonote::tx_blob_entry>& tx_blob, std::vector<cryptonote::tx_verification_context>& tvc, cryptonote::relay_method tx_relay, bool relayed) { return true; }
@ -93,6 +94,7 @@ public:
bool has_block_weights(uint64_t height, uint64_t nblocks) const { return false; }
bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) { return false; }
bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const { return false; }
crypto::hash get_block_id_by_height(uint64_t height) const { return crypto::null_hash; }
void stop() {}
};

Loading…
Cancel
Save