aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml28
-rw-r--r--.github/workflows/depends.yml10
-rw-r--r--.github/workflows/gitian.yml2
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt2
-rw-r--r--README.md8
-rw-r--r--cmake/FindMiniupnpc.cmake59
-rw-r--r--contrib/brew/Brewfile1
-rw-r--r--contrib/epee/include/misc_log_ex.h11
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.inl17
-rw-r--r--docs/proxies.md205
-rw-r--r--external/CMakeLists.txt26
m---------external/miniupnp0
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/blockchain_utilities/blockchain_ancestry.cpp5
-rw-r--r--src/blockchain_utilities/blockchain_depth.cpp5
-rw-r--r--src/blockchain_utilities/blockchain_export.cpp11
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp7
-rw-r--r--src/blockchain_utilities/blockchain_prune.cpp5
-rw-r--r--src/blockchain_utilities/blockchain_prune_known_spent_data.cpp5
-rw-r--r--src/blockchain_utilities/blockchain_stats.cpp5
-rw-r--r--src/blockchain_utilities/blockchain_usage.cpp5
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp10
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h2
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp33
-rw-r--r--src/cryptonote_core/cryptonote_core.h11
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp44
-rw-r--r--src/cryptonote_core/tx_pool.cpp8
-rw-r--r--src/cryptonote_core/tx_pool.h5
-rw-r--r--src/cryptonote_protocol/block_queue.cpp6
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl51
-rw-r--r--src/daemon/command_line_args.h4
-rw-r--r--src/daemon/main.cpp6
-rw-r--r--src/device_trezor/device_trezor_base.hpp3
-rw-r--r--src/device_trezor/trezor/debug_link.hpp3
-rw-r--r--src/device_trezor/trezor/messages_map.hpp6
-rw-r--r--src/device_trezor/trezor/transport.hpp2
-rw-r--r--src/net/error.cpp6
-rw-r--r--src/net/error.h3
-rw-r--r--src/net/fwd.h6
-rw-r--r--src/net/http.cpp8
-rw-r--r--src/net/parse.cpp135
-rw-r--r--src/net/parse.h79
-rw-r--r--src/net/socks.cpp515
-rw-r--r--src/net/socks.h62
-rw-r--r--src/net/socks_connect.cpp26
-rw-r--r--src/net/socks_connect.h5
-rw-r--r--src/p2p/CMakeLists.txt1
-rw-r--r--src/p2p/net_node.cpp43
-rw-r--r--src/p2p/net_node.h24
-rw-r--r--src/p2p/net_node.inl206
-rw-r--r--src/rpc/core_rpc_server.cpp11
-rw-r--r--src/rpc/daemon_handler.cpp80
-rw-r--r--src/serialization/container.h34
-rw-r--r--src/serialization/containers.h1
-rw-r--r--src/wallet/wallet2.cpp126
-rw-r--r--src/wallet/wallet2.h4
-rw-r--r--src/wallet/wallet_rpc_server.cpp286
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h2
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/core_tests/bulletproof_plus.cpp2
-rw-r--r--tests/core_tests/bulletproofs.cpp2
-rw-r--r--tests/core_tests/rct.cpp4
-rw-r--r--tests/core_tests/rct2.cpp2
-rwxr-xr-xtests/functional_tests/functional_tests_rpc.py2
-rw-r--r--tests/performance_tests/check_tx_signature.h4
-rw-r--r--tests/performance_tests/construct_tx.h2
-rw-r--r--tests/trezor/daemon.cpp1
-rw-r--r--tests/unit_tests/net.cpp525
-rw-r--r--tests/unit_tests/node_server.cpp105
-rw-r--r--tests/unit_tests/serialization.cpp14
-rw-r--r--utils/fish/monero-wallet-rpc.fish2
-rw-r--r--utils/fish/monerod.fish2
74 files changed, 2178 insertions, 772 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 489d0ff78..7132adc9c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -14,7 +14,7 @@ on:
env:
# ARCH="default" (not "native") ensures, that a different execution host can execute binaries compiled elsewhere.
BUILD_DEFAULT_LINUX: 'cmake -S . -B build -D ARCH="default" -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Release && cmake --build build --target all && cmake --build build --target wallet_api'
- APT_INSTALL_LINUX: 'apt -y install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler ccache git'
+ APT_INSTALL_LINUX: 'apt -y install build-essential cmake libboost-all-dev libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler ccache git'
APT_SET_CONF: |
tee -a /etc/apt/apt.conf.d/80-custom << EOF
Acquire::Retries "3";
@@ -32,10 +32,10 @@ jobs:
env:
CCACHE_TEMPDIR: /tmp/.ccache-temp
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
submodules: recursive
- - uses: actions/cache@v4
+ - uses: actions/cache@v5
with:
path: /Users/runner/Library/Caches/ccache
key: ccache-${{ runner.os }}-build-${{ github.sha }}
@@ -45,7 +45,7 @@ jobs:
run: |
brew uninstall cmake
brew update
- brew install --quiet cmake boost hidapi openssl zmq libpgm miniupnpc expat libunwind-headers protobuf ccache
+ brew install --quiet cmake boost hidapi openssl zmq libpgm expat libunwind-headers protobuf ccache
- name: build
run: |
${{env.CCACHE_SETTINGS}}
@@ -61,10 +61,10 @@ jobs:
run:
shell: msys2 {0}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
submodules: recursive
- - uses: actions/cache@v4
+ - uses: actions/cache@v5
with:
path: C:\Users\runneradmin\.ccache
key: ccache-${{ runner.os }}-build-${{ github.sha }}
@@ -89,7 +89,7 @@ jobs:
run: pacman -Syyu --noconfirm base-devel git cmake boost boost-libs openssl zeromq unbound libsodium readline expat gtest python3 doxygen graphviz hidapi libusb protobuf
- name: configure git
run: git config --global --add safe.directory '*'
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
submodules: recursive
- uses: ./.github/actions/set-make-job-count
@@ -115,7 +115,7 @@ jobs:
run: ${{env.APT_INSTALL_LINUX}}
- name: configure git
run: git config --global --add safe.directory '*'
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
submodules: recursive
- uses: ./.github/actions/set-make-job-count
@@ -153,10 +153,10 @@ jobs:
run: ${{env.APT_INSTALL_LINUX}}
- name: configure git
run: git config --global --add safe.directory '*'
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
submodules: recursive
- - uses: actions/cache@v4
+ - uses: actions/cache@v5
with:
path: ~/.ccache
key: ccache-${{ matrix.container }}-build-${{ github.sha }}
@@ -200,10 +200,10 @@ jobs:
run: pip install requests psutil monotonic zmq deepdiff
- name: configure git
run: git config --global --add safe.directory '*'
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
submodules: recursive
- - uses: actions/cache@v4
+ - uses: actions/cache@v5
with:
path: ~/.ccache
key: ccache-${{ matrix.container }}-build-${{ github.sha }}
@@ -237,7 +237,7 @@ jobs:
run: apt install -y git python3-pip
- name: configure git
run: git config --global --add safe.directory '*'
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
submodules: recursive
@@ -248,7 +248,7 @@ jobs:
export OUTPUT="$VERSION.tar"
echo "OUTPUT=$OUTPUT" >> $GITHUB_ENV
git-archive-all --prefix "$VERSION/" --force-submodules "$OUTPUT"
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@v7
with:
name: ${{ env.OUTPUT }}
path: ${{ env.OUTPUT }}
diff --git a/.github/workflows/depends.yml b/.github/workflows/depends.yml
index c754260b2..2be2a60e5 100644
--- a/.github/workflows/depends.yml
+++ b/.github/workflows/depends.yml
@@ -68,20 +68,20 @@ jobs:
run: apt update; apt -y install build-essential libtool cmake autotools-dev automake pkg-config python3 gperf bsdmainutils curl git ca-certificates unzip ccache ${{ matrix.toolchain.packages }}
- name: configure git
run: git config --global --add safe.directory '*'
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
submodules: recursive
# Most volatile cache
- name: ccache
- uses: actions/cache@v4
+ uses: actions/cache@v5
with:
path: ~/.ccache
key: ccache-${{ matrix.toolchain.host }}-${{ github.sha }}
restore-keys: ccache-${{ matrix.toolchain.host }}-
# Less volatile cache
- name: depends cache
- uses: actions/cache@v4
+ uses: actions/cache@v5
with:
path: contrib/depends/built
key: depends-${{ matrix.toolchain.host }}-${{ hashFiles('contrib/depends/packages/*') }}
@@ -90,7 +90,7 @@ jobs:
depends-${{ matrix.toolchain.host }}-
# Static cache
- name: OSX SDK cache
- uses: actions/cache@v4
+ uses: actions/cache@v5
with:
path: contrib/depends/sdk-sources
key: sdk-${{ matrix.toolchain.host }}-${{ matrix.toolchain.osx_sdk }}
@@ -104,7 +104,7 @@ jobs:
run: |
${{env.CCACHE_SETTINGS}}
make depends target=${{ matrix.toolchain.host }} -j4
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@v7
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin11' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }}
with:
name: ${{ matrix.toolchain.name }}
diff --git a/.github/workflows/gitian.yml b/.github/workflows/gitian.yml
index 46b23188f..2d2af5799 100644
--- a/.github/workflows/gitian.yml
+++ b/.github/workflows/gitian.yml
@@ -42,7 +42,7 @@ jobs:
echo \`\`\` >> $GITHUB_STEP_SUMMARY
shasum -a256 * >> $GITHUB_STEP_SUMMARY
echo \`\`\` >> $GITHUB_STEP_SUMMARY
- - uses: actions/upload-artifact@v4
+ - uses: actions/upload-artifact@v7
with:
name: ${{ matrix.operating-system.name }}
path: |
diff --git a/.gitignore b/.gitignore
index 9f62575e5..5931d64a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,8 +20,6 @@ cscope.out
cscope.in.out
cscope.po.out
-external/miniupnpc/Makefile
-miniupnpcstrings.h
version/
ClangBuildAnalyzerSession.txt
diff --git a/.gitmodules b/.gitmodules
index 721cce3b4..31c6921e5 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "external/miniupnp"]
- path = external/miniupnp
- url = https://github.com/miniupnp/miniupnp
[submodule "external/rapidjson"]
path = external/rapidjson
url = https://github.com/Tencent/rapidjson
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e4fe53d1d..001fd0220 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -369,7 +369,6 @@ if(NOT MANUAL_SUBMODULES)
endfunction ()
message(STATUS "Checking submodules")
- check_submodule(external/miniupnp)
check_submodule(external/rapidjson)
check_submodule(external/trezor-common)
check_submodule(external/randomx)
@@ -440,7 +439,6 @@ else()
endif()
message(STATUS "Building for a ${ARCH_WIDTH}-bit system")
-# Check if we're on FreeBSD so we can exclude the local miniupnpc (it should be installed from ports instead)
# CMAKE_SYSTEM_NAME checks are commonly known, but specifically taken from libsdl's CMakeLists
if(CMAKE_SYSTEM_NAME MATCHES "kFreeBSD.*|FreeBSD")
set(FREEBSD TRUE)
diff --git a/README.md b/README.md
index b8eb2a99f..5c0c55c3e 100644
--- a/README.md
+++ b/README.md
@@ -220,7 +220,7 @@ sudo dnf install gcc gcc-c++ cmake pkgconf boost-devel openssl-devel zeromq-deve
Install all dependencies at once on openSUSE:
```
-sudo zypper ref && sudo zypper in cppzmq-devel libboost_chrono-devel libboost_date_time-devel libboost_filesystem-devel libboost_locale-devel libboost_program_options-devel libboost_regex-devel libboost_serialization-devel libboost_system-devel libboost_thread-devel libexpat-devel libminiupnpc-devel libsodium-devel libunwind-devel unbound-devel cmake doxygen ccache fdupes gcc-c++ libevent-devel libopenssl-devel pkgconf-pkg-config readline-devel xz-devel libqt5-qttools-devel patterns-devel-C-C++-devel_C_C++
+sudo zypper ref && sudo zypper in cppzmq-devel libboost_chrono-devel libboost_date_time-devel libboost_filesystem-devel libboost_locale-devel libboost_program_options-devel libboost_regex-devel libboost_serialization-devel libboost_system-devel libboost_thread-devel libexpat-devel libsodium-devel libunwind-devel unbound-devel cmake doxygen ccache fdupes gcc-c++ libevent-devel libopenssl-devel pkgconf-pkg-config readline-devel xz-devel libqt5-qttools-devel patterns-devel-C-C++-devel_C_C++
```
Install all dependencies at once on macOS with the provided Brewfile:
@@ -708,8 +708,6 @@ setting the following configuration parameters and environment variables:
* `--p2p-bind-ip 127.0.0.1` on the command line or `p2p-bind-ip=127.0.0.1` in
monerod.conf to disable listening for connections on external interfaces.
-* `--no-igd` on the command line or `no-igd=1` in monerod.conf to disable IGD
- (UPnP port forwarding negotiation), which is pointless with Tor.
* `DNS_PUBLIC=tcp` or `DNS_PUBLIC=tcp://x.x.x.x` where x.x.x.x is the IP of the
desired DNS server, for DNS requests to go over TCP, so that they are routed
through Tor. When IP is not specified, monerod uses the default list of
@@ -728,7 +726,7 @@ setting the following configuration parameters and environment variables:
Example command line to start monerod through Tor:
```bash
-DNS_PUBLIC=tcp torsocks monerod --p2p-bind-ip 127.0.0.1 --no-igd
+DNS_PUBLIC=tcp torsocks monerod --p2p-bind-ip 127.0.0.1
```
A helper script is in contrib/tor/monero-over-tor.sh. It assumes Tor is installed
@@ -742,7 +740,7 @@ allow inbound connections. Full example:
```bash
sudo iptables -I OUTPUT 2 -p tcp -d 127.0.0.1 -m tcp --dport 18081 -j ACCEPT
-DNS_PUBLIC=tcp torsocks ./monerod --p2p-bind-ip 127.0.0.1 --no-igd --rpc-bind-ip 127.0.0.1 \
+DNS_PUBLIC=tcp torsocks ./monerod --p2p-bind-ip 127.0.0.1 --rpc-bind-ip 127.0.0.1 \
--data-dir /home/amnesia/Persistent/your/directory/to/the/blockchain
```
diff --git a/cmake/FindMiniupnpc.cmake b/cmake/FindMiniupnpc.cmake
deleted file mode 100644
index ad2004afc..000000000
--- a/cmake/FindMiniupnpc.cmake
+++ /dev/null
@@ -1,59 +0,0 @@
-# --------------------------------- FindMiniupnpc Start ---------------------------------
-# Locate miniupnp library
-# This module defines
-# MINIUPNP_FOUND, if false, do not try to link to miniupnp
-# MINIUPNP_LIBRARY, the miniupnp variant
-# MINIUPNP_INCLUDE_DIR, where to find miniupnpc.h and family)
-# MINIUPNPC_VERSION_1_7_OR_HIGHER, set if we detect the version of miniupnpc is 1.7 or higher
-#
-# Note that the expected include convention is
-# #include "miniupnpc.h"
-# and not
-# #include <miniupnpc/miniupnpc.h>
-# This is because, the miniupnpc location is not standardized and may exist
-# in locations other than miniupnpc/
-
-if (MINIUPNP_INCLUDE_DIR AND MINIUPNP_LIBRARY)
- # Already in cache, be silent
- set(MINIUPNP_FIND_QUIETLY TRUE)
-endif ()
-
-find_path(MINIUPNP_INCLUDE_DIR miniupnpc.h
- HINTS $ENV{MINIUPNP_INCLUDE_DIR}
- PATH_SUFFIXES miniupnpc
-)
-
-find_library(MINIUPNP_LIBRARY miniupnpc
- HINTS $ENV{MINIUPNP_LIBRARY}
-)
-
-find_library(MINIUPNP_STATIC_LIBRARY libminiupnpc.a
- HINTS $ENV{MINIUPNP_STATIC_LIBRARY}
-)
-
-set(MINIUPNP_INCLUDE_DIRS ${MINIUPNP_INCLUDE_DIR})
-set(MINIUPNP_LIBRARIES ${MINIUPNP_LIBRARY})
-set(MINIUPNP_STATIC_LIBRARIES ${MINIUPNP_STATIC_LIBRARY})
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(
- MiniUPnPc DEFAULT_MSG
- MINIUPNP_INCLUDE_DIR
- MINIUPNP_LIBRARY
-)
-
-IF(MINIUPNPC_FOUND)
- file(STRINGS "${MINIUPNP_INCLUDE_DIR}/miniupnpc.h" MINIUPNPC_API_VERSION_STR REGEX "^#define[\t ]+MINIUPNPC_API_VERSION[\t ]+[0-9]+")
- if(MINIUPNPC_API_VERSION_STR MATCHES "^#define[\t ]+MINIUPNPC_API_VERSION[\t ]+([0-9]+)")
- set(MINIUPNPC_API_VERSION "${CMAKE_MATCH_1}")
- if (${MINIUPNPC_API_VERSION} GREATER "10" OR ${MINIUPNPC_API_VERSION} EQUAL "10")
- message(STATUS "Found miniupnpc API version " ${MINIUPNPC_API_VERSION})
- set(MINIUPNP_FOUND true)
- set(MINIUPNPC_VERSION_1_7_OR_HIGHER true)
- endif()
- endif()
-
-ENDIF()
-
-mark_as_advanced(MINIUPNP_INCLUDE_DIR MINIUPNP_LIBRARY MINIUPNP_STATIC_LIBRARY)
-# --------------------------------- FindMiniupnpc End ---------------------------------
diff --git a/contrib/brew/Brewfile b/contrib/brew/Brewfile
index c74e7b2a2..3697898f5 100644
--- a/contrib/brew/Brewfile
+++ b/contrib/brew/Brewfile
@@ -23,7 +23,6 @@ brew "zmq"
brew "libpgm"
brew "unbound"
brew "libsodium"
-brew "miniupnpc"
brew "readline"
brew "expat"
brew "ccache"
diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h
index 701f8f102..7350d84f4 100644
--- a/contrib/epee/include/misc_log_ex.h
+++ b/contrib/epee/include/misc_log_ex.h
@@ -172,6 +172,17 @@ namespace debug
return return_val; \
}
+#define CATCH_ENTRY_SWALLOW_EX(location) } \
+ catch(const std::exception& ex) \
+{ \
+ (void)(ex); \
+ LOG_ERROR("Exception at [" << location << "], what=" << ex.what()); \
+}\
+ catch(...)\
+{\
+ LOG_ERROR("Exception at [" << location << "], generic exception \"...\"");\
+}
+
#define CATCH_ENTRY_L0(lacation, return_val) CATCH_ENTRY(lacation, return_val)
#define CATCH_ENTRY_L1(lacation, return_val) CATCH_ENTRY(lacation, return_val)
#define CATCH_ENTRY_L2(lacation, return_val) CATCH_ENTRY(lacation, return_val)
diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl
index 60cc1b55f..e202d9152 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.inl
+++ b/contrib/epee/include/net/abstract_tcp_server2.inl
@@ -423,10 +423,13 @@ namespace net_utils
boost::asio::post(
connection_basic::strand_,
[this, self, bytes_transferred]{
- bool success = m_handler.handle_recv(
+ bool success = false;
+ TRY_ENTRY();
+ success = m_handler.handle_recv(
reinterpret_cast<char *>(m_state.data.read.buffer.data()),
bytes_transferred
);
+ CATCH_ENTRY_SWALLOW_EX("m_handler.handle_recv");
std::lock_guard<std::mutex> guard(m_state.lock);
const bool error_status = m_state.status == status_t::INTERRUPTED
|| m_state.status == status_t::TERMINATING
@@ -1150,17 +1153,21 @@ namespace net_utils
// execute terminate inside m_strand. So we wait for the connection's shutdown sequence to complete before stopping
// the io_context.
MDEBUG("Waiting for connection " << m_conn_context.m_connection_id << " to shutdown, current state: " << m_state.status);
- m_state.condition.wait(
+ const bool shutdown = m_state.condition.wait_for(
m_state.lock,
+ std::chrono::seconds(5),
[this]{
return (
m_state.status == status_t::TERMINATED || m_state.status == status_t::WASTED
);
}
);
- MDEBUG("Shut down connection " << m_conn_context.m_connection_id);
+ if (shutdown)
+ MDEBUG("Shut down connection " << m_conn_context.m_connection_id);
+ else
+ MERROR("Connection " << m_conn_context.m_connection_id << " did not shut down");
- return true;
+ return shutdown;
}
template<typename T>
@@ -1186,7 +1193,9 @@ namespace net_utils
auto self = connection<T>::shared_from_this();
++m_state.protocol.wait_callback;
boost::asio::post(connection_basic::strand_, [this, self]{
+ TRY_ENTRY();
m_handler.handle_qued_callback();
+ CATCH_ENTRY_SWALLOW_EX("m_handler.handle_qued_callback");
std::lock_guard<std::mutex> guard(m_state.lock);
--m_state.protocol.wait_callback;
if (m_state.status == status_t::INTERRUPTED)
diff --git a/docs/proxies.md b/docs/proxies.md
new file mode 100644
index 000000000..c9f21ccf3
--- /dev/null
+++ b/docs/proxies.md
@@ -0,0 +1,205 @@
+# Proxy usage in the Monero ecosystem
+The CLI/RPC wallets and daemon both support proxies and use the same parameters
+to configure them. Currently socks 4, 4a, and 5 are supported and can be
+selected with command-line options.
+
+## Wallet
+The CLI and RPC wallets support proxies via the `--proxy` option. The format
+for usage is `[socks5://[user:pass]]host:port`. The square brackets indicate
+an optional portion. This option can only be specified once. Examples:
+
+```
+--proxy 192.168.0.10:1050
+--proxy socks5://192.168.0.10:1050
+--proxy socks5://username:password@192.168.0.10:1050
+--proxy [::1]:1050
+--proxy socks5://[::1]:1050
+--proxy socks5://username:password@[::1]:1050
+```
+
+The first connects to `192.168.0.10` on port `1050` using socks 4a. The second
+connects to the same location using socks 5. The third uses socks 5 at the same
+location and sends user authentication if prompted by the proxy server. The
+last three are identical to the first 3, except an IPv6 address is used
+instead. While IPv6 connections are invalid for Socks 4 and 4a, the proxy
+server itself can be connected using IPv6.
+
+The username and password fields both support "percent-encoding" for special
+character support. As an example, `%40` gets converted to `@`, such that
+`username:p%40ssword` gets converted to `username:p@ssword`. This allows that
+specific character to be used; specifying the character directly will
+incorrectly change the specification of the hostname.
+
+> NOTE: The username+password will show up in the process list and can be read
+> by other programs. It is recommended that `--config-file` be used to store
+> username+password options. The format for a config file is `option=value`,
+> so in this example the file would contain:
+> `proxy=socks5://username:password@192.168.0.10:1080`.
+
+The CLI and RPC wallets currently reject hosts that do **NOT** end in`.onion`
+or `.i2p` **unless** `--daemon-ssl-ca-certificates`,
+`--daemon-ssl-allow-any-cert`, or `--daemon-ssl-allowed-fingerprints` is used.
+If an onion or i2p address is used, the hostname contains the certificate
+verification, providing decent security against man-in-the-middle (MitM)
+attacks. The two `--daemon-ssl-*` options support specifying exact
+certificates, also preventing MitM attacks.
+
+> Perhaps the wallets should be relaxed to allow system-CA checks, but for now
+> certificates must be strictly provided.
+
+## Daemon
+The daemon has two options for proxies `--proxy` and `--tx-proxy` which can be
+used in isolation or together. The `--proxy` option controls how
+IPv4/IPv6/hostname connections are performed, whereas `--tx-proxy` controls
+how local transactions are relayed. Both options support Socks 4, 4a, and 5.
+
+### `--proxy`
+This option should be used when outbound connections to IPv4/IPv6 addresses and
+hostnames (other than `.onion` `.i2p`) need to be proxied. Common examples
+include using Tor exit nodes or a VPN to conceal your local IP. This option
+will **not** use Tor or I2P hidden services for P2P connections; this is
+primarily used for proxying standard IPv4 or IPv6 connections to some remote
+host. Hidden services are not used because this is designed to be more general
+purpose (i.e. a standard socks VPN can be used).
+
+> An additional option for hidden services (separate from `--tx-proxy`) could
+> arguably be added, which could optionally turn off IPv4/IPv6 connections for
+> P2P.
+
+The format for `--proxy` usage: `[socks5://[user:pass]]@127.0.0.1`. The square
+bracket indicate optional portion. See [wallet](#wallet) section above for
+examples and other information on the format. The option can only be specified
+once. The restrictions for MitM attacks apply only to the wallet usage, and not
+to the daemon.
+
+> When using `--proxy`, inbound connections will be impossible unless the
+> proxy server is somehow setup to forward connections. This setup is a
+> difficult because each outgoing socks connections can have a unique binding
+> port. Such a setup is currently out-of-scope for this document.
+
+### `--tx-proxy`
+This option should be used to specify a proxy that can resolve hidden service
+hostnames, so that local transactions can be forwarded over a privacy
+preserving network. Currently only Tor or I2P hidden services are supported.
+This option be specified multiple times, but only once per network (see below).
+
+The format for `--tx-proxy` is
+`network,[socks5://[user:pass@]]ip:port[,max_connections][,disable_noise]`.
+Examples:
+
+```
+--tx-proxy tor,127.0.0.1:1050
+--tx-proxy tor,127.0.0.1:1050,100
+--tx-proxy tor,127.0.0.1:1050,disable_noise
+--tx-proxy tor,127.0.0.1:1050,100,disable_noise
+--tx-proxy tor,socks5://127.0.0.1:1050
+--tx-proxy tor,socks5://127.0.0.1:1050,100
+--tx-proxy tor,socks5://127.0.0.1:1050,disable_noise
+--tx-proxy tor,socks5://127.0.0.1:1050,100,disable_noise
+--tx-proxy tor,socks5://username:password@127.0.0.1:1050
+--tx-proxy tor,socks5://username:password@127.0.0.1:1050,100
+--tx-proxy tor,socks5://username:password@127.0.0.1:1050,disable_noise
+--tx-proxy tor,socks5://username:password@127.0.0.1:1050,100,disable_noise
+--tx-proxy tor,[::1]:1050
+--tx-proxy tor,[::1]:1050,100
+--tx-proxy tor,[::1]:1050,disable_noise
+--tx-proxy tor,[::1]:1050,100,disable_noise
+--tx-proxy tor,socks5://[::1]:1050
+--tx-proxy tor,socks5://[::1]:1050,100
+--tx-proxy tor,socks5://[::1]:1050,disable_noise
+--tx-proxy tor,socks5://[::1]:1050,100,disable_noise
+--tx-proxy tor,socks5://username:password@[::1]:1050
+--tx-proxy tor,socks5://username:password@[::1]:1050,100
+--tx-proxy tor,socks5://username:password@[::1]:1050,disable_noise
+--tx-proxy tor,socks5://username:password@[::1]:1050,100,disable_noise
+--tx-proxy i2p,127.0.0.1:1050
+--tx-proxy i2p,127.0.0.1:1050,100
+--tx-proxy i2p,127.0.0.1:1050,disable_noise
+--tx-proxy i2p,127.0.0.1:1050,100,disable_noise
+--tx-proxy i2p,socks5://127.0.0.1:1050
+--tx-proxy i2p,socks5://127.0.0.1:1050,100
+--tx-proxy i2p,socks5://127.0.0.1:1050,disable_noise
+--tx-proxy i2p,socks5://127.0.0.1:1050,100,disable_noise
+--tx-proxy i2p,socks5://username:password@127.0.0.1:1050
+--tx-proxy i2p,socks5://username:password@127.0.0.1:1050,100
+--tx-proxy i2p,socks5://username:password@127.0.0.1:1050,disable_noise
+--tx-proxy i2p,socks5://username:password@127.0.0.1:1050,100,disable_noise
+--tx-proxy i2p,[::1]:1050
+--tx-proxy i2p,[::1]:1050,100
+--tx-proxy i2p,[::1]:1050,disable_noise
+--tx-proxy i2p,[::1]:1050,100,disable_noise
+--tx-proxy i2p,socks5://[::1]:1050
+--tx-proxy i2p,socks5://[::1]:1050,100
+--tx-proxy i2p,socks5://[::1]:1050,disable_noise
+--tx-proxy i2p,socks5://[::1]:1050,100,disable_noise
+--tx-proxy i2p,socks5://username:password@[::1]:1050
+--tx-proxy i2p,socks5://username:password@[::1]:1050,100
+--tx-proxy i2p,socks5://username:password@[::1]:1050,disable_noise
+--tx-proxy i2p,socks5://username:password@[::1]:1050,100,disable_noise
+```
+
+The above examples are fairly exhaustive of all the possible option scenarios
+that will be incurred by the typical user.
+
+#### The `network` portion of the option
+The first section (before the first `,`) indicates the network - only `tor` or
+`i2p` are valid here.
+
+This portion of the option tells `--add-node`, `--add-priority-node`, and
+`--add-exclusive-node` options to use the specified proxy for those nodes. In
+other words, command-line specified hidden services are forwarded to their
+corresponding `--tx-proxy` server. Hidden services do **NOT** have to be
+specified on the command-line, there are built-in seed nodes for each network.
+
+#### The `ip:port` portion of the option
+The second portion of the option (after the first `,` and _optionally_ ending
+in the next `,`) indicates the location of the socks server. The location
+**must** include an IPv4/IPv6 AND port. The location can optionally include the
+socks version - `socks4`, `socks4a`, and `socks5` are all valid here. If
+the socks version is not specified, `socks4a` is assumed.
+
+An optional username and password can also be included. These fields support
+percent-encoding, see [wallet](#wallet) section for more information.
+
+#### The last portion of the option
+After the ip:port section two options can be specified: the number of max
+connections and `disable_noise`. They can be specified in either order, but
+must be after the ip:port section.
+
+The max connections does exactly as advertised, it limits the number of
+outgoing connections to the proxy. The `disable_noise` feature lowers the
+bandwidth requirements, and decreases the tx-relay time. When **NOT**
+specified, dummy P2P packets are sent periodically to connections (via the
+proxy) to conceal when a transaction is forwarded over the connection. When
+the option is specified, P2P links only send data for peerlist information and
+local outgoing transactions.
+
+### `--anonymous-inbound`
+Currently the daemon cannot configure incoming hidden services connections.
+Instead, the user must manually configure Tor or I2P to accept inbound
+connections. Then, `--anonymous-inbound` must be used to tell the daemon where
+to listen for incoming connections, and the incoming hidden service address.
+The option can be specified once for each network type. The format for usage
+is: `hidden-service-address,[bind-ip:]port[,max_connections]`. Examples:
+
+```
+--anonymous-inbound rveahdfho7wo4b2m.onion:18083,18083
+--anonymous-inbound rveahdfho7wo4b2m.onion:18083,18083,100
+--anonymous-inbound rveahdfho7wo4b2m.onion:18083,127.0.0.1:18083
+--anonymous-inbound rveahdfho7wo4b2m.onion:18083,127.0.0.1:18083,100
+--anonymous-inbound udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p,18083
+--anonymous-inbound udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p,18083,100
+--anonymous-inbound udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p,127.0.0.1:18083
+--anonymous-inbound udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p,127.0.0.1:18083,100
+```
+
+Everything before the first `,` is the hidden service hostname. This must be
+a valid Tor or I2P address. This tells the daemon the **inbound** hidden
+service as configured for the local Tor or I2P daemons.
+
+Everything between `,`s specify the bind ip and bind port. The IP address is
+optional, and defaults to `127.0.0.1`. The Tor and I2P daemons must be
+configured to forward incoming hidden service connections to this IP/Port pair.
+
+Everything after the second `,` is used to specify the number of max inbound
+connections. The field is optional.
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index 538e4d215..5bdd5d9b4 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -28,32 +28,6 @@
#
# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-
-# This is broken up into two parts: first we check for miniupnp, compile it if we can't
-# find it, and thereafter we check for libunbound, and compile it if we can't find it.
-# We always compile if we are building statically to reduce static dependency issues...
-# ...except for FreeBSD, because FreeBSD is a special case that doesn't play well with
-# others.
-
-find_package(Miniupnpc REQUIRED)
-
-message(STATUS "Using in-tree miniupnpc")
-set(UPNPC_NO_INSTALL TRUE CACHE BOOL "Disable miniupnp installation" FORCE)
-set(UPNPC_BUILD_SHARED OFF CACHE BOOL "Disable building shared library" FORCE)
-add_subdirectory(miniupnp/miniupnpc)
-set_property(TARGET libminiupnpc-static PROPERTY FOLDER "external")
-set_property(TARGET libminiupnpc-static PROPERTY POSITION_INDEPENDENT_CODE ON)
-if(MSVC)
- set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4244 -wd4267")
-elseif(NOT MSVC)
- set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value")
-endif()
-if(CMAKE_SYSTEM_NAME MATCHES "NetBSD")
- set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -D_NETBSD_SOURCE")
-endif()
-
-set(UPNP_LIBRARIES "libminiupnpc-static" PARENT_SCOPE)
-
find_package(Unbound)
if(NOT UNBOUND_INCLUDE_DIR)
diff --git a/external/miniupnp b/external/miniupnp
deleted file mode 160000
-Subproject 544e6fcc73c5ad9af48a8985c94f0f1d742ef2e
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3335d3c21..9973d4027 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -30,8 +30,6 @@
if (WIN32 OR STATIC)
add_definitions(-DSTATICLIB)
- # miniupnp changed their static define
- add_definitions(-DMINIUPNP_STATICLIB)
endif ()
function (monero_private_headers group)
diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp
index b0964e4a3..9de1e4420 100644
--- a/src/blockchain_utilities/blockchain_ancestry.cpp
+++ b/src/blockchain_utilities/blockchain_ancestry.cpp
@@ -358,6 +358,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
+ command_line::add_arg(desc_cmd_sett, cryptonote::arg_regtest_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
command_line::add_arg(desc_cmd_sett, arg_txid);
command_line::add_arg(desc_cmd_sett, arg_output);
@@ -400,9 +401,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Starting...");
std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
- bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
- bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
- network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
+ const network_type net_type = core::get_network_type_from_args(vm);
std::string opt_txid_string = command_line::get_arg(vm, arg_txid);
std::string opt_output_string = command_line::get_arg(vm, arg_output);
uint64_t opt_height = command_line::get_arg(vm, arg_height);
diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp
index b98a1f8e2..0ea6d7df1 100644
--- a/src/blockchain_utilities/blockchain_depth.cpp
+++ b/src/blockchain_utilities/blockchain_depth.cpp
@@ -66,6 +66,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
+ command_line::add_arg(desc_cmd_sett, cryptonote::arg_regtest_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
command_line::add_arg(desc_cmd_sett, arg_txid);
command_line::add_arg(desc_cmd_sett, arg_height);
@@ -102,9 +103,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Starting...");
std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
- bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
- bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
- network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
+ const network_type net_type = core::get_network_type_from_args(vm);
std::string opt_txid_string = command_line::get_arg(vm, arg_txid);
uint64_t opt_height = command_line::get_arg(vm, arg_height);
bool opt_include_coinbase = command_line::get_arg(vm, arg_include_coinbase);
diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp
index 82fe524de..2b58124a3 100644
--- a/src/blockchain_utilities/blockchain_export.cpp
+++ b/src/blockchain_utilities/blockchain_export.cpp
@@ -68,6 +68,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, arg_output_file);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
+ command_line::add_arg(desc_cmd_sett, cryptonote::arg_regtest_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
command_line::add_arg(desc_cmd_sett, arg_block_start);
command_line::add_arg(desc_cmd_sett, arg_block_stop);
@@ -105,13 +106,6 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Starting...");
- bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
- bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
- if (opt_testnet && opt_stagenet)
- {
- std::cerr << "Can't specify more than one of --testnet and --stagenet" << std::endl;
- return 1;
- }
bool opt_blocks_dat = command_line::get_arg(vm, arg_blocks_dat);
std::string m_config_folder;
@@ -162,7 +156,8 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Error opening database: " << e.what());
return 1;
}
- r = core_storage->init(db, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET);
+ const network_type net_type = core::get_network_type_from_args(vm);
+ r = core_storage->init(db, net_type);
if (core_storage->get_blockchain_pruning_seed() && !opt_blocks_dat)
{
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index 8a25e1da9..58e152e4d 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -668,13 +668,6 @@ int main(int argc, char* argv[])
}
}
- opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
- opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
- if (opt_testnet && opt_stagenet)
- {
- std::cerr << "Error: Can't specify more than one of --testnet and --stagenet" << ENDL;
- return 1;
- }
m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
mlog_configure(mlog_get_default_log_path("monero-blockchain-import.log"), true);
diff --git a/src/blockchain_utilities/blockchain_prune.cpp b/src/blockchain_utilities/blockchain_prune.cpp
index 4a91cf7cc..d99a46632 100644
--- a/src/blockchain_utilities/blockchain_prune.cpp
+++ b/src/blockchain_utilities/blockchain_prune.cpp
@@ -462,6 +462,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
+ command_line::add_arg(desc_cmd_sett, cryptonote::arg_regtest_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
command_line::add_arg(desc_cmd_sett, arg_db_sync_mode);
command_line::add_arg(desc_cmd_sett, arg_copy_pruned_database);
@@ -496,9 +497,7 @@ int main(int argc, char* argv[])
MINFO("Starting...");
- bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
- bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
- network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
+ const network_type net_type = core::get_network_type_from_args(vm);
bool opt_copy_pruned_database = command_line::get_arg(vm, arg_copy_pruned_database);
std::string data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
while (boost::ends_with(data_dir, "/") || boost::ends_with(data_dir, "\\"))
diff --git a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
index 05aaf42ee..08eb6ee0e 100644
--- a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
+++ b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
@@ -115,6 +115,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
+ command_line::add_arg(desc_cmd_sett, cryptonote::arg_regtest_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
command_line::add_arg(desc_cmd_sett, arg_verbose);
command_line::add_arg(desc_cmd_sett, arg_dry_run);
@@ -151,9 +152,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Starting...");
std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
- bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
- bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
- network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
+ const network_type net_type = core::get_network_type_from_args(vm);
bool opt_verbose = command_line::get_arg(vm, arg_verbose);
bool opt_dry_run = command_line::get_arg(vm, arg_dry_run);
diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp
index 21040a1d8..fc082b4e5 100644
--- a/src/blockchain_utilities/blockchain_stats.cpp
+++ b/src/blockchain_utilities/blockchain_stats.cpp
@@ -147,6 +147,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
+ command_line::add_arg(desc_cmd_sett, cryptonote::arg_regtest_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
command_line::add_arg(desc_cmd_sett, arg_block_start);
command_line::add_arg(desc_cmd_sett, arg_block_stop);
@@ -189,9 +190,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Starting...");
std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
- bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
- bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
- network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
+ const network_type net_type = core::get_network_type_from_args(vm);
block_start = command_line::get_arg(vm, arg_block_start);
block_stop = command_line::get_arg(vm, arg_block_stop);
do_inputs = command_line::get_arg(vm, arg_inputs);
diff --git a/src/blockchain_utilities/blockchain_usage.cpp b/src/blockchain_utilities/blockchain_usage.cpp
index 129a9be21..1cc785283 100644
--- a/src/blockchain_utilities/blockchain_usage.cpp
+++ b/src/blockchain_utilities/blockchain_usage.cpp
@@ -96,6 +96,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
+ command_line::add_arg(desc_cmd_sett, cryptonote::arg_regtest_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
command_line::add_arg(desc_cmd_sett, arg_rct_only);
command_line::add_arg(desc_cmd_sett, arg_input);
@@ -133,9 +134,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Starting...");
- bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
- bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
- network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
+ const network_type net_type = core::get_network_type_from_args(vm);
bool opt_rct_only = command_line::get_arg(vm, arg_rct_only);
// If we wanted to use the memory pool, we would set up a fake_core.
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index a6d7762ff..923ad64af 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -1330,10 +1330,10 @@ namespace cryptonote
return res;
}
//---------------------------------------------------------------
- crypto::hash get_pruned_transaction_hash(const transaction& t, const crypto::hash &pruned_data_hash)
+ bool get_pruned_transaction_hash(const transaction& t, const crypto::hash &pruned_data_hash, crypto::hash &res)
{
// v1 transactions hash the entire blob
- CHECK_AND_ASSERT_THROW_MES(t.version > 1, "Hash for pruned v1 tx cannot be calculated");
+ CHECK_AND_ASSERT_MES(t.version > 1, false, "Hash for pruned v1 tx cannot be calculated");
// v2 transactions hash different parts together, than hash the set of those hashes
crypto::hash hashes[3];
@@ -1350,7 +1350,7 @@ namespace cryptonote
const size_t inputs = t.vin.size();
const size_t outputs = t.vout.size();
bool r = tt.rct_signatures.serialize_rctsig_base(ba, inputs, outputs);
- CHECK_AND_ASSERT_THROW_MES(r, "Failed to serialize rct signatures base");
+ CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures base");
cryptonote::get_blob_hash(ss.str(), hashes[1]);
}
@@ -1361,9 +1361,9 @@ namespace cryptonote
hashes[2] = pruned_data_hash;
// the tx hash is the hash of the 3 hashes
- crypto::hash res = cn_fast_hash(hashes, sizeof(hashes));
+ res = cn_fast_hash(hashes, sizeof(hashes));
t.set_hash(res);
- return res;
+ return true;
}
//---------------------------------------------------------------
bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size)
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index c242823c5..96059bd93 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -118,7 +118,7 @@ namespace cryptonote
bool calculate_transaction_prunable_hash(const transaction& t, const cryptonote::blobdata_ref *blob, crypto::hash& res);
crypto::hash get_transaction_prunable_hash(const transaction& t, const cryptonote::blobdata_ref *blob = NULL);
bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size);
- crypto::hash get_pruned_transaction_hash(const transaction& t, const crypto::hash &pruned_data_hash);
+ bool get_pruned_transaction_hash(const transaction& t, const crypto::hash &pruned_data_hash, crypto::hash& res);
blobdata get_block_hashing_blob(const block& b);
bool calculate_block_hash(const block& b, crypto::hash& res, const blobdata_ref *blob = NULL);
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 627f11450..f50c9ad3f 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -97,16 +97,18 @@ namespace cryptonote
, "Fixed difficulty used for testing."
, 0
};
- const command_line::arg_descriptor<std::string, false, true, 2> arg_data_dir = {
+ const command_line::arg_descriptor<std::string, false, true, 3> arg_data_dir = {
"data-dir"
, "Specify data directory"
, tools::get_default_data_dir()
- , {{ &arg_testnet_on, &arg_stagenet_on }}
- , [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
- if (testnet_stagenet[0])
+ , {{ &arg_testnet_on, &arg_stagenet_on, &arg_regtest_on }}
+ , [](std::array<bool, 3> nets, bool defaulted, std::string val)->std::string {
+ if (nets[0])
return (boost::filesystem::path(val) / "testnet").string();
- else if (testnet_stagenet[1])
+ else if (nets[1])
return (boost::filesystem::path(val) / "stagenet").string();
+ else if (nets[2])
+ return (boost::filesystem::path(val) / "fake").string();
return val;
}
};
@@ -344,13 +346,21 @@ namespace cryptonote
BlockchainDB::init_options(desc);
}
//-----------------------------------------------------------------------------------------------
+ network_type core::get_network_type_from_args(const boost::program_options::variables_map& vm)
+ {
+ const bool testnet = command_line::get_arg(vm, arg_testnet_on);
+ const bool stagenet = command_line::get_arg(vm, arg_stagenet_on);
+ const bool regtest = command_line::get_arg(vm, arg_regtest_on);
+ if (testnet + stagenet + regtest > 1)
+ throw std::runtime_error("More than one network type argument was specified");
+ return testnet ? TESTNET : stagenet ? STAGENET : regtest ? FAKECHAIN : MAINNET;
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::handle_command_line(const boost::program_options::variables_map& vm)
{
if (m_nettype != FAKECHAIN)
{
- const bool testnet = command_line::get_arg(vm, arg_testnet_on);
- const bool stagenet = command_line::get_arg(vm, arg_stagenet_on);
- m_nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
+ m_nettype = get_network_type_from_args(vm);
}
m_config_folder = command_line::get_arg(vm, arg_data_dir);
@@ -474,7 +484,10 @@ namespace cryptonote
bool keep_fakechain = command_line::get_arg(vm, arg_keep_fakechain);
boost::filesystem::path folder(m_config_folder);
- if (m_nettype == FAKECHAIN)
+ // --regtest already appends "fake" through arg_data_dir. Some tests set
+ // FAKECHAIN directly through test_options instead of command line args, so
+ // preserve the legacy fakechain isolation for those callers.
+ if (m_nettype == FAKECHAIN && !command_line::get_arg(vm, arg_regtest_on))
folder /= "fake";
// make sure the data directory exists, and try to lock it
@@ -1081,7 +1094,7 @@ namespace cryptonote
const bool res = m_mempool.add_tx(tx, tx_hash, blob, tx_weight, tvc, tx_relay, relayed, version);
// If new incoming tx passed verification and entered the pool, notify ZMQ
- if (!tvc.m_verifivation_failed && tvc.m_added_to_pool && matches_category(tx_relay, relay_category::legacy))
+ if (!tvc.m_verifivation_failed && res && matches_category(tvc.m_relay, relay_category::legacy))
{
m_blockchain_storage.notify_txpool_event({txpool_event{
.tx = tx,
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index f1339dbb9..777de3319 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -63,7 +63,7 @@ namespace cryptonote
const size_t long_term_block_weight_window;
};
- extern const command_line::arg_descriptor<std::string, false, true, 2> arg_data_dir;
+ extern const command_line::arg_descriptor<std::string, false, true, 3> arg_data_dir;
extern const command_line::arg_descriptor<bool, false> arg_testnet_on;
extern const command_line::arg_descriptor<bool, false> arg_stagenet_on;
extern const command_line::arg_descriptor<bool, false> arg_regtest_on;
@@ -277,6 +277,15 @@ namespace cryptonote
static void init_options(boost::program_options::options_description& desc);
/**
+ * @brief resolves the network type based on command line arguments
+ * @param vm variables map
+ * @return network type corresponding to arg_{testnet,stagenet,regtest}_on, defaulting to MAINNET
+ * @throw std::runtime_error if more than 1 of arg_{testnet,stagenet,regtest}_on is present
+ * @throw boost::bad_any_cast if arg_{testnet,stagenet,regtest}_on weren't added to vm
+ */
+ static network_type get_network_type_from_args(const boost::program_options::variables_map& vm);
+
+ /**
* @brief initializes the core as needed
*
* This function initializes the transaction pool, the Blockchain, and
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 8f044154b..c350e24c4 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -31,6 +31,7 @@
#include <unordered_set>
#include <random>
#include "include_base_utils.h"
+#include "misc_log_ex.h"
#include "string_tools.h"
using namespace epee;
@@ -46,6 +47,40 @@ 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
{
//---------------------------------------------------------------
@@ -213,6 +248,10 @@ namespace cryptonote
return false;
}
+ boost::optional<cryptonote::subaddress_index> recognized_change_index;
+ if (change_addr)
+ recognized_change_index = sanity_check_change_address(*change_addr, subaddresses, sender_account_keys);
+
std::vector<rct::key> amount_keys;
tx.set_null();
amount_keys.clear();
@@ -406,6 +445,11 @@ namespace cryptonote
for(const tx_destination_entry& dst_entr: destinations)
{
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
+ const bool matches_change_addr = change_addr && dst_entr.addr == *change_addr;
+ const bool is_bad_change_dst = matches_change_addr && dst_entr.amount > 0 && !recognized_change_index;
+ CHECK_AND_ASSERT_MES(!is_bad_change_dst, false,
+ "Non-zero amount change address is not recognized as belonging to the sender account");
+
crypto::public_key out_eph_public_key;
crypto::view_tag view_tag;
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 3bc508037..badcde361 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -337,16 +337,16 @@ namespace cryptonote
MERROR("internal error: error adding transaction to txpool: " << e.what());
return false;
}
-
- static_assert(unsigned(relay_method::none) == 0, "expected relay_method::none value to be zero");
- if(meta.fee > 0 && tx_relay != relay_method::forward)
- tvc.m_relay = tx_relay;
}
tvc.m_verifivation_failed = false;
if (tvc.m_added_to_pool)
m_txpool_weight += tx_weight;
+ static_assert(unsigned(relay_method::none) == 0, "expected relay_method::none value to be zero");
+ if (meta.fee > 0 && tx_relay != relay_method::forward)
+ tvc.m_relay = tx_relay;
+
++m_cookie;
MINFO("Transaction added to pool: txid " << id << " weight: " << tx_weight << " fee/byte: " << (fee / (double)(tx_weight ? tx_weight : 1)) << ", count: " << m_added_txs_by_id.size());
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index ed75a3dfc..f3630368b 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -112,6 +112,8 @@ namespace cryptonote
* @param id the transaction's hash
* @tx_relay how the transaction was received
* @param tx_weight the transaction's weight
+ * @return True if tx passes verification checks AND is first observation
+ * of tx in `broadcasted` relay method.
*/
bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob,
size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed,
@@ -138,7 +140,8 @@ namespace cryptonote
* passes the non-input consensus tests (e.g. for newly received relayed txs), then leave
* "nic_verified_hf_version" as its default value of 0 (there is no v0 fork).
*
- * @return true if the transaction passes validations, otherwise false
+ * @return True if tx passes verification checks AND is first observation
+ * of tx in `broadcasted` relay method.
*/
bool add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed,
uint8_t version, uint8_t nic_verified_hf_version = 0);
diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp
index 7eadb72f2..b8fdb6b10 100644
--- a/src/cryptonote_protocol/block_queue.cpp
+++ b/src/cryptonote_protocol/block_queue.cpp
@@ -338,10 +338,10 @@ std::pair<uint64_t, uint64_t> block_queue::get_next_span_if_scheduled(std::vecto
void block_queue::reset_next_span_time(boost::posix_time::ptime t)
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
- CHECK_AND_ASSERT_THROW_MES(!blocks.empty(), "No next span to reset time");
+ CHECK_AND_ASSERT_MES_NO_RET(!blocks.empty(), "No next span to reset time");
block_map::iterator i = blocks.begin();
- CHECK_AND_ASSERT_THROW_MES(i != blocks.end(), "No next span to reset time");
- CHECK_AND_ASSERT_THROW_MES(i->blocks.empty(), "Next span is not empty");
+ CHECK_AND_ASSERT_MES_NO_RET(i != blocks.end(), "No next span to reset time");
+ CHECK_AND_ASSERT_MES_NO_RET(i->blocks.empty(), "Next span is not empty");
(boost::posix_time::ptime&)i->time = t; // sod off, time doesn't influence sorting
}
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 6a3fd2532..6c36a3b5b 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -116,7 +116,7 @@ namespace cryptonote
if (is_pruned)
{
if ((parse_success = cryptonote::parse_and_validate_tx_base_from_blob(tx_entry.blob, tx)))
- tx_hash = cryptonote::get_pruned_transaction_hash(tx, tx_entry.prunable_hash);
+ parse_success = cryptonote::get_pruned_transaction_hash(tx, tx_entry.prunable_hash, tx_hash);
}
else
{
@@ -1475,14 +1475,28 @@ namespace cryptonote
return 1;
}
+ bool stopped = false;
+ auto cleanup_on_exit = epee::misc_utils::create_scope_leave_handler([this, &stopped, &context, span_connection_id, start_height]() {
+ if (!m_core.cleanup_handle_incoming_blocks())
+ {
+ LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
+ return;
+ }
+
+ if (stopped)
+ return;
+
+ m_block_queue.remove_spans(span_connection_id, start_height);
+ });
+
uint64_t block_process_time_full = 0, transactions_process_time_full = 0;
size_t num_txs = 0, blockidx = 0;
for(const block_complete_entry& block_entry: blocks)
{
if (m_stopping)
{
- m_core.cleanup_handle_incoming_blocks();
- return 1;
+ stopped = true;
+ return 1;
}
// process transactions
@@ -1501,13 +1515,6 @@ namespace cryptonote
}))
LOG_ERROR_CCONTEXT("span connection id not found");
- if (!m_core.cleanup_handle_incoming_blocks())
- {
- LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
- return 1;
- }
- // in case the peer had dropped beforehand, remove the span anyway so other threads can wake up and get it
- m_block_queue.remove_spans(span_connection_id, start_height);
return 1;
}
TIME_MEASURE_FINISH(transactions_process_time);
@@ -1534,14 +1541,6 @@ namespace cryptonote
}))
LOG_ERROR_CCONTEXT("span connection id not found");
- if (!m_core.cleanup_handle_incoming_blocks())
- {
- LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
- return 1;
- }
-
- // in case the peer had dropped beforehand, remove the span anyway so other threads can wake up and get it
- m_block_queue.remove_spans(span_connection_id, start_height);
return 1;
}
if(bvc.m_marked_as_orphaned)
@@ -1554,14 +1553,6 @@ namespace cryptonote
}))
LOG_ERROR_CCONTEXT("span connection id not found");
- if (!m_core.cleanup_handle_incoming_blocks())
- {
- LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
- return 1;
- }
-
- // in case the peer had dropped beforehand, remove the span anyway so other threads can wake up and get it
- m_block_queue.remove_spans(span_connection_id, start_height);
return 1;
}
@@ -1573,13 +1564,7 @@ namespace cryptonote
MDEBUG(context << "Block process time (" << blocks.size() << " blocks, " << num_txs << " txs): " << block_process_time_full + transactions_process_time_full << " (" << transactions_process_time_full << "/" << block_process_time_full << ") ms");
- if (!m_core.cleanup_handle_incoming_blocks())
- {
- LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
- return 1;
- }
-
- m_block_queue.remove_spans(span_connection_id, start_height);
+ cleanup_on_exit.reset();
const uint64_t current_blockchain_height = m_core.get_current_blockchain_height();
if (current_blockchain_height > previous_height)
diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h
index a0b15d021..a890e92ae 100644
--- a/src/daemon/command_line_args.h
+++ b/src/daemon/command_line_args.h
@@ -98,7 +98,7 @@ namespace daemon_args
const command_line::arg_descriptor<std::string> arg_proxy = {
"proxy",
- "Network communication through proxy: <socks-ip:port> i.e. \"127.0.0.1:9050\"",
+ "Network communication through proxy: [socks5://[user:pass@]]<socks-ip:port> i.e. \"127.0.0.1:9050\"",
"",
};
const command_line::arg_descriptor<bool> arg_proxy_allow_dns_leaks = {
@@ -138,7 +138,7 @@ namespace daemon_args
const command_line::arg_descriptor<bool> arg_restricted_zmq_rpc = {
"restricted-zmq-rpc"
- , "Restrict ZMQ RPC to view-only / non-sensitive methods"
+ , "Restrict ZMQ RPC by disabling some sensitive methods; does not guarantee filtering of sensitive data"
, false
};
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index 39ef04772..a61fbda6f 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -247,10 +247,8 @@ int main(int argc, char const * argv[])
return 1;
}
- const bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
- const bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
- const bool regtest = command_line::get_arg(vm, cryptonote::arg_regtest_on);
- if (testnet + stagenet + regtest > 1)
+ try { cryptonote::core::get_network_type_from_args(vm); }
+ catch (const std::runtime_error&)
{
std::cerr << "Can't specify more than one of --tesnet and --stagenet and --regtest" << ENDL;
return 1;
diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp
index 5b6920313..f698e7014 100644
--- a/src/device_trezor/device_trezor_base.hpp
+++ b/src/device_trezor/device_trezor_base.hpp
@@ -33,6 +33,7 @@
#include <cstddef>
#include <string>
+#include <type_traits>
#include "device/device.hpp"
#include "device/device_default.hpp"
#include "device/device_cold.hpp"
@@ -147,7 +148,7 @@ namespace trezor {
bool open_session = false)
{
// Require strictly protocol buffers response in the template.
- BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
+ static_assert(std::is_base_of<google::protobuf::Message, t_message>::value);
const bool accepting_base = boost::is_same<google::protobuf::Message, t_message>::value;
if (resp_types && !accepting_base){
throw std::invalid_argument("Cannot specify list of accepted types and not using generic response");
diff --git a/src/device_trezor/trezor/debug_link.hpp b/src/device_trezor/trezor/debug_link.hpp
index b7a252833..ba61dec9c 100644
--- a/src/device_trezor/trezor/debug_link.hpp
+++ b/src/device_trezor/trezor/debug_link.hpp
@@ -32,6 +32,7 @@
#include "transport.hpp"
#include "messages/messages-debug.pb.h"
+#include <type_traits>
namespace hw {
@@ -60,7 +61,7 @@ namespace trezor {
const boost::optional<messages::MessageType> &resp_type = boost::none,
bool no_wait = false)
{
- BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
+ static_assert(std::is_base_of<google::protobuf::Message, t_message>::value);
m_transport->write(req);
if (no_wait){
diff --git a/src/device_trezor/trezor/messages_map.hpp b/src/device_trezor/trezor/messages_map.hpp
index 30b8f875c..d2b50a3f9 100644
--- a/src/device_trezor/trezor/messages_map.hpp
+++ b/src/device_trezor/trezor/messages_map.hpp
@@ -72,14 +72,14 @@ namespace trezor {
template<class t_message=google::protobuf::Message>
static messages::MessageType get_message_wire_number() {
- BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
+ static_assert(std::is_base_of<google::protobuf::Message, t_message>::value);
return get_message_wire_number(t_message::default_instance().GetDescriptor()->name());
}
};
template<class t_message=google::protobuf::Message>
std::shared_ptr<t_message> message_ptr_retype(std::shared_ptr<google::protobuf::Message> & in){
- BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
+ static_assert(std::is_base_of<google::protobuf::Message, t_message>::value);
if (!in){
return nullptr;
}
@@ -89,7 +89,7 @@ namespace trezor {
template<class t_message=google::protobuf::Message>
std::shared_ptr<t_message> message_ptr_retype_static(std::shared_ptr<google::protobuf::Message> & in){
- BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
+ static_assert(std::is_base_of<google::protobuf::Message, t_message>::value);
if (!in){
return nullptr;
}
diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp
index 827b189fd..7ec07afde 100644
--- a/src/device_trezor/trezor/transport.hpp
+++ b/src/device_trezor/trezor/transport.hpp
@@ -383,7 +383,7 @@ namespace trezor {
boost::optional<messages::MessageType> resp_type = boost::none)
{
// Require strictly protocol buffers response in the template.
- BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
+ static_assert(std::is_base_of<google::protobuf::Message, t_message>::value);
// Write the request
transport.write(req);
diff --git a/src/net/error.cpp b/src/net/error.cpp
index 254db7ae1..621a0b1d3 100644
--- a/src/net/error.cpp
+++ b/src/net/error.cpp
@@ -54,6 +54,8 @@ namespace
return "Failed to retrieve desired DNS record";
case net::error::expected_tld:
return "Expected top-level domain";
+ case net::error::invalid_encoding:
+ return "Invalid encoding";
case net::error::invalid_host:
return "Host value is not valid";
case net::error::invalid_i2p_address:
@@ -62,8 +64,12 @@ namespace
return "CIDR netmask outside of 0-32 range";
case net::error::invalid_port:
return "Invalid port value (expected 0-65535)";
+ case net::error::invalid_scheme:
+ return "Invalid/unsupported scheme was provided";
case net::error::invalid_tor_address:
return "Invalid Tor address";
+ case net::error::unexpected_userinfo:
+ return "User or pass was provided unexpectedly";
case net::error::unsupported_address:
return "Network address not supported";
default:
diff --git a/src/net/error.h b/src/net/error.h
index 969eefc41..27b376546 100644
--- a/src/net/error.h
+++ b/src/net/error.h
@@ -41,11 +41,14 @@ namespace net
bogus_dnssec = 1, //!< Invalid response signature from DNSSEC enabled domain
dns_query_failure, //!< Failed to retrieve desired DNS record
expected_tld, //!< Expected a tld
+ invalid_encoding, //!< Invalid percent encoding
invalid_host, //!< Hostname is not valid
invalid_i2p_address,
invalid_mask, //!< Outside of 0-32 range
invalid_port, //!< Outside of 0-65535 range
+ invalid_scheme, //!< Provided URI scheme was unspported
invalid_tor_address,//!< Invalid base32 or length
+ unexpected_userinfo,//!< User or pass was provided unexpectedly
unsupported_address,//!< Type not supported by `get_network_address`
};
diff --git a/src/net/fwd.h b/src/net/fwd.h
index b105da115..ffae07cc9 100644
--- a/src/net/fwd.h
+++ b/src/net/fwd.h
@@ -34,13 +34,19 @@
namespace net
{
enum class error : int;
+ struct scheme_and_authority;
class tor_address;
+ struct uri_components;
+ struct user_and_pass;
+ struct userinfo_and_hostport;
class i2p_address;
namespace socks
{
class client;
template<typename> class connect_handler;
+ struct connector;
+ struct endpoint;
enum class error : int;
enum class version : std::uint8_t;
}
diff --git a/src/net/http.cpp b/src/net/http.cpp
index c0ed3d430..ed7d5a889 100644
--- a/src/net/http.cpp
+++ b/src/net/http.cpp
@@ -45,15 +45,17 @@ bool client::set_proxy(const std::string &address)
}
else
{
- const auto endpoint = get_tcp_endpoint(address);
+ auto endpoint = socks::endpoint::get(address);
if (!endpoint)
{
- auto always_fail = net::socks::connector{boost::asio::ip::tcp::endpoint()};
+ auto always_fail = net::socks::connector{};
set_connector(always_fail);
}
else
{
- set_connector(net::socks::connector{*endpoint});
+ set_connector(
+ net::socks::connector{std::make_shared<socks::endpoint>(std::move(*endpoint))}
+ );
}
}
diff --git a/src/net/parse.cpp b/src/net/parse.cpp
index f989d7de4..27fd5db43 100644
--- a/src/net/parse.cpp
+++ b/src/net/parse.cpp
@@ -29,6 +29,9 @@
#include "parse.h"
+#include <type_traits>
+#include "hex.h"
+#include "net/socks.h"
#include "net/tor_address.h"
#include "net/i2p_address.h"
#include "string_tools.h"
@@ -36,6 +39,95 @@
namespace net
{
+ namespace
+ {
+ bool percent_decoding(std::string& out)
+ {
+ auto pos = out.find('%');
+ while (pos != std::string::npos)
+ {
+ if (out.size() - pos < 3)
+ return false;
+ if (!epee::from_hex::to_buffer(epee::as_mut_byte_span(out[pos]), {out.data() + pos + 1, 2}))
+ return false;
+ out.erase(pos + 1, 2);
+ pos = out.find('%', pos + 1);
+ }
+
+ return true;
+ }
+ } // anonymous
+
+ scheme_and_authority::scheme_and_authority(boost::string_ref uri)
+ : scheme(), authority()
+ {
+ static_assert(std::is_same<std::string::size_type, boost::string_ref::size_type>());
+
+ // Stop at scheme end or path begin. URN not supported
+ const auto split = uri.find_first_of(":/");
+ if (split != boost::string_ref::npos && uri.substr(split).starts_with("://"))
+ {
+ scheme.assign(uri.data(), split);
+ uri = uri.substr(split + 3);
+ }
+
+ uri = uri.substr(0, uri.find('/'));
+ authority.assign(uri.data(), uri.size());
+ }
+
+ userinfo_and_hostport::userinfo_and_hostport(boost::string_ref authority)
+ : userinfo(), hostport()
+ {
+ static_assert(std::is_same<std::string::size_type, boost::string_ref::size_type>());
+
+ const auto split = authority.find('@');
+ if (split != boost::string_ref::npos)
+ {
+ userinfo.assign(authority.data(), split);
+ authority = authority.substr(split + 1);
+ }
+
+ hostport.assign(authority.data(), authority.size());
+ }
+
+ boost::optional<user_and_pass> user_and_pass::get(boost::string_ref userinfo)
+ {
+ static_assert(std::is_same<std::string::size_type, boost::string_ref::size_type>());
+ boost::optional<user_and_pass> out = user_and_pass{};
+
+ const auto split = userinfo.find(':');
+ if (split != boost::string_ref::npos)
+ {
+ out->user.assign(userinfo.data(), split);
+ userinfo = userinfo.substr(split + 1);
+ }
+ else
+ {
+ out->user.assign(userinfo.data(), userinfo.size());
+ userinfo = {};
+ }
+
+ out->pass.assign(userinfo.data(), userinfo.size());
+ if (percent_decoding(out->user) && percent_decoding(out->pass))
+ return out;
+ return boost::none;
+ }
+
+ boost::optional<uri_components> uri_components::get(const boost::string_ref uri)
+ {
+ scheme_and_authority result1{uri};
+ userinfo_and_hostport result2{result1.authority};
+ auto result3 = user_and_pass::get(result2.userinfo);
+ if (!result3)
+ return boost::none;
+
+ boost::optional<uri_components> out = uri_components{};
+ out->scheme = std::move(result1.scheme);
+ out->userinfo = std::move(*result3);
+ out->hostport = std::move(result2.hostport);
+ return out;
+ }
+
void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port)
{
// If IPv6 address format with port "[addr:addr:addr:...:addr]:port"
@@ -104,7 +196,6 @@ namespace net
if (epee::string_tools::get_ip_int32_from_string(ip, host_str))
return {epee::net_utils::ipv4_network_address{ip, port}};
}
-
return make_error_code(net::error::unsupported_address);
}
@@ -165,4 +256,46 @@ namespace net
return result;
}
+
+ namespace socks
+ {
+ endpoint::endpoint()
+ : endpoint(boost::asio::ip::tcp::endpoint{})
+ {}
+
+ endpoint::endpoint(const boost::asio::ip::tcp::endpoint& address)
+ : address(address), userinfo(), ver(version::v4a)
+ {}
+
+ expect<endpoint> endpoint::get(const boost::string_ref uri)
+ {
+ auto components = uri_components::get(uri);
+ if (!components)
+ return {net::error::invalid_encoding};
+ auto tcp_endpoint = get_tcp_endpoint(components->hostport);
+ if (!tcp_endpoint)
+ return tcp_endpoint.error();
+
+ endpoint out{};
+ if (components->scheme.empty() || components->scheme == "socks" || components->scheme == "socks4a")
+ out.ver = version::v4a;
+ else if (components->scheme == "socks4")
+ out.ver = version::v4;
+ else if (components->scheme == "socks5")
+ out.ver = version::v5;
+ else
+ return {net::error::invalid_scheme};
+
+ // Only version 5 supports user/pass authentication
+ if (!components->userinfo.user.empty() || !components->userinfo.pass.empty())
+ {
+ if (out.ver != version::v5)
+ return {net::error::unexpected_userinfo};
+ }
+
+ out.address = std::move(*tcp_endpoint);
+ out.userinfo = std::move(components->userinfo);
+ return out;
+ }
+ }
}
diff --git a/src/net/parse.h b/src/net/parse.h
index 6ece931c6..68f63fdb9 100644
--- a/src/net/parse.h
+++ b/src/net/parse.h
@@ -30,14 +30,75 @@
#pragma once
#include <boost/asio/ip/tcp.hpp>
+#include <boost/optional/optional.hpp>
#include <boost/utility/string_ref.hpp>
#include <cstdint>
#include "common/expect.h"
+#include "net/fwd.h"
#include "net/net_utils_base.h"
namespace net
{
+ //! \brief Separates scheme, authority, and path sections of a URI.
+ struct scheme_and_authority
+ {
+ //! \param uri with optional scheme, authority, and optional path. No URNs.
+ explicit scheme_and_authority(boost::string_ref uri);
+
+ std::string scheme;
+ std::string authority;
+ };
+
+ //! \brief Separates the userinfo and host+port from URI authority.
+ struct userinfo_and_hostport
+ {
+ //! \param authority portion of a URI.
+ explicit userinfo_and_hostport(boost::string_ref authority);
+
+ std::string userinfo;
+ std::string hostport;
+ };
+
+ //! \brief Separates the user and pass sections from URI userinfo.
+ struct user_and_pass
+ {
+ user_and_pass()
+ : user(), pass()
+ {}
+
+ /*!
+ * \param userinfo section of a URI.
+ * \return User and pass with percent encoding removed. `boost::none`
+ * if bad percent encoding
+ */
+ static boost::optional<user_and_pass> get(boost::string_ref userinfo);
+
+ std::string user;
+ std::string pass;
+ };
+
+ //! \brief Separates scheme, user, pass, and host+port sections of a URI.
+ struct uri_components
+ {
+ uri_components()
+ : scheme(), userinfo(), hostport()
+ {}
+
+ /*!
+ * \param uri with optional scheme, optional user, optional pass,
+ * authority, and optional path. URN not supported.
+ * \return Scheme, user, pass, and host+port sections of a URI with
+ * percent encoding removed on user and pass. `boost::none` if
+ * bad percent encoding.
+ */
+ static boost::optional<uri_components> get(boost::string_ref uri);
+
+ std::string scheme;
+ user_and_pass userinfo;
+ std::string hostport;
+ };
+
/*!
* \brief Takes a valid address string (IP, Tor, I2P, or DNS name) and splits it into host and port
*
@@ -79,5 +140,21 @@ namespace net
get_ipv4_subnet_address(boost::string_ref address, bool allow_implicit_32 = false);
expect<boost::asio::ip::tcp::endpoint> get_tcp_endpoint(const boost::string_ref address);
-}
+ namespace socks
+ {
+ //! \brief Separates TCP address, user+pass, and socks version
+ struct endpoint
+ {
+ endpoint();
+ explicit endpoint(const boost::asio::ip::tcp::endpoint& address);
+
+ //! \param uri with optional scheme, optional userinfo, and host+port.
+ static expect<endpoint> get(boost::string_ref uri);
+
+ boost::asio::ip::tcp::endpoint address;
+ user_and_pass userinfo;
+ version ver;
+ };
+ }
+}
diff --git a/src/net/socks.cpp b/src/net/socks.cpp
index 43d98024b..fc5be424e 100644
--- a/src/net/socks.cpp
+++ b/src/net/socks.cpp
@@ -31,6 +31,7 @@
#include <algorithm>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/buffer.hpp>
+#include <boost/asio/coroutine.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/write.hpp>
@@ -38,8 +39,10 @@
#include <boost/endian/conversion.hpp>
#include <cstring>
#include <limits>
+#include <numeric>
#include <string>
+#include "net/parse.h"
#include "net/net_utils_base.h"
#include "net/tor_address.h"
#include "net/i2p_address.h"
@@ -54,6 +57,16 @@ namespace socks
constexpr const std::uint8_t v4tor_resolve_command = 0xf0;
constexpr const std::uint8_t v4_request_granted = 90;
+ constexpr const std::uint8_t v5_noauth_method = 0;
+ constexpr const std::uint8_t v5_userpass_method = 2;
+ constexpr const std::uint8_t v5_connect_command = 1;
+ constexpr const std::uint8_t v5_reserved = 0;
+ constexpr const std::uint8_t v5_ipv4_type = 1;
+ constexpr const std::uint8_t v5_domain_type = 3;
+ constexpr const std::uint8_t v5_ipv6_type = 4;
+ constexpr const std::uint8_t v5_reply_success = 0;
+ constexpr const std::uint8_t v5_userpass_version = 1;
+
struct v4_header
{
std::uint8_t version;
@@ -62,6 +75,114 @@ namespace socks
boost::endian::big_uint32_t ip;
};
+ struct v5_noauth_initial
+ {
+ std::uint8_t version;
+ std::uint8_t n_methods;
+ std::uint8_t method;
+
+ static constexpr v5_noauth_initial make() noexcept
+ {
+ return {5, 1, v5_noauth_method};
+ }
+ };
+
+ struct v5_auth_initial
+ {
+ std::uint8_t version;
+ std::uint8_t n_methods;
+ std::uint8_t method1;
+ std::uint8_t method2;
+
+ static constexpr v5_auth_initial make() noexcept
+ {
+ return {5, 2, v5_noauth_method, v5_userpass_method};
+ }
+ };
+
+ struct v5_response_initial
+ {
+ std::uint8_t version;
+ std::uint8_t method;
+ };
+
+ struct v5_ipv4_connect
+ {
+ std::uint8_t version;
+ std::uint8_t command;
+ std::uint8_t reserved;
+ std::uint8_t type;
+ boost::endian::big_uint32_t ip;
+ boost::endian::big_uint16_t port;
+
+ static v5_ipv4_connect make(const std::uint32_t ip, const std::uint16_t port) noexcept
+ {
+ return {5, v5_connect_command, v5_reserved, v5_ipv4_type, ip, port};
+ }
+ };
+
+ struct v5_domain_connect
+ {
+ std::uint8_t version;
+ std::uint8_t command;
+ std::uint8_t reserved;
+ std::uint8_t type;
+ std::uint8_t length;
+
+ static constexpr v5_domain_connect make(const std::uint8_t length) noexcept
+ {
+ return {5, v5_connect_command, v5_reserved, v5_domain_type, length};
+ }
+ };
+
+ struct v5_ipv6_connect
+ {
+ std::uint8_t version;
+ std::uint8_t command;
+ std::uint8_t reserved;
+ std::uint8_t type;
+ char ip[16];
+ boost::endian::big_uint16_t port;
+
+ static v5_ipv6_connect make(const boost::asio::ip::address_v6& ip, const std::uint16_t port)
+ {
+ v5_ipv6_connect out{5, v5_connect_command, v5_reserved, v5_ipv6_type};
+ out.port = port;
+
+ const auto ip_bytes = ip.to_bytes();
+ static_assert(sizeof(out.ip) == sizeof(ip_bytes), "unexpected ipv6 bytes size");
+ std::memcpy(std::addressof(out.ip), std::addressof(ip_bytes), sizeof(out.ip));
+
+ return out;
+ }
+ };
+
+ struct v5_response_auth
+ {
+ std::uint8_t version;
+ std::uint8_t status;
+ };
+
+ struct v5_response_connect
+ {
+ std::uint8_t version;
+ std::uint8_t reply;
+ std::uint8_t reserved;
+ std::uint8_t type;
+ };
+
+ struct v5_response_ipv4
+ {
+ boost::endian::big_uint32_t ip;
+ boost::endian::big_uint16_t port;
+ };
+
+ struct v5_response_ipv6
+ {
+ char ip[16];
+ boost::endian::big_uint16_t port;
+ };
+
std::size_t write_domain_header(epee::span<std::uint8_t> out, const std::uint8_t command, const std::uint16_t port, const boost::string_ref domain)
{
if (std::numeric_limits<std::size_t>::max() - sizeof(v4_header) - 2 < domain.size())
@@ -86,6 +207,131 @@ namespace socks
return buf_size;
}
+ std::size_t write_v5_userpass(epee::span<std::uint8_t> out, const user_and_pass& userinfo)
+ {
+ static constexpr const std::uint8_t max_length = std::numeric_limits<std::uint8_t>::max();
+ if (max_length < userinfo.user.size())
+ return 0;
+ if (max_length < userinfo.pass.size())
+ return 0;
+
+ static_assert(max_length < std::numeric_limits<std::size_t>::max());
+ static_assert(max_length < std::numeric_limits<std::size_t>::max() - max_length);
+ static_assert(3 <= std::numeric_limits<std::size_t>::max() - max_length - max_length);
+
+ if (out.size() < 3 + userinfo.user.size() + userinfo.pass.size())
+ return 0;
+
+ const std::size_t initial = out.size();
+
+ out[0] = v5_userpass_version;
+ out[1] = std::uint8_t(userinfo.user.size());
+ out.remove_prefix(2);
+
+ std::memcpy(out.data(), userinfo.user.data(), userinfo.user.size());
+ out.remove_prefix(userinfo.user.size());
+
+ out[0] = std::uint8_t(userinfo.pass.size());
+ out.remove_prefix(1);
+
+ std::memcpy(out.data(), userinfo.pass.data(), userinfo.pass.size());
+ out.remove_prefix(userinfo.pass.size());
+ return initial - out.size();
+ }
+
+ std::array<std::uint16_t, 2> write_v5_initial(epee::span<std::uint8_t> out, const user_and_pass* userinfo)
+ {
+ std::array<std::uint16_t, 2> sizes{{}};
+
+ if (userinfo && (!userinfo->user.empty() || !userinfo->pass.empty()))
+ {
+ const auto header = v5_auth_initial::make();
+ if (out.size() < sizeof(header))
+ return sizes;
+ std::memcpy(out.data(), std::addressof(header), sizeof(header));
+ out.remove_prefix(sizeof(header));
+
+ const std::size_t auth = write_v5_userpass(out, *userinfo);
+ if (!auth)
+ return sizes;
+ out.remove_prefix(auth);
+
+ std::get<0>(sizes) = sizeof(header);
+ std::get<1>(sizes) = auth;
+ }
+ else
+ {
+ const auto header = v5_noauth_initial::make();
+ if (out.size() < sizeof(header))
+ return sizes;
+ std::memcpy(out.data(), std::addressof(header), sizeof(header));
+ out.remove_prefix(sizeof(header));
+
+ std::get<0>(sizes) = sizeof(header);
+ }
+
+ return sizes;
+ }
+
+ template<typename T>
+ std::array<std::uint16_t, 3> write_v5_address_connect(epee::span<std::uint8_t> out, const T& address, const user_and_pass* userinfo)
+ {
+ std::array<std::uint16_t, 3> sizes{{}};
+
+ const auto result = write_v5_initial(out, userinfo);
+ if (!std::get<0>(result))
+ return sizes;
+
+ for (std::size_t length : result)
+ out.remove_prefix(length);
+
+ if (out.size() < sizeof(address))
+ return sizes;
+ std::memcpy(out.data(), std::addressof(address), sizeof(address));
+
+ std::get<0>(sizes) = std::get<0>(result);
+ std::get<1>(sizes) = std::get<1>(result);
+ std::get<2>(sizes) = sizeof(address);
+ return sizes;
+ }
+
+ std::array<std::uint16_t, 3> write_v5_domain_connect(epee::span<std::uint8_t> out, const std::uint16_t port, const boost::string_ref domain, const user_and_pass* userinfo)
+ {
+ std::array<std::uint16_t, 3> sizes{{}};
+ if (std::numeric_limits<std::uint8_t>::max() < domain.size())
+ return sizes;
+
+ const auto result = write_v5_initial(out, userinfo);
+ if (!std::get<0>(result))
+ return sizes;
+
+ for (std::size_t length : result)
+ out.remove_prefix(length);
+
+ const auto request = v5_domain_connect::make(std::uint8_t(domain.size()));
+ static_assert(sizeof(port) <= std::numeric_limits<std::size_t>::max() - sizeof(request));
+ if (std::numeric_limits<std::size_t>::max() - sizeof(request) - sizeof(port) < domain.size())
+ return sizes;
+
+ const std::size_t last_size = sizeof(request) + sizeof(port) + domain.size();
+ if (out.size() < last_size)
+ return sizes;
+
+ std::memcpy(out.data(), std::addressof(request), sizeof(request));
+ out.remove_prefix(sizeof(request));
+
+ std::memcpy(out.data(), domain.data(), domain.size());
+ out.remove_prefix(domain.size());
+
+ const boost::endian::big_uint16_t big_port{port};
+ std::memcpy(out.data(), std::addressof(big_port), sizeof(big_port));
+
+ std::get<0>(sizes) = std::get<0>(result);
+ std::get<1>(sizes) = std::get<1>(result);
+ std::get<2>(sizes) = last_size;
+ return sizes;
+ }
+
struct socks_category : boost::system::error_category
{
explicit socks_category() noexcept
@@ -101,6 +347,23 @@ namespace socks
{
switch (socks::error(value))
{
+ case socks::error::general_failure:
+ return "Socks general server failure";
+ case socks::error::not_allowed:
+ return "Socks connection not allowed by ruleset";
+ case socks::error::network_unreachable:
+ return "Socks network unreachable";
+ case socks::error::host_unreachable:
+ return "Socks host unreachable";
+ case socks::error::connection_refused:
+ return "Socks connection refused";
+ case socks::error::ttl_expired:
+ return "Socks TTL expired";
+ case socks::error::command_not_supported:
+ return "Socks command not supported";
+ case socks::error::address_type_not_supported:
+ return "Socks address type not supported";
+
case socks::error::rejected:
return "Socks request rejected or failed";
case socks::error::identd_connection:
@@ -108,6 +371,8 @@ namespace socks
case socks::error::identd_user:
return "Socks request rejected because the client program and identd report different user-ids";
+ case socks::error::auth_failure:
+ return "Socks authentication failure";
case socks::error::bad_read:
return "Socks boost::async_read read fewer bytes than expected";
case socks::error::bad_write:
@@ -125,6 +390,10 @@ namespace socks
{
switch (socks::error(value))
{
+ case socks::error::network_unreachable:
+ return boost::system::errc::host_unreachable;
+ case socks::error::connection_refused:
+ return boost::system::errc::connection_refused;
case socks::error::bad_read:
case socks::error::bad_write:
return boost::system::errc::io_error;
@@ -158,18 +427,18 @@ namespace socks
if (self_)
{
client& self = *self_;
- self.buffer_size_ = std::min(bytes, sizeof(self.buffer_));
+ std::get<0>(self.buffer_size_) = std::min(bytes, sizeof(self.buffer_));
if (error)
- self.done(error, std::move(self_));
- else if (self.buffer().size() < sizeof(v4_header))
- self.done(socks::error::bad_read, std::move(self_));
+ self.done(error, self_);
+ else if (std::get<0>(self.buffer_size_) < sizeof(v4_header))
+ self.done(socks::error::bad_read, self_);
else if (self.buffer_[0] != 0) // response version
- self.done(socks::error::unexpected_version, std::move(self_));
+ self.done(socks::error::unexpected_version, self_);
else if (self.buffer_[1] != v4_request_granted)
- self.done(socks::error(int(self.buffer_[1]) + 1), std::move(self_));
+ self.done(socks::error(int(self.buffer_[1]) + 1), self_);
else
- self.done(boost::system::error_code{}, std::move(self_));
+ self.done(boost::system::error_code{}, self_);
}
}
};
@@ -181,6 +450,7 @@ namespace socks
static boost::asio::mutable_buffer get_buffer(client& self) noexcept
{
static_assert(sizeof(v4_header) <= sizeof(self.buffer_), "buffer too small for v4 response");
+ std::get<0>(self.buffer_size_) = sizeof(v4_header);
return boost::asio::buffer(self.buffer_, sizeof(v4_header));
}
@@ -190,22 +460,179 @@ namespace socks
{
client& self = *self_;
if (error)
- self.done(error, std::move(self_));
- else if (bytes < self.buffer().size())
- self.done(socks::error::bad_write, std::move(self_));
+ self.done(error, self_);
+ else if (bytes < std::get<0>(self.buffer_size_))
+ self.done(socks::error::bad_write, self_);
else
boost::asio::async_read(self.proxy_, get_buffer(self), boost::asio::bind_executor(self.strand_, completed{std::move(self_)}));
}
}
};
+ struct client::process_v5 : boost::asio::coroutine
+ {
+ std::shared_ptr<client> self_;
+
+ explicit process_v5(std::shared_ptr<client> self)
+ : boost::asio::coroutine(), self_(std::move(self))
+ {}
+
+ static boost::asio::mutable_buffer get_read_buffer(client& self, const std::size_t size)
+ {
+ const std::size_t offset =
+ std::accumulate(self.buffer_size_.begin(), self.buffer_size_.end(), std::size_t(0));
+ if (sizeof(self.buffer_) < offset || sizeof(self.buffer_) - offset < size)
+ throw std::runtime_error{"Not enough room for reading socks v5 buffer"};
+ return boost::asio::buffer(self.buffer_ + offset, size);
+ }
+
+ template<unsigned I>
+ static boost::asio::const_buffer get_write_buffer(const client& self) noexcept
+ {
+ const std::size_t offset =
+ std::accumulate(self.buffer_size_.begin(), self.buffer_size_.begin() + I, std::size_t(0));
+ return boost::asio::buffer(
+ self.buffer_ + offset, std::get<I>(self.buffer_size_)
+ );
+ }
+
+ void operator()(const boost::system::error_code error, std::size_t bytes)
+ {
+ if (!self_)
+ return;
+
+ client& self = *self_;
+ if (error)
+ {
+ self.done(error, self_);
+ return;
+ }
+
+ bool send_userpass = false;
+ BOOST_ASIO_CORO_REENTER(this)
+ {
+ // initial header already written
+
+ BOOST_ASIO_CORO_YIELD boost::asio::async_read(
+ self.proxy_,
+ get_read_buffer(self, sizeof(v5_response_initial)),
+ boost::asio::bind_executor(self.strand_, std::move(*this))
+ );
+ {
+ v5_response_initial header{};
+
+ assert(bytes == sizeof(header));
+ const auto buf = get_read_buffer(self, sizeof(header));
+ std::memcpy(std::addressof(header), buf.data(), sizeof(header));
+ if (header.version != 5)
+ {
+ self.done(socks::error::unexpected_version, self_);
+ return;
+ }
+ if (header.method != v5_noauth_method && header.method != v5_userpass_method)
+ {
+ self.done(socks::error::auth_failure, self_);
+ return;
+ }
+ send_userpass = (header.method == v5_userpass_method);
+ }
+
+ if (send_userpass)
+ {
+ if (!std::get<1>(self.buffer_size_))
+ {
+ self.done(socks::error::auth_failure, self_);
+ return;
+ }
+
+ BOOST_ASIO_CORO_YIELD boost::asio::async_write(
+ self.proxy_, get_write_buffer<1>(self), boost::asio::bind_executor(self.strand_, std::move(*this))
+ );
+ assert(bytes == std::get<1>(self.buffer_size_));
+
+ BOOST_ASIO_CORO_YIELD boost::asio::async_read(
+ self.proxy_,
+ get_read_buffer(self, sizeof(v5_response_auth)),
+ boost::asio::bind_executor(self.strand_, std::move(*this))
+ );
+ {
+ v5_response_auth header{};
+
+ assert(bytes == sizeof(header));
+ const auto buf = get_read_buffer(self, sizeof(header));
+ std::memcpy(std::addressof(header), buf.data(), sizeof(header));
+ if (header.version != v5_userpass_version)
+ {
+ self.done(socks::error::unexpected_version, self_);
+ return;
+ }
+ if (header.status != v5_reply_success)
+ {
+ self.done(socks::error::auth_failure, self_);
+ return;
+ }
+ }
+ }
+
+ BOOST_ASIO_CORO_YIELD boost::asio::async_write(
+ self.proxy_, get_write_buffer<2>(self), boost::asio::bind_executor(self.strand_, std::move(*this))
+ );
+ assert(bytes == std::get<2>(self.buffer_size_));
+
+ self.buffer_size_ = {};
+ BOOST_ASIO_CORO_YIELD boost::asio::async_read(
+ self.proxy_,
+ get_read_buffer(self, sizeof(v5_response_connect)),
+ boost::asio::bind_executor(self.strand_, std::move(*this))
+ );
+ {
+ v5_response_connect header{};
+
+ assert(bytes == sizeof(header));
+ const auto buf = get_read_buffer(self, sizeof(header));
+ std::memcpy(std::addressof(header), buf.data(), sizeof(header));
+ if (header.version != 5)
+ {
+ self.done(socks::error::unexpected_version, self_);
+ return;
+ }
+ if (header.reply != v5_reply_success)
+ {
+ self.done(socks::error(int(header.reply)), self_);
+ return;
+ }
+
+ if (header.type == v5_ipv4_type)
+ bytes = sizeof(v5_response_ipv4);
+ else if (header.type == v5_ipv6_type)
+ bytes = sizeof(v5_response_ipv6);
+ else
+ {
+ self.done(socks::error::unexpected_version, self_);
+ return;
+ }
+ }
+
+ std::get<0>(self.buffer_size_) = sizeof(v5_response_connect);
+ BOOST_ASIO_CORO_YIELD boost::asio::async_read(
+ self.proxy_,
+ get_read_buffer(self, bytes),
+ boost::asio::bind_executor(self.strand_, std::move(*this))
+ );
+ std::get<0>(self.buffer_size_) =
+ std::min(sizeof(self.buffer_), sizeof(v5_response_connect) + bytes);
+ self.done(error, self_);
+ }
+ }
+ };
+
struct client::write
{
std::shared_ptr<client> self_;
static boost::asio::const_buffer get_buffer(client const& self) noexcept
{
- return boost::asio::buffer(self.buffer_, self.buffer_size_);
+ return boost::asio::buffer(self.buffer_, std::get<0>(self.buffer_size_));
}
void operator()(const boost::system::error_code error)
@@ -214,20 +641,24 @@ namespace socks
{
client& self = *self_;
if (error)
- self.done(error, std::move(self_));
+ self.done(error, self_);
+ else if (self.ver_ == version::v5)
+ boost::asio::async_write(self.proxy_, get_buffer(self), boost::asio::bind_executor(self.strand_, process_v5{std::move(self_)}));
else
boost::asio::async_write(self.proxy_, get_buffer(self), boost::asio::bind_executor(self.strand_, read{std::move(self_)}));
}
}
};
+
+
client::client(stream_type::socket&& proxy, socks::version ver)
- : proxy_(std::move(proxy)), strand_(proxy_.get_executor()), buffer_size_(0), buffer_(), ver_(ver)
+ : proxy_(std::move(proxy)), strand_(proxy_.get_executor()), buffer_size_{{}}, buffer_(), ver_(ver)
{}
client::~client() {}
- bool client::set_connect_command(const epee::net_utils::ipv4_network_address& address)
+ bool client::set_connect_command(const epee::net_utils::ipv4_network_address& address, const user_and_pass* userinfo)
{
switch (socks_version())
{
@@ -235,6 +666,13 @@ namespace socks
case version::v4a:
case version::v4a_tor:
break;
+ case version::v5:
+ buffer_size_ = write_v5_address_connect(
+ buffer_,
+ v5_ipv4_connect::make(boost::endian::big_to_native(address.ip()), address.port()),
+ userinfo
+ );
+ return std::get<0>(buffer_size_) != 0;
default:
return false;
}
@@ -242,43 +680,64 @@ namespace socks
static_assert(sizeof(v4_header) < sizeof(buffer_), "buffer size too small for request");
static_assert(0 < sizeof(buffer_), "buffer size too small for null termination");
+ if (userinfo && (!userinfo->user.empty() || !userinfo->pass.empty()))
+ return false;
+
// version 4
const v4_header temp{4, v4_connect_command, address.port(), boost::endian::big_to_native(address.ip())};
std::memcpy(std::addressof(buffer_), std::addressof(temp), sizeof(temp));
buffer_[sizeof(temp)] = 0;
- buffer_size_ = sizeof(temp) + 1;
+
+ buffer_size_ = {};
+ std::get<0>(buffer_size_) = sizeof(temp) + 1;
return true;
}
- bool client::set_connect_command(const boost::string_ref domain, std::uint16_t port)
+ bool client::set_connect_command(const epee::net_utils::ipv6_network_address& address, const user_and_pass* userinfo)
+ {
+ if (socks_version() != version::v5)
+ return false;
+ buffer_size_ = write_v5_address_connect(
+ buffer_, v5_ipv6_connect::make(address.ip(), address.port()), userinfo
+ );
+ return std::get<0>(buffer_size_) != 0;
+ }
+
+ bool client::set_connect_command(const boost::string_ref domain, std::uint16_t port, const user_and_pass* userinfo)
{
switch (socks_version())
{
case version::v4a:
case version::v4a_tor:
break;
-
+ case version::v5:
+ buffer_size_ = write_v5_domain_connect(buffer_, port, domain, userinfo);
+ return std::get<0>(buffer_size_) != 0;
default:
return false;
}
+ if (userinfo && (!userinfo->user.empty() || !userinfo->pass.empty()))
+ return false;
+
const std::size_t buf_used = write_domain_header(buffer_, v4_connect_command, port, domain);
- buffer_size_ = buf_used;
+ buffer_size_ = {};
+ std::get<0>(buffer_size_) = buf_used;
return buf_used != 0;
}
- bool client::set_connect_command(const net::tor_address& address)
+ bool client::set_connect_command(const net::tor_address& address, const user_and_pass* userinfo)
{
if (!address.is_unknown())
- return set_connect_command(address.host_str(), address.port());
+ return set_connect_command(address.host_str(), address.port(), userinfo);
return false;
}
- bool client::set_connect_command(const net::i2p_address& address)
+ bool client::set_connect_command(const net::i2p_address& address, const user_and_pass* userinfo)
{
if (!address.is_unknown())
- return set_connect_command(address.host_str(), address.port());
+ return set_connect_command(address.host_str(), address.port(), userinfo);
return false;
}
@@ -288,13 +747,14 @@ namespace socks
return false;
const std::size_t buf_used = write_domain_header(buffer_, v4tor_resolve_command, 0, domain);
- buffer_size_ = buf_used;
+ buffer_size_ = {};
+ std::get<0>(buffer_size_) = buf_used;
return buf_used != 0;
}
bool client::connect_and_send(std::shared_ptr<client> self, const stream_type::endpoint& proxy_address)
{
- if (self && !self->buffer().empty())
+ if (self && std::get<0>(self->buffer_size_))
{
client& alias = *self;
alias.proxy_.async_connect(proxy_address, boost::asio::bind_executor(alias.strand_, write{std::move(self)}));
@@ -305,10 +765,13 @@ namespace socks
bool client::send(std::shared_ptr<client> self)
{
- if (self && !self->buffer().empty())
+ if (self && std::get<0>(self->buffer_size_))
{
client& alias = *self;
- boost::asio::async_write(alias.proxy_, write::get_buffer(alias), boost::asio::bind_executor(alias.strand_, read{std::move(self)}));
+ if (alias.ver_ == version::v5)
+ boost::asio::async_write(alias.proxy_, write::get_buffer(alias), boost::asio::bind_executor(alias.strand_, process_v5{std::move(self)}));
+ else
+ boost::asio::async_write(alias.proxy_, write::get_buffer(alias), boost::asio::bind_executor(alias.strand_, read{std::move(self)}));
return true;
}
return false;
diff --git a/src/net/socks.h b/src/net/socks.h
index 1c80ece2c..06d946d6a 100644
--- a/src/net/socks.h
+++ b/src/net/socks.h
@@ -28,6 +28,7 @@
#pragma once
+#include <array>
#include <cstdint>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
@@ -46,6 +47,7 @@ namespace epee
namespace net_utils
{
class ipv4_network_address;
+ class ipv6_network_address;
}
}
@@ -58,19 +60,30 @@ namespace socks
{
v4 = 0,
v4a,
- v4a_tor //!< Extensions defined in Tor codebase
+ v4a_tor, //!< Extensions defined in Tor codebase
+ v5
};
//! Possible errors with socks communication. Defined in https://www.openssh.com/txt/socks4.protocol
enum class error : int
{
// 0 is reserved for success value
- // 1-256 -> reserved for error values from socks server (+1 from wire value).
+ // v5 errors
+ general_failure = 1,
+ not_allowed,
+ network_unreachable,
+ host_unreachable,
+ connection_refused,
+ ttl_expired,
+ command_not_supported,
+ address_type_not_supported,
+ // v4 errors
rejected = 92,
identd_connection,
identd_user,
// Specific to application
- bad_read = 257,
+ auth_failure = 257,
+ bad_read,
bad_write,
unexpected_version
};
@@ -94,7 +107,7 @@ namespace socks
{
boost::asio::ip::tcp::socket proxy_;
boost::asio::strand<boost::asio::ip::tcp::socket::executor_type> strand_;
- std::uint16_t buffer_size_;
+ std::array<std::uint16_t, 3> buffer_size_;
std::uint8_t buffer_[1024];
socks::version ver_;
@@ -109,7 +122,7 @@ namespace socks
\param error when processing last command (if any).
\param self `shared_ptr<client>` handle to `this`.
*/
- virtual void done(boost::system::error_code error, std::shared_ptr<client> self) = 0;
+ virtual void done(boost::system::error_code error, const std::shared_ptr<client>& self) = 0;
public:
using stream_type = boost::asio::ip::tcp;
@@ -118,6 +131,7 @@ namespace socks
struct write;
struct read;
struct completed;
+ struct process_v5;
/*!
\param proxy ownership is passed into `this`. Does not have to be
@@ -139,33 +153,47 @@ namespace socks
//! \return Socks version.
socks::version socks_version() const noexcept { return ver_; }
- //! \return Contents of internal buffer.
+ //! \return Contents of first internal buffer
epee::span<const std::uint8_t> buffer() const noexcept
{
- return {buffer_, buffer_size_};
+ return {buffer_, std::get<0>(buffer_size_)};
}
- //! \post `buffer.empty()`.
- void clear_command() noexcept { buffer_size_ = 0; }
+ //! \post `buffer_[0] = 0, buffer_[1] = 0`.
+ void clear_command() noexcept { buffer_size_ = {}; }
//! Try to set `address` as remote connection request.
- bool set_connect_command(const epee::net_utils::ipv4_network_address& address);
+ bool set_connect_command(
+ const epee::net_utils::ipv4_network_address& address,
+ const user_and_pass* userinfo = nullptr);
+
+ //! Try to set `address` as remote connection request.
+ bool set_connect_command(
+ const epee::net_utils::ipv6_network_address& address,
+ const user_and_pass* userinfo = nullptr);
//! Try to set `domain` + `port` as remote connection request.
- bool set_connect_command(boost::string_ref domain, std::uint16_t port);
+ bool set_connect_command(
+ boost::string_ref domain,
+ std::uint16_t port,
+ const user_and_pass* userinfo = nullptr);
//! Try to set `address` as remote Tor hidden service connection request.
- bool set_connect_command(const net::tor_address& address);
+ bool set_connect_command(
+ const net::tor_address& address,
+ const user_and_pass* userinfo = nullptr);
//! Try to set `address` as remote i2p hidden service connection request.
- bool set_connect_command(const net::i2p_address& address);
+ bool set_connect_command(
+ const net::i2p_address& address,
+ const user_and_pass* userinfo = nullptr);
//! Try to set `domain` as remote DNS A record lookup request.
bool set_resolve_command(boost::string_ref domain);
/*!
- Asynchronously connect to `proxy_address` then issue command in
- `buffer()`. The `done(...)` method will be invoked upon completion
+ Asynchronously connect to `proxy_address` then issue command(s) in
+ `buffer_`. The `done(...)` method will be invoked upon completion
with `self` and potential `error`s.
\note Must use one of the `self->set_*_command` calls before using
@@ -181,7 +209,7 @@ namespace socks
/*!
Assume existing connection to proxy server; asynchronously issue
- command in `buffer()`. The `done(...)` method will be invoked
+ command in `buffer_`. The `done(...)` method will be invoked
upon completion with `self` and potential `error`s.
\note Must use one of the `self->set_*_command` calls before using
@@ -215,7 +243,7 @@ namespace socks
{
Handler handler_;
- virtual void done(boost::system::error_code error, std::shared_ptr<client>) override
+ virtual void done(boost::system::error_code error, const std::shared_ptr<client>&) override
{
handler_(error, take_socket());
}
diff --git a/src/net/socks_connect.cpp b/src/net/socks_connect.cpp
index 8ecbf6d08..c7c963cb7 100644
--- a/src/net/socks_connect.cpp
+++ b/src/net/socks_connect.cpp
@@ -28,6 +28,7 @@
#include "socks_connect.h"
+#include <boost/asio/ip/address_v6.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
#include <cstdint>
@@ -36,6 +37,7 @@
#include "net/error.h"
#include "net/net_utils_base.h"
+#include "net/parse.h"
#include "net/socks.h"
#include "string_tools.h"
#include "string_tools_lexical.h"
@@ -44,9 +46,22 @@ namespace net
{
namespace socks
{
+ namespace
+ {
+ bool get_v6_address(boost::asio::ip::address_v6& out, const std::string& source)
+ {
+ boost::system::error_code error{};
+ out = boost::asio::ip::make_address_v6(source, error);
+ return !error;
+ }
+ } // anonymous
+
boost::unique_future<boost::asio::ip::tcp::socket>
connector::operator()(const std::string& remote_host, const std::string& remote_port, boost::asio::steady_timer& timeout) const
{
+ if (!proxy_address)
+ throw std::runtime_error{"Unexpected nullptr of net::socks::endpoint"};
+
struct future_socket
{
boost::promise<boost::asio::ip::tcp::socket> result_;
@@ -68,18 +83,21 @@ namespace socks
bool is_set = false;
std::uint32_t ip_address = 0;
+ boost::asio::ip::address_v6 v6_address{};
boost::promise<boost::asio::ip::tcp::socket> result{};
out = result.get_future();
const auto proxy = net::socks::make_connect_client(
- boost::asio::ip::tcp::socket{MONERO_GET_EXECUTOR(timeout)}, net::socks::version::v4a, future_socket{std::move(result)}
+ boost::asio::ip::tcp::socket{MONERO_GET_EXECUTOR(timeout)}, proxy_address->ver, future_socket{std::move(result)}
);
if (epee::string_tools::get_ip_int32_from_string(ip_address, remote_host))
- is_set = proxy->set_connect_command(epee::net_utils::ipv4_network_address{ip_address, port});
+ is_set = proxy->set_connect_command(epee::net_utils::ipv4_network_address{ip_address, port}, std::addressof(proxy_address->userinfo));
+ else if (get_v6_address(v6_address, remote_host))
+ is_set = proxy->set_connect_command(epee::net_utils::ipv6_network_address{v6_address, port}, std::addressof(proxy_address->userinfo));
else
- is_set = proxy->set_connect_command(remote_host, port);
+ is_set = proxy->set_connect_command(remote_host, port, std::addressof(proxy_address->userinfo));
- if (!is_set || !net::socks::client::connect_and_send(proxy, proxy_address))
+ if (!is_set || !net::socks::client::connect_and_send(proxy, proxy_address->address))
throw std::system_error{net::error::invalid_host, "Address for socks proxy"};
timeout.async_wait(net::socks::client::async_close{std::move(proxy)});
diff --git a/src/net/socks_connect.h b/src/net/socks_connect.h
index 587e3cd3c..2baef8f94 100644
--- a/src/net/socks_connect.h
+++ b/src/net/socks_connect.h
@@ -31,8 +31,11 @@
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/thread/future.hpp>
+#include <memory>
#include <string>
+#include "net/fwd.h"
+
namespace net
{
namespace socks
@@ -40,7 +43,7 @@ namespace socks
//! Primarily for use with `epee::net_utils::http_client`.
struct connector
{
- boost::asio::ip::tcp::endpoint proxy_address;
+ std::shared_ptr<endpoint> proxy_address;
/*! Creates a new socket, asynchronously connects to `proxy_address`,
and requests a connection to `remote_host` on `remote_port`. Sets
diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt
index cc408cc3b..cdb440550 100644
--- a/src/p2p/CMakeLists.txt
+++ b/src/p2p/CMakeLists.txt
@@ -38,7 +38,6 @@ target_link_libraries(p2p
version
cryptonote_core
net
- ${UPNP_LIBRARIES}
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
index 4111b0b71..9f0e49f52 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -81,7 +81,7 @@ namespace
return {std::move(*address)};
}
- bool start_socks(std::shared_ptr<net::socks::client> client, const boost::asio::ip::tcp::endpoint& proxy, const epee::net_utils::network_address& remote)
+ bool start_socks(std::shared_ptr<net::socks::client> client, const net::socks::endpoint& proxy, const epee::net_utils::network_address& remote)
{
CHECK_AND_ASSERT_MES(client != nullptr, false, "Unexpected null client");
@@ -89,21 +89,28 @@ namespace
switch (remote.get_type_id())
{
case net::tor_address::get_type_id():
- set = client->set_connect_command(remote.as<net::tor_address>());
+ set = client->set_connect_command(remote.as<net::tor_address>(), std::addressof(proxy.userinfo));
break;
case net::i2p_address::get_type_id():
- set = client->set_connect_command(remote.as<net::i2p_address>());
+ set = client->set_connect_command(remote.as<net::i2p_address>(), std::addressof(proxy.userinfo));
break;
case epee::net_utils::ipv4_network_address::get_type_id():
- set = client->set_connect_command(remote.as<epee::net_utils::ipv4_network_address>());
+ set = client->set_connect_command(remote.as<epee::net_utils::ipv4_network_address>(), std::addressof(proxy.userinfo));
break;
+ case epee::net_utils::ipv6_network_address::get_type_id():
+ if (client->socks_version() == net::socks::version::v5)
+ {
+ set = client->set_connect_command(remote.as<epee::net_utils::ipv6_network_address>(), std::addressof(proxy.userinfo));
+ break;
+ }
+ /* fallthrough */
default:
- MERROR("Unsupported network address in socks_connect");
+ MERROR("Unsupported network address in socks_connect. Try socks5://");
return false;
}
const bool sent =
- set && net::socks::client::connect_and_send(std::move(client), proxy);
+ set && net::socks::client::connect_and_send(std::move(client), proxy.address);
CHECK_AND_ASSERT_MES(sent, false, "Unexpected failure to init socks client");
return true;
}
@@ -147,7 +154,7 @@ namespace nodetool
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_exclusive_node = {"add-exclusive-node", "Specify list of peers to connect to only."
" If this option is given the options add-priority-node and seed-node are ignored"};
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"};
- const command_line::arg_descriptor<std::vector<std::string> > arg_tx_proxy = {"tx-proxy", "Send local txes through proxy: <network-type>,<socks-ip:port>[,max_connections][,disable_noise] i.e. \"tor,127.0.0.1:9050,100,disable_noise\""};
+ const command_line::arg_descriptor<std::vector<std::string> > arg_tx_proxy = {"tx-proxy", "Send local txes through proxy: <network-type>,[socks5://[user:pass@]]<socks-ip:port>[,max_connections][,disable_noise] i.e. \"tor,127.0.0.1:9050,100,disable_noise\""};
const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound = {"anonymous-inbound", "<hidden-service-address>,<[bind-ip:]port>[,max_connections] i.e. \"x.onion,127.0.0.1:18083,100\""};
const command_line::arg_descriptor<std::string> arg_ban_list = {"ban-list", "Specify ban list file, one IP address per line"};
const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true};
@@ -155,7 +162,7 @@ namespace nodetool
const command_line::arg_descriptor<bool> arg_enable_dns_blocklist = {"enable-dns-blocklist", "Apply realtime blocklist from DNS", false};
const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"};
- const command_line::arg_descriptor<std::string> arg_igd = {"igd", "UPnP port mapping (disabled, enabled, delayed)", "delayed"};
+ const command_line::arg_descriptor<std::string> arg_igd = {"igd", "UPnP port mapping (disabled)", "disabled"};
const command_line::arg_descriptor<bool> arg_p2p_use_ipv6 = {"p2p-use-ipv6", "Enable IPv6 for p2p", false};
const command_line::arg_descriptor<bool> arg_p2p_ignore_ipv4 = {"p2p-ignore-ipv4", "Ignore unsuccessful IPv4 bind for p2p", false};
const command_line::arg_descriptor<int64_t> arg_out_peers = {"out-peers", "set max number of out peers", -1};
@@ -189,7 +196,7 @@ namespace nodetool
const boost::string_ref zone{next->begin(), next->size()};
++next;
- CHECK_AND_ASSERT_MES(!next.eof() && !next->empty(), boost::none, "No ipv4:port given for --" << arg_tx_proxy.name);
+ CHECK_AND_ASSERT_MES(!next.eof() && !next->empty(), boost::none, "No ip:port given for --" << arg_tx_proxy.name);
const boost::string_ref proxy{next->begin(), next->size()};
++next;
@@ -227,14 +234,14 @@ namespace nodetool
return boost::none;
}
- std::uint32_t ip = 0;
- std::uint16_t port = 0;
- if (!epee::string_tools::parse_peer_from_string(ip, port, std::string{proxy}) || port == 0)
+ auto endpoint = net::socks::endpoint::get(proxy);
+ if (!endpoint)
{
- MERROR("Invalid ipv4:port given for --" << arg_tx_proxy.name);
+ MERROR("Invalid --" << arg_tx_proxy.name << " value: " << endpoint.error().message());
return boost::none;
}
- proxies.back().address = ip::tcp::endpoint{ip::address_v4{boost::endian::native_to_big(ip)}, port};
+
+ proxies.back().address = std::move(*endpoint);
}
return proxies;
@@ -327,7 +334,7 @@ namespace nodetool
}
boost::optional<boost::asio::ip::tcp::socket>
- socks_connect_internal(const std::atomic<bool>& stop_signal, boost::asio::io_context& service, const boost::asio::ip::tcp::endpoint& proxy, const epee::net_utils::network_address& remote)
+ socks_connect_internal(const std::atomic<bool>& stop_signal, boost::asio::io_context& service, const net::socks::endpoint& proxy, const epee::net_utils::network_address& remote)
{
using socket_type = net::socks::client::stream_type::socket;
using client_result = std::pair<boost::system::error_code, socket_type>;
@@ -349,7 +356,7 @@ namespace nodetool
socks_result = socks_promise.get_future();
auto client = net::socks::make_connect_client(
- boost::asio::ip::tcp::socket{service}, net::socks::version::v4a, notify{std::move(socks_promise)}
+ boost::asio::ip::tcp::socket{service}, proxy.ver, notify{std::move(socks_promise)}
);
close_client.self = client;
if (!start_socks(std::move(client), proxy, remote))
@@ -361,7 +368,7 @@ namespace nodetool
{
if (socks_connect_timeout < std::chrono::steady_clock::now() - start)
{
- MERROR("Timeout on socks connect (" << proxy << " to " << remote.str() << ")");
+ MERROR("Timeout on socks connect (" << proxy.address << " to " << remote.str() << ")");
return boost::none;
}
@@ -378,7 +385,7 @@ namespace nodetool
return {std::move(result.second)};
}
- MERROR("Failed to make socks connection to " << remote.str() << " (via " << proxy << "): " << result.first.message());
+ MERROR("Failed to make socks connection to " << remote.str() << " (via " << proxy.address << "): " << result.first.message());
}
catch (boost::broken_promise const&)
{}
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 423e2607e..8e3312f29 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -56,7 +56,7 @@
#include "math_helper.h"
#include "net_node_common.h"
#include "net/enums.h"
-#include "net/fwd.h"
+#include "net/parse.h"
#include "common/command_line.h"
PUSH_WARNINGS
@@ -74,7 +74,7 @@ namespace nodetool
{}
std::int64_t max_connections;
- boost::asio::ip::tcp::endpoint address;
+ net::socks::endpoint address;
epee::net_utils::zone zone;
bool noise;
};
@@ -104,7 +104,7 @@ namespace nodetool
// hides boost::future and chrono stuff from mondo template file
boost::optional<boost::asio::ip::tcp::socket>
- socks_connect_internal(const std::atomic<bool>& stop_signal, boost::asio::io_context& service, const boost::asio::ip::tcp::endpoint& proxy, const epee::net_utils::network_address& remote);
+ socks_connect_internal(const std::atomic<bool>& stop_signal, boost::asio::io_context& service, const net::socks::endpoint& proxy, const epee::net_utils::network_address& remote);
template<class base_type>
@@ -216,7 +216,7 @@ namespace nodetool
epee::net_utils::network_address m_our_address; // in anonymity networks
peerlist_manager m_peerlist;
config m_config;
- boost::asio::ip::tcp::endpoint m_proxy_address;
+ net::socks::endpoint m_proxy_address;
std::atomic<unsigned int> m_current_number_of_out_peers;
std::atomic<unsigned int> m_current_number_of_in_peers;
boost::shared_mutex m_seed_nodes_lock;
@@ -237,12 +237,6 @@ namespace nodetool
}
};
- enum igd_t
- {
- no_igd,
- igd,
- delayed_igd,
- };
public:
typedef t_payload_net_handler payload_net_handler;
@@ -254,7 +248,6 @@ namespace nodetool
m_rpc_credits_per_hash(0),
m_allow_local_ip(false),
m_hide_my_port(false),
- m_igd(no_igd),
m_offline(false),
is_closing(false),
m_network_id(),
@@ -382,14 +375,6 @@ namespace nodetool
bool is_peer_used(const peerlist_entry& peer);
bool is_peer_used(const anchor_peerlist_entry& peer);
bool is_addr_connected(const epee::net_utils::network_address& peer);
- void add_upnp_port_mapping_impl(uint32_t port, bool ipv6=false);
- void add_upnp_port_mapping_v4(uint32_t port);
- void add_upnp_port_mapping_v6(uint32_t port);
- void add_upnp_port_mapping(uint32_t port, bool ipv4=true, bool ipv6=false);
- void delete_upnp_port_mapping_impl(uint32_t port, bool ipv6=false);
- void delete_upnp_port_mapping_v4(uint32_t port);
- void delete_upnp_port_mapping_v6(uint32_t port);
- void delete_upnp_port_mapping(uint32_t port);
template<class t_callback>
bool try_ping(basic_node_data& node_data, p2p_connection_context& context, const t_callback &cb);
bool try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f);
@@ -462,7 +447,6 @@ namespace nodetool
uint32_t m_rpc_credits_per_hash;
bool m_allow_local_ip;
bool m_hide_my_port;
- igd_t m_igd;
bool m_offline;
bool m_use_ipv6;
bool m_require_ipv4;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index bca8a52af..11474aaaa 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -60,9 +60,6 @@
#include "cryptonote_core/cryptonote_core.h"
#include "net/parse.h"
-#include <miniupnp/miniupnpc/miniupnpc.h>
-#include <miniupnp/miniupnpc/upnpcommands.h>
-#include <miniupnp/miniupnpc/upnperrors.h>
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.p2p"
@@ -431,8 +428,9 @@ namespace nodetool
{
bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
+ bool regtest = command_line::get_arg(vm, cryptonote::arg_regtest_on);
const bool pad_txs = command_line::get_arg(vm, arg_pad_transactions);
- m_nettype = testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET;
+ m_nettype = testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : regtest ? cryptonote::FAKECHAIN : cryptonote::MAINNET;
network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
public_zone.m_connect = &public_connect;
@@ -443,34 +441,8 @@ namespace nodetool
public_zone.m_can_pingback = true;
m_external_port = command_line::get_arg(vm, arg_p2p_external_port);
m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip);
- const bool has_no_igd = command_line::get_arg(vm, arg_no_igd);
- const std::string sigd = command_line::get_arg(vm, arg_igd);
- if (sigd == "enabled")
- {
- if (has_no_igd)
- {
- MFATAL("Cannot have both --" << arg_no_igd.name << " and --" << arg_igd.name << " enabled");
- return false;
- }
- m_igd = igd;
- }
- else if (sigd == "disabled")
- {
- m_igd = no_igd;
- }
- else if (sigd == "delayed")
- {
- if (has_no_igd && !command_line::is_arg_defaulted(vm, arg_igd))
- {
- MFATAL("Cannot have both --" << arg_no_igd.name << " and --" << arg_igd.name << " delayed");
- return false;
- }
- m_igd = has_no_igd ? no_igd : delayed_igd;
- }
- else
- {
- MFATAL("Invalid value for --" << arg_igd.name << ", expected enabled, disabled or delayed");
- return false;
+ if (!command_line::is_arg_defaulted(vm, arg_igd)) {
+ MWARNING("UPnP port mapping support was removed. The --igd option is currently non-functional.");
}
m_offline = command_line::get_arg(vm, cryptonote::arg_offline);
m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
@@ -777,6 +749,10 @@ namespace nodetool
{
return get_ip_seed_nodes();
}
+ if (m_nettype == cryptonote::FAKECHAIN)
+ {
+ return {};
+ }
if (!m_enable_dns_seed_nodes)
{
// TODO: a domain can be set through socks, so that the remote side does the lookup for the DNS seed nodes.
@@ -928,8 +904,8 @@ namespace nodetool
CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line");
if (proxy.size())
{
- const auto endpoint = net::get_tcp_endpoint(proxy);
- CHECK_AND_ASSERT_MES(endpoint, false, "Failed to parse proxy: " << proxy << " - " << endpoint.error());
+ const auto endpoint = net::socks::endpoint::get(proxy);
+ CHECK_AND_ASSERT_MES(endpoint, false, "Failed to parse proxy: " << proxy << " - " << endpoint.error().message());
network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
public_zone.m_connect = &socks_connect;
public_zone.m_proxy_address = *endpoint;
@@ -1018,16 +994,6 @@ namespace nodetool
if(m_external_port)
MDEBUG("External port defined as " << m_external_port);
- // add UPnP port mapping
- if(m_igd == igd)
- {
- add_upnp_port_mapping_v4(m_listening_port);
- if (m_use_ipv6)
- {
- add_upnp_port_mapping_v6(m_listening_port_ipv6);
- }
- }
-
return res;
}
//-----------------------------------------------------------------------------------
@@ -1109,9 +1075,6 @@ namespace nodetool
{
for(auto& zone : m_network_zones)
zone.second.m_net_server.deinit_server();
- // remove UPnP port mapping
- if(m_igd == igd)
- delete_upnp_port_mapping(m_listening_port);
}
return store_config();
}
@@ -1144,6 +1107,8 @@ namespace nodetool
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::send_stop_signal()
{
+ MDEBUG("[node] stopping server payload handler");
+ m_payload_handler.stop();
MDEBUG("[node] sending stop signal");
for (auto& zone : m_network_zones)
{
@@ -1166,7 +1131,6 @@ namespace nodetool
zone.second.m_net_server.send_stop_signal(close_all_connections);
}
MDEBUG("[node] Stop signal sent");
- m_payload_handler.stop();
return true;
}
//-----------------------------------------------------------------------------------
@@ -2207,17 +2171,8 @@ namespace nodetool
}
else
{
- if (m_igd == delayed_igd)
- {
- MWARNING("No incoming connections, trying to setup IGD");
- add_upnp_port_mapping(m_listening_port);
- m_igd = igd;
- }
- else
- {
- const el::Level level = el::Level::Warning;
- MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port());
- }
+ const el::Level level = el::Level::Warning;
+ MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port());
}
}
return true;
@@ -3076,139 +3031,6 @@ namespace nodetool
e.clear();
}
- template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::add_upnp_port_mapping_impl(uint32_t port, bool ipv6) // if ipv6 false, do ipv4
- {
- std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)";
- MDEBUG("Attempting to add IGD port mapping " << ipversion << ".");
- int result;
- const int ipv6_arg = ipv6 ? 1 : 0;
-
-#if MINIUPNPC_API_VERSION > 13
- // default according to miniupnpc.h
- unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result);
-#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result);
-#endif
- UPNPUrls urls;
- IGDdatas igdData;
- char lanAddress[64];
- result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
- freeUPNPDevlist(deviceList);
- if (result > 0) {
- if (result == 1) {
- std::ostringstream portString;
- portString << port;
-
- // Delete the port mapping before we create it, just in case we have dangling port mapping from the daemon not being shut down correctly
- UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0);
-
- int portMappingResult;
- portMappingResult = UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), portString.str().c_str(), lanAddress, CRYPTONOTE_NAME, "TCP", 0, "0");
- if (portMappingResult != 0) {
- LOG_ERROR("UPNP_AddPortMapping failed, error: " << strupnperror(portMappingResult));
- } else {
- MLOG_GREEN(el::Level::Info, "Added IGD port mapping.");
- }
- } else if (result == 2) {
- MWARNING("IGD was found but reported as not connected.");
- } else if (result == 3) {
- MWARNING("UPnP device was found but not recognized as IGD.");
- } else {
- MWARNING("UPNP_GetValidIGD returned an unknown result code.");
- }
-
- FreeUPNPUrls(&urls);
- } else {
- MINFO("No IGD was found.");
- }
- }
-
- template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::add_upnp_port_mapping_v4(uint32_t port)
- {
- add_upnp_port_mapping_impl(port, false);
- }
-
- template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::add_upnp_port_mapping_v6(uint32_t port)
- {
- add_upnp_port_mapping_impl(port, true);
- }
-
- template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port, bool ipv4, bool ipv6)
- {
- if (ipv4) add_upnp_port_mapping_v4(port);
- if (ipv6) add_upnp_port_mapping_v6(port);
- }
-
-
- template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::delete_upnp_port_mapping_impl(uint32_t port, bool ipv6)
- {
- std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)";
- MDEBUG("Attempting to delete IGD port mapping " << ipversion << ".");
- int result;
- const int ipv6_arg = ipv6 ? 1 : 0;
-#if MINIUPNPC_API_VERSION > 13
- // default according to miniupnpc.h
- unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result);
-#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result);
-#endif
- UPNPUrls urls;
- IGDdatas igdData;
- char lanAddress[64];
- result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
- freeUPNPDevlist(deviceList);
- if (result > 0) {
- if (result == 1) {
- std::ostringstream portString;
- portString << port;
-
- int portMappingResult;
- portMappingResult = UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0);
- if (portMappingResult != 0) {
- LOG_ERROR("UPNP_DeletePortMapping failed, error: " << strupnperror(portMappingResult));
- } else {
- MLOG_GREEN(el::Level::Info, "Deleted IGD port mapping.");
- }
- } else if (result == 2) {
- MWARNING("IGD was found but reported as not connected.");
- } else if (result == 3) {
- MWARNING("UPnP device was found but not recognized as IGD.");
- } else {
- MWARNING("UPNP_GetValidIGD returned an unknown result code.");
- }
-
- FreeUPNPUrls(&urls);
- } else {
- MINFO("No IGD was found.");
- }
- }
-
- template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v4(uint32_t port)
- {
- delete_upnp_port_mapping_impl(port, false);
- }
-
- template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v6(uint32_t port)
- {
- delete_upnp_port_mapping_impl(port, true);
- }
-
- template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port)
- {
- delete_upnp_port_mapping_v4(port);
- delete_upnp_port_mapping_v6(port);
- }
-
template<typename t_payload_net_handler>
boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>>
node_server<t_payload_net_handler>::socks_connect(network_zone& zone, const epee::net_utils::network_address& remote, epee::net_utils::ssl_support_t ssl_support)
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index c84746975..c68449630 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -2064,6 +2064,15 @@ namespace cryptonote
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ADD_AUX_POW>(invoke_http_mode::JON_RPC, "add_aux_pow", req, res, r))
return r;
+ const bool restricted = m_restricted && ctx;
+
+ if (restricted && req.aux_pow.size() > 10)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_RESTRICTED;
+ error_resp.message = "Too many aux pow hashes";
+ return false;
+ }
+
if (req.aux_pow.empty())
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
@@ -2099,7 +2108,7 @@ namespace cryptonote
while ((1u << path_domain) < aux_pow.size())
++path_domain;
uint32_t nonce;
- const uint32_t max_nonce = 65535;
+ const uint32_t max_nonce = restricted ? 16384 : 65535;
bool collision = true;
for (nonce = 0; nonce <= max_nonce; ++nonce)
{
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 671889ddd..10b20282e 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -30,6 +30,7 @@
#include "rpc/zmq_restricted_methods.h"
#include <algorithm>
+#include <chrono>
#include <cstring>
#include <stdexcept>
@@ -43,6 +44,15 @@
#include "ringct/rctSigs.h"
#include "version.h"
+namespace
+{
+constexpr size_t restricted_max_fake_outs = 5000;
+constexpr auto restricted_histogram_cutoff = std::chrono::hours{3 * 24};
+constexpr size_t restricted_max_txs = 100;
+constexpr size_t restricted_max_key_images = 5000;
+constexpr size_t restricted_max_block_headers = 1000;
+}
+
namespace cryptonote
{
@@ -236,6 +246,13 @@ namespace rpc
void DaemonHandler::handle(const GetTransactions::Request& req, GetTransactions::Response& res)
{
+ if (m_restricted && req.tx_hashes.size() > restricted_max_txs)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Too many transactions requested in restricted mode";
+ return;
+ }
+
std::vector<cryptonote::transaction> found_txs_vec;
std::vector<crypto::hash> missed_vec;
@@ -301,6 +318,13 @@ namespace rpc
void DaemonHandler::handle(const KeyImagesSpent::Request& req, KeyImagesSpent::Response& res)
{
+ if (m_restricted && req.key_images.size() > restricted_max_key_images)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Too many key images queried in restricted mode";
+ return;
+ }
+
res.spent_status.resize(req.key_images.size(), KeyImagesSpent::STATUS::UNSPENT);
std::vector<bool> chain_spent_status;
@@ -534,17 +558,17 @@ namespace rpc
res.info.tx_count = chain.get_total_transactions() - res.info.height; //without coinbase
- res.info.tx_pool_size = m_core.get_pool_transactions_count();
+ res.info.tx_pool_size = m_core.get_pool_transactions_count(!m_restricted);
- res.info.alt_blocks_count = chain.get_alternative_blocks_count();
+ res.info.alt_blocks_count = m_restricted ? 0 : chain.get_alternative_blocks_count();
- uint64_t total_conn = m_p2p.get_public_connections_count();
- res.info.outgoing_connections_count = m_p2p.get_public_outgoing_connections_count();
- res.info.incoming_connections_count = total_conn - res.info.outgoing_connections_count;
+ uint64_t total_conn = m_restricted ? 0 : m_p2p.get_public_connections_count();
+ res.info.outgoing_connections_count = m_restricted ? 0 : m_p2p.get_public_outgoing_connections_count();
+ res.info.incoming_connections_count = m_restricted ? 0 : total_conn - res.info.outgoing_connections_count;
- res.info.white_peerlist_size = m_p2p.get_public_white_peers_count();
+ res.info.white_peerlist_size = m_restricted ? 0 : m_p2p.get_public_white_peers_count();
- res.info.grey_peerlist_size = m_p2p.get_public_gray_peers_count();
+ res.info.grey_peerlist_size = m_restricted ? 0 : m_p2p.get_public_gray_peers_count();
res.info.mainnet = m_core.get_nettype() == MAINNET;
res.info.testnet = m_core.get_nettype() == TESTNET;
@@ -554,8 +578,8 @@ namespace rpc
res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.info.adjusted_time = m_core.get_blockchain_storage().get_adjusted_time(res.info.height);
- res.info.start_time = (uint64_t)m_core.get_start_time();
- res.info.version = MONERO_VERSION;
+ res.info.start_time = m_restricted ? 0 : (uint64_t)m_core.get_start_time();
+ res.info.version = m_restricted ? "" : MONERO_VERSION;
res.status = Message::STATUS_OK;
res.error_details = "";
@@ -674,6 +698,13 @@ namespace rpc
void DaemonHandler::handle(const GetBlockHeadersByHeight::Request& req, GetBlockHeadersByHeight::Response& res)
{
+ if (m_restricted && req.heights.size() > restricted_max_block_headers)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Too many block headers requested in restricted mode";
+ return;
+ }
+
res.headers.resize(req.heights.size());
for (size_t i=0; i < req.heights.size(); i++)
@@ -791,6 +822,23 @@ namespace rpc
void DaemonHandler::handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res)
{
+ size_t amounts = req.amounts.size();
+ if (m_restricted && amounts == 0)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Restricted RPC will not serve histograms on the whole blockchain. Use your own node.";
+ return;
+ }
+
+ using clock = std::chrono::system_clock;
+ const clock::time_point cutoff{std::chrono::seconds{req.recent_cutoff}};
+ if (m_restricted && clock::now() - cutoff > restricted_histogram_cutoff)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Recent cutoff is too old";
+ return;
+ }
+
std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t> > histogram;
try
{
@@ -816,6 +864,13 @@ namespace rpc
void DaemonHandler::handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res)
{
+ if (m_restricted && req.outputs.size() > restricted_max_fake_outs)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Too many outs requested";
+ return;
+ }
+
try
{
for (const auto& i : req.outputs)
@@ -870,6 +925,13 @@ namespace rpc
{
try
{
+ if (m_restricted && req.amounts != std::vector<uint64_t>(1, 0))
+ {
+ res.distributions.clear();
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Restricted RPC can only get output distribution for rct outputs. Use your own node.";
+ return;
+ }
res.distributions.reserve(req.amounts.size());
const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
diff --git a/src/serialization/container.h b/src/serialization/container.h
index c64549c1e..7ba4d307f 100644
--- a/src/serialization/container.h
+++ b/src/serialization/container.h
@@ -28,6 +28,12 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+#include <algorithm>
+#include <cstdint>
+#include <cstddef>
+#include <limits>
+#include <type_traits>
+
namespace serialization
{
namespace detail
@@ -57,8 +63,30 @@ namespace serialization
return true;
}
- template <typename C>
- void do_reserve(C &c, size_t N) {}
+ //! @brief Reserve space for N elements if applicable for container.
+ template<typename... C>
+ void do_reserve(const C&...) {}
+ template<typename C>
+ auto do_reserve(C &c, std::size_t N, std::size_t B) -> decltype(c.reserve(N))
+ {
+ using T = typename C::value_type;
+
+ static constexpr std::size_t max_compression_ratio =
+ is_blob_type<T>::type::value ? 1 :
+ use_container_varint<T>() ? sizeof(T) :
+ (std::is_same<T, char>::value || std::is_same<T, unsigned char>::value) ? 1:
+ 4; // default
+
+ // max compression ratio for upfront memory usage
+ B /= sizeof(T);
+ B = std::max(std::size_t(1), B);
+ if (std::numeric_limits<std::size_t>::max() / max_compression_ratio <= B)
+ B = std::numeric_limits<std::size_t>::max();
+ else
+ B *= max_compression_ratio;
+
+ return c.reserve(std::min(N, B));
+ }
}
}
@@ -77,7 +105,7 @@ bool do_serialize_container(Archive<false> &ar, C &v)
return false;
}
- ::serialization::detail::do_reserve(v, cnt);
+ ::serialization::detail::do_reserve(v, cnt, ar.remaining_bytes());
for (size_t i = 0; i < cnt; i++) {
if (i > 0)
diff --git a/src/serialization/containers.h b/src/serialization/containers.h
index dd2de829a..d4c3b6626 100644
--- a/src/serialization/containers.h
+++ b/src/serialization/containers.h
@@ -87,7 +87,6 @@ namespace serialization
{
namespace detail
{
- template <typename T> void do_reserve(std::vector<T> &c, size_t N) { c.reserve(N); }
template <typename T> void do_add(std::vector<T> &c, T &&e) { c.emplace_back(std::forward<T>(e)); }
template <typename T> void do_add(std::deque<T> &c, T &&e) { c.emplace_back(std::forward<T>(e)); }
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index af1f03d2c..723635481 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -436,7 +436,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
{
proxy = command_line::get_arg(vm, opts.proxy);
THROW_WALLET_EXCEPTION_IF(
- !net::get_tcp_endpoint(proxy),
+ !net::socks::endpoint::get(proxy),
tools::error::wallet_internal_error,
std::string{"Invalid address specified for --"} + opts.proxy.name);
}
@@ -983,7 +983,7 @@ bool get_pruned_tx(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry &entry,
// only v2 txes can calculate their txid after pruned
if (bd[0] > 1)
{
- tx_hash = cryptonote::get_pruned_transaction_hash(tx, ph);
+ CHECK_AND_ASSERT_MES(cryptonote::get_pruned_transaction_hash(tx, ph, tx_hash), false, "Failed to get pruned tx hash");
}
else
{
@@ -5502,7 +5502,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
{
get_custom_background_key(password, key, kdf_rounds);
crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
- const bool is_background_wallet = json.Parse(account_data.c_str()).HasParseError() && json.IsObject();
+ const bool is_background_wallet = !json.Parse(account_data.c_str()).HasParseError() && json.IsObject();
no_spend_key = no_spend_key || is_background_wallet;
}
}
@@ -8282,6 +8282,8 @@ bool wallet2::parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx
for (size_t idx: ptx.construction_data.selected_transfers)
CHECK_AND_ASSERT_MES(idx < m_transfers.size(), false, "Transfer index out of range");
CHECK_AND_ASSERT_MES(ptx.construction_data.sources.size() == ptx.tx.vin.size(), false, "Mismatched sources/vin sizes");
+ CHECK_AND_ASSERT_MES(!ptx.tx.vin.empty(), false, "Multisig tx has no inputs");
+ CHECK_AND_ASSERT_MES(!ptx.construction_data.sources.empty(), false, "Multisig tx has no sources");
}
return true;
@@ -12479,7 +12481,7 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
req.client = get_client_signature();
uint64_t pre_call_credits = m_rpc_payment_state.credits;
bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
- THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ THROW_WALLET_EXCEPTION_IF(!ok || res.txs.size() != 1,
error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX);
}
@@ -12487,15 +12489,12 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
cryptonote::transaction tx;
crypto::hash tx_hash{};
cryptonote::blobdata tx_data;
- crypto::hash tx_prefix_hash{};
- bool ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
+ bool ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
- THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash),
- error::wallet_internal_error, "Failed to validate transaction from daemon");
THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error,
"Failed to get the right transaction from daemon");
- tx_key_data.tx_prefix_hash = std::string(tx_prefix_hash.data, 32);
+ tx_key_data.tx_prefix_hash = std::string(get_transaction_prefix_hash(tx).data, 32);
}
std::vector<crypto::secret_key> tx_keys;
@@ -12605,6 +12604,7 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
cryptonote::transaction tx;
crypto::hash tx_hash;
THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error, "Failed to get tx from daemon");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
std::vector<std::vector<crypto::signature>> signatures;
@@ -12726,6 +12726,7 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
cryptonote::transaction tx;
crypto::hash tx_hash;
THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error, "failed to get tx from daemon");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
// check signature size
size_t num_sigs = 0;
@@ -12848,19 +12849,8 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
}
else
{
- crypto::secret_key scalar1;
- crypto::derivation_to_scalar(found_derivation, n, scalar1);
- rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
- const rct::key C = tx.rct_signatures.outPk[n].mask;
- rct::key Ctmp;
- THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
- THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.amount.bytes) != 0, error::wallet_internal_error, "Bad ECDH input amount");
- rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
- if (rct::equalKeys(C, Ctmp))
- amount = rct::h2d(ecdh_info.amount);
- else
- amount = 0;
+ rct::key mask;
+ amount = decodeRct(tx.rct_signatures, found_derivation, n, mask, hw::get_device("default"));
}
received += amount;
}
@@ -12884,27 +12874,15 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
- THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ THROW_WALLET_EXCEPTION_IF(!ok || res.txs.size() != 1,
error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
}
cryptonote::transaction tx;
crypto::hash tx_hash;
- if (res.txs.size() == 1)
- {
- ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
- THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
- }
- else
- {
- cryptonote::blobdata tx_data;
- ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
- THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
- THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx),
- error::wallet_internal_error, "Failed to validate transaction from daemon");
- tx_hash = cryptonote::get_transaction_hash(tx);
- }
+ ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error,
"Failed to get the right transaction from daemon");
@@ -12982,27 +12960,15 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
ok = net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
- THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ THROW_WALLET_EXCEPTION_IF(!ok || res.txs.size() != 1,
error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
}
cryptonote::transaction tx;
crypto::hash tx_hash;
- if (res.txs.size() == 1)
- {
- ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
- THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
- }
- else
- {
- cryptonote::blobdata tx_data;
- ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
- THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
- THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx),
- error::wallet_internal_error, "Failed to validate transaction from daemon");
- tx_hash = cryptonote::get_transaction_hash(tx);
- }
+ ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
@@ -13143,27 +13109,15 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account
uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature();
ok = net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
- THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ THROW_WALLET_EXCEPTION_IF(!ok || res.txs.size() != 1,
error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
}
cryptonote::transaction tx;
crypto::hash tx_hash;
- if (res.txs.size() == 1)
- {
- ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
- THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
- }
- else
- {
- cryptonote::blobdata tx_data;
- ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
- THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
- THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx),
- error::wallet_internal_error, "Failed to validate transaction from daemon");
- tx_hash = cryptonote::get_transaction_hash(tx);
- }
+ ok = get_pruned_tx(res.txs.front(), tx, tx_hash);
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
@@ -13176,7 +13130,7 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account
{
std::string err;
uint64_t bc_height = get_daemon_blockchain_height(err);
- if (err.empty())
+ if (err.empty() && bc_height > res.txs.front().block_height)
confirmations = bc_height - res.txs.front().block_height;
}
@@ -13433,11 +13387,26 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
loaded = true;
}
catch(...) {}
- if (!loaded && m_load_deprecated_formats)
+ try
{
- std::istringstream iss(sig_decoded);
- boost::archive::portable_binary_iarchive ar(iss);
- ar >> proofs >> subaddr_spendkeys.parent();
+ if (!loaded && m_load_deprecated_formats)
+ {
+ std::istringstream iss(sig_decoded);
+ boost::archive::portable_binary_iarchive ar(iss);
+ ar >> proofs >> subaddr_spendkeys.parent();
+ loaded = true;
+ }
+ }
+ catch(...) {}
+
+ THROW_WALLET_EXCEPTION_IF(!loaded, error::wallet_internal_error, "Failed to parse reserve proof signature data");
+
+ std::unordered_set<crypto::key_image> seen_key_images;
+ std::set<std::pair<crypto::hash, uint64_t>> seen_outputs;
+ for (const reserve_proof_entry &proof : proofs)
+ {
+ THROW_WALLET_EXCEPTION_IF(!seen_key_images.insert(proof.key_image).second, error::wallet_internal_error, "Duplicate key image in reserve proof");
+ THROW_WALLET_EXCEPTION_IF(!seen_outputs.emplace(proof.txid, proof.index_in_tx).second, error::wallet_internal_error, "Duplicate output in reserve proof");
}
THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(address.m_spend_public_key) == 0, error::wallet_internal_error,
@@ -13538,11 +13507,8 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
if (amount == 0)
{
// decode rct
- crypto::secret_key shared_secret;
- crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
- rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
- amount = rct::h2d(ecdh_info.amount);
+ rct::key mask_;
+ amount = decodeRct(tx.rct_signatures, derivation, proof.index_in_tx, mask_, hw::get_device("default"));
}
total += amount;
if (kispent_res.spent_status[i])
@@ -15398,7 +15364,7 @@ void wallet2::update_multisig_rescan_info(const std::vector<std::vector<rct::key
m_key_images[td.m_key_image] = n;
}
//----------------------------------------------------------------------------------------------------
-size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
+size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs, bool refresh_after_import)
{
CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
@@ -15523,8 +15489,8 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
update_multisig_rescan_info(m_multisig_rescan_k, m_multisig_rescan_info, n);
}
-
- refresh(false);
+ if (refresh_after_import)
+ refresh(false);
return n_outputs;
}
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index a765dc475..cedfecbeb 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -994,9 +994,11 @@ private:
cryptonote::blobdata export_multisig();
/*!
* Import a set of multisig info from multisig partners
+ * \param info Multisig info from other participants
+ * \param refresh_after_import Whether to refresh the wallet and rescan spent outputs after importing
* \return the number of inputs which were imported
*/
- size_t import_multisig(std::vector<cryptonote::blobdata> info);
+ size_t import_multisig(std::vector<cryptonote::blobdata> info, bool refresh_after_import = true);
/*!
* \brief Rewrites to the wallet file for wallet upgrade (doesn't generate key, assumes it's already there)
* \param wallet_name Name of wallet file (should exist)
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 531dc3197..da0a0a1c1 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -75,8 +75,20 @@ using namespace epee;
} while(0)
#define CHECK_IF_BACKGROUND_SYNCING() \
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING_BASE(false)
+
+#define CHECK_IF_RESTRICTED_BACKGROUND_SYNCING() \
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING_BASE(true)
+
+#define CHECK_IF_RESTRICTED_BACKGROUND_SYNCING_BASE(check_restricted) \
do \
{ \
+ if (check_restricted && m_restricted) \
+ { \
+ er.code = WALLET_RPC_ERROR_CODE_DENIED; \
+ er.message = "Command unavailable in restricted mode."; \
+ return false; \
+ } \
if (!m_wallet) { return not_open(er); } \
if (m_wallet->is_background_wallet()) \
{ \
@@ -95,13 +107,13 @@ using namespace epee;
#define PRE_VALIDATE_BACKGROUND_SYNC() \
do \
{ \
- if (!m_wallet) { return not_open(er); } \
if (m_restricted) \
{ \
er.code = WALLET_RPC_ERROR_CODE_DENIED; \
er.message = "Command unavailable in restricted mode."; \
return false; \
} \
+ if (!m_wallet) { return not_open(er); } \
if (m_wallet->key_on_device()) \
{ \
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \
@@ -704,8 +716,7 @@ namespace tools
}
bool wallet_rpc_server::on_set_subaddr_lookahead(const wallet_rpc::COMMAND_RPC_SET_SUBADDR_LOOKAHEAD::request& req, wallet_rpc::COMMAND_RPC_SET_SUBADDR_LOOKAHEAD::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
const std::string wallet_file = m_wallet->get_wallet_file();
if (wallet_file == "" || m_wallet->verify_password(req.password))
{
@@ -730,8 +741,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
if (req.count < 1 || req.count > 65536) {
@@ -768,8 +778,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
m_wallet->set_subaddress_label(req.index, req.label);
@@ -831,8 +840,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
m_wallet->add_subaddress_account(req.label);
@@ -849,8 +857,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
m_wallet->set_subaddress_label({req.account_index, 0}, req.label);
@@ -885,8 +892,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
m_wallet->set_account_tag(req.accounts, req.tag);
@@ -901,8 +907,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
m_wallet->set_account_tag(req.accounts, "");
@@ -917,8 +922,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
m_wallet->set_account_tag_description(req.tag, req.description);
@@ -948,8 +952,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_freeze(const wallet_rpc::COMMAND_RPC_FREEZE::request& req, wallet_rpc::COMMAND_RPC_FREEZE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
if (req.key_image.empty())
@@ -977,8 +980,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_thaw(const wallet_rpc::COMMAND_RPC_THAW::request& req, wallet_rpc::COMMAND_RPC_THAW::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
if (req.key_image.empty())
@@ -1235,13 +1237,13 @@ namespace tools
std::vector<uint8_t> extra;
LOG_PRINT_L3("on_transfer starts");
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
else if (req.unlock_time)
{
er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME;
@@ -1295,13 +1297,13 @@ namespace tools
std::vector<cryptonote::tx_destination_entry> dsts;
std::vector<uint8_t> extra;
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
else if (req.unlock_time)
{
er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME;
@@ -1345,13 +1347,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
if (m_wallet->key_on_device())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -1429,13 +1431,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
if (m_wallet->key_on_device())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -1656,13 +1658,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
if (m_wallet->key_on_device())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -1716,13 +1718,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
CHECK_MULTISIG_ENABLED();
CHECK_IF_BACKGROUND_SYNCING();
@@ -1747,13 +1749,13 @@ namespace tools
std::vector<cryptonote::tx_destination_entry> dsts;
std::vector<uint8_t> extra;
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
else if (req.unlock_time)
{
er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME;
@@ -1813,13 +1815,13 @@ namespace tools
std::vector<cryptonote::tx_destination_entry> dsts;
std::vector<uint8_t> extra;
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
else if (req.unlock_time)
{
er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME;
@@ -1899,6 +1901,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_relay_tx(const wallet_rpc::COMMAND_RPC_RELAY_TX::request& req, wallet_rpc::COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+
if (!m_wallet) return not_open(er);
cryptonote::blobdata blob;
@@ -2046,13 +2055,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
try
{
@@ -2252,13 +2261,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
if (req.key_type.compare("mnemonic") == 0)
{
@@ -2331,14 +2340,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- if (m_restricted)
- {
- er.code = WALLET_RPC_ERROR_CODE_DENIED;
- er.message = "Command unavailable in restricted mode.";
- return false;
- }
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
@@ -2427,14 +2429,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- if (m_restricted)
- {
- er.code = WALLET_RPC_ERROR_CODE_DENIED;
- er.message = "Command unavailable in restricted mode.";
- return false;
- }
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
tools::wallet2::message_signature_type_t signature_type = tools::wallet2::sign_with_spend_key;
if (req.signature_type == "spend" || req.signature_type == "")
@@ -2453,13 +2448,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
cryptonote::address_parse_info info;
er.message = "";
@@ -2497,13 +2492,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_stop_wallet(const wallet_rpc::COMMAND_RPC_STOP_WALLET::request& req, wallet_rpc::COMMAND_RPC_STOP_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
try
{
@@ -2520,14 +2515,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- if (m_restricted)
- {
- er.code = WALLET_RPC_ERROR_CODE_DENIED;
- er.message = "Command unavailable in restricted mode.";
- return false;
- }
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
if (req.txids.size() != req.notes.size())
{
@@ -2593,14 +2581,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_set_attribute(const wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- if (m_restricted)
- {
- er.code = WALLET_RPC_ERROR_CODE_DENIED;
- er.message = "Command unavailable in restricted mode.";
- return false;
- }
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
m_wallet->set_attribute(req.key, req.value);
@@ -2609,13 +2590,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_attribute(const wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
if (!m_wallet->get_attribute(req.key, res.value))
{
@@ -2897,13 +2878,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
uint64_t min_height = 0, max_height = CRYPTONOTE_MAX_BLOCK_NUMBER;
if (req.filter_by_height)
@@ -2974,13 +2955,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
crypto::hash txid;
cryptonote::blobdata txid_blob;
@@ -3067,13 +3048,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_export_outputs(const wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
if (m_wallet->key_on_device())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -3097,13 +3078,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_import_outputs(const wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
if (m_wallet->key_on_device())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -3135,8 +3116,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = m_wallet->export_key_images(req.all);
@@ -3160,13 +3140,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
if (!m_wallet->is_trusted_daemon())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -3281,14 +3261,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- if (m_restricted)
- {
- er.code = WALLET_RPC_ERROR_CODE_DENIED;
- er.message = "Command unavailable in restricted mode.";
- return false;
- }
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
cryptonote::address_parse_info info;
er.message = "";
@@ -3324,14 +3297,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_edit_address_book(const wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- if (m_restricted)
- {
- er.code = WALLET_RPC_ERROR_CODE_DENIED;
- er.message = "Command unavailable in restricted mode.";
- return false;
- }
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
const auto ab = m_wallet->get_address_book();
if (req.index >= ab.size())
@@ -3387,14 +3353,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- if (m_restricted)
- {
- er.code = WALLET_RPC_ERROR_CODE_DENIED;
- er.message = "Command unavailable in restricted mode.";
- return false;
- }
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
const auto ab = m_wallet->get_address_book();
if (req.index >= ab.size())
@@ -3414,13 +3373,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
try
{
m_wallet->refresh(m_wallet->is_trusted_daemon(), req.start_height, res.blocks_fetched, res.received_money);
@@ -3459,14 +3418,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_scan_tx(const wallet_rpc::COMMAND_RPC_SCAN_TX::request& req, wallet_rpc::COMMAND_RPC_SCAN_TX::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- if (m_restricted)
- {
- er.code = WALLET_RPC_ERROR_CODE_DENIED;
- er.message = "Command unavailable in restricted mode.";
- return false;
- }
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
std::unordered_set<crypto::hash> txids;
std::list<std::string>::const_iterator i = req.txids.begin();
@@ -3499,14 +3451,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- if (m_restricted)
- {
- er.code = WALLET_RPC_ERROR_CODE_DENIED;
- er.message = "Command unavailable in restricted mode.";
- return false;
- }
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
try
{
m_wallet->rescan_spent();
@@ -3522,6 +3467,12 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
if (!m_wallet) return not_open(er);
if (!m_wallet->is_trusted_daemon())
{
@@ -3557,6 +3508,12 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
if (!m_wallet) return not_open(er);
cryptonote::COMMAND_RPC_STOP_MINING::request daemon_req;
cryptonote::COMMAND_RPC_STOP_MINING::response daemon_res;
@@ -3579,6 +3536,12 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
if (m_wallet_dir.empty())
{
er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR;
@@ -3672,6 +3635,12 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
if (m_wallet_dir.empty())
{
er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR;
@@ -3743,6 +3712,12 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_close_wallet(const wallet_rpc::COMMAND_RPC_CLOSE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CLOSE_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
if (!m_wallet) return not_open(er);
if (req.autosave_current)
@@ -3764,14 +3739,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
- if (m_restricted)
- {
- er.code = WALLET_RPC_ERROR_CODE_DENIED;
- er.message = "Command unavailable in restricted mode.";
- return false;
- }
- CHECK_IF_BACKGROUND_SYNCING();
+ CHECK_IF_RESTRICTED_BACKGROUND_SYNCING();
if (m_wallet->verify_password(req.old_password))
{
try
@@ -3883,6 +3851,12 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_generate_from_keys(const wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::request &req, wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::response &res, epee::json_rpc::error &er, const connection_context *ctx)
{
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
if (m_wallet_dir.empty())
{
er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR;
@@ -4059,6 +4033,12 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_restore_deterministic_wallet(const wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::request &req, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::response &res, epee::json_rpc::error &er, const connection_context *ctx)
{
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
if (m_wallet_dir.empty())
{
er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR;
@@ -4277,13 +4257,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
if (m_wallet->multisig())
{
er.code = WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG;
@@ -4307,13 +4287,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_make_multisig(const wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
if (m_wallet->multisig())
{
er.code = WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG;
@@ -4346,13 +4326,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_export_multisig(const wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
bool ready;
if (!m_wallet->multisig(&ready))
{
@@ -4387,13 +4367,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_import_multisig(const wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
bool ready;
uint32_t threshold, total;
if (!m_wallet->multisig(&ready, &threshold, &total))
@@ -4431,7 +4411,7 @@ namespace tools
try
{
- res.n_outputs = m_wallet->import_multisig(info);
+ res.n_outputs = m_wallet->import_multisig(info, req.refresh_after_import);
}
catch (const std::exception &e)
{
@@ -4440,21 +4420,24 @@ namespace tools
return false;
}
- if (m_wallet->is_trusted_daemon())
+ if (req.refresh_after_import)
{
- try
+ if (m_wallet->is_trusted_daemon())
{
- m_wallet->rescan_spent();
+ try
+ {
+ m_wallet->rescan_spent();
+ }
+ catch (const std::exception &e)
+ {
+ er.message = std::string("Success, but failed to update spent status after import multisig info: ") + e.what();
+ }
}
- catch (const std::exception &e)
+ else
{
- er.message = std::string("Success, but failed to update spent status after import multisig info: ") + e.what();
+ er.message = "Success, but cannot update spent status after import multisig info as daemon is untrusted";
}
}
- else
- {
- er.message = "Success, but cannot update spent status after import multisig info as daemon is untrusted";
- }
return true;
}
@@ -4467,13 +4450,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
bool ready;
uint32_t threshold, total;
if (!m_wallet->multisig(&ready, &threshold, &total))
@@ -4511,13 +4494,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
bool ready;
uint32_t threshold, total;
if (!m_wallet->multisig(&ready, &threshold, &total))
@@ -4581,13 +4564,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
bool ready;
uint32_t threshold, total;
if (!m_wallet->multisig(&ready, &threshold, &total))
@@ -4699,13 +4682,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
+ if (!m_wallet) return not_open(er);
if (m_wallet->has_proxy_option() && !req.proxy.empty())
{
@@ -4718,10 +4701,25 @@ namespace tools
ssl_allowed_fingerprints.reserve(req.ssl_allowed_fingerprints.size());
for (const std::string &fp: req.ssl_allowed_fingerprints)
{
- ssl_allowed_fingerprints.push_back({});
- std::vector<uint8_t> &v = ssl_allowed_fingerprints.back();
- for (auto c: fp)
- v.push_back(c);
+ std::vector<uint8_t> decoded;
+ try
+ {
+ decoded = epee::from_hex_locale::to_vector(fp);
+ }
+ catch (const std::exception &)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
+ er.message = "ssl_allowed_fingerprints[] entries must be hex-encoded SHA-256 values";
+ return false;
+ }
+
+ if (decoded.size() != SSL_FINGERPRINT_SIZE)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
+ er.message = "Each ssl_allowed_fingerprints[] entry must decode to exactly " BOOST_PP_STRINGIZE(SSL_FINGERPRINT_SIZE) " bytes";
+ return false;
+ }
+ ssl_allowed_fingerprints.emplace_back(std::move(decoded));
}
epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index bd3321ec0..26a1e2ab4 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -2454,9 +2454,11 @@ namespace wallet_rpc
struct request_t
{
std::vector<std::string> info;
+ bool refresh_after_import;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(info)
+ KV_SERIALIZE_OPT(refresh_after_import, true)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index ec0d46893..29d75aeab 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -36,8 +36,6 @@ set(TEST_DATA_DIR "${CMAKE_CURRENT_LIST_DIR}/data")
if (WIN32 AND STATIC)
add_definitions(-DSTATICLIB)
- # miniupnp changed their static define
- add_definitions(-DMINIUPNP_STATICLIB)
endif ()
find_package(GTest)
diff --git a/tests/core_tests/bulletproof_plus.cpp b/tests/core_tests/bulletproof_plus.cpp
index d32f9b689..75e927009 100644
--- a/tests/core_tests/bulletproof_plus.cpp
+++ b/tests/core_tests/bulletproof_plus.cpp
@@ -136,7 +136,7 @@ bool gen_bpp_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
rct_txes.resize(rct_txes.size() + 1);
- bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx && !post_tx(rct_txes.back(), n))
diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp
index ee747ab99..71bae61d1 100644
--- a/tests/core_tests/bulletproofs.cpp
+++ b/tests/core_tests/bulletproofs.cpp
@@ -136,7 +136,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
rct_txes.resize(rct_txes.size() + 1);
- bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx && !post_tx(rct_txes.back(), n))
diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp
index a8e545dbd..1f281239d 100644
--- a/tests/core_tests/rct.cpp
+++ b/tests/core_tests/rct.cpp
@@ -122,7 +122,7 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry
std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
- bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes[n], tx_key, additional_tx_keys, true);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), rct_txes[n], tx_key, additional_tx_keys, true);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
events.push_back(rct_txes[n]);
starting_rct_tx_hashes.push_back(get_transaction_hash(rct_txes[n]));
@@ -229,7 +229,7 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry
std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[0].get_keys().m_account_address.m_spend_public_key] = {0,0};
- bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), tx, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), tx, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx)
diff --git a/tests/core_tests/rct2.cpp b/tests/core_tests/rct2.cpp
index 971f321a5..696e6a2b7 100644
--- a/tests/core_tests/rct2.cpp
+++ b/tests/core_tests/rct2.cpp
@@ -136,7 +136,7 @@ bool gen_rct2_tx_validation_base::generate_with(std::vector<test_event_entry>& e
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
rct_txes.resize(rct_txes.size() + 1);
- bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx && !post_tx(rct_txes.back(), n))
diff --git a/tests/functional_tests/functional_tests_rpc.py b/tests/functional_tests/functional_tests_rpc.py
index d8ec78810..ec761e0e6 100755
--- a/tests/functional_tests/functional_tests_rpc.py
+++ b/tests/functional_tests/functional_tests_rpc.py
@@ -48,7 +48,7 @@ WALLET_DIRECTORY = builddir + "/functional-tests-directory"
FUNCTIONAL_TESTS_DIRECTORY = builddir + "/tests/functional_tests"
DIFFICULTY = 10
-monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", str(DIFFICULTY), "--no-igd", "--p2p-bind-port", "monerod_p2p_port", "--rpc-bind-port", "monerod_rpc_port", "--zmq-rpc-bind-port", "monerod_zmq_port", "--zmq-pub", "monerod_zmq_pub", "--non-interactive", "--disable-dns-checkpoints", "--check-updates", "disabled", "--rpc-ssl", "disabled", "--data-dir", "monerod_data_dir", "--log-level", "1", "--rpc-max-connections-per-private-ip", "100", "--rpc-max-connections", "100"]
+monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", str(DIFFICULTY), "--p2p-bind-port", "monerod_p2p_port", "--rpc-bind-port", "monerod_rpc_port", "--zmq-rpc-bind-port", "monerod_zmq_port", "--zmq-pub", "monerod_zmq_pub", "--non-interactive", "--disable-dns-checkpoints", "--check-updates", "disabled", "--rpc-ssl", "disabled", "--data-dir", "monerod_data_dir", "--log-level", "1", "--rpc-max-connections-per-private-ip", "100", "--rpc-max-connections", "100"]
monerod_extra = [
["--offline"],
["--rpc-payment-address", "44SKxxLQw929wRF6BA9paQ1EWFshNnKhXM3qz6Mo3JGDE2YG3xyzVutMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUiqhcwR", "--rpc-payment-difficulty", str(DIFFICULTY), "--rpc-payment-credits", "5000", "--offline"],
diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h
index 3578a513a..6ba09f739 100644
--- a/tests/performance_tests/check_tx_signature.h
+++ b/tests/performance_tests/check_tx_signature.h
@@ -72,7 +72,7 @@ public:
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0};
rct::RCTConfig rct_config{range_proof_type, bp_version};
- if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, tx_key, additional_tx_keys, rct, rct_config))
+ if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, boost::none, std::vector<uint8_t>(), m_tx, tx_key, additional_tx_keys, rct, rct_config))
return false;
get_transaction_prefix_hash(m_tx, m_tx_prefix_hash);
@@ -136,7 +136,7 @@ public:
m_txes.resize(a_num_txes + (extra_outs > 0 ? 1 : 0));
for (size_t n = 0; n < a_num_txes; ++n)
{
- if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_txes[n], tx_key, additional_tx_keys, true, {rct::RangeProofPaddedBulletproof, 2}))
+ if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, boost::none, std::vector<uint8_t>(), m_txes[n], tx_key, additional_tx_keys, true, {rct::RangeProofPaddedBulletproof, 2}))
return false;
}
diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h
index 96da25306..d7f02a6d5 100644
--- a/tests/performance_tests/construct_tx.h
+++ b/tests/performance_tests/construct_tx.h
@@ -74,7 +74,7 @@ public:
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0};
rct::RCTConfig rct_config{range_proof_type, bp_version};
- return cryptonote::construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, m_destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, tx_key, additional_tx_keys, rct, rct_config);
+ return cryptonote::construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, m_destinations, boost::none, std::vector<uint8_t>(), m_tx, tx_key, additional_tx_keys, rct, rct_config);
}
private:
diff --git a/tests/trezor/daemon.cpp b/tests/trezor/daemon.cpp
index 546a8a180..8eeb30377 100644
--- a/tests/trezor/daemon.cpp
+++ b/tests/trezor/daemon.cpp
@@ -56,7 +56,6 @@ void mock_daemon::default_options(boost::program_options::variables_map & vm)
tools::options::set_option(vm, nodetool::arg_p2p_add_exclusive_node, po::variable_value(exclusive_nodes, false));
tools::options::set_option(vm, nodetool::arg_p2p_bind_ip, po::variable_value(std::string("127.0.0.1"), false));
- tools::options::set_option(vm, nodetool::arg_no_igd, po::variable_value(true, false));
tools::options::set_option(vm, cryptonote::arg_offline, po::variable_value(true, false));
tools::options::set_option(vm, "disable-dns-checkpoints", po::variable_value(true, false));
diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp
index 16ec7dd21..30e5f5589 100644
--- a/tests/unit_tests/net.cpp
+++ b/tests/unit_tests/net.cpp
@@ -40,6 +40,7 @@
#include <boost/endian/conversion.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/range/combine.hpp>
+#include <boost/optional/optional.hpp>
#include <boost/system/error_code.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/thread/thread.hpp>
@@ -966,6 +967,239 @@ TEST(get_network_address_host_and_port, hostname)
na_host_and_port_test("xmrchain.net:18081", "xmrchain.net", "18081");
}
+TEST(scheme_and_authority, basic)
+{
+ const auto check = [] (const net::scheme_and_authority& actual, const boost::string_ref scheme, const boost::string_ref authority)
+ {
+ EXPECT_EQ(actual.scheme, scheme);
+ EXPECT_EQ(actual.authority, authority);
+ };
+
+ // valid (some ipv6 hostnames are non-standard but allowed)
+ check(net::scheme_and_authority{"socks://host:port/path"}, "socks", "host:port");
+ check(net::scheme_and_authority{"socks://[::ffff]:8080/path"}, "socks", "[::ffff]:8080");
+ check(net::scheme_and_authority{"socks://192.168.0.1/path"}, "socks", "192.168.0.1");
+ check(net::scheme_and_authority{"socks://host"}, "socks", "host");
+ check(net::scheme_and_authority{"socks://@host"}, "socks", "@host");
+ check(net::scheme_and_authority{"socks://user:pass@host"}, "socks", "user:pass@host");
+ check(net::scheme_and_authority{"host:port/path"}, "", "host:port");
+ check(net::scheme_and_authority{"[::ffff]:8080/path"}, "", "[::ffff]:8080");
+ check(net::scheme_and_authority{"192.168.0.1/path"}, "", "192.168.0.1");
+ check(net::scheme_and_authority{"host"}, "", "host");
+ check(net::scheme_and_authority{"192.168.0.1"}, "", "192.168.0.1");
+ check(net::scheme_and_authority{"192.168.0.1:80/path"}, "", "192.168.0.1:80");
+ check(net::scheme_and_authority{"::ffff"}, "", "::ffff");
+ check(net::scheme_and_authority{"[::ffff]:8080"}, "", "[::ffff]:8080");
+ check(net::scheme_and_authority{"example.com/some://valid/path"}, "", "example.com");
+
+ // unsupported URIs (URN cases)
+ check(net::scheme_and_authority{"urn:isbn:number"}, "", "urn:isbn:number");
+ check(net::scheme_and_authority{"urn:isbn/number"}, "", "urn:isbn");
+
+ // invalid cases _not_ strictly rejected until hostname is parsed fully
+ check(net::scheme_and_authority{""}, "", "");
+ check(net::scheme_and_authority{"socks://"}, "socks", "");
+ check(net::scheme_and_authority{"socks:/"}, "", "socks:");
+ check(net::scheme_and_authority{"192.168.0.1:80://"}, "", "192.168.0.1:80:");
+ check(net::scheme_and_authority{"user@::ffff:443"}, "", "user@::ffff:443");
+ check(net::scheme_and_authority{"socks://user:p%61ss@ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:8080"}, "socks", "user:p%61ss@ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:8080");
+}
+
+
+TEST(userinfo_and_hostport, basic)
+{
+ const auto check = [] (const net::userinfo_and_hostport& actual, const boost::string_ref userinfo, const boost::string_ref hostport)
+ {
+ EXPECT_EQ(actual.userinfo, userinfo);
+ EXPECT_EQ(actual.hostport, hostport);
+ };
+
+ check(net::userinfo_and_hostport{"@host:port"}, "", "host:port");
+ check(net::userinfo_and_hostport{"host:port"}, "", "host:port");
+ check(net::userinfo_and_hostport{"[::ffff]"}, "", "[::ffff]");
+ check(net::userinfo_and_hostport{"::ffff"}, "", "::ffff");
+ check(net::userinfo_and_hostport{"user@[::ffff]"}, "user", "[::ffff]");
+ check(net::userinfo_and_hostport{"user:%70ass@192.168.0.1"}, "user:%70ass", "192.168.0.1");
+ check(net::userinfo_and_hostport{"user:pass@host:8080"}, "user:pass", "host:8080");
+
+ // invalid hostname not strictly rejected
+ check(net::userinfo_and_hostport{""}, "", "");
+ check(net::userinfo_and_hostport{"@"}, "", "");
+ check(net::userinfo_and_hostport{":@"}, ":", "");
+ check(net::userinfo_and_hostport{"user@::ffff:443"}, "user", "::ffff:443");
+}
+
+TEST(user_and_pass, basic)
+{
+ const auto check = [] (
+ const boost::optional<net::user_and_pass>& actual,
+ const boost::optional<std::string>& user = boost::none,
+ const boost::string_ref pass = "")
+ {
+ ASSERT_EQ(bool(actual), bool(user));
+ if (actual)
+ {
+ EXPECT_EQ(actual->user, *user);
+ EXPECT_EQ(actual->pass, pass);
+ }
+ };
+
+ check(net::user_and_pass::get(""), std::string{""}, "");
+ check(net::user_and_pass::get("user"), std::string{"user"}, "");
+ check(net::user_and_pass::get("user:"), std::string{"user"}, "");
+ check(net::user_and_pass::get(":pass"), std::string{}, "pass");
+ check(net::user_and_pass::get("user:pass"), std::string("user"), "pass");
+ check(net::user_and_pass::get("user:p%3Ass"), std::string("user"), "p:ss");
+ check(net::user_and_pass::get("%2fser:"), std::string{"/ser"}, "");
+ check(net::user_and_pass::get("user:pas%21"), std::string{"user"}, "pas!");
+ check(net::user_and_pass::get("user::pass"), std::string{"user"}, ":pass");
+ check(net::user_and_pass::get("user%3A:pass"), std::string{"user:"}, "pass");
+ check(net::user_and_pass::get("%25%3A:pass"), std::string{"%:"}, "pass");
+ check(net::user_and_pass::get("user:%00%FF"), std::string{"user"}, boost::string_ref{"\x00\xFF", 2});
+
+ // invalid percent encodings
+ check(net::user_and_pass::get("user%3T"));
+ check(net::user_and_pass::get("user%T"));
+ check(net::user_and_pass::get("user%3"));
+ check(net::user_and_pass::get("user%"));
+}
+
+TEST(uri_components, get)
+{
+ const auto present = [] (const boost::string_ref value)
+ {
+ return boost::optional<std::string>{std::string{value}};
+ };
+ const auto check = [] (
+ const boost::optional<net::uri_components>& actual,
+ const boost::optional<std::string>& scheme = boost::none,
+ const boost::string_ref user = "",
+ const boost::string_ref pass = "",
+ const boost::string_ref hostport = "")
+ {
+ ASSERT_EQ(bool(actual), bool(scheme));
+ if (actual)
+ {
+ EXPECT_EQ(actual->scheme, *scheme);
+ EXPECT_EQ(actual->userinfo.user, user);
+ EXPECT_EQ(actual->userinfo.pass, pass);
+ EXPECT_EQ(actual->hostport, hostport);
+ }
+ };
+
+ // valid (some ipv6 hostnames are non-standard but allowed)
+ check(net::uri_components::get("socks://host:port/path"), present("socks"), "", "", "host:port");
+ check(net::uri_components::get("socks://[::ffff]:8080/path"), present("socks"), "", "", "[::ffff]:8080");
+ check(net::uri_components::get("socks://192.168.0.1/path"), present("socks"), "", "", "192.168.0.1");
+ check(net::uri_components::get("socks://host"), present("socks"), "", "", "host");
+ check(net::uri_components::get("socks://@host"), present("socks"), "", "", "host");
+ check(net::uri_components::get("socks://:@host"), present("socks"), "", "", "host");
+ check(net::uri_components::get("socks://user:@host"), present("socks"), "user", "", "host");
+ check(net::uri_components::get("socks://:pass@host"), present("socks"), "", "pass", "host");
+ check(net::uri_components::get("socks://user:pass@host"), present("socks"), "user", "pass", "host");
+ check(net::uri_components::get("host:port/path"), present(""), "", "", "host:port");
+ check(net::uri_components::get("[::ffff]:8080/path"), present(""), "", "", "[::ffff]:8080");
+ check(net::uri_components::get("192.168.0.1/path"), present(""), "", "", "192.168.0.1");
+ check(net::uri_components::get("host"), present(""), "", "", "host");
+ check(net::uri_components::get("192.168.0.1"), present(""), "", "", "192.168.0.1");
+ check(net::uri_components::get("192.168.0.1:80/path"), present(""), "", "", "192.168.0.1:80");
+ check(net::uri_components::get("::ffff"), present(""), "", "", "::ffff");
+ check(net::uri_components::get("[::ffff]:8080"), present(""), "", "", "[::ffff]:8080");
+ check(net::uri_components::get("example.com/some://valid/path"), present(""), "", "", "example.com");
+
+ // unsupported URIs (URN cases)
+ check(net::uri_components::get("urn:isbn:number"), present(""), "", "", "urn:isbn:number");
+ check(net::uri_components::get("urn:isbn/number"), present(""), "", "", "urn:isbn");
+
+ // invalid cases _not_ strictly rejected until hostname is parsed fully
+ check(net::uri_components::get(""), present(""), "", "", "");
+ check(net::uri_components::get("socks://"), present("socks"), "", "", "");
+ check(net::uri_components::get("socks:/"), present(""), "", "", "socks:");
+ check(net::uri_components::get("192.168.0.1:80://"), present(""), "", "", "192.168.0.1:80:");
+ check(net::uri_components::get("user@::ffff:443"), present(""), "user", "", "::ffff:443");
+ check(net::uri_components::get("socks://user:p%61ss@ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:8080"), present("socks"), "user", "pass", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:8080");
+
+ // invalid percent encodings
+ check(net::uri_components::get("scheme://user%3T@host"));
+ check(net::uri_components::get("user%T@host"));
+ check(net::uri_components::get("scheme://user:%3pass@host"));
+ check(net::uri_components::get("user%@host"));
+}
+
+TEST(socks_endpoint, get)
+{
+ namespace ip = boost::asio::ip;
+ const auto present = [] (const boost::asio::ip::address& value)
+ {
+ return boost::optional<boost::asio::ip::address>{value};
+ };
+ const auto check = [] (
+ const expect<net::socks::endpoint>& actual,
+ const boost::optional<boost::asio::ip::address>& address = boost::none,
+ const std::uint16_t port = 0,
+ const boost::string_ref user = "",
+ const boost::string_ref pass = "",
+ const net::socks::version ver = net::socks::version::v4a)
+ {
+ ASSERT_EQ(bool(actual), bool(address)) << actual.error().message();
+ if (actual)
+ {
+ ASSERT_TRUE(bool(address));
+ EXPECT_EQ(actual->address.address(), *address);
+ EXPECT_EQ(actual->address.port(), port);
+ EXPECT_EQ(actual->userinfo.user, user);
+ EXPECT_EQ(actual->userinfo.pass, pass);
+ EXPECT_EQ(actual->ver, ver);
+ }
+ };
+
+ check(net::socks::endpoint::get("socks://[::ffff]:8080/path"), present(ip::make_address_v6("::ffff")), 8080, "", "", net::socks::version::v4a);
+ check(net::socks::endpoint::get("socks5://user:%70ass@[::ffff]:8080/path"), present(ip::make_address_v6("::ffff")), 8080, "user", "pass", net::socks::version::v5);
+ check(net::socks::endpoint::get("socks4a://192.168.0.1:1/path"), present(ip::make_address_v4("192.168.0.1")), 1, "", "", net::socks::version::v4a);
+ check(net::socks::endpoint::get("socks5://%75@192.168.0.1:1/path"), present(ip::make_address_v4("192.168.0.1")), 1, "u", "", net::socks::version::v5);
+ check(net::socks::endpoint::get("[::ffff]:8080/path"), present(ip::make_address_v6("::ffff")), 8080, "", "", net::socks::version::v4a);
+ check(net::socks::endpoint::get("192.168.0.1:50"), present(ip::make_address_v4("192.168.0.1")), 50, "", "", net::socks::version::v4a);
+ check(net::socks::endpoint::get("192.168.0.1:80/path"), present(ip::make_address_v4("192.168.0.1")), 80, "", "", net::socks::version::v4a);
+
+ // URNs should be rejected
+ check(net::socks::endpoint::get("urn:isbn:number"));
+ check(net::socks::endpoint::get("urn:isbn/number"));
+
+ // port required for socks
+ check(net::socks::endpoint::get("socks5://192.168.0.1/path"));
+ check(net::socks::endpoint::get("192.168.0.1/path"));
+ check(net::socks::endpoint::get("::ffff"));
+
+ // invalid for socks - hostnames not allowed
+ check(net::socks::endpoint::get("socks://host:/path"));
+ check(net::socks::endpoint::get("socks://host:1"));
+ check(net::socks::endpoint::get("socks://@host:1"));
+ check(net::socks::endpoint::get("socks://:@host:1"));
+ check(net::socks::endpoint::get("socks://user:@host:1"));
+ check(net::socks::endpoint::get("socks://:pass@host:1"));
+ check(net::socks::endpoint::get("socks://user:pass@host:1"));
+ check(net::socks::endpoint::get("host:1"));
+ check(net::socks::endpoint::get("host:1/path"));
+ check(net::socks::endpoint::get("example.com:1/some://valid/path"));
+
+ // invalid cases rejected - more bad hostnames
+ check(net::socks::endpoint::get(""));
+ check(net::socks::endpoint::get("socks://"));
+ check(net::socks::endpoint::get("socks:/"));
+ check(net::socks::endpoint::get("192.168.0.1:80://"));
+ check(net::socks::endpoint::get("::ffff:443"));
+ check(net::socks::endpoint::get("socks5://user:p%61ss@ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:8080"));
+
+ // invalid percent encodings
+ check(net::socks::endpoint::get("sock5://user%3T@127.0.0.1:1"));
+ check(net::socks::endpoint::get("socks5::user%T@127.0.0.1:1"));
+ check(net::socks::endpoint::get("socks5://user:%3pass@127.0.0.1:1"));
+ check(net::socks::endpoint::get("socks5://user%@127.0.0.1:1"));
+
+ // user+pass requires socks5
+ check(net::socks::endpoint::get("socks://user:pass@[::ffff]:8080"));
+}
+
namespace
{
using stream_type = boost::asio::ip::tcp;
@@ -1035,6 +1269,58 @@ TEST(socks_client, unsupported_command)
EXPECT_FALSE(test_client->set_resolve_command("example.com"));
EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(test_client->set_connect_command(epee::net_utils::ipv6_network_address{}));
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ test_client = net::socks::make_connect_client(
+ stream_type::socket{io_service}, net::socks::version::v5, std::bind( [] {} )
+ );
+ ASSERT_TRUE(bool(test_client));
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ net::user_and_pass userinfo{};
+ userinfo.user = std::string(256, 'a');
+
+ EXPECT_FALSE(test_client->set_connect_command(userinfo.user, 8080));
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(test_client->set_connect_command("a", 8080, std::addressof(userinfo)));
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv4_network_address{}, std::addressof(userinfo)
+ )
+ );
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv6_network_address{}, std::addressof(userinfo)
+ )
+ );
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ userinfo.pass = std::move(userinfo.user);
+ userinfo.user.clear();
+
+ EXPECT_FALSE(test_client->set_connect_command("a", 8080, std::addressof(userinfo)));
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv4_network_address{}, std::addressof(userinfo)
+ )
+ );
+ EXPECT_TRUE(test_client->buffer().empty());
+
+ EXPECT_FALSE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv6_network_address{}, std::addressof(userinfo)
+ )
+ );
+ EXPECT_TRUE(test_client->buffer().empty());
}
TEST(socks_client, no_command)
@@ -1082,6 +1368,62 @@ TEST(socks_client, connect_command)
while (!called);
}
+TEST(socks_client, v5_ipv6_connect_command)
+{
+ io_thread io{};
+ stream_type::socket client{io.io_service};
+
+ const boost::asio::ip::address_v6::bytes_type address{0xDE, 0xAD, 0xBE, 0xEF};
+
+ std::atomic<bool> called{false};
+ auto test_client = net::socks::make_connect_client(
+ std::move(client), net::socks::version::v5, checked_client{std::addressof(called), false}
+ );
+ ASSERT_TRUE(bool(test_client));
+
+ ASSERT_TRUE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv6_network_address{boost::asio::ip::address_v6{address}, 80}
+ )
+ );
+ EXPECT_FALSE(test_client->buffer().empty());
+ ASSERT_TRUE(net::socks::client::connect_and_send(std::move(test_client), io.acceptor.local_endpoint()));
+ while (!io.connected)
+ ASSERT_FALSE(called);
+
+ {
+ const std::uint8_t expected_bytes[] = {5, 1, 0};
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {5, 0};
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+ {
+ const std::uint8_t expected_bytes[] = {
+ 5, 1, 0, 4, 0xDE, 0xAD, 0xBE, 0xEF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0x50
+ };
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {
+ 5, 0, 0, 4, 0xBE, 0xEF, 0xDE, 0xAD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0x50
+ };
+
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+
+ // yikes!
+ while (!called);
+}
+
+
TEST(socks_client, connect_command_failed)
{
io_thread io{};
@@ -1118,6 +1460,56 @@ TEST(socks_client, connect_command_failed)
while (!called);
}
+
+TEST(socks_client, v5_ipv4_connect_command_failed)
+{
+ io_thread io{};
+ stream_type::socket client{io.io_service};
+
+ std::atomic<bool> called{false};
+ auto test_client = net::socks::make_connect_client(
+ std::move(client), net::socks::version::v5, checked_client{std::addressof(called), true}
+ );
+ ASSERT_TRUE(bool(test_client));
+
+ ASSERT_TRUE(
+ test_client->set_connect_command(
+ epee::net_utils::ipv4_network_address{boost::endian::native_to_big(std::uint32_t(5000)), 80}
+ )
+ );
+ EXPECT_FALSE(test_client->buffer().empty());
+ ASSERT_TRUE(net::socks::client::connect_and_send(std::move(test_client), io.acceptor.local_endpoint()));
+ while (!io.connected)
+ ASSERT_FALSE(called);
+
+ {
+ const std::uint8_t expected_bytes[] = {5, 1, 0};
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {5, 0};
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+ {
+ const std::uint8_t expected_bytes[] = {
+ 5, 1, 0, 1, 0, 0, 0x13, 0x88, 0, 0x50
+ };
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {5, 2, 0, 1, 0, 0, 0, 0, 0, 0x50};
+
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+
+ // yikes!
+ while (!called);
+}
+
TEST(socks_client, resolve_command)
{
static std::uint8_t reply_bytes[] = {0, 90, 0, 0, 0xff, 0, 0xad, 0};
@@ -1133,7 +1525,7 @@ TEST(socks_client, resolve_command)
, expected_(false)
{};
- virtual void done(boost::system::error_code error, std::shared_ptr<client> self) override
+ virtual void done(boost::system::error_code error, const std::shared_ptr<client>& self) override
{
EXPECT_EQ(this, self.get());
EXPECT_EQ(expected_, bool(error)) << "Resolve failure: " << error.message();
@@ -1189,6 +1581,120 @@ TEST(socks_client, resolve_command)
while (test_client->called_ == 1);
}
+TEST(socks_client, v5_username_host_connect)
+{
+ io_thread io{};
+ stream_type::socket client{io.io_service};
+
+ std::atomic<bool> called{false};
+ auto test_client = net::socks::make_connect_client(
+ std::move(client), net::socks::version::v5, checked_client{std::addressof(called), false}
+ );
+ ASSERT_TRUE(bool(test_client));
+
+ const auto userinfo =
+ net::user_and_pass::get("user:pass").value_or(net::user_and_pass{});
+ ASSERT_TRUE(
+ test_client->set_connect_command("example.com", 80, std::addressof(userinfo))
+ );
+ EXPECT_FALSE(test_client->buffer().empty());
+ ASSERT_TRUE(net::socks::client::connect_and_send(std::move(test_client), io.acceptor.local_endpoint()));
+ while (!io.connected)
+ ASSERT_FALSE(called);
+
+ {
+ const std::uint8_t expected_bytes[] = {5, 2, 0, 2};
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {5, 2};
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+ {
+ const std::uint8_t expected_bytes[] = {
+ 1, 4, 'u', 's', 'e', 'r', 4, 'p', 'a', 's', 's'
+ };
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {1, 0};
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+ {
+ const std::uint8_t expected_bytes[] = {
+ 5, 1, 0, 3, 11, 'e','x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o',
+ 'm', 0, 0x50
+ };
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {
+ 5, 0, 0, 1, 0xDE, 0xAD, 0xBE, 0xEF, 0x50, 00
+ };
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+
+ // yikes!
+ while (!called);
+}
+
+TEST(socks_client, v5_usernameskipped_host_connect)
+{
+ io_thread io{};
+ stream_type::socket client{io.io_service};
+
+ std::atomic<bool> called{false};
+ auto test_client = net::socks::make_connect_client(
+ std::move(client), net::socks::version::v5, checked_client{std::addressof(called), false}
+ );
+ ASSERT_TRUE(bool(test_client));
+
+ const auto userinfo =
+ net::user_and_pass::get("user:pass").value_or(net::user_and_pass{});
+ ASSERT_TRUE(
+ test_client->set_connect_command("example.com", 80, std::addressof(userinfo))
+ );
+ EXPECT_FALSE(test_client->buffer().empty());
+ ASSERT_TRUE(net::socks::client::connect_and_send(std::move(test_client), io.acceptor.local_endpoint()));
+ while (!io.connected)
+ ASSERT_FALSE(called);
+
+ {
+ const std::uint8_t expected_bytes[] = {5, 2, 0, 2};
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {5, 0};
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+ {
+ const std::uint8_t expected_bytes[] = {
+ 5, 1, 0, 3, 11, 'e','x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o',
+ 'm', 0, 0x50
+ };
+
+ std::uint8_t actual_bytes[sizeof(expected_bytes)];
+ boost::asio::read(io.server, boost::asio::buffer(actual_bytes));
+ EXPECT_TRUE(std::memcmp(expected_bytes, actual_bytes, sizeof(actual_bytes)) == 0);
+
+ const std::uint8_t reply_bytes[] = {
+ 5, 0, 0, 1, 0xDE, 0xAD, 0xBE, 0xEF, 0x50, 00
+ };
+ boost::asio::write(io.server, boost::asio::buffer(reply_bytes));
+ }
+
+ // yikes!
+ while (!called);
+}
+
TEST(socks_connector, host)
{
io_thread io{};
@@ -1196,7 +1702,9 @@ TEST(socks_connector, host)
timeout.expires_after(std::chrono::seconds{5});
boost::unique_future<boost::asio::ip::tcp::socket> sock =
- net::socks::connector{io.acceptor.local_endpoint()}("example.com", "8080", timeout);
+ net::socks::connector{
+ std::make_shared<net::socks::endpoint>(io.acceptor.local_endpoint())
+ }("example.com", "8080", timeout);
while (!io.connected)
ASSERT_FALSE(sock.is_ready());
@@ -1223,7 +1731,9 @@ TEST(socks_connector, ipv4)
timeout.expires_after(std::chrono::seconds{5});
boost::unique_future<boost::asio::ip::tcp::socket> sock =
- net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout);
+ net::socks::connector{
+ std::make_shared<net::socks::endpoint>(io.acceptor.local_endpoint())
+ }("250.88.125.99", "8080", timeout);
while (!io.connected)
ASSERT_FALSE(sock.is_ready());
@@ -1249,7 +1759,9 @@ TEST(socks_connector, error)
timeout.expires_after(std::chrono::seconds{5});
boost::unique_future<boost::asio::ip::tcp::socket> sock =
- net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout);
+ net::socks::connector{
+ std::make_shared<net::socks::endpoint>(io.acceptor.local_endpoint())
+ }("250.88.125.99", "8080", timeout);
while (!io.connected)
ASSERT_FALSE(sock.is_ready());
@@ -1275,7 +1787,9 @@ TEST(socks_connector, timeout)
timeout.expires_after(std::chrono::milliseconds{10});
boost::unique_future<boost::asio::ip::tcp::socket> sock =
- net::socks::connector{io.acceptor.local_endpoint()}("250.88.125.99", "8080", timeout);
+ net::socks::connector{
+ std::make_shared<net::socks::endpoint>(io.acceptor.local_endpoint())
+ }("250.88.125.99", "8080", timeout);
ASSERT_EQ(boost::future_status::ready, sock.wait_for(boost::chrono::seconds{3}));
EXPECT_THROW(sock.get().is_open(), boost::system::system_error);
@@ -1842,4 +2356,3 @@ TEST(zmq, read_write_termination)
ASSERT_FALSE(bool(received));
EXPECT_EQ(net::zmq::make_error_code(ETERM), received.error());
}
-
diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp
index 11853c0fd..525bdc4c9 100644
--- a/tests/unit_tests/node_server.cpp
+++ b/tests/unit_tests/node_server.cpp
@@ -147,6 +147,68 @@ static bool is_blocked(Server &server, const epee::net_utils::network_address &a
return false;
}
+namespace
+{
+ using path_t = boost::filesystem::path;
+ using ec_t = boost::system::error_code;
+
+ path_t create_temp_dir(const char* pattern)
+ {
+ ec_t ec;
+ path_t path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(pattern, ec);
+ if (ec)
+ return path_t{};
+
+ const bool success = boost::filesystem::create_directory(path, ec);
+ if (!ec && success)
+ return path;
+
+ return path_t{};
+ }
+
+ void remove_tree(const path_t& path)
+ {
+ ec_t ec;
+ boost::filesystem::remove_all(path, ec);
+ }
+
+ boost::program_options::variables_map make_regtest_options(const path_t& dir)
+ {
+ boost::program_options::options_description desc;
+ cryptonote::core::init_options(desc);
+ Server::init_options(desc);
+
+ std::vector<std::string> args{
+ "--regtest",
+ "--p2p-bind-ip=127.0.0.1",
+ "--out-peers=0",
+ "--in-peers=0",
+ "--data-dir",
+ dir.string(),
+ "--check-updates=disabled",
+ "--disable-dns-checkpoints",
+ "--offline",
+ };
+
+ boost::program_options::variables_map vm;
+ boost::program_options::store(
+ boost::program_options::command_line_parser(args).options(desc).run(),
+ vm
+ );
+ boost::program_options::notify(vm);
+ return vm;
+ }
+
+ nodetool::peerlist_entry make_peer(const epee::net_utils::network_address& address, const nodetool::peerid_type id, const int64_t last_seen)
+ {
+ nodetool::peerlist_entry peer = AUTO_VAL_INIT(peer);
+ peer.adr = address;
+ peer.id = id;
+ peer.last_seen = last_seen;
+ return peer;
+ }
+}
+
TEST(ban, add)
{
test_core pr_core;
@@ -1229,7 +1291,6 @@ TEST(node_server, race_condition)
"--out-peers=0",
"--data-dir",
dir.string(),
- "--no-igd",
"--add-exclusive-node=127.0.0.1:48080",
"--check-updates=disabled",
"--disable-dns-checkpoints",
@@ -1254,5 +1315,47 @@ TEST(node_server, race_condition)
remove_tree(dir);
}
+TEST(regtest, isolates_p2p_state_from_mainnet_data_dir)
+{
+ const path_t dir = create_temp_dir("regtest-%%%%%%%%%%%%%%%%");
+ ASSERT_TRUE(!dir.empty());
+ auto cleanup = epee::misc_utils::create_scope_leave_handler([&dir]{
+ remove_tree(dir);
+ });
+
+ nodetool::peerlist_types peers{};
+ peers.white.push_back(make_peer(MAKE_IPV4_ADDRESS_PORT(11, 22, 33, 44, 18080), 1, 100));
+ peers.gray.push_back(make_peer(MAKE_IPV4_ADDRESS_PORT(55, 66, 77, 88, 18080), 2, 200));
+
+ const path_t mainnet_state = dir / P2P_NET_DATA_FILENAME;
+ ASSERT_TRUE(nodetool::peerlist_storage{}.store(mainnet_state.string(), peers));
+
+ test_core pr_core;
+ cryptonote::t_cryptonote_protocol_handler<test_core> cprotocol(pr_core, NULL);
+ Server server(cprotocol);
+ cprotocol.set_p2p_endpoint(&server);
+
+ const auto vm = make_regtest_options(dir);
+ ASSERT_TRUE(server.init(vm));
+ ASSERT_EQ(0u, server.get_public_white_peers_count());
+ ASSERT_EQ(0u, server.get_public_gray_peers_count());
+ ASSERT_TRUE(server.deinit());
+
+ const path_t regtest_state = dir / "fake" / P2P_NET_DATA_FILENAME;
+ ASSERT_TRUE(boost::filesystem::exists(regtest_state));
+
+ auto base_storage = nodetool::peerlist_storage::open(mainnet_state.string());
+ ASSERT_TRUE(bool(base_storage));
+ nodetool::peerlist_types base_public = base_storage->take_zone(epee::net_utils::zone::public_);
+ EXPECT_EQ(1u, base_public.white.size());
+ EXPECT_EQ(1u, base_public.gray.size());
+
+ auto regtest_storage = nodetool::peerlist_storage::open(regtest_state.string());
+ ASSERT_TRUE(bool(regtest_storage));
+ nodetool::peerlist_types regtest_public = regtest_storage->take_zone(epee::net_utils::zone::public_);
+ EXPECT_TRUE(regtest_public.white.empty());
+ EXPECT_TRUE(regtest_public.gray.empty());
+}
+
namespace nodetool { template class node_server<cryptonote::t_cryptonote_protocol_handler<test_core>>; }
namespace cryptonote { template class t_cryptonote_protocol_handler<test_core>; }
diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp
index fdf603272..0cf0275e5 100644
--- a/tests/unit_tests/serialization.cpp
+++ b/tests/unit_tests/serialization.cpp
@@ -304,6 +304,20 @@ TEST(Serialization, serializes_vector_int64_as_fixed_int)
ASSERT_EQ(57, blob.size());
}
+TEST(Serialization, deserializes_vector_reserve)
+{
+ std::vector<int64_t> v;
+ string blob;
+
+ tools::write_varint(std::back_inserter(blob), unsigned(100));
+ blob.append(std::string(100, 0));
+
+ ASSERT_LT(v.capacity(), 20);
+ ASSERT_FALSE(serialization::parse_binary(blob, v));
+ ASSERT_LT(v.capacity(), 100); // could fail if lib allocates more in reserve call
+}
+
+
namespace
{
template<typename T>
diff --git a/utils/fish/monero-wallet-rpc.fish b/utils/fish/monero-wallet-rpc.fish
index d89f58b49..6dc10e15b 100644
--- a/utils/fish/monero-wallet-rpc.fish
+++ b/utils/fish/monero-wallet-rpc.fish
@@ -30,7 +30,7 @@ complete -c monero-wallet-rpc -l tx-notify -r -d "Run a program for each new inc
complete -c monero-wallet-rpc -l no-dns -d "Do not use DNS"
complete -c monero-wallet-rpc -l offline -d "Do not connect to a daemon, nor use DNS"
complete -c monero-wallet-rpc -l extra-entropy -r -F -d "File containing extra entropy to initialize the PRNG (any data, aim for 256 bits of entropy to be useful, which typically means more than 256 bits of data)"
-complete -c monero-wallet-cli -l allow-mismatched-daemon-version -d "Allow communicating with a daemon that uses a different version"
+complete -c monero-wallet-rpc -l allow-mismatched-daemon-version -d "Allow communicating with a daemon that uses a different version"
complete -c monero-wallet-rpc -l rpc-bind-port -r -d "Sets bind port for server"
complete -c monero-wallet-rpc -l disable-rpc-login -d "Disable HTTP authentication for RPC connections served by this process"
complete -c monero-wallet-rpc -l restricted-rpc -d "Restricts to view-only commands"
diff --git a/utils/fish/monerod.fish b/utils/fish/monerod.fish
index 04003f0ff..29ed72e2f 100644
--- a/utils/fish/monerod.fish
+++ b/utils/fish/monerod.fish
@@ -74,8 +74,6 @@ complete -c monerod -l ban-list -r -F -d "Specify ban list file, one IP address
complete -c monerod -l hide-my-port -d "Do not announce yourself as peerlist candidate"
complete -c monerod -l no-sync -d "Don't synchronize the blockchain with other peers"
complete -c monerod -l enable-dns-blocklist -d "Apply realtime blocklist from DNS"
-complete -c monerod -l no-igd -d "Disable UPnP port mapping"
-complete -c monerod -l igd -r -a "Enabled disabled enabled" -d "UPnP port mapping. Default: delayed"
complete -c monerod -l out-peers -r -d "Set max number of out peers. Default: -1"
complete -c monerod -l in-peers -r -d "Set max number of in peers. Default: -1"
complete -c monerod -l tos-flag -r -d "Set TOS flag. Default: -1"