mirror of
https://github.com/wownero/wownero.git
synced 2026-11-07 08:54:05 +01:00
fix(gui): update the wallet library to Wownero 0.11.5.0
Apply the prepared GUI security port on the current public gui branch. The complete Git tree equals candidate 9e9bb026d3d33a29a1607a1bce449e2583b1223f, built during private preparation, and includes the public v0.11.5.0 source.
This commit is contained in:
+1
-1
@@ -10,6 +10,6 @@
|
||||
branch = monero
|
||||
[submodule "external/randomwow"]
|
||||
path = external/randomwow
|
||||
url = https://codeberg.org/wownero/RandomWOW
|
||||
url = https://github.com/wownero/RandomWOW
|
||||
branch = 1.2.1-wow
|
||||
|
||||
|
||||
+15
-5
@@ -100,6 +100,13 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
if(APPLE)
|
||||
# C++17 aligned new/delete (std::align_val_t) require macOS deployment target >= 10.14. The depends
|
||||
# macOS toolchain targets older (and Apple Silicon aarch64 cross-builds hit this on boost/any.hpp).
|
||||
# Disable aligned allocation so wownero builds under C++17 on all macOS targets.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-aligned-allocation")
|
||||
endif()
|
||||
|
||||
# When built as Feather/wowlet's wallet submodule (it sets BUILD_GUI_DEPS=ON before
|
||||
# add_subdirectory(monero)), match Feather's anti-AV namespace rename so this library's symbols line
|
||||
# up with the app, which Feather compiles with the same -Dcryptonote=MWYSUTPAXIBT define. Gated so it
|
||||
@@ -616,7 +623,6 @@ find_package(HIDAPI)
|
||||
|
||||
add_definition_if_library_exists(c memset_s "string.h" HAVE_MEMSET_S)
|
||||
add_definition_if_library_exists(c explicit_bzero "strings.h" HAVE_EXPLICIT_BZERO)
|
||||
add_definition_if_function_found(strptime HAVE_STRPTIME)
|
||||
|
||||
add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP)
|
||||
|
||||
@@ -808,9 +814,8 @@ else()
|
||||
# mingw doesn't support LTO (multiple definition errors at link time)
|
||||
set(USE_LTO_DEFAULT false)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,10485760")
|
||||
if(NOT BUILD_64)
|
||||
add_definitions(-DWINVER=0x0600 -D_WIN32_WINNT=0x0600)
|
||||
endif()
|
||||
# TryAcquireSRWLock* requires Windows 7 or later.
|
||||
add_definitions(-DWINVER=0x0601 -D_WIN32_WINNT=0x0601)
|
||||
endif()
|
||||
set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wold-style-definition -Wstrict-prototypes")
|
||||
set(CXX_WARNINGS "-Wno-reorder -Wno-missing-field-initializers")
|
||||
@@ -913,6 +918,11 @@ else()
|
||||
add_c_flag_if_supported( -Werror=return-type C_SECURITY_FLAGS)
|
||||
add_cxx_flag_if_supported(-Werror=return-type CXX_SECURITY_FLAGS)
|
||||
|
||||
# A missing WinAPI prototype (e.g. TryAcquireSRWLock*) must fail the build
|
||||
if(MINGW)
|
||||
add_c_flag_if_supported(-Werror=implicit-function-declaration C_SECURITY_FLAGS)
|
||||
endif()
|
||||
|
||||
message(STATUS "Using C security hardening flags: ${C_SECURITY_FLAGS}")
|
||||
message(STATUS "Using C++ security hardening flags: ${CXX_SECURITY_FLAGS}")
|
||||
message(STATUS "Using linker security hardening flags: ${LD_SECURITY_FLAGS}")
|
||||
@@ -1172,7 +1182,7 @@ 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 AppKit -framework IOKit")
|
||||
list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework AppKit -framework IOKit -framework Security")
|
||||
else()
|
||||
find_library(COREFOUNDATION CoreFoundation)
|
||||
find_library(APPKIT AppKit)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ~~Mo~~Wownero - Such privacy! Many coins! Wow!
|
||||
|
||||
<img src="https://codeberg.org/wownero/meta/raw/commit/e7bd4f0e4520b7dc0a7b0bd01083b12b2667365b/images/wow.gif">
|
||||
<img src="https://raw.githubusercontent.com/wownero/meta/e7bd4f0e4520b7dc0a7b0bd01083b12b2667365b/images/wow.gif">
|
||||
|
||||
## Introduction
|
||||
|
||||
@@ -15,7 +15,7 @@ Unlike Opposing Projects.
|
||||
- Twitter: [@w0wn3r0](https://twitter.com/w0wn3r0)
|
||||
- Reddit: [/r/wownero](https://www.reddit.com/r/wownero)
|
||||
- Mail: [wownero@wownero.org](mailto:wownero@wownero.org)
|
||||
- Git: [codeberg.org/wownero/wownero](https://codeberg.org/wownero/wownero)
|
||||
- Git: [github.com/wownero/wownero](https://github.com/wownero/wownero)
|
||||
- Matrix General Chat Room: [#wownero-gen:wowne.ro](https://matrix.to/#/#wownero-gen:wowne.ro)
|
||||
- IRC: [OFTC #wownero](https://webchat.oftc.net/?channels=wownero)
|
||||
- Discord: [discord.gg/q2rmxeYvWy](https://discord.gg/q2rmxeYvWy)
|
||||
@@ -31,7 +31,7 @@ Unlike Opposing Projects.
|
||||
|
||||
## Wallets
|
||||
|
||||
- Wonero CLI Wallet: [codeberg.org/wownero/wownero](https://codeberg.org/wownero/wownero/releases)
|
||||
- Wonero CLI Wallet: [github.com/wownero/wownero](https://github.com/wownero/wownero/releases)
|
||||
- Stack Wallet iOS & Android Mobile Wallet: [stackwallet.com](https://stackwallet.com)
|
||||
|
||||
## Blockchain Explorers
|
||||
@@ -62,7 +62,7 @@ BTC: `bc1qcw9zglp3fxyl25zswemw7jczlqryms2lsmu464`
|
||||
|
||||
If you have a fix or code change, feel free to submit it as a pull request. Ahead of a scheduled software upgrade, a development branch will be created with the new release version tag. Pull requests that address bugs should be made to Master. Pull requests that require review and testing (generally, optimizations and new features) should be made to the development branch. All pull requests will be considered safe until the US dollar valuation of 1 Wownero equals $1000. After this valuation has been reached, more research will be needed to introduce experimental cryptography and/or code into the codebase.
|
||||
|
||||
Things to Do, Work in Progress, and Help Wanted tasks are tracked in the [Meta](https://codeberg.org/wownero/meta/issues) repo.
|
||||
Things to Do, Work in Progress, and Help Wanted tasks are tracked in the [Meta](https://github.com/wownero/meta/issues) repo.
|
||||
|
||||
Join `#wownero` on IRC OFTC to participate in development conversation.
|
||||
|
||||
@@ -83,7 +83,7 @@ Dates are provided in the format YYYY-MM-DD.
|
||||
| - | 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.3.3 | Dynamic coinbase unlock (up to 1 mo.), Deterministic unlock times, Enforce maximum coinbase amount, show_qr_code wallet command, CLSAG
|
||||
| 331,170 | 2021-07-04 | Junkie Jeff | v0.10.0.0 | v0.10.2.0 | Bulletproofs+, Miner Block Header Signing, Vote by Block, Change coinbase unlock time to 1 day, Reset difficulty and switch back to Monero's difficulty algorithm
|
||||
| 514,000 | 2023-04-01 | Kunty Karen | v0.11.0.0 | v0.11.4.0 | View tags, fee changes, adjusted dynamic block weight algorithm, multisig security fixes, RPC broadcast node donation sub-address, Limit tx_extra max size to ~1kb, 12-hour difficulty adjustment window
|
||||
| 514,000 | 2023-04-01 | Kunty Karen | v0.11.0.0 | v0.11.5.0 | View tags, fee changes, adjusted dynamic block weight algorithm, multisig security fixes, RPC broadcast node donation sub-address, Limit tx_extra max size to ~1kb, 12-hour difficulty adjustment window
|
||||
|
||||
X's indicate that these details have not been determined as of commit date.
|
||||
|
||||
@@ -110,7 +110,7 @@ Packaging for your favorite distribution would be a welcome contribution!
|
||||
|
||||
sudo pacman -Syu --needed base-devel cmake boost openssl zeromq libpgm unbound libsodium libunwind xz readline expat gtest python3 ccache doxygen graphviz qt5-tools hidapi libusb protobuf systemd
|
||||
|
||||
git clone https://codeberg.org/wownero/wownero && cd wownero
|
||||
git clone https://github.com/wownero/wownero && cd wownero
|
||||
|
||||
make
|
||||
|
||||
@@ -118,7 +118,7 @@ Packaging for your favorite distribution would be a welcome contribution!
|
||||
|
||||
sudo apt update && sudo apt install build-essential cmake pkg-config libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-locale-dev libboost-program-options-dev libboost-regex-dev libboost-serialization-dev libboost-system-dev libboost-thread-dev python3 ccache doxygen graphviz
|
||||
|
||||
git clone https://codeberg.org/wownero/wownero && cd wownero
|
||||
git clone https://github.com/wownero/wownero && cd wownero
|
||||
|
||||
make
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ $(1)_config_env+=PKG_CONFIG_PATH=$($($(1)_type)_prefix)/share/pkgconfig
|
||||
$(1)_config_env+=PATH="$(build_prefix)/bin:$(PATH)"
|
||||
$(1)_build_env+=PATH="$(build_prefix)/bin:$(PATH)"
|
||||
$(1)_stage_env+=PATH="$(build_prefix)/bin:$(PATH)"
|
||||
$(1)_autoconf=./configure --host=$($($(1)_type)_host) --prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)"
|
||||
$(1)_autoconf=./configure --build=$(BUILD) --host=$($($(1)_type)_host) --prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)"
|
||||
|
||||
ifeq ($(filter $(1),libusb unbound),)
|
||||
$(1)_autoconf += --disable-dependency-tracking
|
||||
|
||||
@@ -6,7 +6,7 @@ else
|
||||
CC_target=$(host)
|
||||
endif
|
||||
darwin_CC=clang -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -B$(host_prefix)/native/bin/$(host)-
|
||||
darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B$(host_prefix)/native/bin/$(host)-
|
||||
darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -fno-aligned-allocation -B$(host_prefix)/native/bin/$(host)-
|
||||
|
||||
darwin_CFLAGS=-pipe
|
||||
darwin_CXXFLAGS=$(darwin_CFLAGS)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package=libusb
|
||||
$(package)_version=1.0.26
|
||||
$(package)_version=1.0.30
|
||||
$(package)_download_path=https://github.com/libusb/libusb/releases/download/v$($(package)_version)
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
|
||||
$(package)_sha256_hash=12ce7a61fc9854d1d2a1ffe095f7b5fac19ddba095c259e6067a46500381b5a5
|
||||
$(package)_sha256_hash=fea36f34f9156400209595e300840767ab1a385ede1dc7ee893015aea9c6dbaf
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
autoreconf -i
|
||||
@@ -15,6 +15,12 @@ define $(package)_set_vars
|
||||
$(package)_config_opts_darwin=--disable-udev
|
||||
endef
|
||||
|
||||
# Remove blobs
|
||||
define $(package)_preprocess_cmds
|
||||
rm -rf tests/fuzz/corpus && \
|
||||
rm doc/libusb.png
|
||||
endef
|
||||
|
||||
ifneq ($(host_os),darwin)
|
||||
define $(package)_config_cmds
|
||||
cp -f $(BASEDIR)/config.guess config.guess &&\
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package=openssl
|
||||
$(package)_version=3.0.19
|
||||
$(package)_download_path=https://www.openssl.org/source
|
||||
$(package)_version=3.0.22
|
||||
$(package)_download_path=https://github.com/openssl/openssl/releases/download/openssl-$($(package)_version)
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=fa5a4143b8aae18be53ef2f3caf29a2e0747430b8bc74d32d88335b94ab63072
|
||||
$(package)_sha256_hash=67ebca7e50d17383028045486653492195b83db95f8558709701bb47b5c1ef81
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_env=AR="$($(package)_ar)" ARFLAGS=$($(package)_arflags) RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package=unbound
|
||||
$(package)_version=1.19.1
|
||||
$(package)_version=1.26.1
|
||||
$(package)_download_path=https://www.nlnetlabs.nl/downloads/$(package)/
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=bc1d576f3dd846a0739adc41ffaa702404c6767d2b6082deb9f2f97cbb24a3a9
|
||||
$(package)_sha256_hash=35a6dc0e425a9282c3426d9a3043144011bf0534aed4b73ab62c52aee0af1503
|
||||
$(package)_dependencies=openssl expat
|
||||
$(package)_patches=disable-glibc-reallocarray.patch
|
||||
$(package)_patches=disable-glibc-reallocarray.patch hardcode_config_date.patch
|
||||
|
||||
|
||||
define $(package)_set_vars
|
||||
@@ -20,7 +20,10 @@ endef
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
patch -p1 < $($(package)_patch_dir)/disable-glibc-reallocarray.patch &&\
|
||||
autoconf
|
||||
patch -p1 < $($(package)_patch_dir)/hardcode_config_date.patch &&\
|
||||
autoconf && \
|
||||
rm doc/*.odp doc/*.pdf contrib/*.tar.gz contrib/*.tar.bz2 &&\
|
||||
rm -rf testdata dnscrypt/testdata
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index f1c5b04..13f4ce6 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -994,7 +994,7 @@ if test "`uname`" = "Linux"; then
|
||||
GCC_DOCKER_LINTFLAGS='-syntax'
|
||||
AC_SUBST(GCC_DOCKER_LINTFLAGS)
|
||||
fi
|
||||
-AX_BUILD_DATE_EPOCH(CONFIG_DATE, [%Y%m%d])
|
||||
+CONFIG_DATE=20000101
|
||||
AC_ARG_VAR(SOURCE_DATE_EPOCH, [If it is set, it uses the value of that variable instead of the current time as the build timestamp. The format is a unix timestamp. This enables reproducible build output.])
|
||||
AC_SUBST(CONFIG_DATE)
|
||||
|
||||
@@ -2559,7 +2559,7 @@ char *unbound_stat_strdup_log(const char *s, const char* file, int line,
|
||||
dnl if we build from source tree, the man pages need @date@ and @version@
|
||||
dnl if this is a distro tarball, that was already done by makedist.sh
|
||||
AC_SUBST(version, [VERSION_MAJOR.VERSION_MINOR.VERSION_MICRO])
|
||||
-AX_BUILD_DATE_EPOCH(date, [[%b %e, %Y]])
|
||||
+date='Jan 1, 2000'
|
||||
AC_SUBST(date)
|
||||
|
||||
AC_CONFIG_FILES([Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-anchor.8 doc/unbound-checkconf.8 doc/unbound.conf.5 doc/unbound-control.8 doc/unbound-host.1 smallapp/unbound-control-setup.sh dnstap/dnstap_config.h dnscrypt/dnscrypt_config.h contrib/libunbound.pc contrib/unbound.socket contrib/unbound.service contrib/unbound_portable.service])
|
||||
@@ -69,6 +69,7 @@ namespace http
|
||||
virtual void set_auto_connect(bool auto_connect) = 0;
|
||||
virtual bool connect(std::chrono::milliseconds timeout) = 0;
|
||||
virtual bool disconnect() = 0;
|
||||
virtual bool shutdown() { return disconnect(); } // tear down: the client need not be usable afterwards; overrides should interrupt blocked calls and be callable from another thread (the default plain disconnect() gives neither guarantee)
|
||||
virtual bool cancel_read() = 0;
|
||||
virtual bool is_connected(bool *ssl = NULL) = 0;
|
||||
virtual bool invoke(const boost::string_ref uri, const boost::string_ref method, const boost::string_ref body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) = 0;
|
||||
|
||||
@@ -100,6 +100,14 @@ namespace net_utils
|
||||
{
|
||||
public:
|
||||
typedef typename t_protocol_handler::connection_context t_connection_context;
|
||||
|
||||
enum status_t {
|
||||
TERMINATED,
|
||||
RUNNING,
|
||||
INTERRUPTED,
|
||||
TERMINATING,
|
||||
WASTED,
|
||||
};
|
||||
private:
|
||||
using connection_t = connection<t_protocol_handler>;
|
||||
using connection_ptr = boost::shared_ptr<connection_t>;
|
||||
@@ -149,14 +157,6 @@ namespace net_utils
|
||||
boost::optional<network_address> real_remote
|
||||
);
|
||||
|
||||
enum status_t {
|
||||
TERMINATED,
|
||||
RUNNING,
|
||||
INTERRUPTED,
|
||||
TERMINATING,
|
||||
WASTED,
|
||||
};
|
||||
|
||||
struct state_t {
|
||||
struct stat_t {
|
||||
struct {
|
||||
@@ -320,7 +320,9 @@ namespace net_utils
|
||||
|
||||
bool speed_limit_is_enabled() const; ///< tells us should we be sleeping here (e.g. do not sleep on RPC connections)
|
||||
|
||||
bool cancel();
|
||||
bool cancel(bool wait_for_shutdown = false);
|
||||
|
||||
status_t get_status() const noexcept { return m_state.status; }
|
||||
|
||||
private:
|
||||
//----------------- i_service_endpoint ---------------------
|
||||
@@ -378,8 +380,21 @@ namespace net_utils
|
||||
/// wait for service workers stop
|
||||
bool timed_wait_server_stop(uint64_t wait_mseconds);
|
||||
|
||||
/// Mark the server as stopping without closing connections or stopping the io_context.
|
||||
bool mark_stop_signal_sent();
|
||||
|
||||
/// Close boosted_tcp_server-owned connections, including ones not yet registered with the protocol handler.
|
||||
void close_server_connections();
|
||||
|
||||
/// Stop the server io_context.
|
||||
void stop_io_context();
|
||||
|
||||
/// Stop the server.
|
||||
void send_stop_signal(std::function<void()> close_all_connections = [](){});
|
||||
///
|
||||
/// Warning: Do NOT call this if the io_context is shared for connections
|
||||
/// managed outside the boosted_tcp_server. See p2p net_node shutdown for
|
||||
/// the correct staged shutdown in that case.
|
||||
void send_stop_signal();
|
||||
|
||||
bool is_stop_signal_sent() const noexcept { return m_stop_signal_sent; };
|
||||
|
||||
|
||||
@@ -824,7 +824,8 @@ namespace net_utils
|
||||
return false;
|
||||
|
||||
// Wait for the write queue to fall below the max. If it doesn't after a
|
||||
// randomized delay, drop the connection.
|
||||
// randomized delay, drop the connection. P2P senders fail fast instead of
|
||||
// parking an io_context worker thread here.
|
||||
auto wait_consume = [this] {
|
||||
auto random_delay = []{
|
||||
using engine = std::mt19937;
|
||||
@@ -848,6 +849,12 @@ namespace net_utils
|
||||
if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT &&
|
||||
m_state.data.write.total_bytes <= static_cast<shared_state&>(connection_basic::get_state()).response_soft_limit)
|
||||
return true;
|
||||
|
||||
if (m_connection_type == e_connection_type_P2P) {
|
||||
MWARNING("Connection " << m_conn_context.m_connection_id << " tripped write limit, terminating");
|
||||
terminate_async();
|
||||
return false;
|
||||
}
|
||||
m_state.data.write.wait_consume = true;
|
||||
bool success = m_state.condition.wait_for(
|
||||
m_state.lock,
|
||||
@@ -886,7 +893,11 @@ namespace net_utils
|
||||
};
|
||||
if (!wait_sender())
|
||||
return false;
|
||||
constexpr size_t CHUNK_SIZE = 32 * 1024;
|
||||
/* CHUNK_SIZE indirectly caps outgoing to 128 * 1024 * 1000
|
||||
(ABSTRACT_SERVER_SEND_QUE_MAX_COUNT). The "soft" limit total is currently
|
||||
100 MiB (ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT). These values will
|
||||
need to be re-visited alongside block limit increases. */
|
||||
constexpr size_t CHUNK_SIZE = 128 * 1024;
|
||||
if (m_connection_type == e_connection_type_RPC ||
|
||||
message.size() <= 2 * CHUNK_SIZE
|
||||
) {
|
||||
@@ -898,13 +909,19 @@ namespace net_utils
|
||||
start_write();
|
||||
}
|
||||
else {
|
||||
std::size_t soft_limit = 0;
|
||||
const epee::misc_utils::auto_scope_leave_caller scope_exit_handler =
|
||||
epee::misc_utils::create_scope_leave_handler([&soft_limit, this] {
|
||||
m_state.data.write.total_bytes += soft_limit;
|
||||
});
|
||||
|
||||
while (!message.empty()) {
|
||||
if (!wait_consume())
|
||||
return false;
|
||||
m_state.data.write.queue.emplace_front(
|
||||
message.take_slice(CHUNK_SIZE)
|
||||
);
|
||||
m_state.data.write.total_bytes += m_state.data.write.queue.front().size();
|
||||
soft_limit += m_state.data.write.queue.front().size();
|
||||
start_write();
|
||||
}
|
||||
}
|
||||
@@ -960,14 +977,16 @@ namespace net_utils
|
||||
|
||||
ec_t ec;
|
||||
#if !defined(_WIN32) || !defined(__i686)
|
||||
connection_basic::socket_.next_layer().set_option(
|
||||
boost::asio::detail::socket_option::integer<IPPROTO_IP, IP_TOS>{
|
||||
connection_basic::get_tos_flag()
|
||||
},
|
||||
ec
|
||||
);
|
||||
if (ec.value())
|
||||
return false;
|
||||
if (real_remote->get_type_id() == ipv4_network_address::get_type_id()) {
|
||||
connection_basic::socket_.next_layer().set_option(
|
||||
boost::asio::detail::socket_option::integer<IPPROTO_IP, IP_TOS>{
|
||||
connection_basic::get_tos_flag()
|
||||
},
|
||||
ec
|
||||
);
|
||||
if (ec.value())
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
connection_basic::socket_.next_layer().set_option(
|
||||
boost::asio::ip::tcp::no_delay{false},
|
||||
@@ -1117,9 +1136,9 @@ namespace net_utils
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool connection<T>::cancel()
|
||||
bool connection<T>::cancel(const bool wait_for_shutdown)
|
||||
{
|
||||
return close(false);
|
||||
return close(wait_for_shutdown);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -1138,7 +1157,9 @@ namespace net_utils
|
||||
bool connection<T>::close(const bool wait_for_shutdown)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_state.lock);
|
||||
if (m_state.status != status_t::RUNNING)
|
||||
if (m_state.status == status_t::TERMINATED || m_state.status == status_t::WASTED)
|
||||
return true;
|
||||
if (!wait_for_shutdown && m_state.status != status_t::RUNNING)
|
||||
return false;
|
||||
terminate_async();
|
||||
|
||||
@@ -1215,16 +1236,13 @@ namespace net_utils
|
||||
template<typename T>
|
||||
bool connection<T>::add_ref()
|
||||
{
|
||||
try {
|
||||
auto self = connection<T>::shared_from_this();
|
||||
std::lock_guard<std::mutex> guard(m_state.lock);
|
||||
this->self = std::move(self);
|
||||
++m_state.protocol.reference_counter;
|
||||
return true;
|
||||
}
|
||||
catch (boost::bad_weak_ptr &exception) {
|
||||
auto self = connection<T>::weak_from_this().lock();
|
||||
if (!self)
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(m_state.lock);
|
||||
this->self = std::move(self);
|
||||
++m_state.protocol.reference_counter;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -1284,7 +1302,7 @@ namespace net_utils
|
||||
template<class t_protocol_handler>
|
||||
boosted_tcp_server<t_protocol_handler>::~boosted_tcp_server()
|
||||
{
|
||||
this->send_stop_signal();
|
||||
send_stop_signal();
|
||||
timed_wait_server_stop(10000);
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
@@ -1575,26 +1593,55 @@ namespace net_utils
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
template<class t_protocol_handler>
|
||||
void boosted_tcp_server<t_protocol_handler>::send_stop_signal(std::function<void()> close_all_connections)
|
||||
bool boosted_tcp_server<t_protocol_handler>::mark_stop_signal_sent()
|
||||
{
|
||||
m_stop_signal_sent = true;
|
||||
if (m_stop_signal_sent.exchange(true))
|
||||
{
|
||||
MDEBUG("Stop signal already sent");
|
||||
return false;
|
||||
}
|
||||
typename connection<t_protocol_handler>::shared_state *state = static_cast<typename connection<t_protocol_handler>::shared_state*>(m_state.get());
|
||||
state->stop_signal_sent = true;
|
||||
TRY_ENTRY();
|
||||
connections_mutex.lock();
|
||||
for (auto &c: connections_)
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
template<class t_protocol_handler>
|
||||
void boosted_tcp_server<t_protocol_handler>::close_server_connections()
|
||||
{
|
||||
decltype(connections_) connections;
|
||||
{
|
||||
c->cancel();
|
||||
boost::unique_lock<boost::mutex> lock(connections_mutex);
|
||||
connections.swap(connections_);
|
||||
}
|
||||
connections_.clear();
|
||||
connections_mutex.unlock();
|
||||
|
||||
// Since we shut down connections in the strand, we want to make sure to complete the shutdown sequence before
|
||||
// stopping the io_context. We let the caller handle closing because the caller is the one keeping track of all
|
||||
// connections (connections_ is only a subset of all connections).
|
||||
close_all_connections();
|
||||
for (auto &c: connections)
|
||||
{
|
||||
c->cancel(true/*wait_for_shutdown*/);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
template<class t_protocol_handler>
|
||||
void boosted_tcp_server<t_protocol_handler>::stop_io_context()
|
||||
{
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(connections_mutex);
|
||||
if (!connections_.empty())
|
||||
{
|
||||
MERROR("Stopping io_context with " << connections_.size() << " server-owned connections still open");
|
||||
}
|
||||
}
|
||||
MDEBUG("Stopping io_context");
|
||||
io_context_.stop();
|
||||
MDEBUG("Done with send_stop_signal");
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
template<class t_protocol_handler>
|
||||
void boosted_tcp_server<t_protocol_handler>::send_stop_signal()
|
||||
{
|
||||
TRY_ENTRY();
|
||||
if (!mark_stop_signal_sent())
|
||||
return;
|
||||
close_server_connections();
|
||||
stop_io_context();
|
||||
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::send_stop_signal()", void());
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#pragma once
|
||||
#include "memwipe.h"
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <boost/utility/string_view.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -70,7 +70,7 @@ namespace net_utils
|
||||
|
||||
std::string get_value_from_uri_line(const std::string& param_name, const std::string& uri);
|
||||
|
||||
static inline void add_field(std::string& out, const boost::string_ref name, const boost::string_ref value)
|
||||
static inline void add_field(std::string& out, boost::string_view name, boost::string_view value)
|
||||
{
|
||||
out.append(name.data(), name.size()).append(": ");
|
||||
out.append(value.data(), value.size()).append("\r\n");
|
||||
@@ -80,6 +80,42 @@ namespace net_utils
|
||||
add_field(out, field.first, field.second);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
inline bool parse_header_line(boost::string_view line, boost::string_view& name, boost::string_view& value)
|
||||
{
|
||||
if(!line.empty() && line.back() == '\r')
|
||||
line.remove_suffix(1);
|
||||
if(line.empty())
|
||||
return false;
|
||||
if(line.front() == ' ' || line.front() == '\t')
|
||||
return false;
|
||||
|
||||
const size_t colon = line.find(':');
|
||||
if(colon == boost::string_view::npos || colon == 0)
|
||||
return false;
|
||||
|
||||
name = line.substr(0, colon);
|
||||
value = line.substr(colon + 1);
|
||||
while(!value.empty() && (value.front() == ' ' || value.front() == '\t'))
|
||||
value.remove_prefix(1);
|
||||
while(!value.empty() && (value.back() == ' ' || value.back() == '\t'))
|
||||
value.remove_suffix(1);
|
||||
|
||||
for(char c : name)
|
||||
{
|
||||
const bool alnum = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
|
||||
const bool allowed = alnum ||
|
||||
c == '!' || c == '#' || c == '$' || c == '%' || c == '&' ||
|
||||
c == '\'' || c == '*' || c == '+' || c == '-' || c == '.' ||
|
||||
c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
|
||||
if(!allowed)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
struct http_header_info
|
||||
{
|
||||
|
||||
@@ -207,6 +207,11 @@ namespace net_utils
|
||||
m_reconnect = false;
|
||||
return m_net_client.cancel_read();
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool shutdown() override
|
||||
{
|
||||
return m_net_client.shutdown(); // no m_lock; interrupts an invoke blocked while holding it
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool is_connected(bool *ssl = NULL) override
|
||||
{
|
||||
@@ -426,7 +431,11 @@ namespace net_utils
|
||||
recv_buff.assign(m_header_cache.begin()+pos+4, m_header_cache.end());
|
||||
m_header_cache.erase(m_header_cache.begin()+pos+4, m_header_cache.end());
|
||||
|
||||
analize_cached_header_and_invoke_state();
|
||||
if(!analize_cached_header_and_invoke_state())
|
||||
{
|
||||
m_state = reciev_machine_state_error;
|
||||
return false;
|
||||
}
|
||||
if (!on_header(m_response_info))
|
||||
{
|
||||
MDEBUG("Connection cancelled by on_header");
|
||||
@@ -597,7 +606,7 @@ namespace net_utils
|
||||
}
|
||||
if(!get_chunk_head(m_chunked_cache, m_len_in_remain, is_matched))
|
||||
{
|
||||
LOG_ERROR("http_stream_filter::handle_chunked(*) Failed to get length from chunked head:" << m_chunked_cache);
|
||||
LOG_ERROR("http_stream_filter::handle_chunked(*) Failed to get length from chunked head (" << m_chunked_cache.size() << " bytes)");
|
||||
m_state = reciev_machine_state_error;
|
||||
return false;
|
||||
}
|
||||
@@ -659,64 +668,46 @@ namespace net_utils
|
||||
{
|
||||
MTRACE("http_stream_filter::parse_cached_header(*)");
|
||||
|
||||
const char *ptr = m_cache_to_process.c_str();
|
||||
while (ptr[0] != '\r' || ptr[1] != '\n')
|
||||
size_t cur = 0;
|
||||
while(cur < m_cache_to_process.size())
|
||||
{
|
||||
// optional \n
|
||||
if (*ptr == '\n')
|
||||
++ptr;
|
||||
// an identifier composed of letters or -
|
||||
const char *key_pos = ptr;
|
||||
while (isalnum(*ptr) || *ptr == '_' || *ptr == '-')
|
||||
++ptr;
|
||||
const char *key_end = ptr;
|
||||
// optional space (not in RFC, but in previous code)
|
||||
if (*ptr == ' ')
|
||||
++ptr;
|
||||
CHECK_AND_ASSERT_MES(*ptr == ':', true, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process);
|
||||
++ptr;
|
||||
// optional whitespace, but not newlines - line folding is obsolete, let's ignore it
|
||||
while (isblank(*ptr))
|
||||
++ptr;
|
||||
const char *value_pos = ptr;
|
||||
while (*ptr != '\r' && *ptr != '\n')
|
||||
++ptr;
|
||||
const char *value_end = ptr;
|
||||
// optional trailing whitespace
|
||||
while (value_end > value_pos && isblank(*(value_end-1)))
|
||||
--value_end;
|
||||
if (*ptr == '\r')
|
||||
++ptr;
|
||||
CHECK_AND_ASSERT_MES(*ptr == '\n', true, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process);
|
||||
++ptr;
|
||||
const size_t line_end = m_cache_to_process.find('\n', cur);
|
||||
CHECK_AND_ASSERT_MES(line_end != std::string::npos, false, "http_stream_filter::parse_cached_header() invalid header (" << m_cache_to_process.size() << " bytes)");
|
||||
|
||||
const std::string key = std::string(key_pos, key_end - key_pos);
|
||||
const std::string value = std::string(value_pos, value_end - value_pos);
|
||||
if (!key.empty())
|
||||
{
|
||||
if (!string_tools::compare_no_case(key, "Connection"))
|
||||
body_info.m_connection = value;
|
||||
else if(!string_tools::compare_no_case(key, "Referrer"))
|
||||
body_info.m_referer = value;
|
||||
else if(!string_tools::compare_no_case(key, "Content-Length"))
|
||||
body_info.m_content_length = value;
|
||||
else if(!string_tools::compare_no_case(key, "Content-Type"))
|
||||
body_info.m_content_type = value;
|
||||
else if(!string_tools::compare_no_case(key, "Transfer-Encoding"))
|
||||
body_info.m_transfer_encoding = value;
|
||||
else if(!string_tools::compare_no_case(key, "Content-Encoding"))
|
||||
body_info.m_content_encoding = value;
|
||||
else if(!string_tools::compare_no_case(key, "Host"))
|
||||
body_info.m_host = value;
|
||||
else if(!string_tools::compare_no_case(key, "Cookie"))
|
||||
body_info.m_cookie = value;
|
||||
else if(!string_tools::compare_no_case(key, "User-Agent"))
|
||||
body_info.m_user_agent = value;
|
||||
else if(!string_tools::compare_no_case(key, "Origin"))
|
||||
body_info.m_origin = value;
|
||||
else
|
||||
body_info.m_etc_fields.emplace_back(key, value);
|
||||
}
|
||||
boost::string_view line(m_cache_to_process.data() + cur, line_end - cur);
|
||||
cur = line_end + 1;
|
||||
|
||||
if(line == "\r" || line.empty())
|
||||
break;
|
||||
|
||||
boost::string_view name;
|
||||
boost::string_view value;
|
||||
CHECK_AND_ASSERT_MES(detail::parse_header_line(line, name, value), false, "http_stream_filter::parse_cached_header() invalid header (" << m_cache_to_process.size() << " bytes)");
|
||||
|
||||
std::string key(name.data(), name.size());
|
||||
std::string val(value.data(), value.size());
|
||||
if (!string_tools::compare_no_case(key, "Connection"))
|
||||
body_info.m_connection = std::move(val);
|
||||
else if(!string_tools::compare_no_case(key, "Referer"))
|
||||
body_info.m_referer = std::move(val);
|
||||
else if(!string_tools::compare_no_case(key, "Content-Length"))
|
||||
body_info.m_content_length = std::move(val);
|
||||
else if(!string_tools::compare_no_case(key, "Content-Type"))
|
||||
body_info.m_content_type = std::move(val);
|
||||
else if(!string_tools::compare_no_case(key, "Transfer-Encoding"))
|
||||
body_info.m_transfer_encoding = std::move(val);
|
||||
else if(!string_tools::compare_no_case(key, "Content-Encoding"))
|
||||
body_info.m_content_encoding = std::move(val);
|
||||
else if(!string_tools::compare_no_case(key, "Host"))
|
||||
body_info.m_host = std::move(val);
|
||||
else if(!string_tools::compare_no_case(key, "Cookie"))
|
||||
body_info.m_cookie = std::move(val);
|
||||
else if(!string_tools::compare_no_case(key, "User-Agent"))
|
||||
body_info.m_user_agent = std::move(val);
|
||||
else if(!string_tools::compare_no_case(key, "Origin"))
|
||||
body_info.m_origin = std::move(val);
|
||||
else
|
||||
body_info.m_etc_fields.emplace_back(std::move(key), std::move(val));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -725,25 +716,25 @@ namespace net_utils
|
||||
{
|
||||
//First line response, look like this: "HTTP/1.1 200 OK"
|
||||
const char *ptr = m_header_cache.c_str();
|
||||
CHECK_AND_ASSERT_MES(!memcmp(ptr, "HTTP/", 5), false, "Invalid first response line: " + m_header_cache);
|
||||
CHECK_AND_ASSERT_MES(!memcmp(ptr, "HTTP/", 5), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)");
|
||||
ptr += 5;
|
||||
CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line: " + m_header_cache);
|
||||
CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)");
|
||||
unsigned long ul;
|
||||
char *end;
|
||||
ul = strtoul(ptr, &end, 10);
|
||||
CHECK_AND_ASSERT_MES(ul <= INT_MAX && *end =='.', false, "Invalid first response line: " + m_header_cache);
|
||||
CHECK_AND_ASSERT_MES(ul <= INT_MAX && *end =='.', false, "Invalid first response line (" << m_header_cache.size() << " header bytes)");
|
||||
m_response_info.m_http_ver_hi = ul;
|
||||
ptr = end + 1;
|
||||
CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr);
|
||||
CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)");
|
||||
ul = strtoul(ptr, &end, 10);
|
||||
CHECK_AND_ASSERT_MES(ul <= INT_MAX && isblank(*end), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr);
|
||||
CHECK_AND_ASSERT_MES(ul <= INT_MAX && isblank(*end), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)");
|
||||
m_response_info.m_http_ver_lo = ul;
|
||||
ptr = end + 1;
|
||||
while (isblank(*ptr))
|
||||
++ptr;
|
||||
CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line: " + m_header_cache);
|
||||
CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)");
|
||||
ul = strtoul(ptr, &end, 10);
|
||||
CHECK_AND_ASSERT_MES(ul >= 100 && ul <= 999 && isspace(*end), false, "Invalid first response line: " + m_header_cache);
|
||||
CHECK_AND_ASSERT_MES(ul >= 100 && ul <= 999 && isspace(*end), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)");
|
||||
m_response_info.m_response_code = ul;
|
||||
ptr = end;
|
||||
// ignore the optional text, till the end
|
||||
@@ -751,7 +742,7 @@ namespace net_utils
|
||||
++ptr;
|
||||
if (*ptr == '\r')
|
||||
++ptr;
|
||||
CHECK_AND_ASSERT_MES(*ptr == '\n', false, "Invalid first response line: " << m_header_cache);
|
||||
CHECK_AND_ASSERT_MES(*ptr == '\n', false, "Invalid first response line (" << m_header_cache.size() << " header bytes)");
|
||||
++ptr;
|
||||
|
||||
m_header_cache.erase(0, ptr - m_header_cache.c_str());
|
||||
@@ -783,7 +774,7 @@ namespace net_utils
|
||||
std::string fake_str; //gcc error workaround
|
||||
|
||||
bool res = parse_header(m_response_info.m_header_info, m_header_cache);
|
||||
CHECK_AND_ASSERT_MES(res, false, "http_stream_filter::analize_cached_reply_header_and_invoke_state(): failed to anilize reply header: " << m_header_cache);
|
||||
CHECK_AND_ASSERT_MES(res, false, "http_stream_filter::analize_cached_reply_header_and_invoke_state(): failed to anilize reply header (" << m_header_cache.size() << " bytes)");
|
||||
|
||||
set_reply_content_encoder();
|
||||
|
||||
@@ -805,7 +796,7 @@ namespace net_utils
|
||||
string_tools::trim(m_response_info.m_header_info.m_transfer_encoding);
|
||||
if(string_tools::compare_no_case(m_response_info.m_header_info.m_transfer_encoding, "chunked"))
|
||||
{
|
||||
LOG_ERROR("Wrong Transfer-Encoding:" << m_response_info.m_header_info.m_transfer_encoding);
|
||||
LOG_ERROR("Wrong Transfer-Encoding (" << m_response_info.m_header_info.m_transfer_encoding.size() << " bytes)");
|
||||
m_state = reciev_machine_state_error;
|
||||
return false;
|
||||
}
|
||||
@@ -818,7 +809,7 @@ namespace net_utils
|
||||
//In the response header the length was specified
|
||||
if(!content_len_valid)
|
||||
{
|
||||
LOG_ERROR("http_stream_filter::analize_cached_reply_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<<m_response_info.m_header_info.m_content_length);
|
||||
LOG_ERROR("http_stream_filter::analize_cached_reply_header_and_invoke_state(): Failed to get_len_from_content_lenght() (" << m_response_info.m_header_info.m_content_length.size() << " bytes)");
|
||||
m_state = reciev_machine_state_error;
|
||||
return false;
|
||||
}
|
||||
@@ -844,7 +835,7 @@ namespace net_utils
|
||||
}else
|
||||
{ //Apparently there are no signs of the form of transfer, will receive data until the connection is closed
|
||||
m_state = reciev_machine_state_error;
|
||||
MERROR("Undefined transfer type, consider http_body_transfer_connection_close method. header: " << m_header_cache);
|
||||
MERROR("Undefined transfer type, consider http_body_transfer_connection_close method (" << m_header_cache.size() << " header bytes)");
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
@@ -875,7 +866,7 @@ namespace net_utils
|
||||
boundary = result[7];
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Failed to match boundary in content-type=" << head_info.m_content_type);
|
||||
LOG_ERROR("Failed to match boundary in content-type (" << head_info.m_content_type.size() << " bytes)");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "net_utils_base.h"
|
||||
#include "http_auth.h"
|
||||
#include "http_base.h"
|
||||
#include "syncobj.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "net.http"
|
||||
@@ -65,6 +66,9 @@ namespace net_utils
|
||||
critical_section m_lock;
|
||||
};
|
||||
|
||||
// RPC limits groupable IPv6 clients by /64 to avoid per-address limit bypasses.
|
||||
std::string get_rpc_connection_limit_key(const net_utils::network_address& address);
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
//
|
||||
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include "http_protocol_handler.h"
|
||||
#include "reg_exp_definer.h"
|
||||
#include "string_tools.h"
|
||||
@@ -100,7 +101,7 @@ namespace net_utils
|
||||
entry.m_etc_header_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val]));
|
||||
else
|
||||
{
|
||||
LOG_ERROR("simple_http_connection_handler::parse_header() not matched last entry in:"<<std::string(it_current_bound, it_end));
|
||||
LOG_ERROR("simple_http_connection_handler::parse_header() not matched last entry (" << std::distance(it_current_bound, it_end) << " bytes)");
|
||||
}
|
||||
|
||||
it_current_bound = result[(int)result.size()-1].first;
|
||||
@@ -121,7 +122,7 @@ namespace net_utils
|
||||
|
||||
if(!parse_header(it_begin, end_header_it+4, entry))
|
||||
{
|
||||
LOG_ERROR("Failed to parse header:" << std::string(it_begin, end_header_it+2));
|
||||
LOG_ERROR("Failed to parse header (" << std::distance(it_begin, end_header_it + 2) << " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -138,7 +139,7 @@ namespace net_utils
|
||||
std::string boundary;
|
||||
if(!match_boundary(content_type, boundary))
|
||||
{
|
||||
MERROR("Failed to match boundary in content type: " << content_type);
|
||||
MERROR("Failed to match boundary in content type (" << content_type.size() << " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -224,7 +225,7 @@ namespace net_utils
|
||||
CRITICAL_REGION_LOCAL(m_config.m_lock);
|
||||
if (m_config.m_connection_count)
|
||||
--m_config.m_connection_count;
|
||||
auto elem = m_config.m_connections.find(m_conn_context.m_remote_address.host_str());
|
||||
auto elem = m_config.m_connections.find(get_rpc_connection_limit_key(m_conn_context.m_remote_address));
|
||||
if (elem != m_config.m_connections.end())
|
||||
{
|
||||
if (elem->second == 1 || elem->second == 0)
|
||||
@@ -242,7 +243,7 @@ namespace net_utils
|
||||
bool simple_http_connection_handler<t_connection_context>::after_init_connection()
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_config.m_lock);
|
||||
++m_config.m_connections[m_conn_context.m_remote_address.host_str()];
|
||||
++m_config.m_connections[get_rpc_connection_limit_key(m_conn_context.m_remote_address)];
|
||||
++m_config.m_connection_count;
|
||||
m_initialized = true;
|
||||
return true;
|
||||
@@ -429,7 +430,7 @@ namespace net_utils
|
||||
}else
|
||||
{
|
||||
m_state = http_state_error;
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::handle_invoke_query_line(): Failed to match first line: " << m_cache);
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::handle_invoke_query_line(): Failed to match first line (" << m_cache.size() << " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -453,14 +454,14 @@ namespace net_utils
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::analize_cached_request_header_and_invoke_state(size_t pos)
|
||||
{
|
||||
LOG_PRINT_L3("HTTP HEAD:\r\n" << m_cache.substr(0, pos));
|
||||
LOG_PRINT_L3("HTTP HEAD: " << pos << " bytes");
|
||||
|
||||
m_query_info.m_full_request_buf_size = pos;
|
||||
m_query_info.m_request_head.assign(m_cache.begin(), m_cache.begin()+pos);
|
||||
|
||||
if(!parse_cached_header(m_query_info.m_header_info, m_cache, pos))
|
||||
{
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::analize_cached_request_header_and_invoke_state(): failed to anilize request header: " << m_cache);
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::analize_cached_request_header_and_invoke_state(): failed to anilize request header (" << pos << " bytes)");
|
||||
m_state = http_state_error;
|
||||
return false;
|
||||
}
|
||||
@@ -476,7 +477,7 @@ namespace net_utils
|
||||
m_body_transfer_type = http_body_transfer_measure;
|
||||
if(!get_len_from_content_lenght(m_query_info.m_header_info.m_content_length, m_len_summary))
|
||||
{
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::analize_cached_request_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<<m_query_info.m_header_info.m_content_length);
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::analize_cached_request_header_and_invoke_state(): Failed to get_len_from_content_lenght() (" << m_query_info.m_header_info.m_content_length.size() << " bytes)");
|
||||
m_state = http_state_error;
|
||||
return false;
|
||||
}
|
||||
@@ -546,54 +547,52 @@ namespace net_utils
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos)
|
||||
{
|
||||
STATIC_REGEXP_EXPR_1(rexp_mach_field,
|
||||
"\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)"
|
||||
// 12 3 4 5 6 7 8 9 10 11
|
||||
"|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
|
||||
//11 1213 14
|
||||
boost::regex::icase | boost::regex::normal);
|
||||
|
||||
boost::smatch result;
|
||||
std::string::const_iterator it_current_bound = m_cache_to_process.begin();
|
||||
std::string::const_iterator it_end_bound = m_cache_to_process.begin()+pos;
|
||||
|
||||
body_info.clear();
|
||||
if(pos > m_cache_to_process.size() || pos > HTTP_MAX_HEADER_LEN)
|
||||
return false;
|
||||
|
||||
//lookup all fields and fill well-known fields
|
||||
while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched)
|
||||
size_t cur = 0;
|
||||
|
||||
while(cur < pos)
|
||||
{
|
||||
const size_t field_val = 14;
|
||||
const size_t field_etc_name = 12;
|
||||
const size_t line_end = m_cache_to_process.find('\n', cur);
|
||||
if(line_end == std::string::npos || line_end >= pos)
|
||||
break;
|
||||
|
||||
int i = 2; //start position = 2
|
||||
if(result[i++].matched)//"Connection"
|
||||
body_info.m_connection = result[field_val];
|
||||
else if(result[i++].matched)//"Referer"
|
||||
body_info.m_referer = result[field_val];
|
||||
else if(result[i++].matched)//"Content-Length"
|
||||
body_info.m_content_length = result[field_val];
|
||||
else if(result[i++].matched)//"Content-Type"
|
||||
body_info.m_content_type = result[field_val];
|
||||
else if(result[i++].matched)//"Transfer-Encoding"
|
||||
body_info.m_transfer_encoding = result[field_val];
|
||||
else if(result[i++].matched)//"Content-Encoding"
|
||||
body_info.m_content_encoding = result[field_val];
|
||||
else if(result[i++].matched)//"Host"
|
||||
body_info.m_host = result[field_val];
|
||||
else if(result[i++].matched)//"Cookie"
|
||||
body_info.m_cookie = result[field_val];
|
||||
else if(result[i++].matched)//"User-Agent"
|
||||
body_info.m_user_agent = result[field_val];
|
||||
else if(result[i++].matched)//"Origin"
|
||||
body_info.m_origin = result[field_val];
|
||||
else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
|
||||
body_info.m_etc_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val]));
|
||||
boost::string_view line(m_cache_to_process.data() + cur, line_end - cur);
|
||||
cur = line_end + 1;
|
||||
|
||||
// End of header block.
|
||||
if(line == "\r" || line.empty())
|
||||
break;
|
||||
|
||||
boost::string_view name;
|
||||
boost::string_view value;
|
||||
if(!detail::parse_header_line(line, name, value))
|
||||
return false;
|
||||
|
||||
if(boost::iequals(name, "Connection"))
|
||||
body_info.m_connection = std::string(value.data(), value.size());
|
||||
else if(boost::iequals(name, "Referer"))
|
||||
body_info.m_referer = std::string(value.data(), value.size());
|
||||
else if(boost::iequals(name, "Content-Length"))
|
||||
body_info.m_content_length = std::string(value.data(), value.size());
|
||||
else if(boost::iequals(name, "Content-Type"))
|
||||
body_info.m_content_type = std::string(value.data(), value.size());
|
||||
else if(boost::iequals(name, "Transfer-Encoding"))
|
||||
body_info.m_transfer_encoding = std::string(value.data(), value.size());
|
||||
else if(boost::iequals(name, "Content-Encoding"))
|
||||
body_info.m_content_encoding = std::string(value.data(), value.size());
|
||||
else if(boost::iequals(name, "Host"))
|
||||
body_info.m_host = std::string(value.data(), value.size());
|
||||
else if(boost::iequals(name, "Cookie"))
|
||||
body_info.m_cookie = std::string(value.data(), value.size());
|
||||
else if(boost::iequals(name, "User-Agent"))
|
||||
body_info.m_user_agent = std::string(value.data(), value.size());
|
||||
else if(boost::iequals(name, "Origin"))
|
||||
body_info.m_origin = std::string(value.data(), value.size());
|
||||
else
|
||||
{
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::parse_cached_header() not matched last entry in:" << m_cache_to_process);
|
||||
}
|
||||
|
||||
it_current_bound = result[(int)result.size()-1]. first;
|
||||
body_info.m_etc_fields.push_back(std::make_pair(std::string(name.data(), name.size()), std::string(value.data(), value.size())));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
bool parse_res = epee::serialization::load_t_from_json(static_cast<command_type::request&>(req), query_info.m_body); \
|
||||
if (!parse_res) \
|
||||
{ \
|
||||
MERROR("Failed to parse json: \r\n" << query_info.m_body); \
|
||||
MERROR("Failed to parse JSON request"); \
|
||||
response_info.m_response_code = 400; \
|
||||
response_info.m_response_comment = "Bad request"; \
|
||||
return true; \
|
||||
|
||||
@@ -155,7 +155,7 @@ namespace epee
|
||||
return true;
|
||||
|
||||
const bool is_private = na.is_loopback() || na.is_local();
|
||||
const auto elem = config.m_connections.find(na.host_str());
|
||||
const auto elem = config.m_connections.find(net_utils::http::get_rpc_connection_limit_key(na));
|
||||
if (elem != config.m_connections.end())
|
||||
{
|
||||
if (is_private)
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
template<class callback_t>
|
||||
int invoke_async(int command, message_writer in_msg, boost::uuids::uuid connection_id, const callback_t &cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED);
|
||||
|
||||
int send(epee::byte_slice message, const boost::uuids::uuid& connection_id);
|
||||
bool send(epee::byte_slice message, const boost::uuids::uuid& connection_id);
|
||||
bool close(boost::uuids::uuid connection_id, const bool wait_for_shutdown);
|
||||
bool update_connection_context(const t_connection_context& contxt);
|
||||
bool request_callback(boost::uuids::uuid connection_id);
|
||||
@@ -263,6 +263,8 @@ public:
|
||||
}
|
||||
virtual void reset_timer()
|
||||
{
|
||||
if (m_command == connection_context::handshake_command())
|
||||
return;
|
||||
boost::system::error_code ignored_ec;
|
||||
if (!m_cancel_timer_called && m_timer.cancel(ignored_ec) > 0)
|
||||
{
|
||||
@@ -562,7 +564,13 @@ public:
|
||||
return false;
|
||||
}
|
||||
else
|
||||
m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_connection_context);
|
||||
{
|
||||
const int notify_result = m_config.m_pcommands_handler->notify(
|
||||
m_current_head.m_command, buff_to_invoke, m_connection_context
|
||||
);
|
||||
if(notify_result < 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// reuse small buffer
|
||||
if (!temp.empty() && temp.capacity() <= 64 * 1024)
|
||||
@@ -576,32 +584,34 @@ public:
|
||||
{
|
||||
if(m_cache_in_buffer.size() < sizeof(bucket_head2))
|
||||
{
|
||||
if(m_cache_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cache_in_buffer.span(8).data()) != SWAP64LE(LEVIN_SIGNATURE))
|
||||
if(m_cache_in_buffer.size() >= sizeof(uint64_t))
|
||||
{
|
||||
MWARNING(m_connection_context << "Signature mismatch, connection will be closed");
|
||||
return false;
|
||||
std::uint64_t levin_sig;
|
||||
memcpy(&levin_sig, m_cache_in_buffer.span(sizeof(std::uint64_t)).data(), sizeof(std::uint64_t));
|
||||
if (SWAP64LE(LEVIN_SIGNATURE) != levin_sig)
|
||||
{
|
||||
MWARNING(m_connection_context << "Signature mismatch, connection will be closed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
is_continue = false;
|
||||
break;
|
||||
}
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
bucket_head2& phead = *(bucket_head2*)m_cache_in_buffer.span(sizeof(bucket_head2)).data();
|
||||
#else
|
||||
bucket_head2 phead = *(bucket_head2*)m_cache_in_buffer.span(sizeof(bucket_head2)).data();
|
||||
phead.m_signature = SWAP64LE(phead.m_signature);
|
||||
phead.m_cb = SWAP64LE(phead.m_cb);
|
||||
phead.m_command = SWAP32LE(phead.m_command);
|
||||
phead.m_return_code = SWAP32LE(phead.m_return_code);
|
||||
phead.m_flags = SWAP32LE(phead.m_flags);
|
||||
phead.m_protocol_version = SWAP32LE(phead.m_protocol_version);
|
||||
memcpy(&m_current_head, m_cache_in_buffer.span(sizeof(bucket_head2)).data(), sizeof(bucket_head2));
|
||||
#if BYTE_ORDER != LITTLE_ENDIAN
|
||||
m_current_head.m_signature = SWAP64LE(m_current_head.m_signature);
|
||||
m_current_head.m_cb = SWAP64LE(m_current_head.m_cb);
|
||||
m_current_head.m_command = SWAP32LE(m_current_head.m_command);
|
||||
m_current_head.m_return_code = SWAP32LE(m_current_head.m_return_code);
|
||||
m_current_head.m_flags = SWAP32LE(m_current_head.m_flags);
|
||||
m_current_head.m_protocol_version = SWAP32LE(m_current_head.m_protocol_version);
|
||||
#endif
|
||||
if(LEVIN_SIGNATURE != phead.m_signature)
|
||||
if(LEVIN_SIGNATURE != m_current_head.m_signature)
|
||||
{
|
||||
LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed");
|
||||
return false;
|
||||
}
|
||||
m_current_head = phead;
|
||||
|
||||
m_cache_in_buffer.erase(sizeof(bucket_head2));
|
||||
m_state = stream_state_body;
|
||||
@@ -735,8 +745,8 @@ public:
|
||||
`message_writer::finalize_notify`. See additional instructions for
|
||||
`make_fragmented_notify`.
|
||||
|
||||
\return 1 on success */
|
||||
int send(byte_slice message)
|
||||
\return true on success */
|
||||
bool send(byte_slice message)
|
||||
{
|
||||
const misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler(
|
||||
boost::bind(&async_protocol_handler::finish_outer_call, this)
|
||||
@@ -745,9 +755,9 @@ public:
|
||||
if (!send_message(std::move(message)))
|
||||
{
|
||||
LOG_ERROR_CC(m_connection_context, "Failed to send message, dropping it");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------
|
||||
boost::uuids::uuid get_connection_id() {return m_connection_context.m_connection_id;}
|
||||
@@ -927,11 +937,11 @@ void async_protocol_handler_config<t_connection_context>::set_handler(levin_comm
|
||||
}
|
||||
//------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
int async_protocol_handler_config<t_connection_context>::send(byte_slice message, const boost::uuids::uuid& connection_id)
|
||||
bool async_protocol_handler_config<t_connection_context>::send(byte_slice message, const boost::uuids::uuid& connection_id)
|
||||
{
|
||||
async_protocol_handler<t_connection_context>* aph;
|
||||
int r = find_and_lock_connection(connection_id, aph);
|
||||
return LEVIN_OK == r ? aph->send(std::move(message)) : 0;
|
||||
return LEVIN_OK == r ? aph->send(std::move(message)) : false;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
|
||||
@@ -32,10 +32,12 @@
|
||||
//#include <Winsock2.h>
|
||||
//#include <Ws2tcpip.h>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/read.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
@@ -45,6 +47,13 @@
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <functional>
|
||||
#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
|
||||
#include <boost/asio/posix/stream_descriptor.hpp>
|
||||
#include <cerrno>
|
||||
#include <fcntl.h>
|
||||
#include <stdexcept>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "net/net_utils_base.h"
|
||||
#include "net/net_ssl.h"
|
||||
#include "misc_language.h"
|
||||
@@ -109,12 +118,40 @@ namespace net_utils
|
||||
m_initialized(true),
|
||||
m_connected(false),
|
||||
m_deadline(m_io_service, std::chrono::steady_clock::time_point::max()),
|
||||
m_shutdowned(false),
|
||||
#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
|
||||
m_wake_reader(m_io_service),
|
||||
m_wake_writer(-1),
|
||||
#endif
|
||||
m_aborted(false),
|
||||
m_cancel_read(false),
|
||||
m_bytes_sent(0),
|
||||
m_bytes_received(0)
|
||||
{
|
||||
check_deadline();
|
||||
#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
|
||||
// self-pipe so shutdown() can wake a blocked run_one() with an async-signal-safe
|
||||
// write(); without it aborts would wait out the op deadline, so refuse to construct
|
||||
int fds[2];
|
||||
if (0 != ::pipe(fds))
|
||||
throw std::runtime_error("failed to create shutdown wake pipe");
|
||||
bool flags_set = true;
|
||||
for (const int fd : fds)
|
||||
{
|
||||
if (-1 == ::fcntl(fd, F_SETFL, O_NONBLOCK) || -1 == ::fcntl(fd, F_SETFD, FD_CLOEXEC))
|
||||
flags_set = false;
|
||||
}
|
||||
boost::system::error_code ec;
|
||||
if (flags_set)
|
||||
m_wake_reader.assign(fds[0], ec);
|
||||
if (!flags_set || ec)
|
||||
{
|
||||
::close(fds[0]);
|
||||
::close(fds[1]);
|
||||
throw std::runtime_error("failed to set up shutdown wake pipe");
|
||||
}
|
||||
m_wake_writer = fds[1];
|
||||
wait_wake();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*! The first/second parameters are host/port respectively. The third
|
||||
@@ -137,8 +174,12 @@ namespace net_utils
|
||||
~blocked_mode_client()
|
||||
{
|
||||
//profile_tools::local_coast lc("~blocked_mode_client()", 3);
|
||||
try { shutdown(); }
|
||||
try { disconnect(); }
|
||||
catch(...) { /* ignore */ }
|
||||
#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
|
||||
if (m_wake_writer != -1)
|
||||
::close(m_wake_writer); // read end is owned and closed by m_wake_reader
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void set_ssl(ssl_options_t ssl_options)
|
||||
@@ -159,10 +200,16 @@ namespace net_utils
|
||||
inline
|
||||
try_connect_result_t try_connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout)
|
||||
{
|
||||
const auto deadline = std::chrono::steady_clock::now() + timeout;
|
||||
m_deadline.expires_after(timeout);
|
||||
boost::unique_future<boost::asio::ip::tcp::socket> connection = m_connector(addr, port, m_deadline);
|
||||
for (;;)
|
||||
{
|
||||
// aborted or timed out: abandon the attempt; its handlers own their state
|
||||
// and their socket dies with them, at completion or client destruction
|
||||
if (shutdown_requested() || std::chrono::steady_clock::now() >= deadline)
|
||||
return CONNECT_FAILURE;
|
||||
|
||||
m_io_service.restart();
|
||||
m_io_service.run_one();
|
||||
|
||||
@@ -172,6 +219,12 @@ namespace net_utils
|
||||
|
||||
m_ssl_socket->next_layer() = connection.get();
|
||||
m_deadline.cancel();
|
||||
if (shutdown_requested())
|
||||
{
|
||||
boost::system::error_code ignored_ec;
|
||||
m_ssl_socket->next_layer().close(ignored_ec);
|
||||
return CONNECT_FAILURE; // aborted by shutdown() from another thread
|
||||
}
|
||||
if (m_ssl_socket->next_layer().is_open())
|
||||
{
|
||||
m_connected = true;
|
||||
@@ -179,8 +232,13 @@ 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_io_service, *m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout))
|
||||
if (!m_ssl_options.handshake(m_io_service, *m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout, [this] { return shutdown_requested(); }))
|
||||
{
|
||||
if (shutdown_requested())
|
||||
{
|
||||
m_connected = false;
|
||||
return CONNECT_FAILURE; // aborted: says nothing about the server's TLS support
|
||||
}
|
||||
if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
|
||||
{
|
||||
boost::system::error_code ignored_ec;
|
||||
@@ -197,6 +255,15 @@ namespace net_utils
|
||||
}
|
||||
}
|
||||
}
|
||||
// recheck after publishing m_connected: shutdown() between the check above
|
||||
// and the publish must not leave a live connection behind
|
||||
if (shutdown_requested())
|
||||
{
|
||||
m_connected = false;
|
||||
boost::system::error_code ignored_ec;
|
||||
m_ssl_socket->next_layer().close(ignored_ec);
|
||||
return CONNECT_FAILURE;
|
||||
}
|
||||
return CONNECT_SUCCESS;
|
||||
}else
|
||||
{
|
||||
@@ -209,10 +276,13 @@ namespace net_utils
|
||||
inline
|
||||
bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout)
|
||||
{
|
||||
if (shutdown_requested())
|
||||
return false; // torn down: the client never reconnects after shutdown()
|
||||
m_connected = false;
|
||||
try
|
||||
{
|
||||
m_ssl_socket->next_layer().close();
|
||||
m_pending_read.reset();
|
||||
|
||||
// Set SSL options
|
||||
// disable sslv2
|
||||
@@ -256,14 +326,18 @@ namespace net_utils
|
||||
inline
|
||||
bool disconnect()
|
||||
{
|
||||
m_connected = false;
|
||||
try
|
||||
{
|
||||
if(m_connected)
|
||||
if(!shutdown_requested() && m_ssl_socket->next_layer().is_open())
|
||||
{
|
||||
m_connected = false;
|
||||
if(m_ssl_options)
|
||||
shutdown_ssl();
|
||||
m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
|
||||
// The peer or the TLS shutdown deadline may already have closed the connection.
|
||||
boost::system::error_code ignored_ec;
|
||||
m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
|
||||
// shutdown() alone leaves is_open() true and repeats TLS shutdown on later calls.
|
||||
m_ssl_socket->next_layer().close();
|
||||
}
|
||||
}
|
||||
catch(const boost::system::system_error& er)
|
||||
@@ -283,7 +357,6 @@ namespace net_utils
|
||||
inline
|
||||
bool send(const boost::string_ref buff, std::chrono::milliseconds timeout)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
m_deadline.expires_after(timeout);
|
||||
@@ -302,12 +375,25 @@ namespace net_utils
|
||||
async_write(buff.data(), buff.size(), ec);
|
||||
|
||||
// Block until the asynchronous operation has completed.
|
||||
while (ec == boost::asio::error::would_block)
|
||||
while (ec == boost::asio::error::would_block && !shutdown_requested())
|
||||
{
|
||||
m_io_service.restart();
|
||||
m_io_service.run_one();
|
||||
}
|
||||
|
||||
// aborted by shutdown(): the pending handler references this frame, so
|
||||
// close the socket and pump until it completes before returning
|
||||
if (ec == boost::asio::error::would_block)
|
||||
{
|
||||
boost::system::error_code ignored_ec;
|
||||
m_ssl_socket->next_layer().close(ignored_ec);
|
||||
while (ec == boost::asio::error::would_block)
|
||||
{
|
||||
m_io_service.restart();
|
||||
m_io_service.run_one();
|
||||
}
|
||||
}
|
||||
|
||||
if (ec)
|
||||
{
|
||||
LOG_PRINT_L3("Problems at write: " << ec.message());
|
||||
@@ -396,15 +482,66 @@ namespace net_utils
|
||||
{
|
||||
if (!m_connected || !m_ssl_socket->next_layer().is_open())
|
||||
return false;
|
||||
|
||||
boost::system::error_code ec;
|
||||
if (m_ssl_options)
|
||||
{
|
||||
// let Asio process TLS alerts and retain a byte of application data for recv
|
||||
if (!m_pending_read)
|
||||
{
|
||||
const auto pending = std::make_shared<pending_read>();
|
||||
// reconnect may replace the stream before this handler completes
|
||||
const auto socket = m_ssl_socket;
|
||||
socket->async_read_some(boost::asio::buffer(&pending->byte, 1),
|
||||
[socket, pending](const boost::system::error_code& error, size_t) {
|
||||
pending->error = error;
|
||||
if (pending->handler)
|
||||
{
|
||||
auto handler = std::move(pending->handler);
|
||||
handler(error, pending->byte);
|
||||
}
|
||||
});
|
||||
m_pending_read = pending;
|
||||
}
|
||||
m_io_service.restart();
|
||||
m_io_service.poll();
|
||||
ec = m_pending_read->error;
|
||||
}
|
||||
else
|
||||
{
|
||||
// peek for EOF without consuming application data or changing the socket mode
|
||||
auto& socket = m_ssl_socket->next_layer();
|
||||
const bool non_blocking = socket.non_blocking();
|
||||
socket.non_blocking(true, ec);
|
||||
if (!ec)
|
||||
{
|
||||
char byte;
|
||||
socket.receive(boost::asio::buffer(&byte, 1), boost::asio::ip::tcp::socket::message_peek, ec);
|
||||
boost::system::error_code restore_error;
|
||||
socket.non_blocking(non_blocking, restore_error);
|
||||
if (restore_error)
|
||||
{
|
||||
m_connected = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ec && ec != boost::asio::error::would_block &&
|
||||
ec != boost::asio::error::try_again && ec != boost::asio::error::interrupted)
|
||||
{
|
||||
MDEBUG("Peer closed idle connection, marking disconnected: " << ec.message());
|
||||
m_connected = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ssl)
|
||||
*ssl = m_ssl_options.support != ssl_support_t::e_ssl_support_disabled;
|
||||
return true;
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
inline
|
||||
bool recv(std::string& buff, std::chrono::milliseconds timeout)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
// Set a deadline for the asynchronous operation. Since this function uses
|
||||
@@ -435,12 +572,24 @@ namespace net_utils
|
||||
async_read(&buff[0], max_size, boost::asio::transfer_at_least(1), hndlr);
|
||||
|
||||
// Block until the asynchronous operation has completed.
|
||||
while (ec == boost::asio::error::would_block && !m_shutdowned)
|
||||
while (ec == boost::asio::error::would_block && !shutdown_requested())
|
||||
{
|
||||
m_io_service.restart();
|
||||
m_io_service.run_one();
|
||||
}
|
||||
|
||||
// aborted by shutdown(): the pending handler references this frame, so
|
||||
// close the socket and pump until it completes before returning
|
||||
if (ec == boost::asio::error::would_block)
|
||||
{
|
||||
boost::system::error_code ignored_ec;
|
||||
m_ssl_socket->next_layer().close(ignored_ec);
|
||||
while (ec == boost::asio::error::would_block)
|
||||
{
|
||||
m_io_service.restart();
|
||||
m_io_service.run_one();
|
||||
}
|
||||
}
|
||||
if (m_cancel_read)
|
||||
{
|
||||
LOG_ERROR("Cancelling read, returning false");
|
||||
@@ -450,6 +599,7 @@ namespace net_utils
|
||||
|
||||
if (ec)
|
||||
{
|
||||
m_connected = false;
|
||||
MTRACE("READ ENDS: Connection err_code " << ec.value());
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
@@ -460,7 +610,6 @@ namespace net_utils
|
||||
}
|
||||
|
||||
MDEBUG("Problems at read: " << ec.message());
|
||||
m_connected = false;
|
||||
return false;
|
||||
}else
|
||||
{
|
||||
@@ -525,11 +674,24 @@ namespace net_utils
|
||||
async_read((char*)buff.data(), buff.size(), boost::asio::transfer_at_least(buff.size()), hndlr);
|
||||
|
||||
// Block until the asynchronous operation has completed.
|
||||
while (ec == boost::asio::error::would_block && !m_shutdowned)
|
||||
while (ec == boost::asio::error::would_block && !shutdown_requested())
|
||||
{
|
||||
m_io_service.run_one();
|
||||
}
|
||||
|
||||
// aborted by shutdown(): the pending handler references this frame, so
|
||||
// close the socket and pump until it completes before returning
|
||||
if (ec == boost::asio::error::would_block)
|
||||
{
|
||||
boost::system::error_code ignored_ec;
|
||||
m_ssl_socket->next_layer().close(ignored_ec);
|
||||
while (ec == boost::asio::error::would_block)
|
||||
{
|
||||
m_io_service.restart();
|
||||
m_io_service.run_one();
|
||||
}
|
||||
}
|
||||
|
||||
if (ec)
|
||||
{
|
||||
LOG_PRINT_L3("Problems at read: " << ec.message());
|
||||
@@ -569,21 +731,20 @@ namespace net_utils
|
||||
|
||||
bool shutdown()
|
||||
{
|
||||
m_deadline.cancel();
|
||||
boost::system::error_code ec;
|
||||
if(m_ssl_options)
|
||||
shutdown_ssl();
|
||||
m_ssl_socket->next_layer().cancel(ec);
|
||||
if(ec)
|
||||
MDEBUG("Problems at cancel: " << ec.message());
|
||||
m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
|
||||
if(ec)
|
||||
MDEBUG("Problems at shutdown: " << ec.message());
|
||||
m_ssl_socket->next_layer().close(ec);
|
||||
if(ec)
|
||||
MDEBUG("Problems at close: " << ec.message());
|
||||
m_shutdowned = true;
|
||||
m_connected = false;
|
||||
// callable from any thread, on POSIX even a signal handler: only atomics and a self-pipe
|
||||
// write(); a blocked call closes the socket on abort, an idle client's closes at destruction
|
||||
m_aborted = true;
|
||||
m_connected = false;
|
||||
#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
|
||||
// construction guarantees the pipe exists
|
||||
const int saved_errno = errno;
|
||||
const char wake = 0;
|
||||
while (::write(m_wake_writer, &wake, 1) == -1 && errno == EINTR) {}
|
||||
errno = saved_errno; // a full pipe already holds a pending wake
|
||||
#else
|
||||
// Windows console handlers run on their own thread: post() a wake
|
||||
boost::asio::post(m_io_service, []{});
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -616,6 +777,27 @@ namespace net_utils
|
||||
|
||||
private:
|
||||
|
||||
struct pending_read
|
||||
{
|
||||
char byte = 0;
|
||||
boost::system::error_code error = boost::asio::error::would_block;
|
||||
std::function<void(const boost::system::error_code&, char)> handler;
|
||||
};
|
||||
|
||||
bool shutdown_requested() const
|
||||
{
|
||||
return m_aborted.load();
|
||||
}
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
|
||||
// perpetual pipe read so a wake byte written by shutdown() unblocks run_one()
|
||||
void wait_wake()
|
||||
{
|
||||
m_wake_reader.async_read_some(boost::asio::buffer(m_wake_buf),
|
||||
[this] (const boost::system::error_code& ec, std::size_t) { if (!ec) wait_wake(); });
|
||||
}
|
||||
#endif
|
||||
|
||||
void check_deadline()
|
||||
{
|
||||
// Check whether the deadline has passed. We compare the deadline against
|
||||
@@ -682,6 +864,34 @@ namespace net_utils
|
||||
|
||||
void async_read(char* buff, size_t sz, boost::asio::detail::transfer_at_least_t transfer_at_least, handler_obj& hndlr)
|
||||
{
|
||||
// finish the probe before starting another TLS read
|
||||
if (sz && m_pending_read)
|
||||
{
|
||||
const auto pending = m_pending_read;
|
||||
auto on_read = [socket = m_ssl_socket, buff, sz, transfer_at_least, hndlr]
|
||||
(const boost::system::error_code& error, char byte) mutable {
|
||||
if (error)
|
||||
{
|
||||
hndlr(error, 0);
|
||||
return;
|
||||
}
|
||||
buff[0] = byte;
|
||||
if (!transfer_at_least(error, 1) || sz == 1)
|
||||
hndlr(error, 1);
|
||||
else
|
||||
boost::asio::async_read(*socket, boost::asio::buffer(buff + 1, sz - 1),
|
||||
[transfer_at_least](const boost::system::error_code& ec, size_t bytes) mutable {
|
||||
return transfer_at_least(ec, bytes + 1);
|
||||
},
|
||||
[hndlr](const boost::system::error_code& ec, size_t bytes) mutable { hndlr(ec, bytes + 1); });
|
||||
};
|
||||
if (pending->error == boost::asio::error::would_block)
|
||||
pending->handler = std::move(on_read);
|
||||
m_pending_read.reset();
|
||||
if (pending->error != boost::asio::error::would_block)
|
||||
on_read(pending->error, pending->byte);
|
||||
return;
|
||||
}
|
||||
if(m_ssl_options.support == ssl_support_t::e_ssl_support_disabled)
|
||||
boost::asio::async_read(m_ssl_socket->next_layer(), boost::asio::buffer(buff, sz), transfer_at_least, hndlr);
|
||||
else
|
||||
@@ -693,13 +903,22 @@ namespace net_utils
|
||||
boost::asio::io_context m_io_service;
|
||||
boost::asio::ssl::context m_ctx;
|
||||
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_ssl_socket;
|
||||
std::shared_ptr<pending_read> m_pending_read;
|
||||
std::function<connect_func> m_connector;
|
||||
ssl_options_t m_ssl_options;
|
||||
bool m_initialized;
|
||||
bool m_connected;
|
||||
std::atomic<bool> m_connected;
|
||||
boost::asio::steady_timer m_deadline;
|
||||
std::atomic<bool> m_shutdowned;
|
||||
std::atomic<bool> m_cancel_read;
|
||||
#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
|
||||
// self-pipe: shutdown() write()s the pipe to wake run_one() signal-safely
|
||||
boost::asio::posix::stream_descriptor m_wake_reader;
|
||||
int m_wake_writer;
|
||||
char m_wake_buf[8];
|
||||
#endif
|
||||
// sticky: set once by shutdown(), never cleared; the client is being torn down
|
||||
std::atomic<bool> m_aborted;
|
||||
// set by cancel_read() (wallet2::stop() in GUI builds): the next read returns false
|
||||
std::atomic<bool> m_cancel_read;
|
||||
std::atomic<uint64_t> m_bytes_sent;
|
||||
std::atomic<uint64_t> m_bytes_received;
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#define _NET_SSL_H
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -134,6 +135,8 @@ namespace net_utils
|
||||
situations where multiple hostnames are being handled by a server. If
|
||||
`verification == system_ca` the client also does a rfc2818 check to
|
||||
ensure that the server certificate is to the provided hostname.
|
||||
\param abort_requested Polled while waiting; when it returns true the
|
||||
handshake is cancelled and fails instead of running to `timeout`.
|
||||
|
||||
\return True if the SSL handshake completes with peer verification
|
||||
settings. */
|
||||
@@ -143,7 +146,8 @@ namespace net_utils
|
||||
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;
|
||||
std::chrono::milliseconds timeout = std::chrono::seconds(15),
|
||||
const std::function<bool()>& abort_requested = nullptr) const;
|
||||
};
|
||||
|
||||
// https://security.stackexchange.com/questions/34780/checking-client-hello-for-https-classification
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/address_v6.hpp>
|
||||
#include <cstddef>
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
#include "byte_slice.h"
|
||||
@@ -208,6 +209,9 @@ namespace net_utils
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
bool should_group_ipv6_by_prefix(const boost::asio::ip::address_v6& ip);
|
||||
boost::asio::ip::address_v6 get_ipv6_subnet_address(const boost::asio::ip::address_v6& ip, const std::size_t prefix_bits);
|
||||
|
||||
inline bool operator==(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
|
||||
{ return lhs.equal(rhs); }
|
||||
inline bool operator!=(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace epee
|
||||
static bool unserialize_stl_container_t_val(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
|
||||
{
|
||||
container.clear();
|
||||
typename stl_container::value_type exchange_val;
|
||||
typename stl_container::value_type exchange_val{};
|
||||
typename t_storage::harray hval_array = stg.get_first_value(pname, exchange_val, hparent_section);
|
||||
if(!hval_array) return false;
|
||||
container.insert(container.end(), std::move(exchange_val));
|
||||
|
||||
@@ -138,10 +138,10 @@ namespace epee
|
||||
levin::message_writer to_send;
|
||||
stg.store_to_binary(to_send.buffer);
|
||||
|
||||
int res = transport.send(to_send.finalize_notify(command), conn_id);
|
||||
if(res <=0 )
|
||||
bool res = transport.send(to_send.finalize_notify(command), conn_id);
|
||||
if(!res)
|
||||
{
|
||||
MERROR("Failed to notify command " << command << " return code " << res);
|
||||
MERROR("Failed to notify command " << command);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -285,7 +285,7 @@ namespace epee
|
||||
#define END_INVOKE_MAP2() \
|
||||
LOG_ERROR("Unknown command:" << command); \
|
||||
on_levin_traffic(context, false, false, true, in_buff.size(), "invalid-command"); \
|
||||
return LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; \
|
||||
return is_notify ? LEVIN_OK : LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; \
|
||||
} \
|
||||
catch (const std::exception &e) { \
|
||||
MERROR("Error in handle_invoke_map: " << e.what()); \
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace epee
|
||||
{
|
||||
namespace json
|
||||
{
|
||||
#define CHECK_ISSPACE() if(!epee::misc_utils::parse::isspace(*it)){ ASSERT_MES_AND_THROW("Wrong JSON character at: " << std::string(it, buf_end));}
|
||||
#define CHECK_ISSPACE() if(!epee::misc_utils::parse::isspace(*it)){ ASSERT_MES_AND_THROW("Unexpected character in JSON data");}
|
||||
|
||||
/*inline void parse_error()
|
||||
{
|
||||
@@ -168,7 +168,7 @@ namespace epee
|
||||
{
|
||||
//sub section here
|
||||
typename t_storage::hsection new_sec = stg.open_section(name, current_section, true);
|
||||
CHECK_AND_ASSERT_THROW_MES(new_sec, "Failed to insert new section in json: " << std::string(it, buf_end));
|
||||
CHECK_AND_ASSERT_THROW_MES(new_sec, "Failed to insert new section in JSON data");
|
||||
run_handler(new_sec, it, buf_end, stg, recursion + 1);
|
||||
state = match_state_wonder_after_value;
|
||||
}else if(*it == '[')
|
||||
@@ -403,7 +403,7 @@ namespace epee
|
||||
}
|
||||
catch(const std::exception& ex)
|
||||
{
|
||||
MERROR("Failed to parse json, what: " << ex.what());
|
||||
MERROR("Failed to parse json, what: " << misc_utils::parse::transform_to_escape_sequence(ex.what()));
|
||||
return false;
|
||||
}
|
||||
catch(...)
|
||||
|
||||
@@ -28,18 +28,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include "misc_language.h"
|
||||
#include "portable_storage_base.h"
|
||||
#include "parserse_base_utils.h"
|
||||
#include "warnings.h"
|
||||
#include "misc_log_ex.h"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/numeric/conversion/bounds.hpp>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <iomanip>
|
||||
|
||||
namespace epee
|
||||
{
|
||||
@@ -140,36 +137,6 @@ POP_WARNINGS
|
||||
}
|
||||
};
|
||||
|
||||
// For MyMonero/OpenMonero backend compatibility
|
||||
// MyMonero backend sends amount, fees and timestamp values as strings.
|
||||
// Until MM backend is updated, this is needed for compatibility between OpenMonero and MyMonero.
|
||||
template<>
|
||||
struct convert_to_integral<std::string, uint64_t, false>
|
||||
{
|
||||
static void convert(const std::string& from, uint64_t& to)
|
||||
{
|
||||
MTRACE("Converting std::string to uint64_t. Source: " << from);
|
||||
// String only contains digits
|
||||
if(std::all_of(from.begin(), from.end(), epee::misc_utils::parse::isdigit))
|
||||
to = boost::lexical_cast<uint64_t>(from);
|
||||
// MyMonero ISO 8061 timestamp (2017-05-06T16:27:06Z)
|
||||
else if (boost::regex_match (from, boost::regex("\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\dZ")))
|
||||
{
|
||||
// Convert to unix timestamp
|
||||
#ifdef HAVE_STRPTIME
|
||||
struct tm tm;
|
||||
if (strptime(from.c_str(), "%Y-%m-%dT%H:%M:%S", &tm))
|
||||
#else
|
||||
std::tm tm = {};
|
||||
std::istringstream ss(from);
|
||||
if (ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S"))
|
||||
#endif
|
||||
to = std::mktime(&tm);
|
||||
} else
|
||||
ASSERT_AND_THROW_WRONG_CONVERSION();
|
||||
}
|
||||
};
|
||||
|
||||
template<class from_type, class to_type>
|
||||
struct is_convertable: std::integral_constant<bool,
|
||||
std::is_integral<to_type>::value &&
|
||||
|
||||
@@ -31,7 +31,7 @@ set(EPEE_INCLUDE_DIR_BASE "${CMAKE_CURRENT_SOURCE_DIR}/../include")
|
||||
# Add headers to the file list, to be able to search for them and autosave in IDEs.
|
||||
monero_find_all_headers(EPEE_HEADERS_PUBLIC "${EPEE_INCLUDE_DIR_BASE}")
|
||||
|
||||
monero_add_library(epee byte_slice.cpp byte_stream.cpp hex.cpp abstract_http_client.cpp http_auth.cpp mlog.cpp net_helper.cpp net_utils_base.cpp string_tools.cpp parserse_base_utils.cpp
|
||||
monero_add_library(epee byte_slice.cpp byte_stream.cpp hex.cpp abstract_http_client.cpp http_auth.cpp http_protocol_handler.cpp mlog.cpp net_helper.cpp net_utils_base.cpp string_tools.cpp parserse_base_utils.cpp
|
||||
wipeable_string.cpp levin_base.cpp memwipe.c connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp
|
||||
int-util.cpp portable_storage.cpp
|
||||
misc_language.cpp
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2026, 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 "net/http_protocol_handler.h"
|
||||
|
||||
namespace epee { namespace net_utils { namespace http
|
||||
{
|
||||
std::string get_rpc_connection_limit_key(const net_utils::network_address& address)
|
||||
{
|
||||
if (address.get_type_id() == net_utils::ipv6_network_address::get_type_id())
|
||||
{
|
||||
const boost::asio::ip::address_v6 ip = address.as<const net_utils::ipv6_network_address>().ip();
|
||||
if (net_utils::should_group_ipv6_by_prefix(ip))
|
||||
return net_utils::get_ipv6_subnet_address(ip, 64).to_string() + "/64";
|
||||
}
|
||||
|
||||
return address.host_str();
|
||||
}
|
||||
}}}
|
||||
@@ -197,8 +197,7 @@ bool create_rsa_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert)
|
||||
X509_free(cert);
|
||||
return false;
|
||||
}
|
||||
X509_NAME *name = X509_get_subject_name(cert);
|
||||
X509_set_issuer_name(cert, name);
|
||||
X509_set_issuer_name(cert, X509_get_subject_name(cert));
|
||||
|
||||
if (X509_sign(cert, pkey, EVP_sha256()) == 0)
|
||||
{
|
||||
@@ -533,8 +532,16 @@ void ssl_options_t::configure(
|
||||
{
|
||||
// preverified means it passed system or user CA check. System CA is never loaded
|
||||
// when fingerprints are whitelisted.
|
||||
//
|
||||
// The RFC 2818 hostname check must run whenever trust comes from a CA
|
||||
// chain -- system_ca OR user_ca -- otherwise a CA-issued certificate for
|
||||
// any host is accepted for this host (defeats the point of the chain).
|
||||
// It is intentionally skipped for user_certificates (exact pinning) and
|
||||
// none.
|
||||
const bool chain_ca = verification == ssl_verification_t::system_ca ||
|
||||
verification == ssl_verification_t::user_ca;
|
||||
const bool verified = preverified &&
|
||||
(verification != ssl_verification_t::system_ca || host.empty() || MONERO_HOSTNAME_VERIFY(host)(preverified, ctx));
|
||||
(!chain_ca || host.empty() || MONERO_HOSTNAME_VERIFY(host)(preverified, ctx));
|
||||
|
||||
if (!verified && !has_fingerprint(ctx))
|
||||
{
|
||||
@@ -557,7 +564,8 @@ bool ssl_options_t::handshake(
|
||||
boost::asio::ssl::stream_base::handshake_type type,
|
||||
boost::asio::const_buffer buffer,
|
||||
const std::string& host,
|
||||
std::chrono::milliseconds timeout) const
|
||||
std::chrono::milliseconds timeout,
|
||||
const std::function<bool()>& abort_requested) const
|
||||
{
|
||||
configure(socket, type, host);
|
||||
|
||||
@@ -611,6 +619,15 @@ bool ssl_options_t::handshake(
|
||||
boost::asio::post(
|
||||
strand,
|
||||
[&]{
|
||||
std::lock_guard<std::mutex> guard(state.lock);
|
||||
if (state.cancel_handshake) {
|
||||
// cancelled before the socket operation could start: finish here so
|
||||
// the cancelling side cannot leave a handshake running unattended
|
||||
state.wait_handshake = false;
|
||||
state.result = boost::asio::error::operation_aborted;
|
||||
state.condition.notify_all();
|
||||
return;
|
||||
}
|
||||
socket.async_handshake(
|
||||
type,
|
||||
boost::asio::buffer(buffer),
|
||||
@@ -619,10 +636,28 @@ bool ssl_options_t::handshake(
|
||||
}
|
||||
);
|
||||
|
||||
bool aborted = false;
|
||||
while (!io_context.stopped())
|
||||
{
|
||||
io_context.poll_one();
|
||||
std::lock_guard<std::mutex> guard(state.lock);
|
||||
if (!aborted && abort_requested && abort_requested())
|
||||
{
|
||||
// abort from another thread: close the socket so the handshake cannot
|
||||
// stall again, then keep pumping until both handlers have finished
|
||||
aborted = true;
|
||||
if (state.wait_handshake && !state.cancel_handshake)
|
||||
{
|
||||
state.cancel_handshake = true;
|
||||
ec_t ec;
|
||||
socket.next_layer().close(ec);
|
||||
}
|
||||
if (state.wait_timer && !state.cancel_timer)
|
||||
{
|
||||
state.cancel_timer = true;
|
||||
deadline.cancel();
|
||||
}
|
||||
}
|
||||
state.condition.wait_for(
|
||||
state.lock,
|
||||
std::chrono::milliseconds(30),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
|
||||
#include "net/net_utils_base.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
|
||||
#include "string_tools.h"
|
||||
@@ -45,6 +47,34 @@ namespace epee { namespace net_utils
|
||||
bool ipv6_network_address::is_loopback() const { return m_address.is_loopback(); }
|
||||
bool ipv6_network_address::is_local() const { return m_address.is_link_local(); }
|
||||
|
||||
static bool is_ipv6_unique_local(const boost::asio::ip::address_v6& ip)
|
||||
{
|
||||
const boost::asio::ip::address_v6::bytes_type bytes = ip.to_bytes();
|
||||
return (bytes[0] & 0xfe) == 0xfc;
|
||||
}
|
||||
|
||||
bool should_group_ipv6_by_prefix(const boost::asio::ip::address_v6& ip)
|
||||
{
|
||||
return !ip.is_unspecified() && !ip.is_loopback() && !ip.is_multicast() &&
|
||||
!ip.is_link_local() && !ip.is_site_local() && !is_ipv6_unique_local(ip) &&
|
||||
!ip.is_v4_mapped();
|
||||
}
|
||||
|
||||
boost::asio::ip::address_v6 get_ipv6_subnet_address(const boost::asio::ip::address_v6& ip, const std::size_t prefix_bits)
|
||||
{
|
||||
boost::asio::ip::address_v6::bytes_type bytes = ip.to_bytes();
|
||||
const std::size_t bits = std::min(prefix_bits, std::size_t{128});
|
||||
if (bits < 128)
|
||||
{
|
||||
const std::size_t full_bytes = bits / 8;
|
||||
const std::size_t remainder_bits = bits % 8;
|
||||
if (remainder_bits != 0)
|
||||
bytes[full_bytes] &= uint8_t(0xffu << (8 - remainder_bits));
|
||||
std::fill(bytes.begin() + full_bytes + (remainder_bits != 0), bytes.end(), 0);
|
||||
}
|
||||
return boost::asio::ip::address_v6(bytes);
|
||||
}
|
||||
|
||||
|
||||
bool ipv4_network_subnet::equal(const ipv4_network_subnet& other) const noexcept
|
||||
{ return is_same_host(other) && m_mask == other.m_mask; }
|
||||
|
||||
@@ -129,7 +129,7 @@ namespace misc_utils
|
||||
case '/': //Slash character
|
||||
val.push_back('/');break;
|
||||
case 'u': //Unicode code point
|
||||
if (buf_end - it < 4)
|
||||
if (buf_end - it < 5)
|
||||
{
|
||||
ASSERT_MES_AND_THROW("Invalid Unicode escape sequence");
|
||||
}
|
||||
@@ -165,8 +165,7 @@ namespace misc_utils
|
||||
}
|
||||
break;
|
||||
default:
|
||||
val.push_back(*it);
|
||||
LOG_PRINT_L0("Unknown escape sequence :\"\\" << *it << "\"");
|
||||
ASSERT_MES_AND_THROW("Invalid escape sequence in JSON string");
|
||||
}
|
||||
escape_mode = false;
|
||||
}else if(*it == '"')
|
||||
@@ -176,13 +175,13 @@ namespace misc_utils
|
||||
}else if(*it == '\\')
|
||||
{
|
||||
escape_mode = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
val.push_back(*it);
|
||||
}
|
||||
}
|
||||
ASSERT_MES_AND_THROW("Failed to match string in json entry: " << std::string(star_end_string, buf_end));
|
||||
ASSERT_MES_AND_THROW("Unterminated JSON string");
|
||||
}
|
||||
void match_number2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, boost::string_ref& val, bool& is_float_val, bool& is_signed_val)
|
||||
{
|
||||
@@ -215,10 +214,10 @@ namespace misc_utils
|
||||
return;
|
||||
}
|
||||
else
|
||||
ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end));
|
||||
ASSERT_MES_AND_THROW("Invalid number in JSON entry");
|
||||
}
|
||||
}
|
||||
ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end));
|
||||
ASSERT_MES_AND_THROW("Invalid number in JSON entry");
|
||||
}
|
||||
void match_word2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, boost::string_ref& val)
|
||||
{
|
||||
@@ -233,11 +232,11 @@ namespace misc_utils
|
||||
{
|
||||
star_end_string = --it;
|
||||
return;
|
||||
}else
|
||||
ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end));
|
||||
}else
|
||||
ASSERT_MES_AND_THROW("Invalid word in JSON entry");
|
||||
}
|
||||
}
|
||||
ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end));
|
||||
ASSERT_MES_AND_THROW("Invalid word in JSON entry");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@@ -201,7 +202,7 @@ static void handle_line(char* line)
|
||||
static bool same_as_last_line(const std::string& test_line)
|
||||
{
|
||||
// Note that state->offset == state->length, when a new line was entered.
|
||||
HISTORY_STATE* state = history_get_history_state();
|
||||
auto state = std::unique_ptr<HISTORY_STATE, decltype(free)*>{history_get_history_state(), free};
|
||||
return state->length > 0
|
||||
&& test_line.compare(state->entries[state->length-1]->line) == 0;
|
||||
}
|
||||
|
||||
@@ -9,12 +9,18 @@ VERSION=$1
|
||||
DOCKER=`command -v docker`
|
||||
CACHER=`command -v apt-cacher-ng`
|
||||
|
||||
if [ -z "$DOCKER" -o -z "$CACHER" ]; then
|
||||
echo "$0: you must first install docker.io and apt-cacher-ng"
|
||||
echo " e.g. sudo apt-get install docker.io apt-cacher-ng"
|
||||
if [ -z "$DOCKER" ]; then
|
||||
echo "$0: you must first install docker.io"
|
||||
echo " e.g. sudo apt-get install docker.io"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# only use APT cacher if package is present
|
||||
DOCKER_CACHE_LINE=""
|
||||
if [ "$CACHER" ]; then
|
||||
DOCKER_CACHE_LINE="RUN echo 'Acquire::http { Proxy \"http://172.17.0.1:3142\"; };' > /etc/apt/apt.conf.d/50cacher"
|
||||
fi
|
||||
|
||||
GH_USER=${GH_USER-$USER}
|
||||
|
||||
TAG=gitrun-bionic
|
||||
@@ -33,7 +39,7 @@ cat <<EOF > ${TAG}.Dockerfile
|
||||
FROM ubuntu:bionic
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN echo 'Acquire::http { Proxy "http://172.17.0.1:3142"; };' > /etc/apt/apt.conf.d/50cacher
|
||||
$DOCKER_CACHE_LINE
|
||||
RUN echo "$GID" >> /etc/group
|
||||
RUN apt-get update && apt-get --no-install-recommends -y install lsb-release ruby git make wget docker.io python3 curl
|
||||
|
||||
@@ -41,10 +47,10 @@ RUN useradd -ms /bin/bash -U ubuntu -G docker
|
||||
USER ubuntu:docker
|
||||
WORKDIR $WORKDIR
|
||||
|
||||
RUN git clone https://codeberg.org/wownero/gitian.sigs.git sigs; \
|
||||
RUN git clone https://github.com/wownero/gitian.sigs.git sigs; \
|
||||
git clone https://github.com/devrandom/gitian-builder.git builder; \
|
||||
cd builder; git checkout c0f77ca018cb5332bfd595e0aff0468f77542c23; mkdir -p inputs var; cd inputs; \
|
||||
git clone https://codeberg.org/wownero/wownero
|
||||
git clone https://github.com/wownero/wownero
|
||||
|
||||
CMD ["sleep", "infinity"]
|
||||
EOF
|
||||
@@ -66,7 +72,7 @@ cat <<EOF > ${TAG2}.Dockerfile
|
||||
FROM ubuntu:bionic
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN echo 'Acquire::http { Proxy "http://172.17.0.1:3142"; };' > /etc/apt/apt.conf.d/50cacher
|
||||
$DOCKER_CACHE_LINE
|
||||
RUN apt-get update && apt-get --no-install-recommends -y install build-essential git language-pack-en \
|
||||
wget lsb-release curl gcc-7 g++-7 gcc g++ binutils-gold pkg-config autoconf libtool automake faketime \
|
||||
bsdmainutils ca-certificates python cmake gperf
|
||||
@@ -109,7 +115,7 @@ if [ "$check" != "sign" ]; then
|
||||
fi
|
||||
|
||||
if [ ! -d sigs ]; then
|
||||
git clone https://codeberg.org/wownero/gitian.sigs.git sigs
|
||||
git clone https://github.com/wownero/gitian.sigs.git sigs
|
||||
cd sigs
|
||||
git remote add $GH_USER git@github.com:$GH_USER/gitian.sigs.git
|
||||
cd ..
|
||||
|
||||
@@ -26,7 +26,7 @@ packages:
|
||||
- "cmake"
|
||||
- "unzip"
|
||||
remotes:
|
||||
- "url": "https://codeberg.org/wownero/wownero.git"
|
||||
- "url": "https://github.com/wownero/wownero.git"
|
||||
"dir": "wownero"
|
||||
files: []
|
||||
script: |
|
||||
|
||||
@@ -5,7 +5,7 @@ import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
gsigs = 'https://codeberg.org/wownero/gitian.sigs.git'
|
||||
gsigs = 'https://github.com/wownero/gitian.sigs.git'
|
||||
gbrepo = 'https://github.com/devrandom/gitian-builder.git'
|
||||
|
||||
platforms = {'l': ['Linux', 'linux', 'tar.bz2'],
|
||||
@@ -113,7 +113,7 @@ def main():
|
||||
parser = argparse.ArgumentParser(description='Script for running full Gitian builds.', usage='%(prog)s [options] signer version')
|
||||
parser.add_argument('-c', '--commit', action='store_true', dest='commit', help='Indicate that the version argument is for a commit or branch')
|
||||
parser.add_argument('-p', '--pull', action='store_true', dest='pull', help='Indicate that the version argument is the number of a github repository pull request')
|
||||
parser.add_argument('-u', '--url', dest='url', default='https://codeberg.org/wownero/wownero', help='Specify the URL of the repository. Default is %(default)s')
|
||||
parser.add_argument('-u', '--url', dest='url', default='https://github.com/wownero/wownero', help='Specify the URL of the repository. Default is %(default)s')
|
||||
parser.add_argument('-v', '--verify', action='store_true', dest='verify', help='Verify the Gitian build')
|
||||
parser.add_argument('-b', '--build', action='store_true', dest='build', help='Do a Gitian build')
|
||||
parser.add_argument('-B', '--buildsign', action='store_true', dest='buildsign', help='Build both signed and unsigned binaries')
|
||||
|
||||
@@ -26,7 +26,7 @@ packages:
|
||||
- "python"
|
||||
- "cmake"
|
||||
remotes:
|
||||
- "url": "https://codeberg.org/wownero/wownero.git"
|
||||
- "url": "https://github.com/wownero/wownero.git"
|
||||
"dir": "wownero"
|
||||
files: []
|
||||
script: |
|
||||
|
||||
@@ -38,7 +38,7 @@ packages:
|
||||
- "python"
|
||||
- "cmake"
|
||||
remotes:
|
||||
- "url": "https://codeberg.org/wownero/wownero.git"
|
||||
- "url": "https://github.com/wownero/wownero.git"
|
||||
"dir": "wownero"
|
||||
files: []
|
||||
script: |
|
||||
|
||||
@@ -24,7 +24,7 @@ packages:
|
||||
- "python-dev"
|
||||
- "python-setuptools"
|
||||
remotes:
|
||||
- "url": "https://codeberg.org/wownero/wownero.git"
|
||||
- "url": "https://github.com/wownero/wownero.git"
|
||||
"dir": "wownero"
|
||||
files: []
|
||||
script: |
|
||||
|
||||
@@ -35,7 +35,7 @@ alternatives:
|
||||
package: "x86_64-w64-mingw32-gcc"
|
||||
path: "/usr/bin/x86_64-w64-mingw32-gcc-posix"
|
||||
remotes:
|
||||
- "url": "https://codeberg.org/wownero/wownero.git"
|
||||
- "url": "https://github.com/wownero/wownero.git"
|
||||
"dir": "wownero"
|
||||
files: []
|
||||
script: |
|
||||
|
||||
+4
-3
@@ -16,6 +16,7 @@
|
||||
|
||||
#define EASYLOGGING_CC
|
||||
#include "easylogging++.h"
|
||||
#include <cstdint>
|
||||
|
||||
#include <atomic>
|
||||
#include <unistd.h>
|
||||
@@ -2502,11 +2503,11 @@ void DefaultLogDispatchCallback::handle(const LogDispatchData* data) {
|
||||
|
||||
|
||||
template<typename Transform>
|
||||
static inline void utf8canonical(std::string &s, Transform t = [](wint_t c)->wint_t { return c; })
|
||||
static inline void utf8canonical(std::string &s, Transform t = [](std::uint32_t c)->std::uint32_t { return c; })
|
||||
{
|
||||
size_t avail = s.size();
|
||||
const char *ptr = s.data();
|
||||
wint_t cp = 0;
|
||||
std::uint32_t cp = 0;
|
||||
int rbytes = 1, bytes = -1;
|
||||
char wbuf[8], *wptr;
|
||||
size_t w_offset = 0;
|
||||
@@ -2585,7 +2586,7 @@ static inline void utf8canonical(std::string &s, Transform t = [](wint_t c)->win
|
||||
|
||||
void sanitize(std::string &s)
|
||||
{
|
||||
utf8canonical(s, [](wint_t c)->wint_t {
|
||||
utf8canonical(s, [](std::uint32_t c)->std::uint32_t {
|
||||
if (c == 9 || c == 10 || c == 13)
|
||||
return c;
|
||||
if (c < 0x20)
|
||||
|
||||
+1
@@ -2791,6 +2791,7 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe {
|
||||
}
|
||||
};
|
||||
extern ELPP_EXPORT base::type::StoragePointer elStorage;
|
||||
ELPP_EXPORT void sanitize(std::string& s);
|
||||
#define ELPP el::base::Storage::getELPP()
|
||||
class DefaultLogDispatchCallback : public LogDispatchCallback {
|
||||
protected:
|
||||
|
||||
Vendored
+1
-1
Submodule external/randomwow updated: 27b099b6dd...ff47479662
@@ -32,6 +32,11 @@ if (WIN32 OR STATIC)
|
||||
add_definitions(-DSTATICLIB)
|
||||
endif ()
|
||||
|
||||
set(STACK_TRACE_USE_WRAP OFF)
|
||||
if (STACK_TRACE AND NOT STATIC AND CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(STACK_TRACE_USE_WRAP ON)
|
||||
endif()
|
||||
|
||||
function (monero_private_headers group)
|
||||
source_group("${group}\\Private"
|
||||
FILES
|
||||
|
||||
@@ -356,6 +356,7 @@ typedef struct outtx {
|
||||
} outtx;
|
||||
|
||||
std::atomic<uint64_t> mdb_txn_safe::num_active_txns{0};
|
||||
thread_local uint64_t mdb_txn_safe::num_active_txns_per_thread = 0;
|
||||
std::atomic_flag mdb_txn_safe::creation_gate = ATOMIC_FLAG_INIT;
|
||||
|
||||
mdb_threadinfo::~mdb_threadinfo()
|
||||
@@ -373,9 +374,12 @@ mdb_txn_safe::mdb_txn_safe(const bool check) : m_txn(NULL), m_tinfo(NULL), m_che
|
||||
{
|
||||
if (check)
|
||||
{
|
||||
while (creation_gate.test_and_set());
|
||||
num_active_txns++;
|
||||
creation_gate.clear();
|
||||
const bool nested = num_active_txns_per_thread != 0;
|
||||
if (!nested)
|
||||
while (creation_gate.test_and_set());
|
||||
increment_txns(1);
|
||||
if (!nested)
|
||||
creation_gate.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,12 +410,12 @@ mdb_txn_safe::~mdb_txn_safe()
|
||||
}
|
||||
mdb_txn_abort(m_txn);
|
||||
}
|
||||
num_active_txns--;
|
||||
increment_txns(-1);
|
||||
}
|
||||
|
||||
void mdb_txn_safe::uncheck()
|
||||
{
|
||||
num_active_txns--;
|
||||
increment_txns(-1);
|
||||
m_check = false;
|
||||
}
|
||||
|
||||
@@ -466,7 +470,8 @@ void mdb_txn_safe::allow_new_txns()
|
||||
|
||||
void mdb_txn_safe::increment_txns(int i)
|
||||
{
|
||||
num_active_txns += i;
|
||||
num_active_txns_per_thread += i;
|
||||
num_active_txns += i;
|
||||
}
|
||||
|
||||
#define TXN_PREFIX(flags); \
|
||||
@@ -644,18 +649,13 @@ bool BlockchainLMDB::need_resize(uint64_t threshold_size) const
|
||||
float resize_percent = RESIZE_PERCENT;
|
||||
MDEBUG(boost::format("Percent used: %.04f Percent threshold: %.04f") % (100.*size_used/mei.me_mapsize) % (100.*resize_percent));
|
||||
|
||||
if (threshold_size > 0)
|
||||
if (threshold_size > 0 && mei.me_mapsize - size_used <= threshold_size)
|
||||
{
|
||||
if (mei.me_mapsize - size_used < threshold_size)
|
||||
{
|
||||
MINFO("Threshold met (size-based)");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
MINFO("Threshold met (size-based)");
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((double)size_used / mei.me_mapsize > resize_percent)
|
||||
if ((double)size_used / mei.me_mapsize >= resize_percent)
|
||||
{
|
||||
MINFO("Threshold met (percent-based)");
|
||||
return true;
|
||||
@@ -766,7 +766,7 @@ estim:
|
||||
MDEBUG("estimated average block size for batch: " << avg_block_size);
|
||||
|
||||
// bigger safety margin on smaller block sizes
|
||||
if (batch_fudge_factor < 5000.0)
|
||||
if (!batch_bytes && batch_fudge_factor < 5000.0)
|
||||
batch_fudge_factor = 5000.0;
|
||||
threshold_size = avg_block_size * db_expand_factor * batch_fudge_factor;
|
||||
return threshold_size;
|
||||
|
||||
@@ -161,6 +161,7 @@ struct mdb_txn_safe
|
||||
bool m_batch_txn = false;
|
||||
bool m_check;
|
||||
static std::atomic<uint64_t> num_active_txns;
|
||||
static thread_local uint64_t num_active_txns_per_thread;
|
||||
|
||||
// could use a mutex here, but this should be sufficient.
|
||||
static std::atomic_flag creation_gate;
|
||||
|
||||
@@ -185,7 +185,7 @@ static void copy_table(MDB_env *env0, MDB_env *env1, const char *table, unsigned
|
||||
if (dbr) throw std::runtime_error("Failed to commit txn: " + std::string(mdb_strerror(dbr)));
|
||||
tx_active1 = false;
|
||||
MDB_stat stats;
|
||||
dbr = mdb_env_stat(env0, &stats);
|
||||
dbr = mdb_stat(txn0, dbi0, &stats);
|
||||
if (dbr) throw std::runtime_error("Failed to stat " + std::string(table) + " LMDB table: " + std::string(mdb_strerror(dbr)));
|
||||
check_resize(env1, (stats.ms_branch_pages + stats.ms_overflow_pages + stats.ms_leaf_pages) * stats.ms_psize);
|
||||
dbr = mdb_txn_begin(env1, NULL, 0, &txn1);
|
||||
|
||||
@@ -158,6 +158,19 @@ namespace cryptonote
|
||||
return m_points.rbegin()->first;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
uint64_t checkpoints::get_nearest_checkpoint_height(uint64_t block_height) const
|
||||
{
|
||||
if (m_points.empty())
|
||||
return 0;
|
||||
|
||||
auto it = m_points.lower_bound(block_height);
|
||||
if (it == m_points.begin())
|
||||
return 0;
|
||||
|
||||
--it;
|
||||
return it->first;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
const std::map<uint64_t, crypto::hash>& checkpoints::get_points() const
|
||||
{
|
||||
return m_points;
|
||||
|
||||
@@ -129,6 +129,14 @@ namespace cryptonote
|
||||
*/
|
||||
uint64_t get_max_height() const;
|
||||
|
||||
/**
|
||||
* @brief gets the highest checkpoint height less than the given block height
|
||||
*
|
||||
* @param block_height the reference block height
|
||||
* @return the nearest checkpoint height below block_height, or 0 if none
|
||||
*/
|
||||
uint64_t get_nearest_checkpoint_height(uint64_t block_height) const;
|
||||
|
||||
/**
|
||||
* @brief gets the checkpoints container
|
||||
*
|
||||
|
||||
@@ -67,6 +67,11 @@ monero_add_library(common
|
||||
${common_headers}
|
||||
${common_private_headers}
|
||||
DEPENDS generate_translations_header)
|
||||
|
||||
if (STACK_TRACE_USE_WRAP)
|
||||
target_compile_definitions(obj_common PRIVATE STACK_TRACE_USE_WRAP)
|
||||
endif()
|
||||
|
||||
target_link_libraries(common
|
||||
PUBLIC
|
||||
cncrypto
|
||||
@@ -82,5 +87,13 @@ target_link_libraries(common
|
||||
${OPENSSL_LIBRARIES}
|
||||
${EXTRA_LIBRARIES})
|
||||
|
||||
if (STACK_TRACE_USE_WRAP)
|
||||
set_property(TARGET common APPEND PROPERTY LINK_FLAGS
|
||||
"-Wl,--wrap=__cxa_throw")
|
||||
set_property(TARGET common APPEND PROPERTY INTERFACE_LINK_OPTIONS
|
||||
"-Wl,--wrap=__cxa_throw"
|
||||
"-Wl,-u,__wrap___cxa_throw")
|
||||
endif()
|
||||
|
||||
#monero_install_headers(common
|
||||
# ${common_headers})
|
||||
|
||||
@@ -99,17 +99,20 @@ public:
|
||||
{
|
||||
m_flush = false;
|
||||
|
||||
MCLOG_FILE(m_log_level, "msgwriter", m_oss.str());
|
||||
std::string message = m_oss.str();
|
||||
MCLOG_FILE(m_log_level, "msgwriter", message);
|
||||
try { el::base::sanitize(message); }
|
||||
catch (const std::exception&) { message = "<Invalid UTF-8 in message>"; }
|
||||
|
||||
PAUSE_READLINE();
|
||||
if (epee::console_color_default == m_color)
|
||||
{
|
||||
std::cout << m_oss.str();
|
||||
std::cout << message;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_console_color(m_color, m_bright);
|
||||
std::cout << m_oss.str();
|
||||
std::cout << message;
|
||||
epee::reset_console_color();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
#include <libunwind.h>
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
#ifndef STATICLIB
|
||||
#if !defined(STATICLIB) && !defined(STACK_TRACE_USE_WRAP)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@@ -71,12 +71,12 @@
|
||||
#define CXA_THROW_INFO_T void
|
||||
#endif // !__clang__
|
||||
|
||||
#ifdef STATICLIB
|
||||
#if defined(STATICLIB) || defined(STACK_TRACE_USE_WRAP)
|
||||
#define CXA_THROW __wrap___cxa_throw
|
||||
extern "C"
|
||||
__attribute__((noreturn))
|
||||
void __real___cxa_throw(void *ex, CXA_THROW_INFO_T *info, void (*dest)(void*));
|
||||
#else // !STATICLIB
|
||||
#else // STATICLIB || STACK_TRACE_USE_WRAP
|
||||
#define CXA_THROW __cxa_throw
|
||||
extern "C"
|
||||
typedef
|
||||
@@ -84,7 +84,7 @@ typedef
|
||||
__attribute__((noreturn))
|
||||
#endif // __clang__
|
||||
void (cxa_throw_t)(void *ex, CXA_THROW_INFO_T *info, void (*dest)(void*));
|
||||
#endif // !STATICLIB
|
||||
#endif // STATICLIB || STACK_TRACE_USE_WRAP
|
||||
|
||||
extern "C"
|
||||
__attribute__((noreturn))
|
||||
@@ -96,12 +96,12 @@ void CXA_THROW(void *ex, CXA_THROW_INFO_T *info, void (*dest)(void*))
|
||||
tools::log_stack_trace((std::string("Exception: ")+((!status && dsym) ? dsym : (const char*)info)).c_str());
|
||||
free(dsym);
|
||||
|
||||
#ifndef STATICLIB
|
||||
#if !defined(STATICLIB) && !defined(STACK_TRACE_USE_WRAP)
|
||||
#ifndef __clang__ // for GCC the attr can't be applied in typedef like for clang
|
||||
__attribute__((noreturn))
|
||||
#endif // !__clang__
|
||||
cxa_throw_t *__real___cxa_throw = (cxa_throw_t*)dlsym(RTLD_NEXT, "__cxa_throw");
|
||||
#endif // !STATICLIB
|
||||
#endif // !STATICLIB && !STACK_TRACE_USE_WRAP
|
||||
__real___cxa_throw(ex, info, dest);
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,6 @@
|
||||
#define CTHR_THREAD_RETURN return NULL
|
||||
#define CTHR_THREAD_CREATE(thr, func, arg) (pthread_create(&thr, NULL, func, arg) == 0)
|
||||
#define CTHR_THREAD_JOIN(thr) pthread_join(thr, NULL)
|
||||
#define CTHR_THREAD_CLOSE(thr)
|
||||
#define CTHR_THREAD_CLOSE(thr) pthread_detach(thr)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -877,3 +877,5 @@ const ge_p3 ge_p3_H = {
|
||||
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{23443568, -5110398, -8776029, -4345135, 6889568, -14710814, 7474843, 3279062, 14550766, -7453428}
|
||||
};
|
||||
|
||||
const unsigned char sc_inv_eight[32] = {0x79, 0x2f, 0xdc, 0xe2, 0x29, 0xe5, 0x06, 0x61, 0xd0, 0xda, 0x1c, 0x7d, 0xb3, 0x9d, 0xd3, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06};
|
||||
|
||||
@@ -2307,6 +2307,17 @@ void ge_mul8(ge_p1p1 *r, const ge_p2 *t) {
|
||||
ge_p2_dbl(r, &u);
|
||||
}
|
||||
|
||||
void ge_clear_torsion_vartime(unsigned char *out, const ge_p3 *point) {
|
||||
// mul by inv 8, then mul by 8
|
||||
ge_p2 point_inv_8;
|
||||
ge_scalarmult(&point_inv_8, sc_inv_eight, point);
|
||||
ge_p1p1 point_inv_8_mul_8;
|
||||
ge_mul8(&point_inv_8_mul_8, &point_inv_8);
|
||||
ge_p3 torsion_cleared_point;
|
||||
ge_p1p1_to_p3(&torsion_cleared_point, &point_inv_8_mul_8);
|
||||
ge_p3_tobytes(out, &torsion_cleared_point);
|
||||
}
|
||||
|
||||
void ge_fromfe_frombytes_vartime(ge_p2 *r, const unsigned char *s) {
|
||||
fe u, v, w, x, y, z;
|
||||
unsigned char sign;
|
||||
|
||||
@@ -135,6 +135,7 @@ void ge_triple_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const
|
||||
void ge_double_scalarmult_precomp_vartime2(ge_p2 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp);
|
||||
void ge_double_scalarmult_precomp_vartime2_p3(ge_p3 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp);
|
||||
void ge_mul8(ge_p1p1 *, const ge_p2 *);
|
||||
void ge_clear_torsion_vartime(unsigned char *, const ge_p3 *);
|
||||
extern const fe fe_ma2;
|
||||
extern const fe fe_ma;
|
||||
extern const fe fe_fffb1;
|
||||
@@ -143,6 +144,7 @@ extern const fe fe_fffb3;
|
||||
extern const fe fe_fffb4;
|
||||
extern const ge_p3 ge_p3_identity;
|
||||
extern const ge_p3 ge_p3_H;
|
||||
extern const unsigned char sc_inv_eight[32];
|
||||
void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *);
|
||||
void sc_0(unsigned char *);
|
||||
void sc_reduce32(unsigned char *);
|
||||
|
||||
@@ -177,6 +177,23 @@ namespace crypto {
|
||||
return ge_frombytes_vartime(&point, &key) == 0;
|
||||
}
|
||||
|
||||
bool get_valid_torsion_cleared_point_vartime(const ec_point &point, ec_point &torsion_cleared_out) {
|
||||
ge_p3 p3;
|
||||
if (ge_frombytes_vartime(&p3, &point) != 0)
|
||||
return false;
|
||||
ge_clear_torsion_vartime(&torsion_cleared_out, &p3);
|
||||
if (memcmp(&torsion_cleared_out, &EC_I, sizeof(EC_I)) == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public_key pubkey_clear_torsion(const public_key &pubkey) {
|
||||
public_key result;
|
||||
if (!get_valid_torsion_cleared_point_vartime(pubkey, result))
|
||||
static_cast<ec_point &>(result) = EC_I;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool crypto_ops::secret_key_to_public_key(const secret_key &sec, public_key &pub) {
|
||||
ge_p3 point;
|
||||
if (sc_check(&unwrap(sec)) != 0) {
|
||||
|
||||
@@ -113,6 +113,8 @@ namespace crypto {
|
||||
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 &&
|
||||
sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size");
|
||||
|
||||
static const ec_point EC_I = {1};
|
||||
|
||||
class crypto_ops {
|
||||
crypto_ops();
|
||||
crypto_ops(const crypto_ops &);
|
||||
@@ -217,6 +219,10 @@ namespace crypto {
|
||||
return crypto_ops::check_key(key);
|
||||
}
|
||||
|
||||
bool get_valid_torsion_cleared_point_vartime(const ec_point &point, ec_point &torsion_cleared_out);
|
||||
// Return a canonical key in the prime-order subgroup, or identity on decode failure.
|
||||
public_key pubkey_clear_torsion(const public_key &pubkey);
|
||||
|
||||
/* Checks a private key and computes the corresponding public key.
|
||||
*/
|
||||
inline bool secret_key_to_public_key(const secret_key &sec, public_key &pub) {
|
||||
|
||||
@@ -32,9 +32,13 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <sodium/crypto_verify_32.h>
|
||||
#include <sodium/crypto_shorthash_siphash24.h>
|
||||
|
||||
#include "random.h"
|
||||
|
||||
#define CRYPTO_MAKE_COMPARABLE(type) \
|
||||
namespace crypto { \
|
||||
@@ -57,22 +61,29 @@ namespace crypto { \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace crypto {
|
||||
inline std::size_t siphash_to_size_t(const void *data, std::size_t length) {
|
||||
static_assert(sizeof(crypto_siphash_key) == crypto_shorthash_siphash24_KEYBYTES,
|
||||
"crypto_siphash_key size must match the SipHash-2-4 key length");
|
||||
static_assert(sizeof(std::uint64_t) == crypto_shorthash_siphash24_BYTES,
|
||||
"std::uint64_t size must match the SipHash-2-4 digest length");
|
||||
std::uint64_t h;
|
||||
crypto_shorthash_siphash24(reinterpret_cast<unsigned char*>(&h), static_cast<const unsigned char*>(data), length, crypto_siphash_key);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
#define CRYPTO_DEFINE_HASH_FUNCTIONS(type) \
|
||||
namespace crypto { \
|
||||
static_assert(sizeof(std::size_t) <= sizeof(type), "Size of " #type " must be at least that of size_t"); \
|
||||
inline std::size_t hash_value(const type &_v) { \
|
||||
std::size_t h; \
|
||||
memcpy(&h, std::addressof(_v), sizeof(h)); \
|
||||
return h; \
|
||||
return siphash_to_size_t(std::addressof(_v), sizeof(_v)); \
|
||||
} \
|
||||
} \
|
||||
namespace std { \
|
||||
template<> \
|
||||
struct hash<crypto::type> { \
|
||||
std::size_t operator()(const crypto::type &_v) const { \
|
||||
std::size_t h; \
|
||||
memcpy(&h, std::addressof(_v), sizeof(h)); \
|
||||
return h; \
|
||||
return ::crypto::siphash_to_size_t(std::addressof(_v), sizeof(_v)); \
|
||||
} \
|
||||
}; \
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ static void generate_system_random_bytes(size_t n, void *result) {
|
||||
#endif
|
||||
|
||||
static union hash_state state;
|
||||
unsigned char crypto_siphash_key[16];
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
static volatile int curstate; /* To catch thread safety problems. */
|
||||
@@ -107,10 +108,12 @@ FINALIZER(deinit_random) {
|
||||
curstate = 0;
|
||||
#endif
|
||||
memset(&state, 0, sizeof(union hash_state));
|
||||
memset(crypto_siphash_key, 0, sizeof(crypto_siphash_key));
|
||||
}
|
||||
|
||||
INITIALIZER(init_random) {
|
||||
generate_system_random_bytes(32, &state);
|
||||
generate_system_random_bytes(sizeof(crypto_siphash_key), crypto_siphash_key);
|
||||
REGISTER_FINALIZER(deinit_random);
|
||||
#if !defined(NDEBUG)
|
||||
assert(curstate == 0);
|
||||
|
||||
@@ -32,5 +32,15 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void generate_random_bytes_not_thread_safe(size_t n, void *result);
|
||||
void add_extra_entropy_not_thread_safe(const void *ptr, size_t bytes);
|
||||
|
||||
extern unsigned char crypto_siphash_key[16];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+16
-3
@@ -1162,12 +1162,17 @@ static const int rcon[] = {
|
||||
0x01,0x01,0x01,0x01,
|
||||
0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d, // rotate-n-splat
|
||||
0x1b,0x1b,0x1b,0x1b };
|
||||
const uint8_t *key_ptr = key;
|
||||
uint8_t *expanded_key_ptr = expandedKey;
|
||||
const int *rcon_ptr = rcon;
|
||||
int count;
|
||||
|
||||
__asm__(
|
||||
" eor v0.16b,v0.16b,v0.16b\n"
|
||||
" ld1 {v3.16b},[%0],#16\n"
|
||||
" ld1 {v1.4s,v2.4s},[%2],#32\n"
|
||||
" ld1 {v4.16b},[%0]\n"
|
||||
" mov w2,#5\n"
|
||||
" mov %w3,#5\n"
|
||||
" st1 {v3.4s},[%1],#16\n"
|
||||
"\n"
|
||||
"1:\n"
|
||||
@@ -1175,7 +1180,7 @@ __asm__(
|
||||
" ext v5.16b,v0.16b,v3.16b,#12\n"
|
||||
" st1 {v4.4s},[%1],#16\n"
|
||||
" aese v6.16b,v0.16b\n"
|
||||
" subs w2,w2,#1\n"
|
||||
" subs %w3,%w3,#1\n"
|
||||
"\n"
|
||||
" eor v3.16b,v3.16b,v5.16b\n"
|
||||
" ext v5.16b,v0.16b,v5.16b,#12\n"
|
||||
@@ -1201,7 +1206,15 @@ __asm__(
|
||||
" eor v4.16b,v4.16b,v6.16b\n"
|
||||
" b 1b\n"
|
||||
"\n"
|
||||
"2:\n" : : "r"(key), "r"(expandedKey), "r"(rcon));
|
||||
"2:\n"
|
||||
: "+&r"(key_ptr),
|
||||
"+&r"(expanded_key_ptr),
|
||||
"+&r"(rcon_ptr),
|
||||
"=&r"(count),
|
||||
"=m"(*(uint8_t (*)[176]) expandedKey)
|
||||
: "m"(*(const int (*)[8]) rcon),
|
||||
"m"(*(const uint8_t (*)[32]) key)
|
||||
: "v0", "v1", "v2", "v3", "v4", "v5", "v6", "cc");
|
||||
}
|
||||
|
||||
/* An ordinary AES round is a sequence of SubBytes, ShiftRows, MixColumns, AddRoundKey. There
|
||||
|
||||
@@ -53,8 +53,12 @@
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
typedef std::vector<crypto::signature> ring_signature;
|
||||
|
||||
// These are guaranteed ceilings for the entire chain. Some fork rules may limit some further.
|
||||
constexpr size_t COINBASE_VIN_COUNT = 1;
|
||||
constexpr size_t MAX_VIN_COUNT = CRYPTONOTE_MAX_TX_SIZE / sizeof(crypto::key_image);
|
||||
constexpr size_t MAX_NON_COINBASE_VOUT_COUNT = CRYPTONOTE_MAX_TX_SIZE / sizeof(crypto::public_key);
|
||||
constexpr size_t MAX_COINBASE_VOUT_COUNT = /*LEVIN_DEFAULT_MAX_PACKET_SIZE=*/100000000 / sizeof(crypto::public_key);
|
||||
constexpr size_t MAX_TOTAL_KEY_OFFSETS = CRYPTONOTE_MAX_TX_SIZE / sizeof(crypto::public_key);
|
||||
|
||||
/* outputs */
|
||||
|
||||
@@ -143,7 +147,7 @@ namespace cryptonote
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(amount)
|
||||
FIELD(key_offsets)
|
||||
CONTAINER_FIELD_CAPPED(key_offsets, MAX_TOTAL_KEY_OFFSETS)
|
||||
FIELD(k_image)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
@@ -184,11 +188,109 @@ namespace cryptonote
|
||||
VARINT_FIELD(version)
|
||||
if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
|
||||
VARINT_FIELD(unlock_time)
|
||||
FIELD(vin)
|
||||
FIELD(vout)
|
||||
if (typename Archive<W>::is_saving())
|
||||
{
|
||||
// Serializing...
|
||||
FIELD(vin)
|
||||
FIELD(vout)
|
||||
}
|
||||
else
|
||||
{
|
||||
// De-serializing...
|
||||
// vin
|
||||
if (!this->deserialize_vin(ar))
|
||||
{
|
||||
ar.set_fail();
|
||||
return false;
|
||||
}
|
||||
|
||||
// vout
|
||||
const size_t MAX_VOUT_COUNT = (this->is_coinbase() || vin.empty()) ? MAX_COINBASE_VOUT_COUNT : MAX_NON_COINBASE_VOUT_COUNT;
|
||||
CONTAINER_FIELD_CAPPED(vout, MAX_VOUT_COUNT)
|
||||
}
|
||||
FIELD(extra)
|
||||
END_SERIALIZE()
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
bool is_coinbase() const
|
||||
{
|
||||
if (vin.size() != COINBASE_VIN_COUNT)
|
||||
return false;
|
||||
|
||||
if (vin.at(0).type() != typeid(txin_gen))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
private:
|
||||
// Do some validation on vin before doing allocations and reading the archive:
|
||||
// 1. Can't exceed MAX_VIN_COUNT.
|
||||
// 2. If coinbase, only 1 txin_gen.
|
||||
// 3. If not coinbase: type txin_to_key (see check_inputs_types_supported).
|
||||
// 4. If not coinbase: key offsets don't exceed max allowed for whole tx.
|
||||
// Note: can't enforce non-empty because wallets may store empty vin.
|
||||
template<template <bool> class Archive>
|
||||
bool deserialize_vin(Archive<false> &ar)
|
||||
{
|
||||
auto &v = vin;
|
||||
size_t cnt = 0;
|
||||
ar.begin_array(cnt);
|
||||
if (!ar.good())
|
||||
return false;
|
||||
v.clear();
|
||||
|
||||
if (ar.remaining_bytes() < cnt)
|
||||
return false;
|
||||
|
||||
// 1. Can't exceed MAX_VIN_COUNT.
|
||||
if (cnt > MAX_VIN_COUNT)
|
||||
return false;
|
||||
|
||||
::serialization::detail::do_reserve(v, cnt, ar.remaining_bytes());
|
||||
|
||||
size_t total_key_offsets = 0;
|
||||
for (size_t i = 0; i < cnt; ++i) {
|
||||
if (i > 0)
|
||||
ar.delimit_array();
|
||||
v.emplace_back();
|
||||
if (!::serialization::detail::serialize_container_element(ar, v.back()))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
|
||||
// 2. If coinbase, only 1 txin_gen.
|
||||
const auto &in = v.back();
|
||||
const bool coinbase = i == 0 && in.type() == typeid(txin_gen);
|
||||
if (coinbase && cnt > COINBASE_VIN_COUNT)
|
||||
return false;
|
||||
if (coinbase)
|
||||
break;
|
||||
|
||||
// 3. If not coinbase: type txin_to_key (see check_inputs_types_supported).
|
||||
if (in.type() != typeid(txin_to_key))
|
||||
return false;
|
||||
|
||||
// 4. If not coinbase: key offsets don't exceed max allowed for whole tx.
|
||||
const std::size_t next_toal_key_offsets = total_key_offsets + boost::get<cryptonote::txin_to_key>(in).key_offsets.size();
|
||||
if (next_toal_key_offsets < total_key_offsets)
|
||||
return false; // overflow
|
||||
if (next_toal_key_offsets >= MAX_TOTAL_KEY_OFFSETS)
|
||||
return false;
|
||||
total_key_offsets = next_toal_key_offsets;
|
||||
}
|
||||
ar.end_array();
|
||||
v.shrink_to_fit();
|
||||
|
||||
return ar.good();
|
||||
}
|
||||
|
||||
template<template <bool> class Archive>
|
||||
bool deserialize_vin(Archive<true> &ar)
|
||||
{
|
||||
throw std::runtime_error("unexpected writer called deserialize_vin");
|
||||
}
|
||||
|
||||
public:
|
||||
transaction_prefix(){ set_null(); }
|
||||
void set_null()
|
||||
@@ -261,7 +363,7 @@ namespace cryptonote
|
||||
|
||||
ar.tag("signatures");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures);
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures, 0);
|
||||
bool signatures_not_expected = signatures.empty();
|
||||
if (!signatures_not_expected && vin.size() != signatures.size())
|
||||
return false;
|
||||
@@ -277,7 +379,7 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]);
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i], sizeof(crypto::signature));
|
||||
if (signature_size != signatures[i].size())
|
||||
return false;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ using namespace epee;
|
||||
#include "misc_language.h"
|
||||
#include "common/base58.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "int-util.h"
|
||||
#include "common/dns_utils.h"
|
||||
|
||||
@@ -172,17 +173,15 @@ namespace cryptonote {
|
||||
return tools::base58::encode_addr(integrated_address_prefix, t_serializable_object_to_blob(iadr));
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
bool is_coinbase(const transaction& tx)
|
||||
bool check_address(const account_public_address& adr)
|
||||
{
|
||||
if(tx.vin.size() != 1)
|
||||
return false;
|
||||
|
||||
if(tx.vin[0].type() != typeid(txin_gen))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
const auto valid_key = [](const crypto::public_key& key) {
|
||||
const rct::key point = rct::pk2rct(key);
|
||||
return !(point == rct::identity()) && rct::isInMainSubgroup(point);
|
||||
};
|
||||
return valid_key(adr.m_spend_public_key) && valid_key(adr.m_view_public_key);
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
bool get_account_address_from_str(
|
||||
address_parse_info& info
|
||||
, network_type nettype
|
||||
@@ -244,12 +243,6 @@ namespace cryptonote {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!crypto::check_key(info.address.m_spend_public_key) || !crypto::check_key(info.address.m_view_public_key))
|
||||
{
|
||||
LOG_PRINT_L1("Failed to validate address keys");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -285,6 +278,12 @@ namespace cryptonote {
|
||||
info.has_payment_id = false;
|
||||
}
|
||||
|
||||
if (!check_address(info.address))
|
||||
{
|
||||
LOG_PRINT_L1("Failed to validate address keys");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
@@ -95,6 +95,8 @@ namespace cryptonote {
|
||||
, const crypto::hash8& payment_id
|
||||
);
|
||||
|
||||
bool check_address(const account_public_address& adr);
|
||||
|
||||
bool get_account_address_from_str(
|
||||
address_parse_info& info
|
||||
, network_type nettype
|
||||
@@ -108,8 +110,6 @@ namespace cryptonote {
|
||||
, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm = return_first_address
|
||||
);
|
||||
|
||||
bool is_coinbase(const transaction& tx);
|
||||
|
||||
bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b);
|
||||
bool operator ==(const cryptonote::block& a, const cryptonote::block& b);
|
||||
}
|
||||
|
||||
@@ -122,6 +122,13 @@ namespace cryptonote
|
||||
const uint64_t bp_clawback = (bp_base * n_padded_outputs - bp_size) * 4 / 5;
|
||||
return bp_clawback;
|
||||
}
|
||||
|
||||
bool passes_max_size_check(const bool max_size_check, const blobdata_ref &tx_blob)
|
||||
{
|
||||
if (!max_size_check)
|
||||
return true;
|
||||
return tx_blob.size() <= get_max_tx_size();
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
}
|
||||
|
||||
@@ -143,7 +150,7 @@ namespace cryptonote
|
||||
|
||||
bool expand_transaction_1(transaction &tx, bool base_only)
|
||||
{
|
||||
if (tx.version >= 2 && !is_coinbase(tx))
|
||||
if (tx.version >= 2 && !tx.is_coinbase())
|
||||
{
|
||||
rct::rctSig &rv = tx.rct_signatures;
|
||||
if (rv.type == rct::RCTTypeNull)
|
||||
@@ -241,8 +248,9 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx)
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, const bool max_size_check)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(passes_max_size_check(max_size_check, tx_blob), false, "Tx blob too big");
|
||||
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
||||
bool r = ::serialization::serialize(ba, tx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
|
||||
@@ -252,26 +260,30 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool parse_and_validate_tx_base_from_blob(const blobdata_ref& tx_blob, transaction& tx)
|
||||
bool parse_and_validate_tx_base_from_blob(const blobdata_ref& tx_blob, transaction& tx, const bool max_size_check, const bool require_eof)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(passes_max_size_check(max_size_check, tx_blob), false, "Tx blob too big");
|
||||
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
||||
bool r = tx.serialize_base(ba);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
|
||||
CHECK_AND_ASSERT_MES(::serialization::check_stream_state(ba, !require_eof), false, "Unexpected trailing data after transaction base");
|
||||
CHECK_AND_ASSERT_MES(expand_transaction_1(tx, true), false, "Failed to expand transaction data");
|
||||
tx.invalidate_hashes();
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool parse_and_validate_tx_prefix_from_blob(const blobdata_ref& tx_blob, transaction_prefix& tx)
|
||||
bool parse_and_validate_tx_prefix_from_blob(const blobdata_ref& tx_blob, transaction_prefix& tx, const bool max_size_check)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(passes_max_size_check(max_size_check, tx_blob), false, "Tx blob too big");
|
||||
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
||||
bool r = ::serialization::serialize_noeof(ba, tx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction prefix from blob");
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash)
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, const bool max_size_check)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(passes_max_size_check(max_size_check, tx_blob), false, "Tx blob too big");
|
||||
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
||||
bool r = ::serialization::serialize(ba, tx);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
|
||||
@@ -283,9 +295,9 @@ namespace cryptonote
|
||||
return get_transaction_hash(tx, tx_hash);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash)
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const bool max_size_check)
|
||||
{
|
||||
if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash))
|
||||
if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, max_size_check))
|
||||
return false;
|
||||
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||
return true;
|
||||
|
||||
@@ -52,11 +52,12 @@ namespace cryptonote
|
||||
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx, hw::device &hwdev);
|
||||
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h);
|
||||
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx);
|
||||
bool parse_and_validate_tx_prefix_from_blob(const blobdata_ref& tx_blob, transaction_prefix& tx);
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash);
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash);
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx);
|
||||
bool parse_and_validate_tx_base_from_blob(const blobdata_ref& tx_blob, transaction& tx);
|
||||
/* The size check is useful before parsing non-coinbase txs from untrusted sources. Coinbase blob sizes may be uncapped. */
|
||||
bool parse_and_validate_tx_prefix_from_blob(const blobdata_ref& tx_blob, transaction_prefix& tx, const bool max_size_check = false);
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const bool max_size_check = false);
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, const bool max_size_check = false);
|
||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, const bool max_size_check = false);
|
||||
bool parse_and_validate_tx_base_from_blob(const blobdata_ref& tx_blob, transaction& tx, const bool max_size_check = false, const bool require_eof = false);
|
||||
bool is_v1_tx(const blobdata_ref& tx_blob);
|
||||
bool is_v1_tx(const blobdata& tx_blob);
|
||||
|
||||
|
||||
@@ -635,7 +635,8 @@ namespace cryptonote
|
||||
|
||||
if ((b.major_version >= RX_BLOCK_VERSION) && !rx_set)
|
||||
{
|
||||
crypto::rx_set_miner_thread(th_local_index, tools::get_max_concurrency());
|
||||
// Must be non-zero value because 0 means "not a miner thread, run with secure JIT" in rx-slow-hash.c
|
||||
crypto::rx_set_miner_thread(th_local_index + 1, tools::get_max_concurrency());
|
||||
rx_set = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) :
|
||||
m_long_term_block_weights_cache_rolling_median(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE),
|
||||
m_difficulty_for_next_block_top_hash(crypto::null_hash),
|
||||
m_difficulty_for_next_block(1),
|
||||
m_btc_valid(false),
|
||||
m_btc_include_sensitive(true), m_btc_valid(false),
|
||||
m_batch_success(true),
|
||||
m_prepare_height(0),
|
||||
m_rct_ver_cache()
|
||||
@@ -155,6 +155,8 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke
|
||||
std::vector<uint64_t> absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets);
|
||||
std::vector<output_data_t> outputs;
|
||||
|
||||
// We check the m_scan_table cache, but note it may not have the entry.
|
||||
// We have to read the db in that case.
|
||||
bool found = false;
|
||||
auto it = m_scan_table.find(tx_prefix_hash);
|
||||
if (it != m_scan_table.end())
|
||||
@@ -636,7 +638,7 @@ block Blockchain::pop_block_from_blockchain()
|
||||
++pruned;
|
||||
continue;
|
||||
}
|
||||
if (!is_coinbase(tx))
|
||||
if (!tx.is_coinbase())
|
||||
{
|
||||
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
|
||||
|
||||
@@ -1572,7 +1574,7 @@ uint64_t Blockchain::get_current_cumulative_block_weight_median() const
|
||||
// in a lot of places. That flag is not referenced in any of the code
|
||||
// nor any of the makefiles, howeve. Need to look into whether or not it's
|
||||
// necessary at all.
|
||||
bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
|
||||
bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, bool include_sensitive)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
size_t median_weight;
|
||||
@@ -1590,7 +1592,8 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
// this would be the case anyway if we'd lock, and the change happened
|
||||
// just after the block template was created
|
||||
if (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address)) && m_btc_nonce == ex_nonce
|
||||
&& m_btc_pool_cookie == m_tx_pool.cookie() && m_btc.prev_id == get_tail_id()) {
|
||||
&& m_btc_pool_cookie == m_tx_pool.cookie() && m_btc.prev_id == get_tail_id()
|
||||
&& m_btc_include_sensitive == include_sensitive) {
|
||||
MDEBUG("Using cached template");
|
||||
const uint64_t now = time(NULL);
|
||||
if (m_btc.timestamp < now) // ensures it can't get below the median of the last few blocks
|
||||
@@ -1705,8 +1708,8 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
}
|
||||
b.timestamp = time(NULL);
|
||||
|
||||
uint64_t median_ts;
|
||||
if (!check_block_timestamp(b, median_ts))
|
||||
uint64_t median_ts{};
|
||||
if (!check_block_timestamp_main_chain(b, &median_ts))
|
||||
{
|
||||
b.timestamp = median_ts;
|
||||
}
|
||||
@@ -1715,7 +1718,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
|
||||
size_t txs_weight;
|
||||
uint64_t fee;
|
||||
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version))
|
||||
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version, include_sensitive))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1834,16 +1837,16 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
#endif
|
||||
|
||||
if (!from_block)
|
||||
cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, seed_height, seed_hash, pool_cookie);
|
||||
cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, seed_height, seed_hash, pool_cookie, include_sensitive);
|
||||
return true;
|
||||
}
|
||||
LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
|
||||
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, bool include_sensitive)
|
||||
{
|
||||
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
|
||||
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, include_sensitive);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
|
||||
@@ -2312,9 +2315,7 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
|
||||
|
||||
//pack block
|
||||
e.block = std::move(bl.first);
|
||||
e.block_weight = 0;
|
||||
if (arg.prune && m_db->block_exists(arg.blocks[i]))
|
||||
e.block_weight = m_db->get_block_weight(m_db->get_block_height(arg.blocks[i]));
|
||||
e.block_weight = arg.prune ? m_db->get_block_weight(get_block_height(bl.second)) : 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -3875,6 +3876,7 @@ void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_block
|
||||
|
||||
void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, std::vector<uint64_t> &fees) const
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
const uint8_t version = get_current_hard_fork_version();
|
||||
const uint64_t db_height = m_db->height();
|
||||
|
||||
@@ -4085,16 +4087,37 @@ uint64_t Blockchain::get_adjusted_time(uint64_t height) const
|
||||
return (adjusted_current_block_ts < median_ts ? adjusted_current_block_ts : median_ts);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
//TODO: revisit, has changed a bit on upstream
|
||||
bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b, uint64_t& median_ts) const
|
||||
bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b, uint64_t* median_ts_out) const
|
||||
{
|
||||
if (median_ts_out)
|
||||
*median_ts_out = 0;
|
||||
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
median_ts = epee::misc_utils::median(timestamps);
|
||||
|
||||
// Wownero: the future limit and window depend on the hard fork version.
|
||||
const uint8_t version = get_current_hard_fork_version();
|
||||
const uint64_t cryptonote_block_future_time_limit = version >= 8 ? CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 : CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT;
|
||||
const size_t blockchain_timestamp_check_window = version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
|
||||
|
||||
if(b.timestamp > (uint64_t)time(NULL) + cryptonote_block_future_time_limit)
|
||||
{
|
||||
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", "
|
||||
<< b.timestamp << ", bigger than local time + " << cryptonote_block_future_time_limit << " seconds");
|
||||
return false;
|
||||
}
|
||||
|
||||
// if not enough blocks, no proper median yet, return true
|
||||
if(timestamps.size() < blockchain_timestamp_check_window)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint64_t median_ts = epee::misc_utils::median(timestamps);
|
||||
if (median_ts_out)
|
||||
*median_ts_out = median_ts;
|
||||
|
||||
if(b.timestamp < median_ts)
|
||||
{
|
||||
uint8_t version = get_current_hard_fork_version();
|
||||
size_t blockchain_timestamp_check_window = version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
|
||||
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << blockchain_timestamp_check_window << " blocks, " << median_ts);
|
||||
return false;
|
||||
}
|
||||
@@ -4109,37 +4132,26 @@ bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const
|
||||
// true if the block's timestamp is not less than the timestamp of the
|
||||
// median of the selected blocks
|
||||
// false otherwise
|
||||
bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) const
|
||||
bool Blockchain::check_block_timestamp_main_chain(const block& b, uint64_t* median_ts_out) const
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
uint8_t version = get_current_hard_fork_version();
|
||||
uint64_t cryptonote_block_future_time_limit = version >= 8 ? CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 : CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT;
|
||||
size_t blockchain_timestamp_check_window = version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
|
||||
if(b.timestamp > (uint64_t)time(NULL) + cryptonote_block_future_time_limit)
|
||||
{
|
||||
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than local time + 10 minutes");
|
||||
return false;
|
||||
}
|
||||
const uint8_t version = get_current_hard_fork_version();
|
||||
const size_t blockchain_timestamp_check_window = version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
|
||||
|
||||
const auto h = m_db->height();
|
||||
|
||||
// if not enough blocks, no proper median yet, return true
|
||||
if(h < blockchain_timestamp_check_window)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> timestamps;
|
||||
|
||||
// need most recent 60 blocks, get index of first of those
|
||||
size_t offset = h - blockchain_timestamp_check_window;
|
||||
// need most recent window blocks, get index of first of those
|
||||
size_t offset = (h >= blockchain_timestamp_check_window) ? (h - blockchain_timestamp_check_window) : 0;
|
||||
assert(offset <= h);
|
||||
timestamps.reserve(h - offset);
|
||||
for(;offset < h; ++offset)
|
||||
{
|
||||
timestamps.push_back(m_db->get_block_timestamp(offset));
|
||||
}
|
||||
|
||||
return check_block_timestamp(timestamps, b, median_ts);
|
||||
return check_block_timestamp(timestamps, b, median_ts_out);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids)
|
||||
@@ -4216,7 +4228,7 @@ leave:
|
||||
|
||||
// make sure block timestamp is not less than the median timestamp
|
||||
// of a set number of the most recent blocks.
|
||||
if(!check_block_timestamp(bl))
|
||||
if(!check_block_timestamp_main_chain(bl))
|
||||
{
|
||||
MERROR_VER("Block with id: " << id << std::endl << "has invalid timestamp: " << bl.timestamp);
|
||||
bvc.m_verifivation_failed = true;
|
||||
@@ -4797,10 +4809,13 @@ bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc,
|
||||
CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain_lock);
|
||||
db_rtxn_guard rtxn_guard(m_db);
|
||||
if(have_block(id))
|
||||
int where = 0;
|
||||
if(have_block(id, &where))
|
||||
{
|
||||
LOG_PRINT_L3("block with id = " << id << " already exists");
|
||||
bvc.m_already_exists = true;
|
||||
if (where == HAVE_BLOCK_INVALID)
|
||||
bvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5333,6 +5348,14 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
std::map<uint64_t, std::vector<output_data_t>> tx_map;
|
||||
std::vector<std::pair<cryptonote::transaction, crypto::hash>> txes(total_txs);
|
||||
|
||||
// We want to target max memory usage for offsets here to 100mb. We'll have output_data_t
|
||||
// per offset in memory for m_scan_table and tx_map (hence the 2*sizeof(output_data_t)).
|
||||
// If m_scan_table doesn't have an offset entry while handling an incoming block, that's
|
||||
// ok. It should cache miss and just do the db read(s) when handling the block.
|
||||
// Note: we might go over the target max offsets a little, but that's ok.
|
||||
// TODO: return early once we're past the FCMP++ fork.
|
||||
constexpr std::size_t TARGET_MAX_OFFSETS_FOR_SCAN_TABLE_CACHE = 100000000/*100MB*/ / (2*sizeof(output_data_t));
|
||||
|
||||
#define SCAN_TABLE_QUIT(m) \
|
||||
do { \
|
||||
MERROR_VER(m) ;\
|
||||
@@ -5342,10 +5365,13 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
|
||||
// generate sorted tables for all amounts and absolute offsets
|
||||
size_t tx_index = 0, block_index = 0;
|
||||
size_t n_total_offsets = 0;
|
||||
for (const auto &entry : blocks_entry)
|
||||
{
|
||||
if (m_cancel)
|
||||
return false;
|
||||
if (n_total_offsets >= TARGET_MAX_OFFSETS_FOR_SCAN_TABLE_CACHE)
|
||||
break;
|
||||
|
||||
for (const auto &tx_blob : entry.txs)
|
||||
{
|
||||
@@ -5355,7 +5381,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
crypto::hash &tx_prefix_hash = txes[tx_index].second;
|
||||
++tx_index;
|
||||
|
||||
if (!parse_and_validate_tx_base_from_blob(tx_blob.blob, tx))
|
||||
if (!parse_and_validate_tx_base_from_blob(tx_blob.blob, tx, true))
|
||||
SCAN_TABLE_QUIT("Could not parse tx from incoming blocks.");
|
||||
cryptonote::get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||
|
||||
@@ -5367,7 +5393,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
its = m_scan_table.find(tx_prefix_hash);
|
||||
assert(its != m_scan_table.end());
|
||||
|
||||
// get all amounts from tx.vin(s)
|
||||
// initialize amount buckets for tx.vin(s)
|
||||
for (const auto &txin : tx.vin)
|
||||
{
|
||||
const txin_to_key &in_to_key = boost::get < txin_to_key > (txin);
|
||||
@@ -5377,22 +5403,8 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
if (it != its->second.end())
|
||||
SCAN_TABLE_QUIT("Duplicate key_image found from incoming blocks.");
|
||||
|
||||
amounts.push_back(in_to_key.amount);
|
||||
}
|
||||
|
||||
// sort and remove duplicate amounts from amounts list
|
||||
std::sort(amounts.begin(), amounts.end());
|
||||
auto last = std::unique(amounts.begin(), amounts.end());
|
||||
amounts.erase(last, amounts.end());
|
||||
|
||||
// add amount to the offset_map and tx_map
|
||||
for (const uint64_t &amount : amounts)
|
||||
{
|
||||
if (offset_map.find(amount) == offset_map.end())
|
||||
offset_map.emplace(amount, std::vector<uint64_t>());
|
||||
|
||||
if (tx_map.find(amount) == tx_map.end())
|
||||
tx_map.emplace(amount, std::vector<output_data_t>());
|
||||
offset_map.emplace(in_to_key.amount, std::vector<uint64_t>());
|
||||
tx_map.emplace(in_to_key.amount, std::vector<output_data_t>());
|
||||
}
|
||||
|
||||
// add new absolute_offsets to offset_map
|
||||
@@ -5402,13 +5414,25 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
// no need to check for duplicate here.
|
||||
auto absolute_offsets = relative_output_offsets_to_absolute(in_to_key.key_offsets);
|
||||
for (const auto & offset : absolute_offsets)
|
||||
{
|
||||
offset_map[in_to_key.amount].push_back(offset);
|
||||
++n_total_offsets;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_total_offsets >= TARGET_MAX_OFFSETS_FOR_SCAN_TABLE_CACHE)
|
||||
{
|
||||
MWARNING("Preparing large batch to sync, we exceeded our cache limit");
|
||||
break;
|
||||
}
|
||||
}
|
||||
++block_index;
|
||||
}
|
||||
|
||||
amounts.reserve(offset_map.size());
|
||||
for (const auto &offsets : offset_map)
|
||||
amounts.push_back(offsets.first);
|
||||
|
||||
// sort and remove duplicate absolute_offsets in offset_map
|
||||
for (auto &offsets : offset_map)
|
||||
{
|
||||
@@ -5445,10 +5469,13 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
|
||||
// now generate a table for each tx_prefix and k_image hashes
|
||||
tx_index = 0;
|
||||
bool reached_end_of_cache = false;
|
||||
for (const auto &entry : blocks_entry)
|
||||
{
|
||||
if (m_cancel)
|
||||
return false;
|
||||
if (reached_end_of_cache)
|
||||
break;
|
||||
|
||||
for (size_t i = 0; i < entry.txs.size(); ++i)
|
||||
{
|
||||
@@ -5460,32 +5487,34 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
|
||||
auto its = m_scan_table.find(tx_prefix_hash);
|
||||
if (its == m_scan_table.end())
|
||||
SCAN_TABLE_QUIT("Tx not found on scan table from incoming blocks.");
|
||||
{
|
||||
reached_end_of_cache = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (const auto &txin : tx.vin)
|
||||
{
|
||||
const txin_to_key &in_to_key = boost::get < txin_to_key > (txin);
|
||||
auto needed_offsets = relative_output_offsets_to_absolute(in_to_key.key_offsets);
|
||||
|
||||
const auto offset_it = offset_map.find(in_to_key.amount);
|
||||
const auto tx_it = tx_map.find(in_to_key.amount);
|
||||
if (offset_it == offset_map.end() || tx_it == tx_map.end())
|
||||
SCAN_TABLE_QUIT("Amount not found on scan table from incoming blocks.");
|
||||
|
||||
const std::vector<uint64_t> &offsets_found = offset_it->second;
|
||||
const std::vector<output_data_t> &outputs_found = tx_it->second;
|
||||
|
||||
std::vector<output_data_t> outputs;
|
||||
for (const uint64_t & offset_needed : needed_offsets)
|
||||
{
|
||||
size_t pos = 0;
|
||||
bool found = false;
|
||||
// offsets_found is sorted above before output_scan_worker populates outputs_found.
|
||||
const auto found_it = std::lower_bound(offsets_found.begin(), offsets_found.end(), offset_needed);
|
||||
const size_t pos = found_it - offsets_found.begin();
|
||||
const bool found = found_it != offsets_found.end() && *found_it == offset_needed;
|
||||
|
||||
for (const uint64_t &offset_found : offset_map[in_to_key.amount])
|
||||
{
|
||||
if (offset_needed == offset_found)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
++pos;
|
||||
}
|
||||
|
||||
if (found && pos < tx_map[in_to_key.amount].size())
|
||||
outputs.push_back(tx_map[in_to_key.amount].at(pos));
|
||||
if (found && pos < outputs_found.size())
|
||||
outputs.push_back(outputs_found[pos]);
|
||||
else
|
||||
break;
|
||||
}
|
||||
@@ -5863,7 +5892,7 @@ void Blockchain::invalidate_block_template_cache()
|
||||
m_btc_valid = false;
|
||||
}
|
||||
|
||||
void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie)
|
||||
void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie, bool include_sensitive)
|
||||
{
|
||||
MDEBUG("Setting block template cache");
|
||||
m_btc = b;
|
||||
@@ -5875,6 +5904,7 @@ void Blockchain::cache_block_template(const block &b, const cryptonote::account_
|
||||
m_btc_seed_hash = seed_hash;
|
||||
m_btc_seed_height = seed_height;
|
||||
m_btc_pool_cookie = pool_cookie;
|
||||
m_btc_include_sensitive = include_sensitive;
|
||||
m_btc_valid = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -394,8 +394,8 @@ namespace cryptonote
|
||||
*
|
||||
* @return true if block template filled in successfully, else false
|
||||
*/
|
||||
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
|
||||
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
|
||||
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, bool include_sensitive = true);
|
||||
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, bool include_sensitive = true);
|
||||
|
||||
/**
|
||||
* @brief gets data required to create a block template and start mining on it
|
||||
@@ -1231,6 +1231,7 @@ namespace cryptonote
|
||||
uint64_t m_btc_expected_reward;
|
||||
crypto::hash m_btc_seed_hash;
|
||||
uint64_t m_btc_seed_height;
|
||||
bool m_btc_include_sensitive;
|
||||
bool m_btc_valid;
|
||||
|
||||
|
||||
@@ -1521,7 +1522,7 @@ namespace cryptonote
|
||||
bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h);
|
||||
|
||||
/**
|
||||
* @brief checks a block's timestamp
|
||||
* @brief checks a block's timestamp on top of the main chain
|
||||
*
|
||||
* This function grabs the timestamps from the most recent <n> blocks,
|
||||
* where n = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW. If there are not those many
|
||||
@@ -1532,26 +1533,28 @@ namespace cryptonote
|
||||
* false otherwise
|
||||
*
|
||||
* @param b the block to be checked
|
||||
* @param median_ts return-by-reference the median of timestamps
|
||||
* @param[out] median_ts_out the median of timestamps (optional)
|
||||
*
|
||||
* @return true if the block's timestamp is valid, otherwise false
|
||||
*/
|
||||
bool check_block_timestamp(const block& b, uint64_t& median_ts) const;
|
||||
bool check_block_timestamp(const block& b) const { uint64_t median_ts; return check_block_timestamp(b, median_ts); }
|
||||
bool check_block_timestamp_main_chain(const block& b, uint64_t* median_ts_out = nullptr) const;
|
||||
|
||||
/**
|
||||
* @brief checks a block's timestamp
|
||||
*
|
||||
* If the block is not more recent than the median of the recent
|
||||
* timestamps passed here, it is considered invalid.
|
||||
* timestamps passed here, it is considered invalid. If the block is too
|
||||
* recent, according to the local system clock, it is considered invalid.
|
||||
*
|
||||
* @param timestamps a list of the most recent timestamps to check against
|
||||
* @param[inout] timestamps a list of the most recent timestamps to check against
|
||||
* @param b the block to be checked
|
||||
* @param[out] median_ts_out the median of `timestamps` (optional)
|
||||
*
|
||||
* @return true if the block's timestamp is valid, otherwise false
|
||||
*/
|
||||
bool check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b, uint64_t& median_ts) const;
|
||||
bool check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b) const { uint64_t median_ts; return check_block_timestamp(timestamps, b, median_ts); }
|
||||
// Wownero: not static, the limits depend on the current hard fork version.
|
||||
bool check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b,
|
||||
uint64_t* median_ts_out = nullptr) const;
|
||||
|
||||
/**
|
||||
* @brief finish an alternate chain's timestamp window from the main chain
|
||||
@@ -1618,7 +1621,7 @@ namespace cryptonote
|
||||
*
|
||||
* At some point, may be used to push an update to miners
|
||||
*/
|
||||
void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie);
|
||||
void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie, bool include_sensitive);
|
||||
|
||||
/**
|
||||
* @brief sends new block notifications to ZMQ `miner_data` subscribers
|
||||
|
||||
@@ -800,7 +800,7 @@ namespace cryptonote
|
||||
|
||||
transaction tx;
|
||||
crypto::hash txid;
|
||||
if (!parse_and_validate_tx_from_blob(tx_blob, tx, txid))
|
||||
if (!parse_and_validate_tx_from_blob(tx_blob, tx, txid, true))
|
||||
{
|
||||
LOG_PRINT_L1("Incoming transactions failed to parse, rejected");
|
||||
tvc.m_verifivation_failed = true;
|
||||
@@ -1195,7 +1195,7 @@ namespace cryptonote
|
||||
|
||||
for (std::size_t i = 0; i < tx_blobs.size(); ++i)
|
||||
{
|
||||
if (!parse_and_validate_tx_from_blob(tx_blobs[i], txs[i], tx_hashes[i]))
|
||||
if (!parse_and_validate_tx_from_blob(tx_blobs[i], txs[i], tx_hashes[i], true))
|
||||
{
|
||||
LOG_ERROR("Failed to parse relayed transaction");
|
||||
return;
|
||||
@@ -1216,9 +1216,9 @@ namespace cryptonote
|
||||
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
|
||||
bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, bool include_sensitive)
|
||||
{
|
||||
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
|
||||
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, include_sensitive);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
|
||||
@@ -1268,6 +1268,9 @@ namespace cryptonote
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
block_complete_entry get_block_complete_entry(block& b, tx_memory_pool &pool)
|
||||
{
|
||||
const std::unordered_set<crypto::hash> tx_hashes(b.tx_hashes.cbegin(), b.tx_hashes.cend());
|
||||
CHECK_AND_ASSERT_THROW_MES(tx_hashes.size() == b.tx_hashes.size(), "Duplicate transaction hashes in block");
|
||||
|
||||
block_complete_entry bce;
|
||||
bce.block = cryptonote::block_to_blob(b);
|
||||
bce.block_weight = 0; // we can leave it to 0, those txes aren't pruned
|
||||
@@ -1313,26 +1316,8 @@ namespace cryptonote
|
||||
{
|
||||
cryptonote_connection_context exclude_context = {};
|
||||
NOTIFY_NEW_FLUFFY_BLOCK::request arg{};
|
||||
arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height();
|
||||
std::vector<crypto::hash> missed_txs;
|
||||
for (const auto &tx_hash : b.tx_hashes)
|
||||
{
|
||||
if (m_blockchain_storage.have_tx(tx_hash))
|
||||
continue;
|
||||
missed_txs.push_back(tx_hash);
|
||||
}
|
||||
if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b))
|
||||
{
|
||||
LOG_PRINT_L1("Block found but, seems that reorganize just happened after that, do not relay this block");
|
||||
return true;
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(!missed_txs.size(), false, "can't find some transactions in found block:" << get_block_hash(b)
|
||||
<< " b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size());
|
||||
|
||||
arg.current_blockchain_height = get_block_height(b) + 1;
|
||||
block_to_blob(b, arg.b.block);
|
||||
// Relay an empty fluffy block
|
||||
arg.b.txs.clear();
|
||||
|
||||
m_pprotocol->relay_block(arg, exclude_context);
|
||||
}
|
||||
return true;
|
||||
@@ -1548,9 +1533,9 @@ namespace cryptonote
|
||||
return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos, include_sensitive_data);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const
|
||||
bool core::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos, bool include_sensitive) const
|
||||
{
|
||||
return m_mempool.get_pool_for_rpc(tx_infos, key_image_infos);
|
||||
return m_mempool.get_pool_for_rpc(tx_infos, key_image_infos, include_sensitive);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t& current_height) const
|
||||
|
||||
@@ -236,7 +236,7 @@ namespace cryptonote
|
||||
* @note see Blockchain::create_block_template
|
||||
*/
|
||||
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) override;
|
||||
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
|
||||
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, bool include_sensitive = true);
|
||||
|
||||
/**
|
||||
* @copydoc Blockchain::get_miner_data
|
||||
@@ -556,10 +556,11 @@ namespace cryptonote
|
||||
|
||||
/**
|
||||
* @copydoc tx_memory_pool::get_pool_for_rpc
|
||||
* @param include_sensitive include node-private fields (timing)
|
||||
*
|
||||
* @note see tx_memory_pool::get_pool_for_rpc
|
||||
*/
|
||||
bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const;
|
||||
bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos, bool include_sensitive) const;
|
||||
|
||||
/**
|
||||
* @copydoc tx_memory_pool::get_transactions_count
|
||||
|
||||
@@ -48,41 +48,30 @@ using namespace epee;
|
||||
using namespace crypto;
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
/**
|
||||
* @brief check if can re-derive change address from device / keys
|
||||
* @param change_addr address to attempt to re-derive
|
||||
* @param subaddresses subaddress map
|
||||
* @param keys account keys of sender
|
||||
* @return subaddress index of `change_addr` if in the subaddress map and re-derives from device, otherwise nullopt
|
||||
*/
|
||||
boost::optional<cryptonote::subaddress_index> sanity_check_change_address(
|
||||
const cryptonote::account_public_address& change_addr,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index>& subaddresses,
|
||||
const cryptonote::account_keys &keys
|
||||
)
|
||||
{
|
||||
// guess/find subaddress index of `change_addr`, works for main addresses if `subaddresses` is empty
|
||||
cryptonote::subaddress_index subaddr_index{}; // (0, 0) by default
|
||||
const auto subaddr_it = subaddresses.find(change_addr.m_spend_public_key);
|
||||
if (subaddr_it != subaddresses.cend())
|
||||
subaddr_index = subaddr_it->second;
|
||||
|
||||
// if device does not return same address given index, then fail
|
||||
hw::device &hwdev = keys.get_device();
|
||||
const auto recomputed_addr = hwdev.get_subaddress(keys, subaddr_index);
|
||||
if (change_addr != recomputed_addr)
|
||||
return boost::none;
|
||||
|
||||
return boost::optional<cryptonote::subaddress_index>(subaddr_index);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
} //anonymous namespace
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
boost::optional<cryptonote::subaddress_index> sanity_check_change_address(
|
||||
const cryptonote::account_public_address& change_addr,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index>& subaddresses,
|
||||
const cryptonote::account_keys &keys
|
||||
)
|
||||
{
|
||||
// guess/find subaddress index of `change_addr`, works for main addresses if `subaddresses` is empty
|
||||
cryptonote::subaddress_index subaddr_index{}; // (0, 0) by default
|
||||
const auto subaddr_it = subaddresses.find(change_addr.m_spend_public_key);
|
||||
if (subaddr_it != subaddresses.cend())
|
||||
subaddr_index = subaddr_it->second;
|
||||
|
||||
// if device does not return same address given index, then fail
|
||||
hw::device &hwdev = keys.get_device();
|
||||
const auto recomputed_addr = hwdev.get_subaddress(keys, subaddr_index);
|
||||
if (change_addr != recomputed_addr)
|
||||
return boost::none;
|
||||
|
||||
return boost::optional<cryptonote::subaddress_index>(subaddr_index);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
void classify_addresses(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr, size_t &num_stdaddresses, size_t &num_subaddresses, account_public_address &single_dest_subaddress)
|
||||
{
|
||||
@@ -261,6 +250,9 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const tx_destination_entry& destination : destinations)
|
||||
CHECK_AND_ASSERT_MES(check_address(destination.addr), false, "Invalid destination address keys");
|
||||
|
||||
boost::optional<cryptonote::subaddress_index> recognized_change_index;
|
||||
if (change_addr)
|
||||
recognized_change_index = sanity_check_change_address(*change_addr, subaddresses, sender_account_keys);
|
||||
@@ -466,11 +458,12 @@ namespace cryptonote
|
||||
crypto::public_key out_eph_public_key;
|
||||
crypto::view_tag view_tag;
|
||||
|
||||
hwdev.generate_output_ephemeral_keys(tx.version,sender_account_keys, txkey_pub, tx_key,
|
||||
const bool r = hwdev.generate_output_ephemeral_keys(tx.version,sender_account_keys, txkey_pub, tx_key,
|
||||
dst_entr, change_addr, output_index,
|
||||
need_additional_txkeys, additional_tx_keys,
|
||||
additional_tx_public_keys, amount_keys, out_eph_public_key,
|
||||
use_view_tags, view_tag);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to generate output ephemeral keys");
|
||||
|
||||
tx_out out;
|
||||
cryptonote::set_tx_out(dst_entr.amount, out_eph_public_key, use_view_tags, view_tag, out);
|
||||
|
||||
@@ -112,6 +112,15 @@ namespace cryptonote
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
inline bool operator==(const tx_destination_entry &a, const tx_destination_entry &b)
|
||||
{
|
||||
return a.original == b.original
|
||||
&& a.amount == b.amount
|
||||
&& a.addr == b.addr
|
||||
&& a.is_subaddress == b.is_subaddress
|
||||
&& a.is_integrated == b.is_integrated;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
|
||||
struct tx_block_template_backlog_entry
|
||||
@@ -122,6 +131,19 @@ namespace cryptonote
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------
|
||||
/**
|
||||
* @brief check if can re-derive change address from device / keys
|
||||
* @param change_addr address to attempt to re-derive
|
||||
* @param subaddresses subaddress map
|
||||
* @param keys account keys of sender
|
||||
* @return subaddress index of `change_addr` if in the subaddress map and re-derives from device, otherwise nullopt
|
||||
*/
|
||||
boost::optional<cryptonote::subaddress_index> sanity_check_change_address(
|
||||
const cryptonote::account_public_address& change_addr,
|
||||
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index>& subaddresses,
|
||||
const cryptonote::account_keys &keys
|
||||
);
|
||||
void classify_addresses(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr, size_t &num_stdaddresses, size_t &num_subaddresses, account_public_address &single_dest_subaddress);
|
||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx);
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool shuffle_outs = true, bool use_view_tags = false);
|
||||
|
||||
@@ -157,20 +157,10 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version != nic_verified_hf_version && !cryptonote::ver_non_input_consensus(tx, tvc, version))
|
||||
{
|
||||
LOG_PRINT_L1("transaction " << id << " failed non-input consensus rule checks");
|
||||
tvc.m_verifivation_failed = true; // should already be set, but just in case
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t fee;
|
||||
const uint64_t fee = get_tx_fee(tx);
|
||||
bool fee_good = false;
|
||||
try
|
||||
{
|
||||
// get_tx_fee() can throw. It shouldn't throw because we check preconditions in
|
||||
// ver_non_input_consensus(), but let's put it in a try block just in case.
|
||||
fee = get_tx_fee(tx);
|
||||
fee_good = kept_by_block || m_blockchain.check_fee(tx_weight, fee);
|
||||
}
|
||||
catch(...) {}
|
||||
@@ -217,6 +207,14 @@ namespace cryptonote
|
||||
}
|
||||
}
|
||||
|
||||
// Do more expensive verification after plausible no-drop offenses
|
||||
if (version != nic_verified_hf_version && !cryptonote::ver_non_input_consensus(tx, tvc, version))
|
||||
{
|
||||
LOG_PRINT_L1("transaction " << id << " failed non-input consensus rule checks");
|
||||
tvc.m_verifivation_failed = true; // should already be set, but just in case
|
||||
return false;
|
||||
}
|
||||
|
||||
// assume failure during verification steps until success is certain
|
||||
tvc.m_verifivation_failed = true;
|
||||
|
||||
@@ -300,9 +298,10 @@ namespace cryptonote
|
||||
{
|
||||
using clock = std::chrono::system_clock;
|
||||
auto last_relayed_time = std::numeric_limits<decltype(meta.last_relayed_time)>::max();
|
||||
if (tx_relay == relay_method::forward)
|
||||
if (tx_relay == relay_method::forward || tx_relay == relay_method::stem)
|
||||
{
|
||||
last_relayed_time = clock::to_time_t(clock::now() + crypto::random_poisson_seconds{forward_delay_average}());
|
||||
const auto delay = tx_relay == relay_method::forward ? forward_delay_average : dandelionpp_embargo_average;
|
||||
last_relayed_time = clock::to_time_t(clock::now() + crypto::random_poisson_seconds{delay}());
|
||||
set_if_less(m_next_check, time_t(last_relayed_time));
|
||||
}
|
||||
// else the `set_relayed` function will adjust the time accordingly later
|
||||
@@ -416,7 +415,7 @@ namespace cryptonote
|
||||
break;
|
||||
try
|
||||
{
|
||||
const crypto::hash &txid = it->second;
|
||||
const crypto::hash txid = it->second;
|
||||
txpool_tx_meta_t meta;
|
||||
if (!m_blockchain.get_txpool_tx_meta(txid, meta))
|
||||
{
|
||||
@@ -815,6 +814,8 @@ namespace cryptonote
|
||||
case relay_method::local:
|
||||
case relay_method::fluff:
|
||||
case relay_method::block:
|
||||
if (!meta.relayed)
|
||||
break; // if it hasn't been relayed yet, relay it
|
||||
if (now - meta.last_relayed_time <= get_relay_delay(meta.last_relayed_time, meta.receive_time))
|
||||
return true; // continue to next tx
|
||||
break;
|
||||
@@ -1030,7 +1031,8 @@ namespace cryptonote
|
||||
const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted;
|
||||
backlog.reserve(m_blockchain.get_txpool_tx_count(include_sensitive));
|
||||
m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *bd){
|
||||
backlog.push_back({meta.weight, meta.fee, meta.receive_time - now});
|
||||
const uint64_t age = now >= meta.receive_time ? now - meta.receive_time : 0;
|
||||
backlog.push_back({meta.weight, meta.fee, age});
|
||||
return true;
|
||||
}, false, category);
|
||||
}
|
||||
@@ -1241,13 +1243,13 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const
|
||||
bool tx_memory_pool::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos, bool include_sensitive) const
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
tx_infos.reserve(m_blockchain.get_txpool_tx_count());
|
||||
key_image_infos.reserve(m_blockchain.get_txpool_tx_count());
|
||||
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *bd){
|
||||
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *bd){
|
||||
cryptonote::rpc::tx_in_pool txi;
|
||||
txi.tx_hash = txid;
|
||||
if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, txi.tx) : parse_and_validate_tx_from_blob(*bd, txi.tx)))
|
||||
@@ -1265,9 +1267,11 @@ namespace cryptonote
|
||||
txi.max_used_block_hash = meta.max_used_block_id;
|
||||
txi.last_failed_block_height = meta.last_failed_height;
|
||||
txi.last_failed_block_hash = meta.last_failed_id;
|
||||
txi.receive_time = meta.receive_time;
|
||||
// In restricted mode we do not include this data:
|
||||
txi.receive_time = include_sensitive ? meta.receive_time : 0;
|
||||
txi.relayed = meta.relayed;
|
||||
txi.last_relayed_time = meta.dandelionpp_stem ? 0 : meta.last_relayed_time;
|
||||
// In restricted mode we do not include this data:
|
||||
txi.last_relayed_time = (include_sensitive && !meta.dandelionpp_stem) ? meta.last_relayed_time : 0;
|
||||
txi.do_not_relay = meta.do_not_relay;
|
||||
txi.double_spend_seen = meta.double_spend_seen;
|
||||
tx_infos.push_back(txi);
|
||||
@@ -1354,7 +1358,9 @@ namespace cryptonote
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
for(const auto& in: tx.vin)
|
||||
{
|
||||
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, true);//should never fail
|
||||
if (in.type() != typeid(txin_to_key))
|
||||
continue;
|
||||
const auto &tokey_in = boost::get<txin_to_key>(in);
|
||||
if(have_tx_keyimg_as_spent(tokey_in.k_image, txid))
|
||||
return true;
|
||||
}
|
||||
@@ -1553,7 +1559,7 @@ namespace cryptonote
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
//TODO: investigate whether boolean return is appropriate
|
||||
bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version)
|
||||
bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version, bool include_sensitive)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
@@ -1593,7 +1599,8 @@ namespace cryptonote
|
||||
}
|
||||
LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase) << ", relay method " << (unsigned)meta.get_relay_method());
|
||||
|
||||
if (!meta.matches(relay_category::legacy) && !(m_mine_stem_txes && meta.get_relay_method() == relay_method::stem))
|
||||
const relay_category category = include_sensitive ? relay_category::legacy : relay_category::broadcasted;
|
||||
if (!meta.matches(category) && !(m_mine_stem_txes && meta.get_relay_method() == relay_method::stem))
|
||||
{
|
||||
LOG_PRINT_L2(" tx relay method is " << (unsigned)meta.get_relay_method());
|
||||
continue;
|
||||
|
||||
@@ -251,7 +251,7 @@ namespace cryptonote
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version);
|
||||
bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version, bool include_sensitive = true);
|
||||
|
||||
/**
|
||||
* @brief get a list of all transactions in the pool
|
||||
@@ -323,10 +323,11 @@ namespace cryptonote
|
||||
*
|
||||
* @param tx_infos [out] the transactions' information
|
||||
* @param key_image_infos [out] the spent key images' information
|
||||
* @param include_sensitive include fields that are sensitive to node privacy
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const;
|
||||
bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos, bool include_sensitive) const;
|
||||
|
||||
/**
|
||||
* @brief check for presence of key images in the pool
|
||||
|
||||
@@ -43,13 +43,13 @@ bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_avai
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
|
||||
if (!cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx))
|
||||
if (!cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx, true))
|
||||
{
|
||||
MERROR("Failed to parse transaction");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cryptonote::is_coinbase(tx))
|
||||
if (tx.is_coinbase())
|
||||
{
|
||||
MERROR("Transaction is coinbase");
|
||||
return false;
|
||||
|
||||
@@ -97,12 +97,6 @@ namespace cryptonote
|
||||
|
||||
for (const cryptonote::tx_blob_entry& tx_entry: tx_entries)
|
||||
{
|
||||
if (tx_entry.blob.size() > get_max_tx_size())
|
||||
{
|
||||
MERROR("Transaction blob of length " << tx_entry.blob.size() << " is too large to unpack!");
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool is_pruned = tx_entry.prunable_hash != crypto::null_hash;
|
||||
if (is_pruned && !allow_pruned)
|
||||
{
|
||||
@@ -113,14 +107,15 @@ namespace cryptonote
|
||||
cryptonote::transaction tx;
|
||||
crypto::hash tx_hash;
|
||||
bool parse_success = false;
|
||||
const bool max_size_check = true;
|
||||
if (is_pruned)
|
||||
{
|
||||
if ((parse_success = cryptonote::parse_and_validate_tx_base_from_blob(tx_entry.blob, tx)))
|
||||
if ((parse_success = cryptonote::parse_and_validate_tx_base_from_blob(tx_entry.blob, tx, max_size_check, /*require_eof=*/true)))
|
||||
parse_success = cryptonote::get_pruned_transaction_hash(tx, tx_entry.prunable_hash, tx_hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
parse_success = cryptonote::parse_and_validate_tx_from_blob(tx_entry.blob, tx, tx_hash);
|
||||
parse_success = cryptonote::parse_and_validate_tx_from_blob(tx_entry.blob, tx, tx_hash, max_size_check);
|
||||
}
|
||||
|
||||
if (!parse_success)
|
||||
@@ -378,6 +373,11 @@ namespace cryptonote
|
||||
cnx.ip = cnx.host;
|
||||
cnx.port = std::to_string(cntxt.m_remote_address.as<epee::net_utils::ipv4_network_address>().port());
|
||||
}
|
||||
else if (cntxt.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
|
||||
{
|
||||
cnx.ip = cnx.host;
|
||||
cnx.port = std::to_string(cntxt.m_remote_address.as<epee::net_utils::ipv6_network_address>().port());
|
||||
}
|
||||
cnx.rpc_port = cntxt.m_rpc_port;
|
||||
cnx.rpc_credits_per_hash = cntxt.m_rpc_credits_per_hash;
|
||||
|
||||
@@ -593,7 +593,7 @@ namespace cryptonote
|
||||
if (!m_core.check_incoming_block_size(arg.b.block))
|
||||
{
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
// Parse and quick hash incoming block, dropping the connection on failure
|
||||
@@ -609,12 +609,14 @@ namespace cryptonote
|
||||
);
|
||||
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
// Log block info
|
||||
const uint64_t new_block_height = get_block_height(new_block);
|
||||
const uint64_t peer_height = arg.current_blockchain_height;
|
||||
MLOG_P2P_MESSAGE(context << "Received NOTIFY_NEW_FLUFFY_BLOCK " << new_block_hash << " (height "
|
||||
<< arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)");
|
||||
<< new_block_height << ", " << arg.b.txs.size() << " txes, peer's height: " << peer_height << ")");
|
||||
|
||||
// Pause mining and resume after block verification to prevent wasted mining cycles while
|
||||
// validating the next block. Needs more research into if this is a DoS vector or not. Invalid
|
||||
@@ -637,7 +639,7 @@ namespace cryptonote
|
||||
MERROR("sent bad block entry: there are duplicate tx hashes in parsed block: "
|
||||
<< epee::string_tools::buff_to_hex_nodelimer(arg.b.block));
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
// Keeping a map of the full transactions provided in this payload allows us to pass them
|
||||
@@ -654,7 +656,7 @@ namespace cryptonote
|
||||
);
|
||||
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
// try adding block to the blockchain
|
||||
@@ -703,7 +705,7 @@ namespace cryptonote
|
||||
MDEBUG(" tx " << new_block.tx_hashes[txidx]);
|
||||
NOTIFY_REQUEST_FLUFFY_MISSING_TX::request missing_tx_req;
|
||||
missing_tx_req.block_hash = new_block_hash;
|
||||
missing_tx_req.current_blockchain_height = arg.current_blockchain_height;
|
||||
missing_tx_req.current_blockchain_height = new_block_height + 1;
|
||||
missing_tx_req.missing_tx_indices = std::move(need_tx_indices);
|
||||
|
||||
// Post NOTIFY_REQUEST_FLUFFY_MISSING_TX request to peer
|
||||
@@ -715,11 +717,12 @@ namespace cryptonote
|
||||
// drop connection and punish peer
|
||||
LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection");
|
||||
drop_connection_with_score(context, bvc.m_bad_pow ? P2P_IP_FAILS_BEFORE_BLOCK : 1, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
}
|
||||
else if( bvc.m_added_to_main_chain )
|
||||
{
|
||||
arg.current_blockchain_height = new_block_height + 1;
|
||||
// Relay an empty block
|
||||
arg.b.txs.clear();
|
||||
relay_block(arg, context);
|
||||
@@ -739,6 +742,15 @@ namespace cryptonote
|
||||
MLOG_PEER_STATE("requesting chain");
|
||||
}
|
||||
|
||||
if (bvc.m_added_to_main_chain || bvc.m_already_exists)
|
||||
{
|
||||
// Update peer's sync height using this block we just validated
|
||||
// Note: peer_height is not guaranteed to be the height of the block we just validated + 1.
|
||||
// See https://github.com/monero-project/monero/pull/11048#discussion_r3720736824
|
||||
if (peer_height == (new_block_height + 1))
|
||||
context.m_remote_blockchain_height = peer_height;
|
||||
}
|
||||
|
||||
// load json & DNS checkpoints every 10min/hour respectively,
|
||||
// and verify them with respect to what blocks we already have
|
||||
CHECK_AND_ASSERT_MES(m_core.update_checkpoints(), 1, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
|
||||
@@ -754,7 +766,7 @@ namespace cryptonote
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("Requested fluffy tx before handshake, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
std::vector<std::pair<cryptonote::blobdata, block>> local_blocks;
|
||||
@@ -765,7 +777,7 @@ namespace cryptonote
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("failed to find block: " << arg.block_hash << ", dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
std::vector<crypto::hash> txids;
|
||||
@@ -790,7 +802,7 @@ namespace cryptonote
|
||||
<< ", dropping connection"
|
||||
);
|
||||
drop_connection(context, true, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
txids.push_back(b.tx_hashes[tx_idx]);
|
||||
seen[tx_idx] = true;
|
||||
@@ -807,7 +819,7 @@ namespace cryptonote
|
||||
);
|
||||
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -818,14 +830,14 @@ namespace cryptonote
|
||||
LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, "
|
||||
<< "failed to get requested transactions");
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
if (!missed.empty() || txs.size() != txids.size())
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, "
|
||||
<< missed.size() << " requested transactions not found" << ", dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
for(auto& tx: txs)
|
||||
@@ -878,17 +890,23 @@ namespace cryptonote
|
||||
int t_cryptonote_protocol_handler<t_core>::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context)
|
||||
{
|
||||
MLOG_P2P_MESSAGE("Received NOTIFY_NEW_TRANSACTIONS (" << arg.txs.size() << " txes)");
|
||||
std::unordered_set<blobdata> seen;
|
||||
std::unordered_set<crypto::hash> seen;
|
||||
seen.reserve(arg.txs.size());
|
||||
|
||||
for (const auto &blob: arg.txs)
|
||||
{
|
||||
MLOGIF_P2P_MESSAGE(cryptonote::transaction tx; crypto::hash hash; bool ret = cryptonote::parse_and_validate_tx_from_blob(blob, tx, hash);, ret, "Including transaction " << hash);
|
||||
if (seen.find(blob) != seen.end())
|
||||
MLOGIF_P2P_MESSAGE(cryptonote::transaction tx; crypto::hash hash; bool ret = cryptonote::parse_and_validate_tx_from_blob(blob, tx, hash, true);, ret, "Including transaction " << hash);
|
||||
|
||||
crypto::hash digest{};
|
||||
if (!blob.empty())
|
||||
tools::sha256sum(reinterpret_cast<const uint8_t*>(blob.data()), blob.size(), digest);
|
||||
|
||||
if (!seen.insert(digest).second)
|
||||
{
|
||||
LOG_PRINT_CCONTEXT_L1("Duplicate transaction in notification, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
seen.insert(blob);
|
||||
}
|
||||
|
||||
if(context.m_state != cryptonote_connection_context::state_normal)
|
||||
@@ -933,7 +951,7 @@ namespace cryptonote
|
||||
{
|
||||
LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
switch (tvc.m_relay)
|
||||
@@ -977,7 +995,7 @@ namespace cryptonote
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("Requested objects before handshake, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_GET_OBJECTS (" << arg.blocks.size() << " blocks)");
|
||||
if (arg.blocks.size() > CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT)
|
||||
@@ -987,7 +1005,7 @@ namespace cryptonote
|
||||
<< arg.blocks.size() << ") expected not more then "
|
||||
<< CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT);
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
NOTIFY_RESPONSE_GET_OBJECTS::request rsp;
|
||||
@@ -995,7 +1013,7 @@ namespace cryptonote
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("failed to handle request NOTIFY_REQUEST_GET_OBJECTS, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
|
||||
MLOG_P2P_MESSAGE("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()="
|
||||
@@ -1035,7 +1053,7 @@ namespace cryptonote
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("Got NOTIFY_RESPONSE_GET_OBJECTS out of the blue, dropping connection");
|
||||
drop_connection(context, true, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
context.m_expect_response = 0;
|
||||
|
||||
@@ -1073,7 +1091,7 @@ namespace cryptonote
|
||||
LOG_ERROR_CCONTEXT("sent wrong NOTIFY_HAVE_OBJECTS: no blocks");
|
||||
drop_connection(context, true, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
if(context.m_last_response_height > arg.current_blockchain_height)
|
||||
{
|
||||
@@ -1081,13 +1099,15 @@ namespace cryptonote
|
||||
<< " < m_last_response_height=" << context.m_last_response_height << ", dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
if (arg.current_blockchain_height < context.m_remote_blockchain_height)
|
||||
{
|
||||
MINFO(context << "Claims " << arg.current_blockchain_height << ", claimed " << context.m_remote_blockchain_height << " before");
|
||||
hit_score(context, 1);
|
||||
if (context.m_score <= DROP_PEERS_ON_SCORE)
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
context.m_remote_blockchain_height = arg.current_blockchain_height;
|
||||
if (context.m_remote_blockchain_height > m_core.get_target_blockchain_height())
|
||||
@@ -1113,7 +1133,7 @@ namespace cryptonote
|
||||
<< epee::string_tools::buff_to_hex_nodelimer(arg.blocks[i].block) << ", dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
if (b.miner_tx.vin.size() != 1 || b.miner_tx.vin.front().type() != typeid(txin_gen))
|
||||
{
|
||||
@@ -1121,7 +1141,7 @@ namespace cryptonote
|
||||
<< epee::string_tools::buff_to_hex_nodelimer(arg.blocks[i].block) << ", dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
const auto this_height = boost::get<txin_gen>(b.miner_tx.vin[0]).height;
|
||||
@@ -1130,7 +1150,7 @@ namespace cryptonote
|
||||
LOG_ERROR_CCONTEXT("Sent invalid chain");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
// if first block
|
||||
@@ -1142,7 +1162,7 @@ namespace cryptonote
|
||||
LOG_ERROR_CCONTEXT("sent block ahead of expected height, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
if (this_height == 0 || context.get_expected_hash(this_height - 1) != b.prev_id)
|
||||
@@ -1150,7 +1170,7 @@ namespace cryptonote
|
||||
LOG_ERROR_CCONTEXT("Sent invalid chain");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
}
|
||||
else if (b.prev_id != previous)
|
||||
@@ -1158,7 +1178,7 @@ namespace cryptonote
|
||||
LOG_ERROR_CCONTEXT("Sent invalid chain");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
previous = block_hash;
|
||||
|
||||
@@ -1167,7 +1187,7 @@ namespace cryptonote
|
||||
LOG_ERROR_CCONTEXT("Sent invalid chain");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
auto req_it = context.m_requested_objects.find(block_hash);
|
||||
@@ -1177,7 +1197,7 @@ namespace cryptonote
|
||||
<< " wasn't requested, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
if(b.tx_hashes.size() != arg.blocks[i].txs.size())
|
||||
{
|
||||
@@ -1185,7 +1205,7 @@ namespace cryptonote
|
||||
<< ", tx_hashes.size()=" << b.tx_hashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << arg.blocks[i].txs.size() << ", dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
context.m_requested_objects.erase(req_it);
|
||||
@@ -1198,7 +1218,7 @@ namespace cryptonote
|
||||
<< context.m_requested_objects.size() << "), dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
const bool pruned_ok = should_ask_for_pruned_data(context, start_height, arg.blocks.size(), true);
|
||||
@@ -1212,14 +1232,14 @@ namespace cryptonote
|
||||
MERROR(context << "returned a pruned block, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
if (block_entry.block_weight)
|
||||
{
|
||||
MERROR(context << "returned a block weight for a non pruned block, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
for (const tx_blob_entry &tx_entry: block_entry.txs)
|
||||
{
|
||||
@@ -1228,7 +1248,7 @@ namespace cryptonote
|
||||
MERROR(context << "returned at least one pruned object which we did not expect, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1243,7 +1263,7 @@ namespace cryptonote
|
||||
MERROR(context << "returned at least one pruned block with 0 weight, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
++m_sync_bad_spans_downloaded;
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1465,6 +1485,7 @@ namespace cryptonote
|
||||
if (!m_core.prepare_handle_incoming_blocks(blocks, pblocks))
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("Failure in prepare_handle_incoming_blocks");
|
||||
m_block_queue.flush_spans(span_connection_id, true);
|
||||
drop_connections(span_origin);
|
||||
return 1;
|
||||
}
|
||||
@@ -1787,7 +1808,7 @@ skip:
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("Requested chain before handshake, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
NOTIFY_RESPONSE_CHAIN_ENTRY::request r;
|
||||
if(!m_core.find_blockchain_supplement(arg.block_ids, !arg.prune, r))
|
||||
@@ -2480,14 +2501,14 @@ skip:
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("Got NOTIFY_RESPONSE_CHAIN_ENTRY out of the blue, dropping connection");
|
||||
drop_connection(context, true, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
context.m_expect_response = 0;
|
||||
if (arg.start_height + 1 > context.m_expect_height) // we expect an overlapping block
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("Got NOTIFY_RESPONSE_CHAIN_ENTRY past expected height, dropping connection");
|
||||
drop_connection(context, true, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
context.m_last_request_time = boost::date_time::not_a_date_time;
|
||||
@@ -2498,19 +2519,19 @@ skip:
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("sent empty m_block_ids, dropping connection");
|
||||
drop_connection(context, true, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
if (arg.total_height < arg.m_block_ids.size() || arg.start_height > arg.total_height - arg.m_block_ids.size())
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("sent invalid start/nblocks/height, dropping connection");
|
||||
drop_connection(context, true, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
if (!arg.m_block_weights.empty() && arg.m_block_weights.size() != arg.m_block_ids.size())
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("sent invalid block weight array, dropping connection");
|
||||
drop_connection(context, true, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
MDEBUG(context << "first block hash " << arg.m_block_ids.front() << ", last " << arg.m_block_ids.back());
|
||||
|
||||
@@ -2518,12 +2539,14 @@ skip:
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with total_height=" << arg.total_height << " and block_ids=" << arg.m_block_ids.size());
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
if (arg.total_height < context.m_remote_blockchain_height)
|
||||
{
|
||||
MINFO(context << "Claims " << arg.total_height << ", claimed " << context.m_remote_blockchain_height << " before");
|
||||
hit_score(context, 1);
|
||||
if (context.m_score <= DROP_PEERS_ON_SCORE)
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
context.m_remote_blockchain_height = arg.total_height;
|
||||
context.m_last_response_height = arg.start_height + arg.m_block_ids.size()-1;
|
||||
@@ -2533,7 +2556,7 @@ skip:
|
||||
<< ", m_start_height=" << arg.start_height
|
||||
<< ", m_block_ids.size()=" << arg.m_block_ids.size());
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
uint64_t n_use_blocks = m_core.prevalidate_block_hashes(arg.start_height, arg.m_block_ids, arg.m_block_weights);
|
||||
@@ -2541,7 +2564,7 @@ skip:
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("Most blocks are invalid, dropping connection");
|
||||
drop_connection(context, true, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
|
||||
context.m_expected_heights_start = arg.start_height;
|
||||
@@ -2559,7 +2582,7 @@ skip:
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("Duplicate blocks in chain entry response, dropping connection");
|
||||
drop_connection_with_score(context, 5, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
int where;
|
||||
const bool have_block = m_core.have_block_unlocked(arg.m_block_ids[i], &where);
|
||||
@@ -2570,7 +2593,7 @@ skip:
|
||||
{
|
||||
LOG_ERROR_CCONTEXT("First block hash is unknown, dropping connection");
|
||||
drop_connection_with_score(context, 5, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
if (!have_block)
|
||||
expect_unknown = true;
|
||||
@@ -2587,19 +2610,19 @@ skip:
|
||||
case HAVE_BLOCK_INVALID:
|
||||
LOG_ERROR_CCONTEXT("Block is invalid or known without known type, dropping connection");
|
||||
drop_connection(context, true, false);
|
||||
return 1;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
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;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
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;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
break;
|
||||
case HAVE_BLOCK_ALT_CHAIN:
|
||||
@@ -2607,7 +2630,7 @@ skip:
|
||||
{
|
||||
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;
|
||||
return LEVIN_ERROR_CONNECTION;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2810,6 +2833,7 @@ skip:
|
||||
template<class t_core>
|
||||
void t_cryptonote_protocol_handler<t_core>::drop_connection(const boost::uuids::uuid& id)
|
||||
{
|
||||
m_block_queue.flush_spans(id, true);
|
||||
m_p2p->for_connection(id, [this](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{
|
||||
// This _could be_ outside of strand, so careful on actions
|
||||
drop_connection(context, true, false);
|
||||
@@ -2855,6 +2879,8 @@ skip:
|
||||
{
|
||||
MINFO("Target height decreasing from " << previous_target << " to " << target);
|
||||
m_core.set_target_blockchain_height(target);
|
||||
if (target < m_core.get_current_blockchain_height() + 5)
|
||||
m_core.safesyncmode(true);
|
||||
if (target == 0 && context.m_state > cryptonote_connection_context::state_before_handshake && !m_stopping)
|
||||
{
|
||||
MCWARNING("global", "wownerod is now disconnected from the network");
|
||||
|
||||
@@ -147,7 +147,9 @@ namespace levin
|
||||
of waiting in here. */
|
||||
|
||||
p2p.foreach_connection([&outs, blockchain_height] (detail::p2p_context& context) {
|
||||
if (!context.m_is_income && context.m_remote_blockchain_height >= blockchain_height)
|
||||
// Give a +2 allowable window for candidate peers, since we may not have updated the peer's
|
||||
// known sync height yet (e.g. if we haven't received or finished processing their new block(s) yet)
|
||||
if (!context.m_is_income && (context.m_remote_blockchain_height + 2) >= blockchain_height)
|
||||
outs.emplace_back(context.m_connection_id);
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -92,7 +92,7 @@ struct zmq_internals
|
||||
{
|
||||
explicit zmq_internals(t_core& core, t_p2p& p2p, const bool restricted)
|
||||
: rpc_handler{core.get(), p2p.get(), restricted}
|
||||
, server{rpc_handler}
|
||||
, server{rpc_handler, restricted}
|
||||
{}
|
||||
|
||||
cryptonote::rpc::DaemonHandler rpc_handler;
|
||||
|
||||
@@ -645,7 +645,11 @@ bool t_rpc_command_executor::print_connections() {
|
||||
}
|
||||
}
|
||||
|
||||
tools::msg_writer() << std::setw(30) << std::left << "Remote Host"
|
||||
int host_field_width = 15;
|
||||
for (const auto &conn : res.connections)
|
||||
host_field_width = std::max(host_field_width, 8 + (int) conn.address.length());
|
||||
|
||||
tools::msg_writer() << std::setw(host_field_width) << std::left << "Remote Host"
|
||||
<< std::setw(8) << "Type"
|
||||
<< std::setw(6) << "SSL"
|
||||
<< std::setw(20) << "Peer id"
|
||||
@@ -662,11 +666,11 @@ bool t_rpc_command_executor::print_connections() {
|
||||
for (auto & info : res.connections)
|
||||
{
|
||||
std::string address = info.incoming ? "INC " : "OUT ";
|
||||
address += info.ip + ":" + info.port;
|
||||
address += info.address;
|
||||
//std::string in_out = info.incoming ? "INC " : "OUT ";
|
||||
tools::msg_writer()
|
||||
//<< std::setw(30) << std::left << in_out
|
||||
<< std::setw(30) << std::left << address
|
||||
<< std::setw(host_field_width) << std::left << address
|
||||
<< std::setw(8) << (get_address_type_name((epee::net_utils::address_type)info.address_type))
|
||||
<< std::setw(6) << (info.ssl ? "yes" : "no")
|
||||
<< std::setw(20) << info.peer_id
|
||||
|
||||
@@ -5,13 +5,19 @@
|
||||
//
|
||||
|
||||
#include "daemonizer/posix_fork.h"
|
||||
#include "misc_language.h"
|
||||
#include "misc_log_ex.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <fstream>
|
||||
#include <unistd.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef TMPDIR
|
||||
#define TMPDIR "/tmp"
|
||||
@@ -35,26 +41,64 @@ void fork(const std::string & pidfile)
|
||||
// processes.
|
||||
// Only in the final child process do we write the PID to the
|
||||
// file (and close it).
|
||||
std::ofstream pidofs;
|
||||
int pid_fd = -1;
|
||||
auto close_pid_fd = [&pid_fd]()
|
||||
{
|
||||
if (pid_fd >= 0)
|
||||
{
|
||||
close(pid_fd);
|
||||
pid_fd = -1;
|
||||
}
|
||||
};
|
||||
epee::misc_utils::auto_scope_leave_caller pid_fd_guard =
|
||||
epee::misc_utils::create_scope_leave_handler(close_pid_fd);
|
||||
if (! pidfile.empty ())
|
||||
{
|
||||
int oldpid;
|
||||
std::ifstream pidrifs;
|
||||
pidrifs.open(pidfile, std::fstream::in);
|
||||
if (! pidrifs.fail())
|
||||
struct stat st;
|
||||
if (lstat(pidfile.c_str(), &st) == 0)
|
||||
{
|
||||
// Read the PID and send signal 0 to see if the process exists.
|
||||
if (pidrifs >> oldpid && oldpid > 1 && kill(oldpid, 0) == 0)
|
||||
if (S_ISLNK(st.st_mode))
|
||||
{
|
||||
quit("PID file " + pidfile + " already exists and the PID therein is valid");
|
||||
}
|
||||
pidrifs.close();
|
||||
}
|
||||
quit("PID file path is a symlink, refusing: " + pidfile);
|
||||
}
|
||||
if (!S_ISREG(st.st_mode))
|
||||
{
|
||||
quit("PID file path exists and is not a regular file: " + pidfile);
|
||||
}
|
||||
|
||||
pidofs.open(pidfile, std::fstream::out | std::fstream::trunc);
|
||||
if (pidofs.fail())
|
||||
int oldpid = 0;
|
||||
std::ifstream pidrifs;
|
||||
pidrifs.open(pidfile, std::fstream::in);
|
||||
if (!pidrifs.fail())
|
||||
{
|
||||
// Read the PID and send signal 0 to see if the process exists.
|
||||
errno = 0;
|
||||
if (pidrifs >> oldpid && oldpid > 1 && (kill(oldpid, 0) == 0 || errno == EPERM))
|
||||
{
|
||||
quit("PID file " + pidfile + " already exists and the PID therein is valid");
|
||||
}
|
||||
pidrifs.close();
|
||||
}
|
||||
|
||||
if (unlink(pidfile.c_str()) != 0)
|
||||
{
|
||||
quit("Failed to remove stale PID file: " + pidfile + ": " + std::strerror(errno));
|
||||
}
|
||||
}
|
||||
else if (errno != ENOENT)
|
||||
{
|
||||
quit("Failed to open specified PID file for writing");
|
||||
quit("Failed to inspect PID file path: " + pidfile + ": " + std::strerror(errno));
|
||||
}
|
||||
|
||||
#ifdef O_NOFOLLOW
|
||||
const int flags = O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW;
|
||||
#else
|
||||
const int flags = O_WRONLY | O_CREAT | O_EXCL;
|
||||
#endif
|
||||
pid_fd = open(pidfile.c_str(), flags, 0644);
|
||||
if (pid_fd < 0)
|
||||
{
|
||||
quit("Failed to create PID file: " + pidfile + ": " + std::strerror(errno));
|
||||
}
|
||||
}
|
||||
// Fork the process and have the parent exit. If the process was started
|
||||
@@ -65,7 +109,7 @@ void fork(const std::string & pidfile)
|
||||
if (pid > 0)
|
||||
{
|
||||
// We're in the parent process and need to exit.
|
||||
pidofs.close();
|
||||
close_pid_fd();
|
||||
// When the exit() function is used, the program terminates without
|
||||
// invoking local variables' destructors. Only global variables are
|
||||
// destroyed.
|
||||
@@ -86,7 +130,7 @@ void fork(const std::string & pidfile)
|
||||
{
|
||||
if (pid > 0)
|
||||
{
|
||||
pidofs.close();
|
||||
close_pid_fd();
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
@@ -95,11 +139,18 @@ void fork(const std::string & pidfile)
|
||||
}
|
||||
}
|
||||
|
||||
if (! pidofs.fail())
|
||||
if (pid_fd >= 0)
|
||||
{
|
||||
int pid = ::getpid();
|
||||
pidofs << pid << std::endl;
|
||||
pidofs.close();
|
||||
const std::string pid = std::to_string(::getpid()) + "\n";
|
||||
const ssize_t written = write(pid_fd, pid.data(), pid.size());
|
||||
if (written < 0)
|
||||
{
|
||||
quit("Failed to write PID file: " + pidfile + ": " + std::strerror(errno));
|
||||
}
|
||||
if (static_cast<size_t>(written) != pid.size())
|
||||
{
|
||||
quit("Failed to write complete PID file: " + pidfile);
|
||||
}
|
||||
}
|
||||
|
||||
// Close the standard streams. This decouples the daemon from the terminal
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
|
||||
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
#include "device_default.hpp"
|
||||
#include "int-util.h"
|
||||
#include "crypto/wallet/crypto.h"
|
||||
@@ -82,19 +83,32 @@ namespace hw {
|
||||
return true;
|
||||
}
|
||||
|
||||
// serialize mode access so callers cannot observe another wallet's transient signing mode
|
||||
bool device_default::set_mode(device_mode mode) {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(device_locker);
|
||||
return device::set_mode(mode);
|
||||
}
|
||||
|
||||
device::device_mode device_default::get_mode() const {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(device_locker);
|
||||
return device::get_mode();
|
||||
}
|
||||
|
||||
/* ======================================================================= */
|
||||
/* LOCKER */
|
||||
/* ======================================================================= */
|
||||
|
||||
void device_default::lock() { }
|
||||
void device_default::lock() {
|
||||
device_locker.lock();
|
||||
}
|
||||
|
||||
bool device_default::try_lock() { return true; }
|
||||
bool device_default::try_lock() {
|
||||
return device_locker.try_lock();
|
||||
}
|
||||
|
||||
void device_default::unlock() { }
|
||||
void device_default::unlock() {
|
||||
device_locker.unlock();
|
||||
}
|
||||
|
||||
/* ======================================================================= */
|
||||
/* WALLET & ADDRESS */
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
#include "device.hpp"
|
||||
|
||||
namespace hw {
|
||||
@@ -60,6 +61,7 @@ namespace hw {
|
||||
bool disconnect() override;
|
||||
|
||||
bool set_mode(device_mode mode) override;
|
||||
device_mode get_mode() const override;
|
||||
|
||||
device_type get_type() const override {return device_type::SOFTWARE;};
|
||||
|
||||
@@ -141,6 +143,10 @@ namespace hw {
|
||||
bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) override;
|
||||
|
||||
bool close_tx(void) override;
|
||||
|
||||
private:
|
||||
// Locker for concurrent access
|
||||
mutable boost::recursive_mutex device_locker;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -155,12 +155,12 @@ namespace ki {
|
||||
res.emplace_back();
|
||||
auto & cres = res.back();
|
||||
cres.set_out_key(key_to_string(td.get_public_key()));
|
||||
cres.set_tx_pub_key(key_to_string(tx_pub_key));
|
||||
cres.set_tx_pub_key(key_to_string(::crypto::pubkey_clear_torsion(tx_pub_key)));
|
||||
cres.set_internal_output_index(td.m_internal_output_index);
|
||||
cres.set_sub_addr_major(td.m_subaddr_index.major);
|
||||
cres.set_sub_addr_minor(td.m_subaddr_index.minor);
|
||||
if (!additional_tx_pub_keys.empty() && additional_tx_pub_keys.size() > td.m_internal_output_index) {
|
||||
cres.add_additional_tx_pub_keys(key_to_string(additional_tx_pub_keys[td.m_internal_output_index]));
|
||||
cres.add_additional_tx_pub_keys(key_to_string(::crypto::pubkey_clear_torsion(additional_tx_pub_keys[td.m_internal_output_index])));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,10 +467,10 @@ namespace tx {
|
||||
}
|
||||
}
|
||||
|
||||
dst->set_real_out_tx_key(key_to_string(src.real_out_tx_key));
|
||||
dst->set_real_out_tx_key(key_to_string(::crypto::pubkey_clear_torsion(src.real_out_tx_key)));
|
||||
dst->set_real_output_in_tx_index(src.real_output_in_tx_index);
|
||||
if (!src.real_out_additional_tx_keys.empty()) {
|
||||
dst->add_real_out_additional_tx_keys(key_to_string(src.real_out_additional_tx_keys.at(src.real_output_in_tx_index)));
|
||||
if (src.real_output_in_tx_index < src.real_out_additional_tx_keys.size()) {
|
||||
dst->add_real_out_additional_tx_keys(key_to_string(::crypto::pubkey_clear_torsion(src.real_out_additional_tx_keys.at(src.real_output_in_tx_index))));
|
||||
}
|
||||
dst->set_amount(src.amount);
|
||||
dst->set_rct(src.rct);
|
||||
|
||||
@@ -474,14 +474,14 @@ namespace trezor{
|
||||
}
|
||||
|
||||
boost::optional<epee::wipeable_string> bin_data = m_response->parse_hexstr();
|
||||
if (!bin_data){
|
||||
if (!bin_data || bin_data->size() < PROTO_HEADER_SIZE){
|
||||
throw exc::CommunicationException("Response is not well hexcoded");
|
||||
}
|
||||
|
||||
uint16_t msg_tag;
|
||||
uint32_t msg_len;
|
||||
deserialize_message_header(bin_data->data(), msg_tag, msg_len);
|
||||
if (bin_data->size() != msg_len + 6){
|
||||
if (bin_data->size() - PROTO_HEADER_SIZE != msg_len){
|
||||
throw exc::CommunicationException("Response is not well hexcoded");
|
||||
}
|
||||
|
||||
|
||||
@@ -254,7 +254,7 @@ static void make_tx_secret_key_seed(const crypto::secret_key& tx_secret_key_entr
|
||||
rct::keyV hash_context;
|
||||
hash_context.reserve(2 + sources.size());
|
||||
auto hash_context_wiper = epee::misc_utils::create_scope_leave_handler([&]{
|
||||
memwipe(hash_context.data(), hash_context.size());
|
||||
memwipe(hash_context.data(), hash_context.size() * sizeof(rct::key));
|
||||
});
|
||||
hash_context.emplace_back();
|
||||
rct::cn_fast_hash(hash_context.back(), domain_separator.data(), domain_separator.size()); //domain sep
|
||||
@@ -282,7 +282,7 @@ static void make_tx_secret_keys(const crypto::secret_key& tx_secret_key_seed,
|
||||
rct::keyV hash_context;
|
||||
hash_context.resize(2);
|
||||
auto hash_context_wiper = epee::misc_utils::create_scope_leave_handler([&]{
|
||||
memwipe(hash_context.data(), hash_context.size());
|
||||
memwipe(hash_context.data(), hash_context.size() * sizeof(rct::key));
|
||||
});
|
||||
hash_context[0] = rct::sk2rct(tx_secret_key_seed);
|
||||
rct::cn_fast_hash(hash_context[1], domain_separator.data(), domain_separator.size());
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace net
|
||||
{
|
||||
/*!
|
||||
* \brief Canonicalize a hostname by converting letters to lowercase.
|
||||
*
|
||||
* \param host Hostname to canonicalize in-place.
|
||||
*/
|
||||
inline void canonicalize_host(std::string& host) noexcept
|
||||
{
|
||||
for (char& c : host)
|
||||
{
|
||||
if ('A' <= c && c <= 'Z')
|
||||
c = static_cast<char>(c - 'A' + 'a');
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-6
@@ -36,6 +36,7 @@
|
||||
#include <limits>
|
||||
|
||||
#include "net/error.h"
|
||||
#include "net/host.h"
|
||||
#include "serialization/keyvalue_serialization.h"
|
||||
#include "storages/portable_storage.h"
|
||||
#include "string_tools_lexical.h"
|
||||
@@ -105,20 +106,26 @@ namespace net
|
||||
expect<i2p_address> i2p_address::make(const boost::string_ref address)
|
||||
{
|
||||
boost::string_ref host = address.substr(0, address.rfind(':'));
|
||||
MONERO_CHECK(host_check(host));
|
||||
std::string normalized_host{host};
|
||||
net::canonicalize_host(normalized_host);
|
||||
MONERO_CHECK(host_check(normalized_host));
|
||||
|
||||
static_assert(b32_length + sizeof(tld) == sizeof(i2p_address::host_), "bad internal host size");
|
||||
return i2p_address{host};
|
||||
return i2p_address{normalized_host};
|
||||
}
|
||||
|
||||
bool i2p_address::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
|
||||
{
|
||||
i2p_serialized in{};
|
||||
if (in._load(src, hparent) && in.host.size() < sizeof(host_) && (in.host == unknown_host || !host_check(in.host).has_error()))
|
||||
if (in._load(src, hparent) && in.host.size() < sizeof(host_))
|
||||
{
|
||||
std::memcpy(host_, in.host.data(), in.host.size());
|
||||
std::memset(host_ + in.host.size(), 0, sizeof(host_) - in.host.size());
|
||||
return true;
|
||||
net::canonicalize_host(in.host);
|
||||
if (in.host == unknown_host || !host_check(in.host).has_error())
|
||||
{
|
||||
std::memcpy(host_, in.host.data(), in.host.size());
|
||||
std::memset(host_ + in.host.size(), 0, sizeof(host_) - in.host.size());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
static_assert(sizeof(unknown_host) <= sizeof(host_), "bad buffer size");
|
||||
std::memcpy(host_, unknown_host, sizeof(unknown_host)); // include null terminator
|
||||
|
||||
+14
-7
@@ -37,6 +37,7 @@
|
||||
#include <limits>
|
||||
|
||||
#include "net/error.h"
|
||||
#include "net/host.h"
|
||||
#include "serialization/keyvalue_serialization.h"
|
||||
#include "storages/portable_storage.h"
|
||||
#include "string_tools_lexical.h"
|
||||
@@ -112,7 +113,9 @@ namespace net
|
||||
const boost::string_ref port =
|
||||
address.substr(host.size() + (host.size() == address.size() ? 0 : 1));
|
||||
|
||||
MONERO_CHECK(host_check(host));
|
||||
std::string normalized_host{host};
|
||||
net::canonicalize_host(normalized_host);
|
||||
MONERO_CHECK(host_check(normalized_host));
|
||||
|
||||
std::uint16_t porti = default_port;
|
||||
if (!port.empty() && !epee::string_tools::get_xtype_from_string(porti, std::string{port}))
|
||||
@@ -120,18 +123,22 @@ namespace net
|
||||
|
||||
static_assert(v2_length <= v3_length, "bad internal host size");
|
||||
static_assert(v3_length + sizeof(tld) == sizeof(tor_address::host_), "bad internal host size");
|
||||
return tor_address{host, porti};
|
||||
return tor_address{normalized_host, porti};
|
||||
}
|
||||
|
||||
bool tor_address::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
|
||||
{
|
||||
tor_serialized in{};
|
||||
if (in._load(src, hparent) && in.host.size() < sizeof(host_) && (in.host == unknown_host || !host_check(in.host).has_error()))
|
||||
if (in._load(src, hparent) && in.host.size() < sizeof(host_))
|
||||
{
|
||||
std::memcpy(host_, in.host.data(), in.host.size());
|
||||
std::memset(host_ + in.host.size(), 0, sizeof(host_) - in.host.size());
|
||||
port_ = in.port;
|
||||
return true;
|
||||
net::canonicalize_host(in.host);
|
||||
if (in.host == unknown_host || !host_check(in.host).has_error())
|
||||
{
|
||||
std::memcpy(host_, in.host.data(), in.host.size());
|
||||
std::memset(host_ + in.host.size(), 0, sizeof(host_) - in.host.size());
|
||||
port_ = in.port;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
static_assert(sizeof(unknown_host) <= sizeof(host_), "bad buffer size");
|
||||
std::memcpy(host_, unknown_host, sizeof(unknown_host)); // include null terminator
|
||||
|
||||
+12
-1
@@ -144,16 +144,27 @@ namespace zmq
|
||||
static constexpr const int max_out = std::numeric_limits<int>::max();
|
||||
const std::string::size_type initial = payload.size();
|
||||
message part{};
|
||||
bool too_large = false;
|
||||
for (;;)
|
||||
{
|
||||
int last = 0;
|
||||
if ((last = zmq_msg_recv(part.handle(), socket, flags)) < 0)
|
||||
return last;
|
||||
|
||||
payload.append(part.data(), part.size());
|
||||
if (!too_large)
|
||||
too_large =
|
||||
max_message_size < payload.size() ||
|
||||
max_message_size - payload.size() < part.size();
|
||||
if (!too_large)
|
||||
payload.append(part.data(), part.size());
|
||||
if (!zmq_msg_more(part.handle()))
|
||||
break;
|
||||
}
|
||||
if (too_large)
|
||||
{
|
||||
errno = EMSGSIZE;
|
||||
return -1;
|
||||
}
|
||||
const std::string::size_type added = payload.size() - initial;
|
||||
return unsigned(max_out) < added ? max_out : int(added);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,8 @@ namespace net
|
||||
{
|
||||
namespace zmq
|
||||
{
|
||||
constexpr std::size_t max_message_size = 10 * 1024 * 1024; // 10 MiB
|
||||
|
||||
//! \return Category for ZMQ errors.
|
||||
const std::error_category& error_category() noexcept;
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace nodetool
|
||||
const command_line::arg_descriptor<bool> arg_pad_transactions = {
|
||||
"pad-transactions", "Pad relayed transactions to help defend against traffic volume analysis", false
|
||||
};
|
||||
const command_line::arg_descriptor<uint32_t> arg_max_connections_per_ip = {"max-connections-per-ip", "Maximum number of p2p connections allowed from the same IP address", 1};
|
||||
const command_line::arg_descriptor<uint32_t> arg_max_connections_per_ip = {"max-connections-per-ip", "Maximum number of inbound p2p connections allowed from the same IPv4 address or shared across a public IPv6 /64 subnet", 1};
|
||||
|
||||
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)
|
||||
{
|
||||
|
||||
+2
-1
@@ -307,7 +307,7 @@ namespace nodetool
|
||||
|
||||
BEGIN_INVOKE_MAP2(node_server)
|
||||
if (is_filtered_command(context.m_remote_address, command))
|
||||
return LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED;
|
||||
return is_notify ? LEVIN_OK : LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED;
|
||||
|
||||
HANDLE_INVOKE_T2(COMMAND_HANDSHAKE, &node_server::handle_handshake)
|
||||
HANDLE_INVOKE_T2(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync)
|
||||
@@ -448,6 +448,7 @@ namespace nodetool
|
||||
bool m_use_ipv6;
|
||||
bool m_require_ipv4;
|
||||
std::atomic<bool> is_closing;
|
||||
std::atomic<bool> m_stop_signal_sent_once{false};
|
||||
std::unique_ptr<boost::thread> mPeersLoggerThread;
|
||||
//critical_section m_connections_lock;
|
||||
//connections_indexed_container m_connections;
|
||||
|
||||
+55
-23
@@ -81,6 +81,22 @@ static inline boost::asio::ip::address_v4 make_address_v4_from_v6(const boost::a
|
||||
|
||||
namespace nodetool
|
||||
{
|
||||
inline bool is_same_p2p_connection_limit_host(const epee::net_utils::network_address& left, const epee::net_utils::network_address& right)
|
||||
{
|
||||
if (left.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id() &&
|
||||
right.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
|
||||
{
|
||||
const boost::asio::ip::address_v6 left_ip = left.as<const epee::net_utils::ipv6_network_address>().ip();
|
||||
const boost::asio::ip::address_v6 right_ip = right.as<const epee::net_utils::ipv6_network_address>().ip();
|
||||
if (epee::net_utils::should_group_ipv6_by_prefix(left_ip) &&
|
||||
epee::net_utils::should_group_ipv6_by_prefix(right_ip))
|
||||
return epee::net_utils::get_ipv6_subnet_address(left_ip, 64) ==
|
||||
epee::net_utils::get_ipv6_subnet_address(right_ip, 64);
|
||||
}
|
||||
|
||||
return left.is_same_host(right);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_payload_net_handler>
|
||||
node_server<t_payload_net_handler>::~node_server()
|
||||
{
|
||||
@@ -206,8 +222,8 @@ namespace nodetool
|
||||
{
|
||||
if (now >= it->second)
|
||||
{
|
||||
it = m_blocked_subnets.erase(it);
|
||||
MCLOG_CYAN(el::Level::Info, "global", "Subnet " << it->first.host_str() << " unblocked.");
|
||||
it = m_blocked_subnets.erase(it);
|
||||
continue;
|
||||
}
|
||||
if (it->first.matches(ipv4_address))
|
||||
@@ -1088,28 +1104,44 @@ namespace nodetool
|
||||
template<class t_payload_net_handler>
|
||||
bool node_server<t_payload_net_handler>::send_stop_signal()
|
||||
{
|
||||
if (m_stop_signal_sent_once.exchange(true))
|
||||
{
|
||||
MDEBUG("[node] Stop signal already sent");
|
||||
return true;
|
||||
}
|
||||
MDEBUG("[node] stopping server payload handler");
|
||||
m_payload_handler.stop();
|
||||
MDEBUG("[node] sending stop signal");
|
||||
|
||||
MDEBUG("[node] marking net servers as stopping");
|
||||
for (auto& zone : m_network_zones)
|
||||
{
|
||||
const auto close_all_connections = [&, this]()
|
||||
{
|
||||
std::list<boost::uuids::uuid> connection_ids;
|
||||
zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) {
|
||||
connection_ids.push_back(cntxt.m_connection_id);
|
||||
return true;
|
||||
});
|
||||
for (const auto &connection_id: connection_ids)
|
||||
{
|
||||
MDEBUG("Closing connection " << connection_id);
|
||||
// We need to wait for every connection's shutdown sequence to complete before stopping the io_context.
|
||||
zone.second.m_net_server.get_config_object().close(connection_id, true/*wait_for_shutdown*/);
|
||||
MDEBUG("Closed connection " << connection_id);
|
||||
}
|
||||
};
|
||||
zone.second.m_net_server.mark_stop_signal_sent();
|
||||
}
|
||||
|
||||
zone.second.m_net_server.send_stop_signal(close_all_connections);
|
||||
MDEBUG("[node] closing connections");
|
||||
for (auto& zone : m_network_zones)
|
||||
{
|
||||
zone.second.m_net_server.close_server_connections();
|
||||
|
||||
std::list<boost::uuids::uuid> connection_ids;
|
||||
zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
|
||||
{
|
||||
connection_ids.push_back(cntxt.m_connection_id);
|
||||
return true;
|
||||
});
|
||||
for (const auto &connection_id: connection_ids)
|
||||
{
|
||||
MDEBUG("Closing connection " << connection_id);
|
||||
// All zone connections must finish shutting down before any shared io_context is stopped.
|
||||
zone.second.m_net_server.get_config_object().close(connection_id, true/*wait_for_shutdown*/);
|
||||
MDEBUG("Closed connection " << connection_id);
|
||||
}
|
||||
}
|
||||
|
||||
MDEBUG("[node] stopping net server io_contexts");
|
||||
for (auto& zone : m_network_zones)
|
||||
{
|
||||
zone.second.m_net_server.stop_io_context();
|
||||
}
|
||||
MDEBUG("[node] Stop signal sent");
|
||||
return true;
|
||||
@@ -2365,8 +2397,8 @@ namespace nodetool
|
||||
return false;
|
||||
|
||||
network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone());
|
||||
int res = zone.m_net_server.get_config_object().send(message.finalize_notify(command), context.m_connection_id);
|
||||
return res > 0;
|
||||
bool res = zone.m_net_server.get_config_object().send(message.finalize_notify(command), context.m_connection_id);
|
||||
return res;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_payload_net_handler>
|
||||
@@ -2575,9 +2607,9 @@ namespace nodetool
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(context.peer_id)
|
||||
if(context.handshake_complete())
|
||||
{
|
||||
LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but seems that connection already have associated peer_id (double COMMAND_HANDSHAKE?)");
|
||||
LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but connection already completed a handshake (double COMMAND_HANDSHAKE?)");
|
||||
drop_connection(context);
|
||||
return 1;
|
||||
}
|
||||
@@ -2919,7 +2951,7 @@ namespace nodetool
|
||||
|
||||
m_network_zones.at(epee::net_utils::zone::public_).m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
|
||||
{
|
||||
if (cntxt.m_is_income && cntxt.m_remote_address.is_same_host(address)) {
|
||||
if (cntxt.m_is_income && is_same_p2p_connection_limit_host(cntxt.m_remote_address, address)) {
|
||||
count++;
|
||||
|
||||
// the only call location happens BEFORE foreach_connection list is updated
|
||||
|
||||
+28
-15
@@ -350,7 +350,7 @@ namespace rct {
|
||||
{
|
||||
ar.tag("pseudoOuts");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts);
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts, sizeof(key));
|
||||
if (pseudoOuts.size() != inputs)
|
||||
return false;
|
||||
for (size_t i = 0; i < inputs; ++i)
|
||||
@@ -364,12 +364,18 @@ namespace rct {
|
||||
|
||||
ar.tag("ecdhInfo");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, ecdhInfo);
|
||||
|
||||
const bool compressed_ecdh =
|
||||
(type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeBulletproofPlus_FullCommit);
|
||||
const std::size_t min_size = compressed_ecdh ? sizeof(crypto::hash8) : sizeof(key) * 2;
|
||||
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, ecdhInfo, min_size);
|
||||
if (ecdhInfo.size() != outputs)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < outputs; ++i)
|
||||
{
|
||||
if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus || type == RCTTypeBulletproofPlus_FullCommit)
|
||||
if (compressed_ecdh)
|
||||
{
|
||||
// Since RCTTypeBulletproof2 enote types, we don't serialize the blinding factor, and only serialize the
|
||||
// first 8 bytes of ecdhInfo[i].amount
|
||||
@@ -395,7 +401,7 @@ namespace rct {
|
||||
|
||||
ar.tag("outPk");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk);
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk, sizeof(key));
|
||||
if (outPk.size() != outputs)
|
||||
return false;
|
||||
for (size_t i = 0; i < outputs; ++i)
|
||||
@@ -444,7 +450,8 @@ namespace rct {
|
||||
{
|
||||
ar.tag("bp");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs);
|
||||
// Wownero legacy per-output bulletproofs: same minimum wire size as a Bulletproof below.
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs, sizeof(key) * 9);
|
||||
if (bulletproofs.size() != outputs)
|
||||
return false;
|
||||
for (size_t i = 0; i < outputs; ++i)
|
||||
@@ -463,7 +470,7 @@ namespace rct {
|
||||
ar.begin_array();
|
||||
if (nbp > outputs)
|
||||
return false;
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs_plus);
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs_plus, sizeof(key) * 6);
|
||||
for (size_t i = 0; i < nbp; ++i)
|
||||
{
|
||||
FIELDS(bulletproofs_plus[i])
|
||||
@@ -485,7 +492,7 @@ namespace rct {
|
||||
ar.begin_array();
|
||||
if (nbp > outputs)
|
||||
return false;
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs);
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs, sizeof(key) * 9);
|
||||
for (size_t i = 0; i < nbp; ++i)
|
||||
{
|
||||
FIELDS(bulletproofs[i])
|
||||
@@ -500,7 +507,7 @@ namespace rct {
|
||||
{
|
||||
ar.tag("rangeSigs");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs);
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs, sizeof(key) * 193);
|
||||
if (rangeSigs.size() != outputs)
|
||||
return false;
|
||||
for (size_t i = 0; i < outputs; ++i)
|
||||
@@ -516,7 +523,7 @@ namespace rct {
|
||||
{
|
||||
ar.tag("CLSAGs");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, CLSAGs);
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, CLSAGs, sizeof(key) * 3);
|
||||
if (CLSAGs.size() != inputs)
|
||||
return false;
|
||||
for (size_t i = 0; i < inputs; ++i)
|
||||
@@ -527,7 +534,7 @@ namespace rct {
|
||||
ar.begin_object();
|
||||
ar.tag("s");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1, CLSAGs[i].s);
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1, CLSAGs[i].s, sizeof(key));
|
||||
if (CLSAGs[i].s.size() != mixin + 1)
|
||||
return false;
|
||||
for (size_t j = 0; j <= mixin; ++j)
|
||||
@@ -559,9 +566,12 @@ namespace rct {
|
||||
// we keep a byte for size of MGs, because we don't know whether this is
|
||||
// a simple or full rct signature, and it's starting to annoy the hell out of me
|
||||
size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeSimpleBulletproof) ? inputs : 1;
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs);
|
||||
|
||||
// Each MGs has `cc` (`key`) AND at least 1 MGs[i].ss which has at least 1 `key`
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs, sizeof(key) * 2);
|
||||
if (MGs.size() != mg_elements)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < mg_elements; ++i)
|
||||
{
|
||||
// we save the MGs contents directly, because we want it to save its
|
||||
@@ -570,14 +580,17 @@ namespace rct {
|
||||
ar.begin_object();
|
||||
ar.tag("ss");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1, MGs[i].ss);
|
||||
|
||||
// each MGs[i].ss has at least one `key`
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1,MGs[i].ss, sizeof(key));
|
||||
if (MGs[i].ss.size() != mixin + 1)
|
||||
return false;
|
||||
|
||||
for (size_t j = 0; j < mixin + 1; ++j)
|
||||
{
|
||||
ar.begin_array();
|
||||
size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeSimpleBulletproof) ? 1 : inputs) + 1;
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]);
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j], sizeof(key));
|
||||
if (MGs[i].ss[j].size() != mg_ss2_elements)
|
||||
return false;
|
||||
for (size_t k = 0; k < mg_ss2_elements; ++k)
|
||||
@@ -607,7 +620,7 @@ namespace rct {
|
||||
{
|
||||
ar.tag("pseudoOuts");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts);
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts, sizeof(key));
|
||||
if (pseudoOuts.size() != inputs)
|
||||
return false;
|
||||
for (size_t i = 0; i < inputs; ++i)
|
||||
@@ -789,7 +802,7 @@ inline std::ostream &operator <<(std::ostream &o, const rct::key &v) {
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash<rct::key> { std::size_t operator()(const rct::key &k) const { return reinterpret_cast<const std::size_t&>(k); } };
|
||||
template<> struct hash<rct::key> { std::size_t operator()(const rct::key &k) const { return ::crypto::siphash_to_size_t(&k, sizeof(k)); } };
|
||||
}
|
||||
|
||||
BLOB_SERIALIZER(rct::key);
|
||||
|
||||
@@ -1574,6 +1574,7 @@ namespace cryptonote
|
||||
|
||||
COMMAND_RPC_GET_PEER_LIST::request peer_list_req;
|
||||
COMMAND_RPC_GET_PEER_LIST::response peer_list_res;
|
||||
peer_list_req.public_only = true;
|
||||
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;
|
||||
@@ -1835,10 +1836,10 @@ namespace cryptonote
|
||||
return 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp)
|
||||
bool core_rpc_server::get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp, bool include_sensitive)
|
||||
{
|
||||
b = boost::value_initialized<cryptonote::block>();
|
||||
if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce, seed_height, seed_hash))
|
||||
if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce, seed_height, seed_hash, include_sensitive))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
error_resp.message = "Internal error: failed to create block template";
|
||||
@@ -1963,7 +1964,8 @@ namespace cryptonote
|
||||
}
|
||||
}
|
||||
crypto::hash seed_hash, next_seed_hash;
|
||||
if (!get_block_template(info.address, req.prev_block.empty() ? NULL : &prev_block, blob_reserve, reserved_offset, wdiff, res.height, res.expected_reward, b, res.seed_height, seed_hash, next_seed_hash, error_resp))
|
||||
const bool include_sensitive = !m_restricted || !ctx;
|
||||
if (!get_block_template(info.address, req.prev_block.empty() ? NULL : &prev_block, blob_reserve, reserved_offset, wdiff, res.height, res.expected_reward, b, res.seed_height, seed_hash, next_seed_hash, error_resp, include_sensitive))
|
||||
return false;
|
||||
if (b.major_version >= RX_BLOCK_VERSION)
|
||||
{
|
||||
@@ -2233,6 +2235,25 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_restricted && ctx)
|
||||
{
|
||||
block parent;
|
||||
if (!m_core.get_block_by_hash(b.prev_id, parent))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED;
|
||||
error_resp.message = "Unknown parent block";
|
||||
return false;
|
||||
}
|
||||
const uint64_t block_height = get_block_height(parent) + 1;
|
||||
const uint64_t chain_height = m_core.get_current_blockchain_height();
|
||||
if (crypto::rx_seedheight(block_height) != crypto::rx_seedheight(chain_height))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED;
|
||||
error_resp.message = "Block is too old for restricted RPC";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
block_verification_context bvc;
|
||||
if(!m_core.handle_block_found(b, bvc))
|
||||
{
|
||||
@@ -3331,6 +3352,7 @@ namespace cryptonote
|
||||
size_t n_txes = m_core.get_pool_transactions_count();
|
||||
CHECK_PAYMENT_MIN1(req, res, COST_PER_TX_POOL_STATS * n_txes, false);
|
||||
|
||||
const bool restricted = m_restricted && ctx;
|
||||
if (!m_core.get_txpool_backlog(res.backlog))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
|
||||
@@ -3338,6 +3360,10 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
if (restricted)
|
||||
for (tx_backlog_entry& entry : res.backlog)
|
||||
entry.time_in_pool = 0;
|
||||
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
@@ -3787,7 +3813,7 @@ namespace cryptonote
|
||||
|
||||
const command_line::arg_descriptor<std::size_t> core_rpc_server::arg_rpc_max_connections_per_public_ip = {
|
||||
"rpc-max-connections-per-public-ip"
|
||||
, "Max RPC connections per public IP permitted"
|
||||
, "Max RPC connections permitted per public IPv4 address or shared across a public IPv6 /64 subnet"
|
||||
, DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP
|
||||
};
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace cryptonote
|
||||
MAP_URI_AUTO_BIN2("/get_outs.bin", on_get_outs_bin, COMMAND_RPC_GET_OUTPUTS_BIN)
|
||||
MAP_URI_AUTO_JON2("/get_transactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS)
|
||||
MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS)
|
||||
MAP_URI_AUTO_JON2("/get_alt_blocks_hashes", on_get_alt_blocks_hashes, COMMAND_RPC_GET_ALT_BLOCKS_HASHES)
|
||||
MAP_URI_AUTO_JON2_IF("/get_alt_blocks_hashes", on_get_alt_blocks_hashes, COMMAND_RPC_GET_ALT_BLOCKS_HASHES, !m_restricted)
|
||||
MAP_URI_AUTO_JON2("/is_key_image_spent", on_is_key_image_spent, COMMAND_RPC_IS_KEY_IMAGE_SPENT)
|
||||
MAP_URI_AUTO_JON2("/send_raw_transaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX)
|
||||
MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX)
|
||||
@@ -123,7 +123,7 @@ namespace cryptonote
|
||||
MAP_URI_AUTO_JON2_IF("/set_log_hash_rate", on_set_log_hash_rate, COMMAND_RPC_SET_LOG_HASH_RATE, !m_restricted)
|
||||
MAP_URI_AUTO_JON2_IF("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL, !m_restricted)
|
||||
MAP_URI_AUTO_JON2_IF("/set_log_categories", on_set_log_categories, COMMAND_RPC_SET_LOG_CATEGORIES, !m_restricted)
|
||||
MAP_URI_AUTO_JON2("/get_transaction_pool", on_get_transaction_pool, COMMAND_RPC_GET_TRANSACTION_POOL)
|
||||
MAP_URI_AUTO_JON2_IF("/get_transaction_pool", on_get_transaction_pool, COMMAND_RPC_GET_TRANSACTION_POOL, !m_restricted)
|
||||
MAP_URI_AUTO_JON2("/get_transaction_pool_hashes.bin", on_get_transaction_pool_hashes_bin, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN)
|
||||
MAP_URI_AUTO_JON2("/get_transaction_pool_hashes", on_get_transaction_pool_hashes, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES)
|
||||
MAP_URI_AUTO_JON2("/get_transaction_pool_stats", on_get_transaction_pool_stats, COMMAND_RPC_GET_TRANSACTION_POOL_STATS)
|
||||
@@ -286,7 +286,7 @@ private:
|
||||
enum invoke_http_mode { JON, BIN, JON_RPC };
|
||||
template <typename COMMAND_TYPE>
|
||||
bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r);
|
||||
bool get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp);
|
||||
bool get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp, bool include_sensitive = true);
|
||||
bool check_payment(const std::string &client, uint64_t payment, const std::string &rpc, bool same_ts, std::string &message, uint64_t &credits, std::string &top_hash);
|
||||
|
||||
core& m_core;
|
||||
|
||||
@@ -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 15
|
||||
#define CORE_RPC_VERSION_MINOR 16
|
||||
#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)
|
||||
|
||||
|
||||
@@ -756,7 +756,7 @@ namespace rpc
|
||||
|
||||
void DaemonHandler::handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res)
|
||||
{
|
||||
bool r = m_core.get_pool_for_rpc(res.transactions, res.key_images);
|
||||
bool r = m_core.get_pool_for_rpc(res.transactions, res.key_images, !m_restricted);
|
||||
|
||||
if (!r) res.status = Message::STATUS_FAILED;
|
||||
else res.status = Message::STATUS_OK;
|
||||
|
||||
+23
-1
@@ -42,6 +42,7 @@ const char* Message::STATUS_RETRY = "Retry";
|
||||
const char* Message::STATUS_FAILED = "Failed";
|
||||
const char* Message::STATUS_BAD_REQUEST = "Invalid request type";
|
||||
const char* Message::STATUS_BAD_JSON = "Malformed json";
|
||||
const char* Message::STATUS_REQUEST_TOO_LARGE = "Request too large";
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -62,6 +63,17 @@ const rapidjson::Value& get_method_field(const rapidjson::Value& src)
|
||||
}
|
||||
}
|
||||
|
||||
void validate_id_field(const rapidjson::Value& src)
|
||||
{
|
||||
const auto member = src.FindMember(id_field);
|
||||
if (member == src.MemberEnd())
|
||||
return;
|
||||
|
||||
// If present, JSON-RPC 2.0 request ids must be String, Number, or Null.
|
||||
if (!member->value.IsString() && !member->value.IsNumber() && !member->value.IsNull())
|
||||
throw cryptonote::json::WRONG_TYPE{"Expected string, number or null"};
|
||||
}
|
||||
|
||||
void Message::toJson(rapidjson::Writer<epee::byte_stream>& dest) const
|
||||
{
|
||||
dest.StartObject();
|
||||
@@ -80,7 +92,7 @@ FullMessage::FullMessage(std::string&& json_string, bool request)
|
||||
{
|
||||
/* Insitu parsing does not copy data from `contents` to DOM,
|
||||
accelerating string heavy content. */
|
||||
doc.ParseInsitu(std::addressof(contents[0]));
|
||||
doc.ParseInsitu<rapidjson::kParseIterativeFlag>(std::addressof(contents[0]));
|
||||
if (doc.HasParseError() || !doc.IsObject())
|
||||
{
|
||||
throw cryptonote::json::PARSE_FAIL();
|
||||
@@ -92,6 +104,7 @@ FullMessage::FullMessage(std::string&& json_string, bool request)
|
||||
{
|
||||
get_method_field(doc); // throws on errors
|
||||
OBJECT_HAS_MEMBER_OR_THROW(doc, params_field)
|
||||
validate_id_field(doc);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -233,6 +246,15 @@ epee::byte_slice BAD_JSON(const std::string& error_details)
|
||||
return FullMessage::getResponse(fail, invalid);
|
||||
}
|
||||
|
||||
epee::byte_slice REQUEST_TOO_LARGE()
|
||||
{
|
||||
rapidjson::Value invalid;
|
||||
Message fail;
|
||||
fail.status = Message::STATUS_REQUEST_TOO_LARGE;
|
||||
fail.error_details = "Request exceeds maximum message size.";
|
||||
return FullMessage::getResponse(fail, invalid);
|
||||
}
|
||||
|
||||
|
||||
} // namespace rpc
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user