aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml79
-rw-r--r--.travis.yml2
-rw-r--r--Makefile8
-rw-r--r--README.md83
-rw-r--r--contrib/depends/funcs.mk6
-rw-r--r--contrib/depends/toolchain.cmake.in10
-rw-r--r--contrib/epee/include/hex.h12
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.inl21
-rw-r--r--contrib/epee/include/net/net_helper.h2
-rw-r--r--contrib/epee/include/net/net_utils_base.h8
-rw-r--r--contrib/epee/include/serialization/keyvalue_serialization.h2
-rw-r--r--contrib/epee/include/serialization/keyvalue_serialization_overloads.h24
-rw-r--r--contrib/epee/include/storages/levin_abstract_invoke2.h48
-rw-r--r--contrib/epee/include/storages/parserse_base_utils.h1
-rw-r--r--contrib/epee/include/storages/portable_storage.h52
-rw-r--r--contrib/epee/include/storages/portable_storage_base.h8
-rw-r--r--contrib/epee/include/storages/portable_storage_from_bin.h6
-rw-r--r--contrib/epee/include/storages/portable_storage_from_json.h18
-rw-r--r--contrib/epee/src/http_auth.cpp2
-rw-r--r--contrib/epee/src/net_ssl.cpp44
-rw-r--r--contrib/gitian/README.md6
-rwxr-xr-xcontrib/gitian/gitian-build.py13
-rw-r--r--external/easylogging++/easylogging++.h9
-rw-r--r--src/blockchain_db/blockchain_db.cpp6
-rw-r--r--src/blockchain_db/blockchain_db.h18
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp52
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h1
-rw-r--r--src/blockchain_db/locked_txn.h (renamed from src/cryptonote_basic/cryptonote_stat_info.h)51
-rw-r--r--src/blockchain_db/testdb.h1
-rw-r--r--src/blockchain_utilities/blockchain_stats.cpp4
-rw-r--r--src/common/download.cpp14
-rw-r--r--src/common/perf_timer.h4
-rw-r--r--src/common/updates.cpp2
-rw-r--r--src/common/util.cpp15
-rw-r--r--src/crypto/rx-slow-hash.c21
-rw-r--r--src/cryptonote_basic/CMakeLists.txt1
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp18
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h2
-rw-r--r--src/cryptonote_basic/miner.cpp2
-rw-r--r--src/cryptonote_config.h6
-rw-r--r--src/cryptonote_core/blockchain.cpp26
-rw-r--r--src/cryptonote_core/blockchain.h5
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp60
-rw-r--r--src/cryptonote_core/cryptonote_core.h34
-rw-r--r--src/cryptonote_core/tx_pool.cpp75
-rw-r--r--src/cryptonote_core/tx_pool.h5
-rw-r--r--src/cryptonote_core/tx_sanity_check.cpp16
-rw-r--r--src/cryptonote_core/tx_sanity_check.h6
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_defs.h28
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.h7
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl95
-rw-r--r--src/cryptonote_protocol/enums.h2
-rw-r--r--src/cryptonote_protocol/levin_notify.cpp208
-rw-r--r--src/cryptonote_protocol/levin_notify.h12
-rw-r--r--src/daemon/command_parser_executor.cpp39
-rw-r--r--src/daemon/command_server.cpp2
-rw-r--r--src/daemon/daemon.cpp9
-rw-r--r--src/daemon/rpc.h3
-rw-r--r--src/daemon/rpc_command_executor.cpp72
-rw-r--r--src/daemon/rpc_command_executor.h6
-rw-r--r--src/lmdb/table.h4
-rw-r--r--src/net/i2p_address.cpp3
-rw-r--r--src/net/tor_address.cpp3
-rw-r--r--src/p2p/net_node.cpp4
-rw-r--r--src/p2p/net_node.h36
-rw-r--r--src/p2p/net_node.inl186
-rw-r--r--src/p2p/net_node_common.h4
-rw-r--r--src/p2p/net_peerlist.h15
-rw-r--r--src/p2p/p2p_protocol_defs.h195
-rw-r--r--src/rpc/core_rpc_server.cpp85
-rw-r--r--src/rpc/core_rpc_server.h6
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h23
-rw-r--r--src/rpc/daemon_handler.cpp125
-rw-r--r--src/rpc/daemon_handler.h2
-rw-r--r--src/rpc/daemon_messages.cpp618
-rw-r--r--src/rpc/daemon_messages.h17
-rw-r--r--src/rpc/message.cpp192
-rw-r--r--src/rpc/message.h49
-rw-r--r--src/rpc/rpc_args.cpp3
-rw-r--r--src/rpc/rpc_args.h2
-rw-r--r--src/rpc/rpc_payment.cpp4
-rw-r--r--src/serialization/CMakeLists.txt1
-rw-r--r--src/serialization/json_object.cpp621
-rw-r--r--src/serialization/json_object.h164
-rw-r--r--src/simplewallet/simplewallet.cpp206
-rw-r--r--src/simplewallet/simplewallet.h3
-rw-r--r--src/wallet/api/address_book.cpp50
-rw-r--r--src/wallet/node_rpc_proxy.cpp4
-rw-r--r--src/wallet/ringdb.cpp55
-rw-r--r--src/wallet/wallet2.cpp162
-rw-r--r--src/wallet/wallet2.h56
-rw-r--r--src/wallet/wallet_rpc_server.cpp130
-rw-r--r--src/wallet/wallet_rpc_server.h2
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h29
-rw-r--r--tests/core_proxy/core_proxy.h3
-rw-r--r--tests/core_tests/bulletproofs.cpp1
-rw-r--r--tests/core_tests/chaingen.cpp14
-rw-r--r--tests/core_tests/chaingen.h4
-rw-r--r--tests/core_tests/multisig.cpp2
-rw-r--r--tests/core_tests/rct.cpp1
-rw-r--r--tests/core_tests/v2_tests.cpp1
-rwxr-xr-xtests/functional_tests/address_book.py64
-rwxr-xr-xtests/functional_tests/blockchain.py5
-rw-r--r--tests/functional_tests/make_test_signature.cc2
-rwxr-xr-xtests/functional_tests/sign_message.py31
-rwxr-xr-xtests/functional_tests/transfer.py4
-rw-r--r--tests/net_load_tests/clt.cpp34
-rw-r--r--tests/net_load_tests/net_load_tests.h1
-rw-r--r--tests/net_load_tests/srv.cpp2
-rw-r--r--tests/unit_tests/bulletproofs.cpp9
-rw-r--r--tests/unit_tests/epee_utils.cpp12
-rw-r--r--tests/unit_tests/json_serialization.cpp47
-rw-r--r--tests/unit_tests/levin.cpp96
-rw-r--r--tests/unit_tests/net.cpp433
-rw-r--r--tests/unit_tests/node_server.cpp3
-rw-r--r--tests/unit_tests/serialization.cpp1
-rw-r--r--utils/gpg_keys/thecharlatan.asc316
-rw-r--r--utils/gpg_keys/xiphon.asc41
-rwxr-xr-xutils/logs/levin-traffic.awk73
-rw-r--r--utils/python-rpc/framework/wallet.py4
120 files changed, 3442 insertions, 2213 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 000000000..9e1aade2d
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,79 @@
+name: continuous-integration/gh-actions/cli
+
+on: [push, pull_request]
+
+jobs:
+ build-macos:
+ runs-on: macOS-latest
+ steps:
+ - uses: actions/checkout@v1
+ with:
+ submodules: recursive
+ - name: update brew and install dependencies
+ run: brew update && brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf
+ - name: build
+ run: make -j3
+
+ build-windows:
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v1
+ with:
+ submodules: recursive
+ - uses: numworks/setup-msys2@v1
+ - name: update pacman
+ run: msys2do pacman -Syu --noconfirm
+ - name: install monero dependencies
+ run: msys2do pacman -S --noconfirm mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb git
+ - name: build
+ run: msys2do make release-static-win64 -j2
+
+ build-ubuntu:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ with:
+ submodules: recursive
+ - name: remove bundled boost
+ run: sudo rm -rf /usr/local/share/boost
+ - name: update apt
+ run: sudo apt update
+ - name: install monero dependencies
+ run: sudo 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 libnorm-dev libusb-1.0-0-dev libpgm-dev
+ - name: build
+ run: make -j3
+
+ libwallet-ubuntu:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ with:
+ submodules: recursive
+ - name: remove bundled boost
+ run: sudo rm -rf /usr/local/share/boost
+ - name: update apt
+ run: sudo apt update
+ - name: install monero dependencies
+ run: sudo 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 libnorm-dev libusb-1.0-0-dev libpgm-dev
+ - name: build
+ run: cmake -DBUILD_GUI_DEPS=ON && make -j3
+
+ test-ubuntu:
+ needs: build-ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ with:
+ submodules: recursive
+ - name: remove bundled boost
+ run: sudo rm -rf /usr/local/share/boost
+ - name: update apt
+ run: sudo apt update
+ - name: install monero dependencies
+ run: sudo 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 libnorm-dev libusb-1.0-0-dev libpgm-dev
+ - name: install requests
+ run: pip install requests
+ - name: tests
+ env:
+ CTEST_OUTPUT_ON_FAILURE: ON
+ run: make release-test -j3
diff --git a/.travis.yml b/.travis.yml
index 516e4f8e5..ba2215905 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,6 +34,8 @@ env:
- HOST=x86_64-unknown-linux-gnu PACKAGES="gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
# Cross-Mac
- HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git" OSX_SDK=10.11
+# x86_64 Freebsd
+ - HOST=x86_64-unknown-freebsd PACKAGES="clang-8 gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
before_install:
- export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g")
diff --git a/Makefile b/Makefile
index d3d684c5b..2c7bd20a9 100644
--- a/Makefile
+++ b/Makefile
@@ -81,11 +81,11 @@ debug-static-all:
debug-static-win64:
mkdir -p $(builddir)/debug
- cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 $(topdir) && $(MAKE)
+ cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) $(topdir) && $(MAKE)
debug-static-win32:
mkdir -p $(builddir)/debug
- cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 $(topdir) && $(MAKE)
+ cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) $(topdir) && $(MAKE)
cmake-release:
mkdir -p $(builddir)/release
@@ -152,11 +152,11 @@ release-static-linux-i686:
release-static-win64:
mkdir -p $(builddir)/release
- cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 $(topdir) && $(MAKE)
+ cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) $(topdir) && $(MAKE)
release-static-win32:
mkdir -p $(builddir)/release
- cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 $(topdir) && $(MAKE)
+ cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) $(topdir) && $(MAKE)
fuzz:
mkdir -p $(builddir)/fuzz
diff --git a/README.md b/README.md
index 7b6b252e0..0c2488068 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ Portions Copyright (c) 2012-2013 The Cryptonote developers.
- Mail: [dev@getmonero.org](mailto:dev@getmonero.org)
- GitHub: [https://github.com/monero-project/monero](https://github.com/monero-project/monero)
- IRC: [#monero-dev on Freenode](https://webchat.freenode.net/?randomnick=1&channels=%23monero-dev&prompt=1&uio=d4)
+- It is HIGHLY recommended that you join the #monero-dev IRC channel if you are developing software that uses Monero. Due to the nature of this open source software project, joining this channel and idling is the best way to stay updated on best practices and new developments in the Monero ecosystem. All you need to do is join the IRC channel and idle to stay updated with the latest in Monero development. If you do not, you risk wasting resources on developing integrations that are not compatible with the Monero network. The Monero core team and community continuously make efforts to communicate updates, developments, and documentation via other platforms – but for the best information, you need to talk to other Monero developers, and they are on IRC. #monero-dev is about Monero development, not getting help about using Monero, or help about development of other software, including yours, unless it also pertains to Monero code itself. For these cases, checkout #monero.
## Vulnerability response
@@ -54,31 +55,11 @@ Our researchers are available on IRC in [#monero-research-lab on Freenode](https
- You can subscribe to an [announcement listserv](https://lists.getmonero.org) to get critical announcements from the Monero core team. The announcement list can be very helpful for knowing when software updates are needed.
## Translations
-The CLI wallet is available in different languages. If you want to help translate it, see our self-hosted localization platform, Weblate, on [translate.getmonero.org](https://translate.getmonero.org/projects/CLI/). Every translation *must* be uploaded on the platform, pull requests directly editing the code in this repository will be closed. If you need help with Weblate, you can find a guide with screenshots [here](https://github.com/monero-ecosystem/monero-translations/blob/master/weblate.md).
+The CLI wallet is available in different languages. If you want to help translate it, see our self-hosted localization platform, Weblate, on [translate.getmonero.org]( https://translate.getmonero.org/projects/monero/cli-wallet/). Every translation *must* be uploaded on the platform, pull requests directly editing the code in this repository will be closed. If you need help with Weblate, you can find a guide with screenshots [here](https://github.com/monero-ecosystem/monero-translations/blob/master/weblate.md).
 
If you need help/support/info about translations, contact the localization workgroup. You can find the complete list of contacts on the repository of the workgroup: [monero-translations](https://github.com/monero-ecosystem/monero-translations#contacts).
-## Build Status
-
-### IMPORTANT
-
-These builds are of the master branch, which is used for active development and can be either unstable or incompatible with release software. Please compile release branches.
-
-| Operating System | Processor | Status |
-| --------------------- | -------- |--------|
-| Ubuntu 16.04 | i686 | [![Ubuntu 16.04 i686](https://build.getmonero.org/png?builder=monero-static-ubuntu-i686)](https://build.getmonero.org/builders/monero-static-ubuntu-i686)
-| Ubuntu 16.04 | amd64 | [![Ubuntu 16.04 amd64](https://build.getmonero.org/png?builder=monero-static-ubuntu-amd64)](https://build.getmonero.org/builders/monero-static-ubuntu-amd64)
-| Ubuntu 16.04 | armv7 | [![Ubuntu 16.04 armv7](https://build.getmonero.org/png?builder=monero-static-ubuntu-arm7)](https://build.getmonero.org/builders/monero-static-ubuntu-arm7)
-| Debian Stable | armv8 | [![Debian armv8](https://build.getmonero.org/png?builder=monero-static-debian-armv8)](https://build.getmonero.org/builders/monero-static-debian-armv8)
-| macOS 10.11 | amd64 | [![macOS 10.11 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.11)](https://build.getmonero.org/builders/monero-static-osx-10.11)
-| macOS 10.12 | amd64 | [![macOS 10.12 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.12)](https://build.getmonero.org/builders/monero-static-osx-10.12)
-| macOS 10.13 | amd64 | [![macOS 10.13 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.13)](https://build.getmonero.org/builders/monero-static-osx-10.13)
-| FreeBSD 11 | amd64 | [![FreeBSD 11 amd64](https://build.getmonero.org/png?builder=monero-static-freebsd64)](https://build.getmonero.org/builders/monero-static-freebsd64)
-| DragonFly BSD 4.6 | amd64 | [![DragonFly BSD amd64](https://build.getmonero.org/png?builder=monero-static-dragonflybsd-amd64)](https://build.getmonero.org/builders/monero-static-dragonflybsd-amd64)
-| Windows (MSYS2/MinGW) | i686 | [![Windows (MSYS2/MinGW) i686](https://build.getmonero.org/png?builder=monero-static-win32)](https://build.getmonero.org/builders/monero-static-win32)
-| Windows (MSYS2/MinGW) | amd64 | [![Windows (MSYS2/MinGW) amd64](https://build.getmonero.org/png?builder=monero-static-win64)](https://build.getmonero.org/builders/monero-static-win64)
-
## Coverage
| Type | Status |
@@ -153,11 +134,12 @@ Dates are provided in the format YYYY-MM-DD.
| 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.4 | bulletproofs required
| 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.1.2 | New PoW based on Cryptonight-R, new block weight algorithm, slightly more efficient RingCT format
| 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.1.2 | forbid old RingCT transaction format
-| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.15.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming transactions
+| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.15.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming outputs
| XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX |
X's indicate that these details have not been determined as of commit date.
-* indicates estimate as of commit date
+
+\* indicates estimate as of commit date
## Release staging schedule and protocol
@@ -176,31 +158,31 @@ sources are also used for statically-linked builds because distribution
packages often include only shared library binaries (`.so`) but not static
library archives (`.a`).
-| Dep | Min. version | Vendored | Debian/Ubuntu pkg | Arch pkg | Fedora | Optional | Purpose |
-| ------------ | ------------- | -------- | -------------------- | ------------ | ------------------- | -------- | --------------- |
-| GCC | 4.7.3 | NO | `build-essential` | `base-devel` | `gcc` | NO | |
-| CMake | 3.5 | NO | `cmake` | `cmake` | `cmake` | NO | |
-| pkg-config | any | NO | `pkg-config` | `base-devel` | `pkgconf` | NO | |
-| Boost | 1.58 | NO | `libboost-all-dev` | `boost` | `boost-devel` | NO | C++ libraries |
-| OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `openssl-devel` | NO | sha256 sum |
-| libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `zeromq-devel` | NO | ZeroMQ library |
-| OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ |
-| libnorm[2] | ? | NO | `libnorm-dev` | | | YES | For ZeroMQ |
-| libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver |
-| libsodium | ? | NO | `libsodium-dev` | `libsodium` | `libsodium-devel` | NO | cryptography |
-| libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces |
-| liblzma | any | NO | `liblzma-dev` | `xz` | `xz-devel` | YES | For libunwind |
-| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | YES | Input editing |
-| ldns | 1.6.17 | NO | `libldns-dev` | `ldns` | `ldns-devel` | YES | SSL toolkit |
-| expat | 1.1 | NO | `libexpat1-dev` | `expat` | `expat-devel` | YES | XML parsing |
-| GTest | 1.5 | YES | `libgtest-dev`[1] | `gtest` | `gtest-devel` | YES | Test suite |
-| Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | YES | Documentation |
-| Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | YES | Documentation |
-| lrelease | ? | NO | `qttools5-dev-tools` | `qt5-tools` | `qt5-linguist` | YES | Translations |
-| libhidapi | ? | NO | `libhidapi-dev` | `hidapi` | `hidapi-devel` | YES | Hardware wallet |
-| libusb | ? | NO | `libusb-dev` | `libusb` | `libusb-devel` | YES | Hardware wallet |
-| libprotobuf | ? | NO | `libprotobuf-dev` | `protobuf` | `protobuf-devel` | YES | Hardware wallet |
-| protoc | ? | NO | `protobuf-compiler` | `protobuf` | `protobuf-compiler` | YES | Hardware wallet |
+| Dep | Min. version | Vendored | Debian/Ubuntu pkg | Arch pkg | Void pkg | Fedora pkg | Optional | Purpose |
+| ------------ | ------------- | -------- | -------------------- | ------------ | ------------------ | ------------------- | -------- | --------------- |
+| GCC | 4.7.3 | NO | `build-essential` | `base-devel` | `base-devel` | `gcc` | NO | |
+| CMake | 3.5 | NO | `cmake` | `cmake` | `cmake` | `cmake` | NO | |
+| pkg-config | any | NO | `pkg-config` | `base-devel` | `base-devel` | `pkgconf` | NO | |
+| Boost | 1.58 | NO | `libboost-all-dev` | `boost` | `boost-devel` | `boost-devel` | NO | C++ libraries |
+| OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `libressl-devel` | `openssl-devel` | NO | sha256 sum |
+| libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `zeromq-devel` | `zeromq-devel` | NO | ZeroMQ library |
+| OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | | `openpgm-devel` | NO | For ZeroMQ |
+| libnorm[2] | ? | NO | `libnorm-dev` | | | | YES | For ZeroMQ |
+| libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | `unbound-devel` | NO | DNS resolver |
+| libsodium | ? | NO | `libsodium-dev` | `libsodium` | `libsodium-devel` | `libsodium-devel` | NO | cryptography |
+| libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | `libunwind-devel` | YES | Stack traces |
+| liblzma | any | NO | `liblzma-dev` | `xz` | `liblzma-devel` | `xz-devel` | YES | For libunwind |
+| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | `readline-devel` | YES | Input editing |
+| ldns | 1.6.17 | NO | `libldns-dev` | `ldns` | `libldns-devel` | `ldns-devel` | YES | SSL toolkit |
+| expat | 1.1 | NO | `libexpat1-dev` | `expat` | `expat-devel` | `expat-devel` | YES | XML parsing |
+| GTest | 1.5 | YES | `libgtest-dev`[1] | `gtest` | `gtest-devel` | `gtest-devel` | YES | Test suite |
+| Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | `doxygen` | YES | Documentation |
+| Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | `graphviz` | YES | Documentation |
+| lrelease | ? | NO | `qttools5-dev-tools` | `qt5-tools` | `qt5-tools` | `qt5-linguist` | YES | Translations |
+| libhidapi | ? | NO | `libhidapi-dev` | `hidapi` | `hidapi-devel` | `hidapi-devel` | YES | Hardware wallet |
+| libusb | ? | NO | `libusb-dev` | `libusb` | `libusb-devel` | `libusb-devel` | YES | Hardware wallet |
+| libprotobuf | ? | NO | `libprotobuf-dev` | `protobuf` | `protobuf-devel` | `protobuf-devel` | YES | Hardware wallet |
+| protoc | ? | NO | `protobuf-compiler` | `protobuf` | `protobuf` | `protobuf-compiler` | YES | Hardware wallet |
[1] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must
@@ -547,6 +529,11 @@ You can also cross-compile static binaries on Linux for Windows and macOS with t
* Requires: `g++-aarch64-linux-gnu`
* ```make depends target=riscv64-linux-gnu``` for RISC V 64 bit binaries.
* Requires: `g++-riscv64-linux-gnu`
+* ```make depends target=x86_64-unknown-freebsd``` for freebsd binaries.
+ * Requires: `clang-8`
+* ```make depends target=arm-linux-android``` for 32bit android binaries
+* ```make depends target=aarch64-linux-android``` for 64bit android binaries
+
The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names.
diff --git a/contrib/depends/funcs.mk b/contrib/depends/funcs.mk
index 355ae07eb..2d6b37190 100644
--- a/contrib/depends/funcs.mk
+++ b/contrib/depends/funcs.mk
@@ -135,9 +135,9 @@ $(1)_config_env+=$($(1)_config_env_$(host_arch)_$(host_os)) $($(1)_config_env_$(
$(1)_config_env+=PKG_CONFIG_LIBDIR=$($($(1)_type)_prefix)/lib/pkgconfig
$(1)_config_env+=PKG_CONFIG_PATH=$($($(1)_type)_prefix)/share/pkgconfig
-$(1)_config_env+=PATH=$(build_prefix)/bin:$(PATH)
-$(1)_build_env+=PATH=$(build_prefix)/bin:$(PATH)
-$(1)_stage_env+=PATH=$(build_prefix)/bin:$(PATH)
+$(1)_config_env+=PATH="$(build_prefix)/bin:$(PATH)"
+$(1)_build_env+=PATH="$(build_prefix)/bin:$(PATH)"
+$(1)_stage_env+=PATH="$(build_prefix)/bin:$(PATH)"
$(1)_autoconf=./configure --host=$($($(1)_type)_host) --disable-dependency-tracking --prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)"
ifneq ($($(1)_nm),)
diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in
index c56f3eb2e..2634423ab 100644
--- a/contrib/depends/toolchain.cmake.in
+++ b/contrib/depends/toolchain.cmake.in
@@ -136,11 +136,21 @@ endif()
if(ARCHITECTURE STREQUAL "i686")
SET(ARCH_ID "i386")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set(BUILD_TAG "linux-x86")
SET(LINUX_32 ON)
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ set(BUILD_TAG "win-x32")
endif()
endif()
if(ARCHITECTURE STREQUAL "x86_64")
+ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set(BUILD_TAG "linux-x64")
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+ set(BUILD_TAG "freebsd-x64")
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ set(BUILD_TAG "win-x64")
+ endif()
SET(ARCH_ID "x86_64")
endif()
diff --git a/contrib/epee/include/hex.h b/contrib/epee/include/hex.h
index 6e720f128..4e8f90786 100644
--- a/contrib/epee/include/hex.h
+++ b/contrib/epee/include/hex.h
@@ -51,12 +51,22 @@ namespace epee
template<std::size_t N>
static std::array<char, N * 2> array(const std::array<std::uint8_t, N>& src) noexcept
{
- std::array<char, N * 2> out{{}};
+ std::array<char, N * 2> out;
static_assert(N <= 128, "keep the stack size down");
buffer_unchecked(out.data(), {src.data(), src.size()});
return out;
}
+ //! \return An array containing hex of `src`.
+ template<typename T>
+ static std::array<char, sizeof(T) * 2> array(const T& src) noexcept
+ {
+ std::array<char, sizeof(T) * 2> out;
+ static_assert(sizeof(T) <= 128, "keep the stack size down");
+ buffer_unchecked(out.data(), as_byte_span(src));
+ return out;
+ }
+
//! Append `src` as hex to `out`.
static void buffer(std::ostream& out, const span<const std::uint8_t> src);
diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl
index 128ff10aa..43ede3cc1 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.inl
+++ b/contrib/epee/include/net/abstract_tcp_server2.inl
@@ -363,8 +363,8 @@ PRAGMA_WARNING_DISABLE_VS(4355)
}
delay *= 0.5;
- if (delay > 0) {
- long int ms = (long int)(delay * 100);
+ long int ms = (long int)(delay * 100);
+ if (ms > 0) {
reset_timer(boost::posix_time::milliseconds(ms + 1), true);
boost::this_thread::sleep_for(boost::chrono::milliseconds(ms));
}
@@ -721,7 +721,9 @@ PRAGMA_WARNING_DISABLE_VS(4355)
boost::posix_time::milliseconds connection<t_protocol_handler>::get_timeout_from_bytes_read(size_t bytes)
{
boost::posix_time::milliseconds ms = (boost::posix_time::milliseconds)(unsigned)(bytes * TIMEOUT_EXTRA_MS_PER_BYTE);
- ms += m_timer.expires_from_now();
+ const auto cur = m_timer.expires_from_now().total_milliseconds();
+ if (cur > 0)
+ ms += (boost::posix_time::milliseconds)cur;
if (ms > get_default_timeout())
ms = get_default_timeout();
return ms;
@@ -747,7 +749,12 @@ PRAGMA_WARNING_DISABLE_VS(4355)
template<class t_protocol_handler>
void connection<t_protocol_handler>::reset_timer(boost::posix_time::milliseconds ms, bool add)
{
- MTRACE("Setting " << ms << " expiry");
+ if (ms.total_milliseconds() < 0)
+ {
+ MWARNING("Ignoring negative timeout " << ms);
+ return;
+ }
+ MTRACE((add ? "Adding" : "Setting") << " " << ms << " expiry");
auto self = safe_shared_from_this();
if(!self)
{
@@ -760,7 +767,11 @@ PRAGMA_WARNING_DISABLE_VS(4355)
return;
}
if (add)
- ms += m_timer.expires_from_now();
+ {
+ const auto cur = m_timer.expires_from_now().total_milliseconds();
+ if (cur > 0)
+ ms += (boost::posix_time::milliseconds)cur;
+ }
m_timer.expires_from_now(ms);
m_timer.async_wait([=](const boost::system::error_code& ec)
{
diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h
index 81545e502..9446e3588 100644
--- a/contrib/epee/include/net/net_helper.h
+++ b/contrib/epee/include/net/net_helper.h
@@ -103,8 +103,8 @@ namespace net_utils
blocked_mode_client() :
m_io_service(),
m_ctx(boost::asio::ssl::context::tlsv12),
- m_connector(direct_connect{}),
m_ssl_socket(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, m_ctx)),
+ m_connector(direct_connect{}),
m_ssl_options(epee::net_utils::ssl_support_t::e_ssl_support_autodetect),
m_initialized(true),
m_connected(false),
diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h
index 028e605d7..d86c62c17 100644
--- a/contrib/epee/include/net/net_utils_base.h
+++ b/contrib/epee/include/net/net_utils_base.h
@@ -94,17 +94,13 @@ namespace net_utils
BEGIN_KV_SERIALIZE_MAP()
if (is_store)
{
- KV_SERIALIZE_VAL_POD_AS_BLOB_N(m_ip, "ip")
uint32_t ip = SWAP32LE(this_ref.m_ip);
epee::serialization::selector<is_store>::serialize(ip, stg, hparent_section, "m_ip");
}
else
{
- if (!epee::serialization::selector<is_store>::serialize_t_val_as_blob(this_ref.m_ip, stg, hparent_section, "ip"))
- {
- KV_SERIALIZE(m_ip)
- const_cast<ipv4_network_address&>(this_ref).m_ip = SWAP32LE(this_ref.m_ip);
- }
+ KV_SERIALIZE(m_ip)
+ const_cast<ipv4_network_address&>(this_ref).m_ip = SWAP32LE(this_ref.m_ip);
}
KV_SERIALIZE(m_port)
END_KV_SERIALIZE_MAP()
diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h
index 78d294d05..fd343865c 100644
--- a/contrib/epee/include/serialization/keyvalue_serialization.h
+++ b/contrib/epee/include/serialization/keyvalue_serialization.h
@@ -89,6 +89,8 @@ public: \
#define KV_SERIALIZE_OPT_N(variable, val_name, default_value) \
do { \
+ if (is_store && this_ref.variable == default_value) \
+ break; \
if (!epee::serialization::selector<is_store>::serialize(this_ref.variable, stg, hparent_section, val_name)) \
epee::serialize_default(this_ref.variable, default_value); \
} while (0);
diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
index fc8b90a2c..1f9d6b6d7 100644
--- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
+++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
@@ -48,22 +48,10 @@ namespace epee
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
- static bool serialize_t_val(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
- {
- return stg.set_value(pname, d, hparent_section);
- }
- //-------------------------------------------------------------------------------------------------------------------
- template<class t_type, class t_storage>
- static bool unserialize_t_val(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
- {
- return stg.get_value(pname, d, hparent_section);
- }
- //-------------------------------------------------------------------------------------------------------------------
- template<class t_type, class t_storage>
static bool serialize_t_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
std::string blob((const char *)&d, sizeof(d));
- return stg.set_value(pname, blob, hparent_section);
+ return stg.set_value(pname, std::move(blob), hparent_section);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
@@ -114,13 +102,15 @@ namespace epee
template<class stl_container, class t_storage>
static bool serialize_stl_container_t_val (const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
+ using value_type = typename stl_container::value_type;
+
if(!container.size()) return true;
typename stl_container::const_iterator it = container.begin();
- typename t_storage::harray hval_array = stg.insert_first_value(pname, *it, hparent_section);
+ typename t_storage::harray hval_array = stg.insert_first_value(pname, value_type(*it), hparent_section);
CHECK_AND_ASSERT_MES(hval_array, false, "failed to insert first value to storage");
it++;
for(;it!= container.end();it++)
- stg.insert_next_value(hval_array, *it);
+ stg.insert_next_value(hval_array, value_type(*it));
return true;
}
@@ -149,7 +139,7 @@ namespace epee
*p_elem = v;
p_elem++;
}
- return stg.set_value(pname, mb, hparent_section);
+ return stg.set_value(pname, std::move(mb), hparent_section);
}
//--------------------------------------------------------------------------------------------------------------------
template<class stl_container, class t_storage>
@@ -221,7 +211,7 @@ namespace epee
template<class t_type, class t_storage>
static bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
- return stg.set_value(pname, d, hparent_section);
+ return stg.set_value(pname, t_type(d), hparent_section);
}
//-------------------------------------------------------------------------------------------------------------------
template<class t_type, class t_storage>
diff --git a/contrib/epee/include/storages/levin_abstract_invoke2.h b/contrib/epee/include/storages/levin_abstract_invoke2.h
index 06eb9bdaf..b18e04a27 100644
--- a/contrib/epee/include/storages/levin_abstract_invoke2.h
+++ b/contrib/epee/include/storages/levin_abstract_invoke2.h
@@ -34,10 +34,28 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net"
+namespace
+{
+ template<typename context_t>
+ void on_levin_traffic(const context_t &context, bool initiator, bool sent, bool error, size_t bytes, const char *category)
+ {
+ MCINFO("net.p2p.traffic", context << bytes << " bytes " << (sent ? "sent" : "received") << (error ? "/corrupt" : "")
+ << " for category " << category << " initiated by " << (initiator ? "us" : "peer"));
+ }
+ template<typename context_t>
+ void on_levin_traffic(const context_t &context, bool initiator, bool sent, bool error, size_t bytes, int command)
+ {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "command-%u", command);
+ return on_levin_traffic(context, initiator, sent, error, bytes, buf);
+ }
+}
+
namespace epee
{
namespace net_utils
{
+#if 0
template<class t_arg, class t_result, class t_transport>
bool invoke_remote_command2(int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport)
{
@@ -83,16 +101,18 @@ namespace epee
}
return true;
}
+#endif
template<class t_arg, class t_result, class t_transport>
- bool invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport)
+ bool invoke_remote_command2(const epee::net_utils::connection_context_base context, int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport)
{
-
+ const boost::uuids::uuid &conn_id = context.m_connection_id;
typename serialization::portable_storage stg;
out_struct.store(stg);
std::string buff_to_send, buff_to_recv;
stg.store_to_binary(buff_to_send);
+ on_levin_traffic(context, true, true, false, buff_to_send.size(), command);
int res = transport.invoke(command, buff_to_send, buff_to_recv, conn_id);
if( res <=0 )
{
@@ -102,24 +122,30 @@ namespace epee
typename serialization::portable_storage stg_ret;
if(!stg_ret.load_from_binary(buff_to_recv))
{
+ on_levin_traffic(context, true, false, true, buff_to_recv.size(), command);
LOG_ERROR("Failed to load_from_binary on command " << command);
return false;
}
+ on_levin_traffic(context, true, false, false, buff_to_recv.size(), command);
return result_struct.load(stg_ret);
}
template<class t_result, class t_arg, class callback_t, class t_transport>
- bool async_invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport, const callback_t &cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
+ bool async_invoke_remote_command2(const epee::net_utils::connection_context_base &context, int command, const t_arg& out_struct, t_transport& transport, const callback_t &cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
{
+ const boost::uuids::uuid &conn_id = context.m_connection_id;
typename serialization::portable_storage stg;
const_cast<t_arg&>(out_struct).store(stg);//TODO: add true const support to searilzation
std::string buff_to_send;
stg.store_to_binary(buff_to_send);
+ on_levin_traffic(context, true, true, false, buff_to_send.size(), command);
int res = transport.invoke_async(command, epee::strspan<uint8_t>(buff_to_send), conn_id, [cb, command](int code, const epee::span<const uint8_t> buff, typename t_transport::connection_context& context)->bool
{
t_result result_struct = AUTO_VAL_INIT(result_struct);
if( code <=0 )
{
+ if (!buff.empty())
+ on_levin_traffic(context, true, false, true, buff.size(), command);
LOG_PRINT_L1("Failed to invoke command " << command << " return code " << code);
cb(code, result_struct, context);
return false;
@@ -127,16 +153,19 @@ namespace epee
serialization::portable_storage stg_ret;
if(!stg_ret.load_from_binary(buff))
{
+ on_levin_traffic(context, true, false, true, buff.size(), command);
LOG_ERROR("Failed to load_from_binary on command " << command);
cb(LEVIN_ERROR_FORMAT, result_struct, context);
return false;
}
if (!result_struct.load(stg_ret))
{
+ on_levin_traffic(context, true, false, true, buff.size(), command);
LOG_ERROR("Failed to load result struct on command " << command);
cb(LEVIN_ERROR_FORMAT, result_struct, context);
return false;
}
+ on_levin_traffic(context, true, false, false, buff.size(), command);
cb(code, result_struct, context);
return true;
}, inv_timeout);
@@ -149,14 +178,15 @@ namespace epee
}
template<class t_arg, class t_transport>
- bool notify_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport)
+ bool notify_remote_command2(const typename t_transport::connection_context &context, int command, const t_arg& out_struct, t_transport& transport)
{
-
+ const boost::uuids::uuid &conn_id = context.m_connection_id;
serialization::portable_storage stg;
out_struct.store(stg);
std::string buff_to_send;
stg.store_to_binary(buff_to_send);
+ on_levin_traffic(context, true, true, false, buff_to_send.size(), command);
int res = transport.notify(command, epee::strspan<uint8_t>(buff_to_send), conn_id);
if(res <=0 )
{
@@ -173,6 +203,7 @@ namespace epee
serialization::portable_storage strg;
if(!strg.load_from_binary(in_buff))
{
+ on_levin_traffic(context, false, false, true, in_buff.size(), command);
LOG_ERROR("Failed to load_from_binary in command " << command);
return -1;
}
@@ -181,9 +212,11 @@ namespace epee
if (!static_cast<t_in_type&>(in_struct).load(strg))
{
+ on_levin_traffic(context, false, false, true, in_buff.size(), command);
LOG_ERROR("Failed to load in_struct in command " << command);
return -1;
}
+ on_levin_traffic(context, false, false, false, in_buff.size(), command);
int res = cb(command, static_cast<t_in_type&>(in_struct), static_cast<t_out_type&>(out_struct), context);
serialization::portable_storage strg_out;
static_cast<t_out_type&>(out_struct).store(strg_out);
@@ -193,6 +226,7 @@ namespace epee
LOG_ERROR("Failed to store_to_binary in command" << command);
return -1;
}
+ on_levin_traffic(context, false, true, false, buff_out.size(), command);
return res;
}
@@ -203,15 +237,18 @@ namespace epee
serialization::portable_storage strg;
if(!strg.load_from_binary(in_buff))
{
+ on_levin_traffic(context, false, false, true, in_buff.size(), command);
LOG_ERROR("Failed to load_from_binary in notify " << command);
return -1;
}
boost::value_initialized<t_in_type> in_struct;
if (!static_cast<t_in_type&>(in_struct).load(strg))
{
+ on_levin_traffic(context, false, false, true, in_buff.size(), command);
LOG_ERROR("Failed to load in_struct in notify " << command);
return -1;
}
+ on_levin_traffic(context, false, false, false, in_buff.size(), command);
return cb(command, in_struct, context);
}
@@ -296,6 +333,7 @@ namespace epee
#define END_INVOKE_MAP2() \
LOG_ERROR("Unknown command:" << command); \
+ on_levin_traffic(context, false, false, true, in_buff.size(), "invalid-command"); \
return LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; \
}
}
diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h
index fe53628a5..8a498130c 100644
--- a/contrib/epee/include/storages/parserse_base_utils.h
+++ b/contrib/epee/include/storages/parserse_base_utils.h
@@ -157,7 +157,6 @@ namespace misc_utils
while (fi != buf_end && ((lut[(uint8_t)*fi] & 32)) == 0)
++fi;
val.assign(it, fi);
- val.reserve(std::distance(star_end_string, buf_end));
it = fi;
for(;it != buf_end;it++)
{
diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h
index d0e40d606..4b759a24f 100644
--- a/contrib/epee/include/storages/portable_storage.h
+++ b/contrib/epee/include/storages/portable_storage.h
@@ -28,6 +28,8 @@
#pragma once
+#include <type_traits>
+
#include "misc_language.h"
#include "portable_storage_base.h"
#include "portable_storage_to_bin.h"
@@ -59,7 +61,7 @@ namespace epee
bool get_value(const std::string& value_name, t_value& val, hsection hparent_section);
bool get_value(const std::string& value_name, storage_entry& val, hsection hparent_section);
template<class t_value>
- bool set_value(const std::string& value_name, const t_value& target, hsection hparent_section);
+ bool set_value(const std::string& value_name, t_value&& target, hsection hparent_section);
//serial access for arrays of values --------------------------------------
//values
@@ -68,9 +70,9 @@ namespace epee
template<class t_value>
bool get_next_value(harray hval_array, t_value& target);
template<class t_value>
- harray insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section);
+ harray insert_first_value(const std::string& value_name, t_value&& target, hsection hparent_section);
template<class t_value>
- bool insert_next_value(harray hval_array, const t_value& target);
+ bool insert_next_value(harray hval_array, t_value&& target);
//sections
harray get_first_section(const std::string& pSectionName, hsection& h_child_section, hsection hparent_section);
bool get_next_section(harray hSecArray, hsection& h_child_section);
@@ -94,7 +96,7 @@ namespace epee
hsection get_root_section() {return &m_root;}
storage_entry* find_storage_entry(const std::string& pentry_name, hsection psection);
template<class entry_type>
- storage_entry* insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry);
+ storage_entry* insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, entry_type&& entry);
hsection insert_new_section(const std::string& pentry_name, hsection psection);
@@ -241,21 +243,22 @@ namespace epee
}
//---------------------------------------------------------------------------------------------------------------
template<class t_value>
- bool portable_storage::set_value(const std::string& value_name, const t_value& v, hsection hparent_section)
+ bool portable_storage::set_value(const std::string& value_name, t_value&& v, hsection hparent_section)
{
- BOOST_MPL_ASSERT(( boost::mpl::contains<boost::mpl::push_front<storage_entry::types, storage_entry>::type, t_value> ));
+ using t_real_value = typename std::decay<t_value>::type;
+ BOOST_MPL_ASSERT(( boost::mpl::contains<boost::mpl::push_front<storage_entry::types, storage_entry>::type, t_real_value> ));
TRY_ENTRY();
if(!hparent_section)
hparent_section = &m_root;
storage_entry* pentry = find_storage_entry(value_name, hparent_section);
if(!pentry)
{
- pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, v);
+ pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, std::forward<t_value>(v));
if(!pentry)
return false;
return true;
}
- *pentry = storage_entry(v);
+ *pentry = std::forward<t_value>(v);
return true;
CATCH_ENTRY("portable_storage::template<>set_value", false);
}
@@ -274,11 +277,12 @@ namespace epee
}
//---------------------------------------------------------------------------------------------------------------
template<class entry_type>
- storage_entry* portable_storage::insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry)
+ storage_entry* portable_storage::insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, entry_type&& entry)
{
+ static_assert(std::is_rvalue_reference<entry_type&&>(), "unexpected copy of value");
TRY_ENTRY();
CHECK_AND_ASSERT(psection, nullptr);
- auto ins_res = psection->m_entries.insert(std::pair<std::string, storage_entry>(pentry_name, entry));
+ auto ins_res = psection->m_entries.emplace(pentry_name, std::forward<entry_type>(entry));
return &ins_res.first->second;
CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr);
}
@@ -362,41 +366,45 @@ namespace epee
}
//---------------------------------------------------------------------------------------------------------------
template<class t_value>
- harray portable_storage::insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section)
+ harray portable_storage::insert_first_value(const std::string& value_name, t_value&& target, hsection hparent_section)
{
+ using t_real_value = typename std::decay<t_value>::type;
+ static_assert(std::is_rvalue_reference<t_value&&>(), "unexpected copy of value");
TRY_ENTRY();
if(!hparent_section) hparent_section = &m_root;
storage_entry* pentry = find_storage_entry(value_name, hparent_section);
if(!pentry)
{
- pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, array_entry(array_entry_t<t_value>()));
+ pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, array_entry(array_entry_t<t_real_value>()));
if(!pentry)
return nullptr;
}
if(pentry->type() != typeid(array_entry))
- *pentry = storage_entry(array_entry(array_entry_t<t_value>()));
+ *pentry = storage_entry(array_entry(array_entry_t<t_real_value>()));
array_entry& arr = boost::get<array_entry>(*pentry);
- if(arr.type() != typeid(array_entry_t<t_value>))
- arr = array_entry(array_entry_t<t_value>());
+ if(arr.type() != typeid(array_entry_t<t_real_value>))
+ arr = array_entry(array_entry_t<t_real_value>());
- array_entry_t<t_value>& arr_typed = boost::get<array_entry_t<t_value> >(arr);
- arr_typed.insert_first_val(target);
+ array_entry_t<t_real_value>& arr_typed = boost::get<array_entry_t<t_real_value> >(arr);
+ arr_typed.insert_first_val(std::forward<t_value>(target));
return &arr;
CATCH_ENTRY("portable_storage::insert_first_value", nullptr);
}
//---------------------------------------------------------------------------------------------------------------
template<class t_value>
- bool portable_storage::insert_next_value(harray hval_array, const t_value& target)
+ bool portable_storage::insert_next_value(harray hval_array, t_value&& target)
{
+ using t_real_value = typename std::decay<t_value>::type;
+ static_assert(std::is_rvalue_reference<t_value&&>(), "unexpected copy of value");
TRY_ENTRY();
CHECK_AND_ASSERT(hval_array, false);
- CHECK_AND_ASSERT_MES(hval_array->type() == typeid(array_entry_t<t_value>),
- false, "unexpected type in insert_next_value: " << typeid(array_entry_t<t_value>).name());
+ CHECK_AND_ASSERT_MES(hval_array->type() == typeid(array_entry_t<t_real_value>),
+ false, "unexpected type in insert_next_value: " << typeid(array_entry_t<t_real_value>).name());
- array_entry_t<t_value>& arr_typed = boost::get<array_entry_t<t_value> >(*hval_array);
- arr_typed.insert_next_value(target);
+ array_entry_t<t_real_value>& arr_typed = boost::get<array_entry_t<t_real_value> >(*hval_array);
+ arr_typed.insert_next_value(std::forward<t_value>(target));
return true;
CATCH_ENTRY("portable_storage::insert_next_value", false);
}
diff --git a/contrib/epee/include/storages/portable_storage_base.h b/contrib/epee/include/storages/portable_storage_base.h
index ca7c81ddc..40c7524fb 100644
--- a/contrib/epee/include/storages/portable_storage_base.h
+++ b/contrib/epee/include/storages/portable_storage_base.h
@@ -111,16 +111,16 @@ namespace epee
return (t_entry_type*)&(*(m_it++));//fuckoff
}
- t_entry_type& insert_first_val(const t_entry_type& v)
+ t_entry_type& insert_first_val(t_entry_type&& v)
{
m_array.clear();
m_it = m_array.end();
- return insert_next_value(v);
+ return insert_next_value(std::move(v));
}
- t_entry_type& insert_next_value(const t_entry_type& v)
+ t_entry_type& insert_next_value(t_entry_type&& v)
{
- m_array.push_back(v);
+ m_array.push_back(std::move(v));
return m_array.back();
}
diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h
index c0b6cc7b1..b39dc7c92 100644
--- a/contrib/epee/include/storages/portable_storage_from_bin.h
+++ b/contrib/epee/include/storages/portable_storage_from_bin.h
@@ -143,7 +143,7 @@ namespace epee
//TODO: add some optimization here later
while(size--)
sa.m_array.push_back(read<type_name>());
- return storage_entry(array_entry(sa));
+ return storage_entry(array_entry(std::move(sa)));
}
inline
@@ -213,7 +213,7 @@ namespace epee
{
RECURSION_LIMITATION();
section s;//use extra variable due to vs bug, line "storage_entry se(section()); " can't be compiled in visual studio
- storage_entry se(s);
+ storage_entry se(std::move(s));
section& section_entry = boost::get<section>(se);
read(section_entry);
return se;
@@ -268,7 +268,7 @@ namespace epee
//read section name string
std::string sec_name;
read_sec_name(sec_name);
- sec.m_entries.insert(std::make_pair(sec_name, load_storage_entry()));
+ sec.m_entries.emplace(std::move(sec_name), load_storage_entry());
}
}
inline
diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h
index 3e3052541..2b2dc7ff9 100644
--- a/contrib/epee/include/storages/portable_storage_from_json.h
+++ b/contrib/epee/include/storages/portable_storage_from_json.h
@@ -128,20 +128,20 @@ namespace epee
errno = 0;
int64_t nval = strtoll(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
- stg.set_value(name, nval, current_section);
+ stg.set_value(name, int64_t(nval), current_section);
}else
{
errno = 0;
uint64_t nval = strtoull(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
- stg.set_value(name, nval, current_section);
+ stg.set_value(name, uint64_t(nval), current_section);
}
}else
{
errno = 0;
double nval = strtod(val.data(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
- stg.set_value(name, nval, current_section);
+ stg.set_value(name, double(nval), current_section);
}
state = match_state_wonder_after_value;
}else if(isalpha(*it) )
@@ -219,13 +219,13 @@ namespace epee
errno = 0;
int64_t nval = strtoll(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
- h_array = stg.insert_first_value(name, nval, current_section);
+ h_array = stg.insert_first_value(name, int64_t(nval), current_section);
}else
{
errno = 0;
uint64_t nval = strtoull(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
- h_array = stg.insert_first_value(name, nval, current_section);
+ h_array = stg.insert_first_value(name, uint64_t(nval), current_section);
}
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
}else
@@ -233,7 +233,7 @@ namespace epee
errno = 0;
double nval = strtod(val.data(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
- h_array = stg.insert_first_value(name, nval, current_section);
+ h_array = stg.insert_first_value(name, double(nval), current_section);
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
}
@@ -310,20 +310,20 @@ namespace epee
errno = 0;
int64_t nval = strtoll(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
- insert_res = stg.insert_next_value(h_array, nval);
+ insert_res = stg.insert_next_value(h_array, int64_t(nval));
}else
{
errno = 0;
uint64_t nval = strtoull(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
- insert_res = stg.insert_next_value(h_array, nval);
+ insert_res = stg.insert_next_value(h_array, uint64_t(nval));
}
}else
{
errno = 0;
double nval = strtod(val.data(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
- insert_res = stg.insert_next_value(h_array, nval);
+ insert_res = stg.insert_next_value(h_array, double(nval));
}
CHECK_AND_ASSERT_THROW_MES(insert_res, "Failed to insert next value");
state = match_state_array_after_value;
diff --git a/contrib/epee/src/http_auth.cpp b/contrib/epee/src/http_auth.cpp
index 289069daa..5f4907cc2 100644
--- a/contrib/epee/src/http_auth.cpp
+++ b/contrib/epee/src/http_auth.cpp
@@ -584,8 +584,8 @@ namespace
explicit server_parameters(const auth_message& request, const DigestIter& digest)
: nonce(request.nonce)
, opaque(request.opaque)
- , stale(request.stale)
, realm(request.realm)
+ , stale(request.stale)
, value_generator()
, index(boost::fusion::distance(boost::fusion::begin(digest_algorithms), digest))
{
diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp
index 16454fce0..946499129 100644
--- a/contrib/epee/src/net_ssl.cpp
+++ b/contrib/epee/src/net_ssl.cpp
@@ -43,6 +43,10 @@
// openssl req -new -key /tmp/KEY -out /tmp/REQ
// openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT
+#ifdef _WIN32
+static void add_windows_root_certs(SSL_CTX *ctx) noexcept;
+#endif
+
namespace
{
struct openssl_bio_free
@@ -285,7 +289,7 @@ ssl_options_t::ssl_options_t(std::vector<std::vector<std::uint8_t>> fingerprints
boost::asio::ssl::context ssl_options_t::create_context() const
{
- boost::asio::ssl::context ssl_context{boost::asio::ssl::context::tlsv12};
+ boost::asio::ssl::context ssl_context{boost::asio::ssl::context::tls};
if (!bool(*this))
return ssl_context;
@@ -324,7 +328,12 @@ boost::asio::ssl::context ssl_options_t::create_context() const
switch (verification)
{
case ssl_verification_t::system_ca:
+#ifdef _WIN32
+ try { add_windows_root_certs(ssl_context.native_handle()); }
+ catch (const std::exception &e) { ssl_context.set_default_verify_paths(); }
+#else
ssl_context.set_default_verify_paths();
+#endif
break;
case ssl_verification_t::user_certificates:
ssl_context.set_verify_depth(0);
@@ -558,3 +567,36 @@ bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
} // namespace
} // namespace
+#ifdef _WIN32
+
+// https://stackoverflow.com/questions/40307541
+// Because Windows always has to do things wonkily
+#include <wincrypt.h>
+static void add_windows_root_certs(SSL_CTX *ctx) noexcept
+{
+ HCERTSTORE hStore = CertOpenSystemStore(0, "ROOT");
+ if (hStore == NULL) {
+ return;
+ }
+
+ X509_STORE *store = X509_STORE_new();
+ PCCERT_CONTEXT pContext = NULL;
+ while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != NULL) {
+ // convert from DER to internal format
+ X509 *x509 = d2i_X509(NULL,
+ (const unsigned char **)&pContext->pbCertEncoded,
+ pContext->cbCertEncoded);
+ if(x509 != NULL) {
+ X509_STORE_add_cert(store, x509);
+ X509_free(x509);
+ }
+ }
+
+ CertFreeCertificateContext(pContext);
+ CertCloseStore(hStore, 0);
+
+ // attach X509_STORE to boost ssl context
+ SSL_CTX_set_cert_store(ctx, store);
+}
+#endif
+
diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md
index 7e3502bcf..c09786c36 100644
--- a/contrib/gitian/README.md
+++ b/contrib/gitian/README.md
@@ -62,7 +62,7 @@ echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-execute" >> /etc/sudoers.d/gitian-lxc
# make /etc/rc.local script that sets up bridge between guest and host
echo '#!/bin/sh -e' > /etc/rc.local
echo 'brctl addbr br0' >> /etc/rc.local
-echo 'ip addr add 10.0.3.1/24 broadcast 10.0.3.255 dev br0' >> /etc/rc.local
+echo 'ip addr add 10.0.2.2/24 broadcast 10.0.2.255 dev br0' >> /etc/rc.local
echo 'ip link set br0 up' >> /etc/rc.local
echo 'firewall-cmd --zone=trusted --add-interface=br0' >> /etc/rc.local
echo 'exit 0' >> /etc/rc.local
@@ -70,8 +70,8 @@ chmod +x /etc/rc.local
# make sure that USE_LXC is always set when logging in as gitianuser,
# and configure LXC IP addresses
echo 'export USE_LXC=1' >> /home/gitianuser/.profile
-echo 'export GITIAN_HOST_IP=10.0.3.1' >> /home/gitianuser/.profile
-echo 'export LXC_GUEST_IP=10.0.3.5' >> /home/gitianuser/.profile
+echo 'export GITIAN_HOST_IP=10.0.2.2' >> /home/gitianuser/.profile
+echo 'export LXC_GUEST_IP=10.0.2.5' >> /home/gitianuser/.profile
reboot
```
diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py
index 64eb218bb..a8140a8a6 100755
--- a/contrib/gitian/gitian-build.py
+++ b/contrib/gitian/gitian-build.py
@@ -36,8 +36,11 @@ def setup():
os.chdir('..')
make_image_prog = ['bin/make-base-vm', '--suite', 'bionic', '--arch', 'amd64']
if args.docker:
- if not subprocess.call(['docker', '--help'], shell=False, stdout=subprocess.DEVNULL):
- print("Please install docker first manually")
+ try:
+ subprocess.check_output(['docker', '--help'])
+ except:
+ print("ERROR: Could not find 'docker' command. Ensure this is in your PATH.")
+ sys.exit(1)
make_image_prog += ['--docker']
elif not args.kvm:
make_image_prog += ['--lxc']
@@ -66,7 +69,7 @@ def rebuild():
print('\nCompiling ' + args.version + ' ' + os_name)
infile = 'inputs/monero/contrib/gitian/gitian-' + tag_name + '.yml'
subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, infile])
- subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-linux', '--destination', '../sigs/', infile])
+ subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-'+tag_name, '--destination', '../sigs/', infile])
subprocess.check_call('mv build/out/monero-*.' + suffix + ' ../out/'+args.version, shell=True)
print('Moving var/install.log to var/install-' + tag_name + '.log')
subprocess.check_call('mv var/install.log var/install-' + tag_name + '.log', shell=True)
@@ -157,9 +160,9 @@ def main():
elif not args.kvm:
os.environ['USE_LXC'] = '1'
if not 'GITIAN_HOST_IP' in os.environ.keys():
- os.environ['GITIAN_HOST_IP'] = '10.0.3.1'
+ os.environ['GITIAN_HOST_IP'] = '10.0.2.2'
if not 'LXC_GUEST_IP' in os.environ.keys():
- os.environ['LXC_GUEST_IP'] = '10.0.3.5'
+ os.environ['LXC_GUEST_IP'] = '10.0.2.5'
# Disable MacOS build if no SDK found
args.nomac = False
diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h
index 398afd20a..0b65461bc 100644
--- a/external/easylogging++/easylogging++.h
+++ b/external/easylogging++/easylogging++.h
@@ -125,13 +125,18 @@
#else
# define ELPP_OS_NETBSD 0
#endif
+#if defined(__EMSCRIPTEN__)
+# define ELPP_OS_EMSCRIPTEN 1
+#else
+# define ELPP_OS_EMSCRIPTEN 0
+#endif
#if (defined(__DragonFly__))
# define ELPP_OS_DRAGONFLY 1
#else
# define ELPP_OS_DRAGONFLY 0
#endif
// Unix
-#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || ELPP_OS_DRAGONFLY || ELPP_OS_OPENBSD) && (!ELPP_OS_WINDOWS))
+#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || ELPP_OS_EMSCRIPTEN || ELPP_OS_DRAGONFLY || ELPP_OS_OPENBSD) && (!ELPP_OS_WINDOWS))
# define ELPP_OS_UNIX 1
#else
# define ELPP_OS_UNIX 0
@@ -216,7 +221,7 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre
# define ELPP_INTERNAL_INFO(lvl, msg)
#endif // (defined(ELPP_DEBUG_INFO))
#if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG))
-# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_OPENBSD && !ELPP_OS_NETBSD && !ELPP_OS_ANDROID)
+# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_OPENBSD && !ELPP_OS_NETBSD && !ELPP_OS_ANDROID && !ELPP_OS_EMSCRIPTEN)
# define ELPP_STACKTRACE 1
# else
# define ELPP_STACKTRACE 0
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 5fec22d8d..1a6a19da5 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -67,7 +67,7 @@ bool matches_category(relay_method method, relay_category category) noexcept
case relay_method::local:
return false;
case relay_method::block:
- case relay_method::flood:
+ case relay_method::fluff:
return true;
case relay_method::none:
break;
@@ -90,7 +90,7 @@ void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept
is_local = 1;
break;
default:
- case relay_method::flood:
+ case relay_method::fluff:
break;
case relay_method::block:
kept_by_block = 1;
@@ -106,7 +106,7 @@ relay_method txpool_tx_meta_t::get_relay_method() const noexcept
return relay_method::none;
if (is_local)
return relay_method::local;
- return relay_method::flood;
+ return relay_method::fluff;
}
const command_line::arg_descriptor<std::string> arg_db_sync_mode = {
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index b2c5d6cb4..e9fc85803 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -108,7 +108,7 @@ extern const command_line::arg_descriptor<bool, false> arg_db_salvage;
enum class relay_category : uint8_t
{
- broadcasted = 0,//!< Public txes received via block/flooding/fluff
+ broadcasted = 0,//!< Public txes received via block/fluff
relayable, //!< Every tx not marked `relay_method::none`
legacy, //!< `relay_category::broadcasted` + `relay_method::none` for rpc relay requests or historical reasons
all //!< Everything in the db
@@ -1275,6 +1275,22 @@ public:
virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const = 0;
/**
+ * @brief fetches a number of pruned transaction blob from the given hash, in canonical blockchain order
+ *
+ * The subclass should return the pruned transactions stored from the one with the given
+ * hash.
+ *
+ * If the first transaction does not exist, the subclass should return false.
+ * If the first transaction exists, but there are fewer transactions starting with it
+ * than requested, the subclass should return false.
+ *
+ * @param h the hash to look for
+ *
+ * @return true iff the transactions were found
+ */
+ virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const = 0;
+
+ /**
* @brief fetches the prunable transaction blob with the given hash
*
* The subclass should return the prunable transaction stored which has the given
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index e8667adcf..5093015f2 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -163,7 +163,15 @@ int BlockchainLMDB::compare_string(const MDB_val *a, const MDB_val *b)
{
const char *va = (const char*) a->mv_data;
const char *vb = (const char*) b->mv_data;
- return strcmp(va, vb);
+ const size_t sz = std::min(a->mv_size, b->mv_size);
+ int ret = strncmp(va, vb, sz);
+ if (ret)
+ return ret;
+ if (a->mv_size < b->mv_size)
+ return -1;
+ if (a->mv_size > b->mv_size)
+ return 1;
+ return 0;
}
}
@@ -3057,6 +3065,48 @@ bool BlockchainLMDB::get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobd
return true;
}
+bool BlockchainLMDB::get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ if (!count)
+ return true;
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(tx_indices);
+ RCURSOR(txs_pruned);
+
+ bd.reserve(bd.size() + count);
+
+ MDB_val_set(v, h);
+ MDB_val result;
+ int res = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ if (res == MDB_NOTFOUND)
+ return false;
+ if (res)
+ throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx from hash", res).c_str()));
+
+ const txindex *tip = (const txindex *)v.mv_data;
+ const uint64_t id = tip->data.tx_id;
+ MDB_val_set(val_tx_id, id);
+ MDB_cursor_op op = MDB_SET;
+ while (count--)
+ {
+ res = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &result, op);
+ op = MDB_NEXT;
+ if (res == MDB_NOTFOUND)
+ return false;
+ if (res)
+ throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx blob", res).c_str()));
+ bd.emplace_back(reinterpret_cast<char*>(result.mv_data), result.mv_size);
+ }
+
+ TXN_POSTFIX_RDONLY();
+
+ return true;
+}
+
bool BlockchainLMDB::get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index e56711e8f..7c0b4c72c 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -254,6 +254,7 @@ public:
virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
+ virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const;
virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const;
diff --git a/src/cryptonote_basic/cryptonote_stat_info.h b/src/blockchain_db/locked_txn.h
index 4cc1bc764..d14631ddb 100644
--- a/src/cryptonote_basic/cryptonote_stat_info.h
+++ b/src/blockchain_db/locked_txn.h
@@ -1,21 +1,21 @@
// Copyright (c) 2014-2019, The Monero Project
-//
+//
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
-//
+//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
-//
+//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
-//
+//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@@ -25,30 +25,29 @@
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
+//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
-#include "serialization/keyvalue_serialization.h"
-
namespace cryptonote
{
- struct core_stat_info_t
- {
- uint64_t tx_pool_size;
- uint64_t blockchain_height;
- uint64_t mining_speed;
- uint64_t alternative_blocks;
- std::string top_block_id_str;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(tx_pool_size)
- KV_SERIALIZE(blockchain_height)
- KV_SERIALIZE(mining_speed)
- KV_SERIALIZE(alternative_blocks)
- KV_SERIALIZE(top_block_id_str)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<core_stat_info_t> core_stat_info;
+ // This class is meant to create a batch when none currently exists.
+ // If a batch exists, it can't be from another thread, since we can
+ // only be called with the txpool lock taken, and it is held during
+ // the whole prepare/handle/cleanup incoming block sequence.
+ class LockedTXN {
+ public:
+ LockedTXN(BlockchainDB &db): m_db(db), m_batch(false), m_active(false) {
+ m_batch = m_db.batch_start();
+ m_active = true;
+ }
+ void commit() { try { if (m_batch && m_active) { m_db.batch_stop(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::commit filtering exception: " << e.what()); } }
+ void abort() { try { if (m_batch && m_active) { m_db.batch_abort(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::abort filtering exception: " << e.what()); } }
+ ~LockedTXN() { abort(); }
+ private:
+ BlockchainDB &m_db;
+ bool m_batch;
+ bool m_active;
+ };
}
diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h
index a5847dec6..46de38c7e 100644
--- a/src/blockchain_db/testdb.h
+++ b/src/blockchain_db/testdb.h
@@ -69,6 +69,7 @@ public:
virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const override { return cryptonote::blobdata(); }
virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
+ virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const { return false; }
virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override { return false; }
virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; }
diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp
index 2f66d54aa..0d18b8819 100644
--- a/src/blockchain_utilities/blockchain_stats.cpp
+++ b/src/blockchain_utilities/blockchain_stats.cpp
@@ -264,12 +264,12 @@ skip:
{
throw std::runtime_error("Aborting: tx == null_hash");
}
- if (!db->get_tx_blob(tx_id, bd))
+ if (!db->get_pruned_tx_blob(tx_id, bd))
{
throw std::runtime_error("Aborting: tx not found");
}
transaction tx;
- if (!parse_and_validate_tx_from_blob(bd, tx))
+ if (!parse_and_validate_tx_base_from_blob(bd, tx))
{
LOG_PRINT_L0("Bad txn from db");
return 1;
diff --git a/src/common/download.cpp b/src/common/download.cpp
index f07d6798d..2b6a3f9d3 100644
--- a/src/common/download.cpp
+++ b/src/common/download.cpp
@@ -107,13 +107,17 @@ namespace tools
MINFO("Content-Length: " << length);
content_length = length;
boost::filesystem::path path(control->path);
- boost::filesystem::space_info si = boost::filesystem::space(path);
- if (si.available < (size_t)content_length)
+ try
{
- const uint64_t avail = (si.available + 1023) / 1024, needed = (content_length + 1023) / 1024;
- MERROR("Not enough space to download " << needed << " kB to " << path << " (" << avail << " kB available)");
- return false;
+ boost::filesystem::space_info si = boost::filesystem::space(path);
+ if (si.available < (size_t)content_length)
+ {
+ const uint64_t avail = (si.available + 1023) / 1024, needed = (content_length + 1023) / 1024;
+ MERROR("Not enough space to download " << needed << " kB to " << path << " (" << avail << " kB available)");
+ return false;
+ }
}
+ catch (const std::exception &e) { MWARNING("Failed to check for free space: " << e.what()); }
}
if (offset > 0)
{
diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h
index ea2237348..29a37e655 100644
--- a/src/common/perf_timer.h
+++ b/src/common/perf_timer.h
@@ -84,7 +84,7 @@ void set_performance_timer_log_level(el::Level level);
#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr<tools::LoggingPerformanceTimer> PERF_TIMER_NAME(name)(new tools::LoggingPerformanceTimer(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, el::Level::Info))
#define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000000)
#define PERF_TIMER_STOP(name) do { PERF_TIMER_NAME(name).reset(NULL); } while(0)
-#define PERF_TIMER_PAUSE(name) PERF_TIMER_NAME(name)->pause()
-#define PERF_TIMER_RESUME(name) PERF_TIMER_NAME(name)->resume()
+#define PERF_TIMER_PAUSE(name) PERF_TIMER_NAME(name).pause()
+#define PERF_TIMER_RESUME(name) PERF_TIMER_NAME(name).resume()
}
diff --git a/src/common/updates.cpp b/src/common/updates.cpp
index 0bc6ff63c..f620bb53a 100644
--- a/src/common/updates.cpp
+++ b/src/common/updates.cpp
@@ -101,7 +101,7 @@ namespace tools
{
const char *base = user ? "https://downloads.getmonero.org/" : "https://updates.getmonero.org/";
#ifdef _WIN32
- static const char *extension = strncmp(buildtag.c_str(), "install-", 8) ? ".zip" : ".exe";
+ static const char *extension = strncmp(buildtag.c_str(), "source", 6) ? (strncmp(buildtag.c_str(), "install-", 8) ? ".zip" : ".exe") : ".tar.bz2";
#else
static const char extension[] = ".tar.bz2";
#endif
diff --git a/src/common/util.cpp b/src/common/util.cpp
index 57e747837..747235646 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -1239,7 +1239,7 @@ std::string get_nix_version_display_string()
return get_string_prefix_by_width(s, 999999999).second;
};
- std::vector<std::pair<std::string, size_t>> split_string_by_width(const std::string &s, size_t columns)
+ std::vector<std::pair<std::string, size_t>> split_line_by_width(const std::string &s, size_t columns)
{
std::vector<std::string> words;
std::vector<std::pair<std::string, size_t>> lines;
@@ -1279,4 +1279,17 @@ std::string get_nix_version_display_string()
return lines;
}
+ std::vector<std::pair<std::string, size_t>> split_string_by_width(const std::string &s, size_t columns)
+ {
+ std::vector<std::string> lines;
+ std::vector<std::pair<std::string, size_t>> all_lines;
+ boost::split(lines, s, boost::is_any_of("\n"), boost::token_compress_on);
+ for (const auto &e: lines)
+ {
+ std::vector<std::pair<std::string, size_t>> new_lines = split_line_by_width(e, columns);
+ for (auto &l: new_lines)
+ all_lines.push_back(std::move(l));
+ }
+ return all_lines;
+ }
}
diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c
index 0f38356fa..606c67336 100644
--- a/src/crypto/rx-slow-hash.c
+++ b/src/crypto/rx-slow-hash.c
@@ -62,6 +62,7 @@ static CTHR_MUTEX_TYPE rx_dataset_mutex = CTHR_MUTEX_INIT;
static rx_state rx_s[2] = {{CTHR_MUTEX_INIT,{0},0,0},{CTHR_MUTEX_INIT,{0},0,0}};
static randomx_dataset *rx_dataset;
+static int rx_dataset_nomem;
static uint64_t rx_dataset_height;
static THREADV randomx_vm *rx_vm = NULL;
@@ -246,20 +247,25 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch
}
if (miners) {
CTHR_MUTEX_LOCK(rx_dataset_mutex);
- if (rx_dataset == NULL) {
- rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES);
+ if (!rx_dataset_nomem) {
if (rx_dataset == NULL) {
- mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset");
- rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT);
+ rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES);
+ if (rx_dataset == NULL) {
+ mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset");
+ rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT);
+ }
+ if (rx_dataset != NULL)
+ rx_initdata(rx_sp->rs_cache, miners, seedheight);
}
- if (rx_dataset != NULL)
- rx_initdata(rx_sp->rs_cache, miners, seedheight);
}
if (rx_dataset != NULL)
flags |= RANDOMX_FLAG_FULL_MEM;
else {
miners = 0;
- mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset for miner");
+ if (!rx_dataset_nomem) {
+ rx_dataset_nomem = 1;
+ mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset for miner");
+ }
}
CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
}
@@ -313,5 +319,6 @@ void rx_stop_mining(void) {
rx_dataset = NULL;
randomx_release_dataset(rd);
}
+ rx_dataset_nomem = 0;
CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
}
diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt
index 5bb56e083..59040d8a2 100644
--- a/src/cryptonote_basic/CMakeLists.txt
+++ b/src/cryptonote_basic/CMakeLists.txt
@@ -54,7 +54,6 @@ set(cryptonote_basic_private_headers
cryptonote_basic_impl.h
cryptonote_boost_serialization.h
cryptonote_format_utils.h
- cryptonote_stat_info.h
difficulty.h
hardfork.h
miner.h
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 138cf49f4..651d61b06 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -996,17 +996,31 @@ namespace cryptonote
}
}
//---------------------------------------------------------------
- std::string print_money(uint64_t amount, unsigned int decimal_point)
+ static void insert_money_decimal_point(std::string &s, unsigned int decimal_point)
{
if (decimal_point == (unsigned int)-1)
decimal_point = default_decimal_point;
- std::string s = std::to_string(amount);
if(s.size() < decimal_point+1)
{
s.insert(0, decimal_point+1 - s.size(), '0');
}
if (decimal_point > 0)
s.insert(s.size() - decimal_point, ".");
+ }
+ //---------------------------------------------------------------
+ std::string print_money(uint64_t amount, unsigned int decimal_point)
+ {
+ std::string s = std::to_string(amount);
+ insert_money_decimal_point(s, decimal_point);
+ return s;
+ }
+ //---------------------------------------------------------------
+ std::string print_money(const boost::multiprecision::uint128_t &amount, unsigned int decimal_point)
+ {
+ std::stringstream ss;
+ ss << amount;
+ std::string s = ss.str();
+ insert_money_decimal_point(s, decimal_point);
return s;
}
//---------------------------------------------------------------
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index 29e4def64..8ed3b0b43 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -38,6 +38,7 @@
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include <unordered_map>
+#include <boost/multiprecision/cpp_int.hpp>
namespace epee
{
@@ -139,6 +140,7 @@ namespace cryptonote
unsigned int get_default_decimal_point();
std::string get_unit(unsigned int decimal_point = -1);
std::string print_money(uint64_t amount, unsigned int decimal_point = -1);
+ std::string print_money(const boost::multiprecision::uint128_t &amount, unsigned int decimal_point = -1);
//---------------------------------------------------------------
template<class t_object>
bool t_serializable_object_from_blob(t_object& to, const blobdata& b_blob)
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index 688aeaea3..c1e8365ac 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -476,7 +476,7 @@ namespace cryptonote
for(; bl.nonce != std::numeric_limits<uint32_t>::max(); bl.nonce++)
{
crypto::hash h;
- gbh(bl, height, tools::get_max_concurrency(), h);
+ gbh(bl, height, diffic <= 100 ? 0 : tools::get_max_concurrency(), h);
if(check_hash(h, diffic))
{
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index ca127c3ee..4598baad7 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -101,6 +101,9 @@
#define CRYPTONOTE_MEMPOOL_TX_LIVETIME (86400*3) //seconds, three days
#define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week
+
+#define CRYPTONOTE_DANDELIONPP_FLUSH_AVERAGE 5 // seconds
+
// see src/cryptonote_protocol/levin_notify.cpp
#define CRYPTONOTE_NOISE_MIN_EPOCH 5 // minutes
#define CRYPTONOTE_NOISE_EPOCH_RANGE 30 // seconds
@@ -141,8 +144,6 @@
#define RPC_IP_FAILS_BEFORE_BLOCK 3
-#define ALLOW_DEBUG_COMMANDS
-
#define CRYPTONOTE_NAME "bitmonero"
#define CRYPTONOTE_POOLDATA_FILENAME "poolstate.bin"
#define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "data.mdb"
@@ -190,7 +191,6 @@ namespace config
uint8_t const FEE_CALCULATION_MAX_RETRIES = 10;
uint64_t const DEFAULT_DUST_THRESHOLD = ((uint64_t)2000000000); // 2 * pow(10, 9)
uint64_t const BASE_REWARD_CLAMP_THRESHOLD = ((uint64_t)100000000); // pow(10, 8)
- std::string const P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY = "0000000000000000000000000000000000000000000000000000000000000000";
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18;
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19;
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 5070cc169..5578f519e 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -2498,10 +2498,17 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
block b;
CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first.first, b), false, "internal error, invalid block");
blocks.back().first.second = get_miner_tx_hash ? cryptonote::get_transaction_hash(b.miner_tx) : crypto::null_hash;
- std::vector<crypto::hash> mis;
std::vector<cryptonote::blobdata> txs;
- get_transactions_blobs(b.tx_hashes, txs, mis, pruned);
- CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found");
+ if (pruned)
+ {
+ CHECK_AND_ASSERT_MES(m_db->get_pruned_tx_blobs_from(b.tx_hashes.front(), b.tx_hashes.size(), txs), false, "Failed to retrieve all transactions needed");
+ }
+ else
+ {
+ std::vector<crypto::hash> mis;
+ get_transactions_blobs(b.tx_hashes, txs, mis, pruned);
+ CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found");
+ }
size += blocks.back().first.first.size();
for (const auto &t: txs)
size += t.size();
@@ -2534,6 +2541,13 @@ bool Blockchain::add_block_as_invalid(const block_extended_info& bei, const cryp
return true;
}
//------------------------------------------------------------------
+void Blockchain::flush_invalid_blocks()
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ m_invalid_blocks.clear();
+}
+//------------------------------------------------------------------
bool Blockchain::have_block(const crypto::hash& id) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -3409,7 +3423,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
if (version >= HF_VERSION_PER_BYTE_FEE)
{
const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT;
- uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version);
+ uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? std::min<uint64_t>(median, m_long_term_effective_median_block_weight) : median, version);
MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee");
needed_fee = tx_weight * fee_per_byte;
// quantize fee up to 8 decimals
@@ -3467,7 +3481,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
uint64_t already_generated_coins = db_height ? m_db->get_block_already_generated_coins(db_height - 1) : 0;
uint64_t base_reward;
- if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
+ if (!get_block_reward(m_current_block_cumul_weight_limit / 2, 1, already_generated_coins, base_reward, version))
{
MERROR("Failed to determine block reward, using placeholder " << print_money(BLOCK_REWARD_OVERESTIMATE) << " as a high bound");
base_reward = BLOCK_REWARD_OVERESTIMATE;
@@ -3661,7 +3675,7 @@ bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids)
uint64_t fee;
bool relayed, do_not_relay, double_spend_seen, pruned;
MINFO("Removing txid " << txid << " from the pool");
- if(!m_tx_pool.have_tx(txid, relay_category::all) && !m_tx_pool.take_tx(txid, tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen, pruned))
+ if(m_tx_pool.have_tx(txid, relay_category::all) && !m_tx_pool.take_tx(txid, tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen, pruned))
{
MERROR("Failed to remove txid " << txid << " from the pool");
res = false;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 0aecdcb57..4ed7a2f02 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -1017,6 +1017,11 @@ namespace cryptonote
*/
bool has_block_weights(uint64_t height, uint64_t nblocks) const;
+ /**
+ * @brief flush the invalid blocks set
+ */
+ void flush_invalid_blocks();
+
#ifndef IN_UNIT_TESTS
private:
#endif
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index cf23a652c..212616af8 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -84,6 +84,11 @@ namespace cryptonote
, "Run in a regression testing mode."
, false
};
+ const command_line::arg_descriptor<bool> arg_keep_fakechain = {
+ "keep-fakechain"
+ , "Don't delete any existing database when in fakechain mode."
+ , false
+ };
const command_line::arg_descriptor<difficulty_type> arg_fixed_difficulty = {
"fixed-difficulty"
, "Fixed difficulty used for testing."
@@ -174,11 +179,6 @@ namespace cryptonote
, "Relay blocks as normal blocks"
, false
};
- static const command_line::arg_descriptor<bool> arg_pad_transactions = {
- "pad-transactions"
- , "Pad relayed transactions to help defend against traffic volume analysis"
- , false
- };
static const command_line::arg_descriptor<size_t> arg_max_txpool_weight = {
"max-txpool-weight"
, "Set maximum txpool weight in bytes."
@@ -235,8 +235,7 @@ namespace cryptonote
m_disable_dns_checkpoints(false),
m_update_download(0),
m_nettype(UNDEFINED),
- m_update_available(false),
- m_pad_transactions(false)
+ m_update_available(false)
{
m_checkpoints_updating.clear();
set_cryptonote_protocol(pprotocol);
@@ -318,6 +317,7 @@ namespace cryptonote
command_line::add_arg(desc, arg_testnet_on);
command_line::add_arg(desc, arg_stagenet_on);
command_line::add_arg(desc, arg_regtest_on);
+ command_line::add_arg(desc, arg_keep_fakechain);
command_line::add_arg(desc, arg_fixed_difficulty);
command_line::add_arg(desc, arg_dns_checkpoints);
command_line::add_arg(desc, arg_prep_blocks_threads);
@@ -333,7 +333,6 @@ namespace cryptonote
command_line::add_arg(desc, arg_block_download_max_size);
command_line::add_arg(desc, arg_sync_pruned_blocks);
command_line::add_arg(desc, arg_max_txpool_weight);
- command_line::add_arg(desc, arg_pad_transactions);
command_line::add_arg(desc, arg_block_notify);
command_line::add_arg(desc, arg_prune_blockchain);
command_line::add_arg(desc, arg_reorg_notify);
@@ -376,7 +375,6 @@ namespace cryptonote
set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints));
test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height));
m_fluffy_blocks_enabled = !get_arg(vm, arg_no_fluffy_blocks);
- m_pad_transactions = get_arg(vm, arg_pad_transactions);
m_offline = get_arg(vm, arg_offline);
m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints);
if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks))
@@ -471,6 +469,7 @@ namespace cryptonote
size_t max_txpool_weight = command_line::get_arg(vm, arg_max_txpool_weight);
bool prune_blockchain = command_line::get_arg(vm, arg_prune_blockchain);
bool keep_alt_blocks = command_line::get_arg(vm, arg_keep_alt_blocks);
+ bool keep_fakechain = command_line::get_arg(vm, arg_keep_fakechain);
boost::filesystem::path folder(m_config_folder);
if (m_nettype == FAKECHAIN)
@@ -512,7 +511,7 @@ namespace cryptonote
bool sync_on_blocks = true;
uint64_t sync_threshold = 1;
- if (m_nettype == FAKECHAIN)
+ if (m_nettype == FAKECHAIN && !keep_fakechain)
{
// reset the db by removing the database file before opening it
if (!db->remove_data_file(filename))
@@ -1057,17 +1056,6 @@ namespace cryptonote
return handle_incoming_txs({std::addressof(tx_blob), 1}, {std::addressof(tvc), 1}, tx_relay, relayed);
}
//-----------------------------------------------------------------------------------------------
- bool core::get_stat_info(core_stat_info& st_inf) const
- {
- st_inf.mining_speed = m_miner.get_speed();
- st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count();
- st_inf.blockchain_height = m_blockchain_storage.get_current_blockchain_height();
- st_inf.tx_pool_size = m_mempool.get_transactions_count();
- st_inf.top_block_id_str = epee::string_tools::pod_to_hex(m_blockchain_storage.get_tail_id());
- return true;
- }
-
- //-----------------------------------------------------------------------------------------------
bool core::check_tx_semantic(const transaction& tx, bool keeped_by_block) const
{
if(!tx.vin.size())
@@ -1176,10 +1164,10 @@ namespace cryptonote
return m_mempool.check_for_key_images(key_im, spent);
}
//-----------------------------------------------------------------------------------------------
- std::pair<uint64_t, uint64_t> core::get_coinbase_tx_sum(const uint64_t start_offset, const size_t count)
+ std::pair<boost::multiprecision::uint128_t, boost::multiprecision::uint128_t> core::get_coinbase_tx_sum(const uint64_t start_offset, const size_t count)
{
- uint64_t emission_amount = 0;
- uint64_t total_fee_amount = 0;
+ boost::multiprecision::uint128_t emission_amount = 0;
+ boost::multiprecision::uint128_t total_fee_amount = 0;
if (count)
{
const uint64_t end = start_offset + count - 1;
@@ -1201,7 +1189,7 @@ namespace cryptonote
});
}
- return std::pair<uint64_t, uint64_t>(emission_amount, total_fee_amount);
+ return std::pair<boost::multiprecision::uint128_t, boost::multiprecision::uint128_t>(emission_amount, total_fee_amount);
}
//-----------------------------------------------------------------------------------------------
bool core::check_tx_inputs_keyimages_diff(const transaction& tx) const
@@ -1295,7 +1283,7 @@ namespace cryptonote
private_req.txs.push_back(std::move(std::get<1>(tx)));
break;
case relay_method::block:
- case relay_method::flood:
+ case relay_method::fluff:
public_req.txs.push_back(std::move(std::get<1>(tx)));
break;
}
@@ -1659,8 +1647,9 @@ namespace cryptonote
<< "You can set the level of process detailization through \"set_log <level|categories>\" command," << ENDL
<< "where <level> is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)." << ENDL
<< ENDL
- << "Use the \"help\" command to see the list of available commands." << ENDL
- << "Use \"help <command>\" to see a command's documentation." << ENDL
+ << "Use the \"help\" command to see a simplified list of available commands." << ENDL
+ << "Use the \"help_advanced\" command to see an advanced list of available commands." << ENDL
+ << "Use \"help_advanced <command>\" to see a command's documentation." << ENDL
<< "**********************************************************************" << ENDL);
m_starter_message_showed = true;
}
@@ -1903,9 +1892,10 @@ namespace cryptonote
}
static constexpr double threshold = 1. / (864000 / DIFFICULTY_TARGET_V2); // one false positive every 10 days
+ static constexpr unsigned int max_blocks_checked = 150;
const time_t now = time(NULL);
- const std::vector<time_t> timestamps = m_blockchain_storage.get_last_block_timestamps(60);
+ const std::vector<time_t> timestamps = m_blockchain_storage.get_last_block_timestamps(max_blocks_checked);
static const unsigned int seconds[] = { 5400, 3600, 1800, 1200, 600 };
for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n)
@@ -1917,7 +1907,7 @@ namespace cryptonote
MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")");
if (p < threshold)
{
- MWARNING("There were " << b << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck.");
+ MWARNING("There were " << b << (b == max_blocks_checked ? " or more" : "") << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck.");
std::shared_ptr<tools::Notify> block_rate_notify = m_block_rate_notify;
if (block_rate_notify)
@@ -1941,6 +1931,16 @@ namespace cryptonote
bad_semantics_txes_lock.unlock();
}
//-----------------------------------------------------------------------------------------------
+ void core::flush_invalid_blocks()
+ {
+ m_blockchain_storage.flush_invalid_blocks();
+ }
+ //-----------------------------------------------------------------------------------------------
+ bool core::get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes)
+ {
+ return m_mempool.get_complement(hashes, txes);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::update_blockchain_pruning()
{
return m_blockchain_storage.update_blockchain_pruning();
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 4b67984ab..79a846de1 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -45,7 +45,6 @@
#include "blockchain.h"
#include "cryptonote_basic/miner.h"
#include "cryptonote_basic/connection_context.h"
-#include "cryptonote_basic/cryptonote_stat_info.h"
#include "warnings.h"
#include "crypto/hash.h"
#include "span.h"
@@ -556,15 +555,6 @@ namespace cryptonote
bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const;
/**
- * @brief gets some stats about the daemon
- *
- * @param st_inf return-by-reference container for the stats requested
- *
- * @return true
- */
- bool get_stat_info(core_stat_info& st_inf) const;
-
- /**
* @copydoc Blockchain::get_tx_outputs_gindexs
*
* @note see Blockchain::get_tx_outputs_gindexs
@@ -765,7 +755,7 @@ namespace cryptonote
*
* @return the number of blocks to sync in one go
*/
- std::pair<uint64_t, uint64_t> get_coinbase_tx_sum(const uint64_t start_offset, const size_t count);
+ std::pair<boost::multiprecision::uint128_t, boost::multiprecision::uint128_t> get_coinbase_tx_sum(const uint64_t start_offset, const size_t count);
/**
* @brief get the network type we're on
@@ -792,13 +782,6 @@ namespace cryptonote
bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; }
/**
- * @brief get whether transaction relay should be padded
- *
- * @return whether transaction relay should be padded
- */
- bool pad_transactions() const { return m_pad_transactions; }
-
- /**
* @brief check a set of hashes against the precompiled hash set
*
* @return number of usable blocks
@@ -866,6 +849,20 @@ namespace cryptonote
*/
void flush_bad_txs_cache();
+ /**
+ * @brief flushes the invalid block cache
+ */
+ void flush_invalid_blocks();
+
+ /**
+ * @brief returns the set of transactions in the txpool which are not in the argument
+ *
+ * @param hashes hashes of transactions to exclude from the result
+ *
+ * @return true iff success, false otherwise
+ */
+ bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes);
+
private:
/**
@@ -1102,7 +1099,6 @@ namespace cryptonote
bool m_fluffy_blocks_enabled;
bool m_offline;
- bool m_pad_transactions;
std::shared_ptr<tools::Notify> m_block_rate_notify;
};
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 1bc475879..05cbf14a8 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -38,6 +38,7 @@
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_config.h"
#include "blockchain.h"
+#include "blockchain_db/locked_txn.h"
#include "blockchain_db/blockchain_db.h"
#include "common/boost_serialization_helper.h"
#include "int-util.h"
@@ -88,25 +89,6 @@ namespace cryptonote
else
return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
-
- // This class is meant to create a batch when none currently exists.
- // If a batch exists, it can't be from another thread, since we can
- // only be called with the txpool lock taken, and it is held during
- // the whole prepare/handle/cleanup incoming block sequence.
- class LockedTXN {
- public:
- LockedTXN(Blockchain &b): m_blockchain(b), m_batch(false), m_active(false) {
- m_batch = m_blockchain.get_db().batch_start();
- m_active = true;
- }
- void commit() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_stop(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::commit filtering exception: " << e.what()); } }
- void abort() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_abort(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::abort filtering exception: " << e.what()); } }
- ~LockedTXN() { abort(); }
- private:
- Blockchain &m_blockchain;
- bool m_batch;
- bool m_active;
- };
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
@@ -256,7 +238,7 @@ namespace cryptonote
if (kept_by_block)
m_parsed_tx_cache.insert(std::make_pair(id, tx));
CRITICAL_REGION_LOCAL1(m_blockchain);
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
if (!insert_key_images(tx, id, tx_relay))
return false;
@@ -301,7 +283,7 @@ namespace cryptonote
if (kept_by_block)
m_parsed_tx_cache.insert(std::make_pair(id, tx));
CRITICAL_REGION_LOCAL1(m_blockchain);
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
m_blockchain.remove_txpool_tx(id);
if (!insert_key_images(tx, id, tx_relay))
return false;
@@ -362,7 +344,7 @@ namespace cryptonote
if (bytes == 0)
bytes = m_txpool_max_weight;
CRITICAL_REGION_LOCAL1(m_blockchain);
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
bool changed = false;
// this will never remove the first one, but we don't care
@@ -494,7 +476,7 @@ namespace cryptonote
try
{
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(id, meta))
{
@@ -549,7 +531,7 @@ namespace cryptonote
try
{
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(txid, meta))
{
@@ -594,6 +576,39 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
+ bool tx_memory_pool::get_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) const
+ {
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
+ CRITICAL_REGION_LOCAL1(m_blockchain);
+
+ m_blockchain.for_all_txpool_txes([this, &hashes, &txes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) {
+ const auto relay_method = meta.get_relay_method();
+ if (relay_method != relay_method::block && relay_method != relay_method::fluff)
+ return true;
+ const auto i = std::find(hashes.begin(), hashes.end(), txid);
+ if (i == hashes.end())
+ {
+ cryptonote::blobdata bd;
+ try
+ {
+ if (!m_blockchain.get_txpool_tx_blob(txid, bd, cryptonote::relay_category::broadcasted))
+ {
+ MERROR("Failed to get blob for txpool transaction " << txid);
+ return true;
+ }
+ txes.emplace_back(std::move(bd));
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to get blob for txpool transaction " << txid << ": " << e.what());
+ return true;
+ }
+ }
+ return true;
+ }, false);
+ return true;
+ }
+ //---------------------------------------------------------------------------------
void tx_memory_pool::on_idle()
{
m_remove_stuck_tx_interval.do_call([this](){return remove_stuck_transactions();});
@@ -638,7 +653,7 @@ namespace cryptonote
if (!remove.empty())
{
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
for (const std::pair<crypto::hash, uint64_t> &entry: remove)
{
const crypto::hash &txid = entry.first;
@@ -709,7 +724,7 @@ namespace cryptonote
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
const time_t now = time(NULL);
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
for (const auto& hash : hashes)
{
try
@@ -1186,7 +1201,7 @@ namespace cryptonote
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
bool changed = false;
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
for(size_t i = 0; i!= tx.vin.size(); i++)
{
CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, void());
@@ -1278,7 +1293,7 @@ namespace cryptonote
LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool");
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it)
@@ -1415,7 +1430,7 @@ namespace cryptonote
size_t n_removed = 0;
if (!remove.empty())
{
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
for (const crypto::hash &txid: remove)
{
try
@@ -1495,7 +1510,7 @@ namespace cryptonote
}
if (!remove.empty())
{
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
for (const auto &txid: remove)
{
try
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index f716440ad..ca0e50415 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -441,6 +441,11 @@ namespace cryptonote
*/
bool get_transaction_info(const crypto::hash &txid, tx_details &td) const;
+ /**
+ * @brief get transactions not in the passed set
+ */
+ bool get_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) const;
+
private:
/**
diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp
index 03cbb5c26..e99982def 100644
--- a/src/cryptonote_core/tx_sanity_check.cpp
+++ b/src/cryptonote_core/tx_sanity_check.cpp
@@ -28,7 +28,7 @@
#include <stdint.h>
#include <vector>
-#include "cryptonote_basic/cryptonote_basic_impl.h"
+#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "blockchain.h"
#include "tx_sanity_check.h"
@@ -39,7 +39,7 @@
namespace cryptonote
{
-bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob)
+bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available)
{
cryptonote::transaction tx;
@@ -70,14 +70,18 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
n_indices += in_to_key.key_offsets.size();
}
+ return tx_sanity_check(rct_indices, n_indices, rct_outs_available);
+}
+
+bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available)
+{
if (n_indices <= 10)
{
MDEBUG("n_indices is only " << n_indices << ", not checking");
return true;
}
- uint64_t n_available = blockchain.get_num_mature_outputs(0);
- if (n_available < 10000)
+ if (rct_outs_available < 10000)
return true;
if (rct_indices.size() < n_indices * 8 / 10)
@@ -88,9 +92,9 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end());
uint64_t median = epee::misc_utils::median(offsets);
- if (median < n_available * 6 / 10)
+ if (median < rct_outs_available * 6 / 10)
{
- MERROR("median offset index is too low (median is " << median << " out of total " << n_available << "offsets). Transactions should contain a higher fraction of recent outputs.");
+ MERROR("median offset index is too low (median is " << median << " out of total " << rct_outs_available << "offsets). Transactions should contain a higher fraction of recent outputs.");
return false;
}
diff --git a/src/cryptonote_core/tx_sanity_check.h b/src/cryptonote_core/tx_sanity_check.h
index c12d1b0b1..4a469462f 100644
--- a/src/cryptonote_core/tx_sanity_check.h
+++ b/src/cryptonote_core/tx_sanity_check.h
@@ -26,11 +26,11 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <set>
#include "cryptonote_basic/blobdatatype.h"
namespace cryptonote
{
- class Blockchain;
-
- bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob);
+ bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available);
+ bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available);
}
diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h
index 201001c8e..f809bff74 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_defs.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h
@@ -257,7 +257,10 @@ namespace cryptonote
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(current_height)
KV_SERIALIZE(cumulative_difficulty)
- KV_SERIALIZE(cumulative_difficulty_top64)
+ if (is_store)
+ KV_SERIALIZE(cumulative_difficulty_top64)
+ else
+ KV_SERIALIZE_OPT(cumulative_difficulty_top64, (uint64_t)0)
KV_SERIALIZE_VAL_POD_AS_BLOB(top_id)
KV_SERIALIZE_OPT(top_version, (uint8_t)0)
KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0)
@@ -298,7 +301,10 @@ namespace cryptonote
KV_SERIALIZE(start_height)
KV_SERIALIZE(total_height)
KV_SERIALIZE(cumulative_difficulty)
- KV_SERIALIZE(cumulative_difficulty_top64)
+ if (is_store)
+ KV_SERIALIZE(cumulative_difficulty_top64)
+ else
+ KV_SERIALIZE_OPT(cumulative_difficulty_top64, (uint64_t)0)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_weights)
END_KV_SERIALIZE_MAP()
@@ -347,5 +353,23 @@ namespace cryptonote
};
typedef epee::misc_utils::struct_init<request_t> request;
};
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ struct NOTIFY_GET_TXPOOL_COMPLEMENT
+ {
+ const static int ID = BC_COMMANDS_POOL_BASE + 10;
+
+ struct request_t
+ {
+ std::vector<crypto::hash> hashes;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(hashes)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+ };
}
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h
index ddbd45a61..2664716a8 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h
@@ -45,7 +45,6 @@
#include "block_queue.h"
#include "common/perf_timer.h"
#include "cryptonote_basic/connection_context.h"
-#include "cryptonote_basic/cryptonote_stat_info.h"
#include <boost/circular_buffer.hpp>
PUSH_WARNINGS
@@ -77,7 +76,6 @@ namespace cryptonote
{
public:
typedef cryptonote_connection_context connection_context;
- typedef core_stat_info stat_info;
typedef t_cryptonote_protocol_handler<t_core> cryptonote_protocol_handler;
typedef CORE_SYNC_DATA payload_type;
@@ -92,6 +90,7 @@ namespace cryptonote
HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry)
HANDLE_NOTIFY_T2(NOTIFY_NEW_FLUFFY_BLOCK, &cryptonote_protocol_handler::handle_notify_new_fluffy_block)
HANDLE_NOTIFY_T2(NOTIFY_REQUEST_FLUFFY_MISSING_TX, &cryptonote_protocol_handler::handle_request_fluffy_missing_tx)
+ HANDLE_NOTIFY_T2(NOTIFY_GET_TXPOOL_COMPLEMENT, &cryptonote_protocol_handler::handle_notify_get_txpool_complement)
END_INVOKE_MAP2()
bool on_idle();
@@ -102,7 +101,6 @@ namespace cryptonote
bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital);
bool get_payload_sync_data(blobdata& data);
bool get_payload_sync_data(CORE_SYNC_DATA& hshd);
- bool get_stat_info(core_stat_info& stat_inf);
bool on_callback(cryptonote_connection_context& context);
t_core& get_core(){return m_core;}
bool is_synchronized(){return m_synchronized;}
@@ -127,6 +125,7 @@ namespace cryptonote
int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context);
int handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context);
int handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context);
+ int handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context);
//----------------- i_bc_protocol_layout ---------------------------------------
virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context);
@@ -147,6 +146,7 @@ namespace cryptonote
int try_add_next_blocks(cryptonote_connection_context &context);
void notify_new_stripe(cryptonote_connection_context &context, uint32_t stripe);
void skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const;
+ bool request_txpool_complement(cryptonote_connection_context &context);
t_core& m_core;
@@ -156,6 +156,7 @@ namespace cryptonote
std::atomic<bool> m_synchronized;
std::atomic<bool> m_stopping;
std::atomic<bool> m_no_sync;
+ std::atomic<bool> m_ask_for_txpool_complement;
boost::mutex m_sync_lock;
block_queue m_block_queue;
epee::math_helper::once_a_time_seconds<30> m_idle_peer_kicker;
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index e20934a25..3aacce421 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -83,6 +83,7 @@ namespace cryptonote
m_p2p(p_net_layout),
m_syncronized_connections_count(0),
m_synchronized(offline),
+ m_ask_for_txpool_complement(true),
m_stopping(false),
m_no_sync(false)
@@ -154,12 +155,6 @@ namespace cryptonote
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
- bool t_cryptonote_protocol_handler<t_core>::get_stat_info(core_stat_info& stat_inf)
- {
- return m_core.get_stat_info(stat_inf);
- }
- //------------------------------------------------------------------------------------------------------------------------
- template<class t_core>
void t_cryptonote_protocol_handler<t_core>::log_connections()
{
std::stringstream ss;
@@ -188,7 +183,7 @@ namespace cryptonote
auto connection_time = time(NULL) - cntxt.m_started;
ss << std::setw(30) << std::left << std::string(cntxt.m_is_income ? " [INC]":"[OUT]") +
cntxt.m_remote_address.str()
- << std::setw(20) << std::hex << peer_id
+ << std::setw(20) << nodetool::peerid_to_string(peer_id)
<< std::setw(20) << std::hex << support_flags
<< std::setw(30) << std::to_string(cntxt.m_recv_cnt)+ "(" + std::to_string(time(NULL) - cntxt.m_last_recv) + ")" + "/" + std::to_string(cntxt.m_send_cnt) + "(" + std::to_string(time(NULL) - cntxt.m_last_send) + ")"
<< std::setw(25) << get_protocol_state_string(cntxt.m_state)
@@ -248,9 +243,7 @@ namespace cryptonote
cnx.rpc_port = cntxt.m_rpc_port;
cnx.rpc_credits_per_hash = cntxt.m_rpc_credits_per_hash;
- std::stringstream peer_id_str;
- peer_id_str << std::hex << std::setw(16) << peer_id;
- peer_id_str >> cnx.peer_id;
+ cnx.peer_id = nodetool::peerid_to_string(peer_id);
cnx.support_flags = support_flags;
@@ -314,7 +307,7 @@ namespace cryptonote
if (version >= 6 && version != hshd.top_version)
{
if (version < hshd.top_version && version == m_core.get_ideal_hard_fork_version())
- MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version that we think (" <<
+ MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version than we think (" <<
(unsigned)hshd.top_version << " for " << (hshd.current_height - 1) << " instead of " << (unsigned)version <<
") - we may be forked from the network and a software upgrade may be needed");
return false;
@@ -346,7 +339,7 @@ namespace cryptonote
if(m_core.have_block(hshd.top_id))
{
context.m_state = cryptonote_connection_context::state_normal;
- if(is_inital && target == m_core.get_current_blockchain_height())
+ if(is_inital && hshd.current_height >= target && target == m_core.get_current_blockchain_height())
on_connection_synchronized();
return true;
}
@@ -887,6 +880,34 @@ namespace cryptonote
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
+ int t_cryptonote_protocol_handler<t_core>::handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context)
+ {
+ MLOG_P2P_MESSAGE("Received NOTIFY_GET_TXPOOL_COMPLEMENT (" << arg.hashes.size() << " txes)");
+
+ std::vector<std::pair<cryptonote::blobdata, block>> local_blocks;
+ std::vector<cryptonote::blobdata> local_txs;
+
+ std::vector<cryptonote::blobdata> txes;
+ if (!m_core.get_txpool_complement(arg.hashes, txes))
+ {
+ LOG_ERROR_CCONTEXT("failed to get txpool complement");
+ return 1;
+ }
+
+ NOTIFY_NEW_TRANSACTIONS::request new_txes;
+ new_txes.txs = std::move(txes);
+
+ MLOG_P2P_MESSAGE
+ (
+ "-->>NOTIFY_NEW_TRANSACTIONS: "
+ << ", txs.size()=" << new_txes.txs.size()
+ );
+
+ post_notify<NOTIFY_NEW_TRANSACTIONS>(new_txes, context);
+ return 1;
+ }
+ //------------------------------------------------------------------------------------------------------------------------
+ template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context)
{
MLOG_P2P_MESSAGE("Received NOTIFY_NEW_TRANSACTIONS (" << arg.txs.size() << " txes)");
@@ -910,7 +931,7 @@ namespace cryptonote
for (size_t i = 0; i < arg.txs.size(); ++i)
{
cryptonote::tx_verification_context tvc{};
- m_core.handle_incoming_tx({arg.txs[i], crypto::null_hash}, tvc, relay_method::flood, true);
+ m_core.handle_incoming_tx({arg.txs[i], crypto::null_hash}, tvc, relay_method::fluff, true);
if(tvc.m_verifivation_failed)
{
LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection");
@@ -1673,9 +1694,9 @@ skip:
const float max_multiplier = 10.f;
const float min_multiplier = 1.25f;
float multiplier = max_multiplier;
- if (dt/1e6 >= REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY)
+ if (dt >= REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY)
{
- multiplier = max_multiplier - (dt/1e6-REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY) * (max_multiplier - min_multiplier) / (REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD - REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY);
+ multiplier = max_multiplier - (dt-REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY) * (max_multiplier - min_multiplier) / (REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD - REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY);
multiplier = std::min(max_multiplier, std::max(min_multiplier, multiplier));
}
if (dl_speed * .8f > ctx.m_current_speed_down * multiplier)
@@ -2181,7 +2202,8 @@ skip:
MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL
<< "You are now synchronized with the network. You may now start monero-wallet-cli." << ENDL
<< ENDL
- << "Use the \"help\" command to see the list of available commands." << ENDL
+ << "Use the \"help\" command to see a simplified list of available commands." << ENDL
+ << "Use the \"help_advanced\" command to see an advanced list of available commands." << ENDL
<< "**********************************************************************");
m_sync_timer.pause();
if (ELPP->vRegistry()->allowed(el::Level::Info, "sync-info"))
@@ -2202,6 +2224,27 @@ skip:
}
m_core.safesyncmode(true);
m_p2p->clear_used_stripe_peers();
+
+ // ask for txpool complement from any suitable node if we did not yet
+ val_expected = true;
+ if (m_ask_for_txpool_complement.compare_exchange_strong(val_expected, false))
+ {
+ m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool
+ {
+ if(context.m_state < cryptonote_connection_context::state_synchronizing)
+ {
+ MDEBUG(context << "not ready, ignoring");
+ return true;
+ }
+ if (!request_txpool_complement(context))
+ {
+ MERROR(context << "Failed to request txpool complement");
+ return true;
+ }
+ return false;
+ });
+ }
+
return true;
}
//------------------------------------------------------------------------------------------------------------------------
@@ -2351,7 +2394,22 @@ skip:
local mempool before doing the relay. The code was already updating the
DB twice on received transactions - it is difficult to workaround this
due to the internal design. */
- return m_p2p->send_txs(std::move(arg.txs), zone, source, m_core, m_core.pad_transactions()) != epee::net_utils::zone::invalid;
+ return m_p2p->send_txs(std::move(arg.txs), zone, source, m_core) != epee::net_utils::zone::invalid;
+ }
+ //------------------------------------------------------------------------------------------------------------------------
+ template<class t_core>
+ bool t_cryptonote_protocol_handler<t_core>::request_txpool_complement(cryptonote_connection_context &context)
+ {
+ NOTIFY_GET_TXPOOL_COMPLEMENT::request r = {};
+ if (!m_core.get_pool_transaction_hashes(r.hashes, false))
+ {
+ MERROR("Failed to get txpool hashes");
+ return false;
+ }
+ MLOG_P2P_MESSAGE("-->>NOTIFY_GET_TXPOOL_COMPLEMENT: hashes.size()=" << r.hashes.size() );
+ post_notify<NOTIFY_GET_TXPOOL_COMPLEMENT>(r, context);
+ MLOG_PEER_STATE("requesting txpool complement");
+ return true;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
@@ -2464,7 +2522,10 @@ skip:
MINFO("Target height decreasing from " << previous_target << " to " << target);
m_core.set_target_blockchain_height(target);
if (target == 0 && context.m_state > cryptonote_connection_context::state_before_handshake && !m_stopping)
+ {
MCWARNING("global", "monerod is now disconnected from the network");
+ m_ask_for_txpool_complement = true;
+ }
}
m_block_queue.flush_spans(context.m_connection_id, false);
diff --git a/src/cryptonote_protocol/enums.h b/src/cryptonote_protocol/enums.h
index ad4eedf4c..2ec622d94 100644
--- a/src/cryptonote_protocol/enums.h
+++ b/src/cryptonote_protocol/enums.h
@@ -38,6 +38,6 @@ namespace cryptonote
none = 0, //!< Received via RPC with `do_not_relay` set
local, //!< Received via RPC; trying to send over i2p/tor, etc.
block, //!< Received in block, takes precedence over others
- flood //!< Received/sent over public networks
+ fluff //!< Received/sent over public networks
};
}
diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp
index 4b41b5bfc..e45c34e02 100644
--- a/src/cryptonote_protocol/levin_notify.cpp
+++ b/src/cryptonote_protocol/levin_notify.cpp
@@ -33,6 +33,7 @@
#include <chrono>
#include <deque>
#include <stdexcept>
+#include <utility>
#include "common/expect.h"
#include "common/varint.h"
@@ -43,6 +44,14 @@
#include "net/dandelionpp.h"
#include "p2p/net_node.h"
+namespace
+{
+ int get_command_from_message(const cryptonote::blobdata &msg)
+ {
+ return msg.size() >= sizeof(epee::levin::bucket_head2) ? SWAP32LE(((epee::levin::bucket_head2*)msg.data())->m_command) : 0;
+ }
+}
+
namespace cryptonote
{
namespace levin
@@ -57,6 +66,37 @@ namespace levin
constexpr const std::chrono::seconds noise_min_delay{CRYPTONOTE_NOISE_MIN_DELAY};
constexpr const std::chrono::seconds noise_delay_range{CRYPTONOTE_NOISE_DELAY_RANGE};
+ /* A custom duration is used for the poisson distribution because of the
+ variance. If 5 seconds is given to `std::poisson_distribution`, 95% of
+ the values fall between 1-9s in 1s increments (not granular enough). If
+ 5000 milliseconds is given, 95% of the values fall between 4859ms-5141ms
+ in 1ms increments (not enough time variance). Providing 20 quarter
+ seconds yields 95% of the values between 3s-7.25s in 1/4s increments. */
+ using fluff_stepsize = std::chrono::duration<std::chrono::milliseconds::rep, std::ratio<1, 4>>;
+ constexpr const std::chrono::seconds fluff_average_in{CRYPTONOTE_DANDELIONPP_FLUSH_AVERAGE};
+
+ /*! Bitcoin Core is using 1/2 average seconds for outgoing connections
+ compared to incoming. The thinking is that the user controls outgoing
+ connections (Dandelion++ makes similar assumptions in its stem
+ algorithm). The randomization yields 95% values between 1s-4s in
+ 1/4s increments. */
+ constexpr const fluff_stepsize fluff_average_out{fluff_stepsize{fluff_average_in} / 2};
+
+ class random_poisson
+ {
+ std::poisson_distribution<fluff_stepsize::rep> dist;
+ public:
+ explicit random_poisson(fluff_stepsize average)
+ : dist(average.count() < 0 ? 0 : average.count())
+ {}
+
+ fluff_stepsize operator()()
+ {
+ crypto::random_device rand{};
+ return fluff_stepsize{dist(rand)};
+ }
+ };
+
/*! Select a randomized duration from 0 to `range`. The precision will be to
the systems `steady_clock`. As an example, supplying 3 seconds to this
function will select a duration from [0, 3] seconds, and the increments
@@ -129,6 +169,16 @@ namespace levin
return fullBlob;
}
+ bool make_payload_send_txs(connections& p2p, std::vector<blobdata>&& txs, const boost::uuids::uuid& destination, const bool pad)
+ {
+ const cryptonote::blobdata blob = make_tx_payload(std::move(txs), pad);
+ p2p.for_connection(destination, [&blob](detail::p2p_context& context) {
+ on_levin_traffic(context, true, true, false, blob.size(), get_command_from_message(blob));
+ return true;
+ });
+ return p2p.notify(NOTIFY_NEW_TRANSACTIONS::ID, epee::strspan<std::uint8_t>(blob), destination);
+ }
+
/* The current design uses `asio::strand`s. The documentation isn't as clear
as it should be - a `strand` has an internal `mutex` and `bool`. The
`mutex` synchronizes thread access and the `bool` is set when a thread is
@@ -187,15 +237,18 @@ namespace levin
{
struct zone
{
- explicit zone(boost::asio::io_service& io_service, std::shared_ptr<connections> p2p, epee::byte_slice noise_in, bool is_public)
+ explicit zone(boost::asio::io_service& io_service, std::shared_ptr<connections> p2p, epee::byte_slice noise_in, bool is_public, bool pad_txs)
: p2p(std::move(p2p)),
noise(std::move(noise_in)),
next_epoch(io_service),
+ flush_txs(io_service),
strand(io_service),
map(),
channels(),
+ flush_time(std::chrono::steady_clock::time_point::max()),
connection_count(0),
- is_public(is_public)
+ is_public(is_public),
+ pad_txs(pad_txs)
{
for (std::size_t count = 0; !noise.empty() && count < CRYPTONOTE_NOISE_CHANNELS; ++count)
channels.emplace_back(io_service);
@@ -204,11 +257,14 @@ namespace levin
const std::shared_ptr<connections> p2p;
const epee::byte_slice noise; //!< `!empty()` means zone is using noise channels
boost::asio::steady_timer next_epoch;
+ boost::asio::steady_timer flush_txs;
boost::asio::io_service::strand strand;
net::dandelionpp::connection_map map;//!< Tracks outgoing uuid's for noise channels or Dandelion++ stems
std::deque<noise_channel> channels; //!< Never touch after init; only update elements on `noise_channel.strand`
+ std::chrono::steady_clock::time_point flush_time; //!< Next expected Dandelion++ fluff flush
std::atomic<std::size_t> connection_count; //!< Only update in strand, can be read at any time
const bool is_public; //!< Zone is public ipv4/ipv6 connections
+ const bool pad_txs; //!< Pad txs to the next boundary for privacy
};
} // detail
@@ -245,49 +301,112 @@ namespace levin
}
};
- //! Sends a message to every active connection
- class flood_notify
+ //! Sends txs on connections with expired timers, and queues callback for next timer expiration (if any).
+ struct fluff_flush
{
std::shared_ptr<detail::zone> zone_;
- epee::byte_slice message_; // Requires manual copy
- boost::uuids::uuid source_;
+ std::chrono::steady_clock::time_point flush_time_;
- public:
- explicit flood_notify(std::shared_ptr<detail::zone> zone, epee::byte_slice message, const boost::uuids::uuid& source)
- : zone_(std::move(zone)), message_(message.clone()), source_(source)
- {}
+ static void queue(std::shared_ptr<detail::zone> zone, const std::chrono::steady_clock::time_point flush_time)
+ {
+ assert(zone != nullptr);
+ assert(zone->strand.running_in_this_thread());
- flood_notify(flood_notify&&) = default;
- flood_notify(const flood_notify& source)
- : zone_(source.zone_), message_(source.message_.clone()), source_(source.source_)
- {}
+ detail::zone& this_zone = *zone;
+ this_zone.flush_time = flush_time;
+ this_zone.flush_txs.expires_at(flush_time);
+ this_zone.flush_txs.async_wait(this_zone.strand.wrap(fluff_flush{std::move(zone), flush_time}));
+ }
- void operator()() const
+ void operator()(const boost::system::error_code error)
{
if (!zone_ || !zone_->p2p)
return;
assert(zone_->strand.running_in_this_thread());
- /* The foreach should be quick, but then it iterates and acquires the
- same lock for every connection. So do in a strand because two threads
- will ping-pong each other with cacheline invalidations. Revisit if
- algorithm changes or the locking strategy within the levin config
- class changes. */
-
- std::vector<boost::uuids::uuid> connections;
- connections.reserve(connection_id_reserve_size);
- zone_->p2p->foreach_connection([this, &connections] (detail::p2p_context& context) {
- /* Only send to outgoing connections when "flooding" over i2p/tor.
- Otherwise this makes the tx linkable to a hidden service address,
- making things linkable across connections. */
+ const bool timer_error = bool(error);
+ if (timer_error)
+ {
+ if (error != boost::system::errc::operation_canceled)
+ throw boost::system::system_error{error, "fluff_flush timer failed"};
+
+ // new timer canceled this one set in future
+ if (zone_->flush_time < flush_time_)
+ return;
+ }
+
+ const auto now = std::chrono::steady_clock::now();
+ auto next_flush = std::chrono::steady_clock::time_point::max();
+ std::vector<std::pair<std::vector<blobdata>, boost::uuids::uuid>> connections{};
+ zone_->p2p->foreach_connection([timer_error, now, &next_flush, &connections] (detail::p2p_context& context)
+ {
+ if (!context.fluff_txs.empty())
+ {
+ if (context.flush_time <= now || timer_error) // flush on canceled timer
+ {
+ context.flush_time = std::chrono::steady_clock::time_point::max();
+ connections.emplace_back(std::move(context.fluff_txs), context.m_connection_id);
+ context.fluff_txs.clear();
+ }
+ else // not flushing yet
+ next_flush = std::min(next_flush, context.flush_time);
+ }
+ else // nothing to flush
+ context.flush_time = std::chrono::steady_clock::time_point::max();
+ return true;
+ });
+
+ for (auto& connection : connections)
+ make_payload_send_txs(*zone_->p2p, std::move(connection.first), connection.second, zone_->pad_txs);
+
+ if (next_flush != std::chrono::steady_clock::time_point::max())
+ fluff_flush::queue(std::move(zone_), next_flush);
+ else
+ zone_->flush_time = next_flush; // signal that no timer is set
+ }
+ };
+
+ /*! The "fluff" portion of the Dandelion++ algorithm. Every tx is queued
+ per-connection and flushed with a randomized poisson timer. This
+ implementation only has one system timer per-zone, and instead tracks
+ the lowest flush time. */
+ struct fluff_notify
+ {
+ std::shared_ptr<detail::zone> zone_;
+ std::vector<blobdata> txs_;
+ boost::uuids::uuid source_;
+
+ void operator()()
+ {
+ if (!zone_ || !zone_->p2p || txs_.empty())
+ return;
+
+ assert(zone_->strand.running_in_this_thread());
+
+ const auto now = std::chrono::steady_clock::now();
+ auto next_flush = std::chrono::steady_clock::time_point::max();
+
+ random_poisson in_duration(fluff_average_in);
+ random_poisson out_duration(fluff_average_out);
+
+ zone_->p2p->foreach_connection([this, now, &in_duration, &out_duration, &next_flush] (detail::p2p_context& context)
+ {
if (this->source_ != context.m_connection_id && (this->zone_->is_public || !context.m_is_income))
- connections.emplace_back(context.m_connection_id);
+ {
+ if (context.fluff_txs.empty())
+ context.flush_time = now + (context.m_is_income ? in_duration() : out_duration());
+
+ next_flush = std::min(next_flush, context.flush_time);
+ context.fluff_txs.reserve(context.fluff_txs.size() + this->txs_.size());
+ for (const blobdata& tx : this->txs_)
+ context.fluff_txs.push_back(tx); // must copy instead of move (multiple conns)
+ }
return true;
});
- for (const boost::uuids::uuid& connection : connections)
- zone_->p2p->send(message_.clone(), connection);
+ if (next_flush < zone_->flush_time)
+ fluff_flush::queue(std::move(zone_), next_flush);
}
};
@@ -432,6 +551,10 @@ namespace levin
else
message = zone_->noise.clone();
+ zone_->p2p->for_connection(channel.connection, [&](detail::p2p_context& context) {
+ on_levin_traffic(context, true, true, false, message.size(), "noise");
+ return true;
+ });
if (zone_->p2p->send(std::move(message), channel.connection))
{
if (!channel.queue.empty() && channel.active.empty())
@@ -451,7 +574,7 @@ namespace levin
}
};
- //! Prepares connections for new channel epoch and sets timer for next epoch
+ //! Prepares connections for new channel/dandelionpp epoch and sets timer for next epoch
struct start_epoch
{
// Variables allow for Dandelion++ extension
@@ -481,8 +604,8 @@ namespace levin
};
} // anonymous
- notify::notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, bool is_public)
- : zone_(std::make_shared<detail::zone>(service, std::move(p2p), std::move(noise), is_public))
+ notify::notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, const bool is_public, const bool pad_txs)
+ : zone_(std::make_shared<detail::zone>(service, std::move(p2p), std::move(noise), is_public, pad_txs))
{
if (!zone_->p2p)
throw std::logic_error{"cryptonote::levin::notify cannot have nullptr p2p argument"};
@@ -533,9 +656,19 @@ namespace levin
channel.next_noise.cancel();
}
- bool notify::send_txs(std::vector<blobdata> txs, const boost::uuids::uuid& source, const bool pad_txs)
+ void notify::run_fluff()
{
if (!zone_)
+ return;
+ zone_->flush_txs.cancel();
+ }
+
+ bool notify::send_txs(std::vector<blobdata> txs, const boost::uuids::uuid& source)
+ {
+ if (txs.empty())
+ return true;
+
+ if (!zone_)
return false;
if (!zone_->noise.empty() && !zone_->channels.empty())
@@ -565,12 +698,7 @@ namespace levin
}
else
{
- const std::string payload = make_tx_payload(std::move(txs), pad_txs);
- epee::byte_slice message =
- epee::levin::make_notify(NOTIFY_NEW_TRANSACTIONS::ID, epee::strspan<std::uint8_t>(payload));
-
- // traditional monero send technique
- zone_->strand.dispatch(flood_notify{zone_, std::move(message), source});
+ zone_->strand.dispatch(fluff_notify{zone_, std::move(txs), source});
}
return true;
diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h
index 8bc9b72fa..ce652d933 100644
--- a/src/cryptonote_protocol/levin_notify.h
+++ b/src/cryptonote_protocol/levin_notify.h
@@ -82,7 +82,7 @@ namespace levin
{}
//! Construct an instance with available notification `zones`.
- explicit notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, bool is_public);
+ explicit notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, bool is_public, bool pad_txs);
notify(const notify&) = delete;
notify(notify&&) = default;
@@ -104,11 +104,14 @@ namespace levin
//! Run the logic for the next stem timeout imemdiately. Only use in testing.
void run_stems();
+ //! Run the logic for flushing all Dandelion++ fluff queued txs. Only use in testing.
+ void run_fluff();
+
/*! Send txs using `cryptonote_protocol_defs.h` payload format wrapped in a
levin header. The message will be sent in a "discreet" manner if
enabled - if `!noise.empty()` then the `command`/`payload` will be
queued to send at the next available noise interval. Otherwise, a
- standard Monero flood notification will be used.
+ Dandelion++ fluff algorithm will be used.
\note Eventually Dandelion++ stem sending will be used here when
enabled.
@@ -117,12 +120,9 @@ namespace levin
\param source The source of the notification. `is_nil()` indicates this
node is the source. Dandelion++ will use this to map a source to a
particular stem.
- \param pad_txs A request to pad txs to help conceal origin via
- statistical analysis. Ignored if noise was enabled during
- construction.
\return True iff the notification is queued for sending. */
- bool send_txs(std::vector<blobdata> txs, const boost::uuids::uuid& source, bool pad_txs);
+ bool send_txs(std::vector<blobdata> txs, const boost::uuids::uuid& source);
};
} // levin
} // net
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 99a460c78..3a4081e39 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -153,6 +153,16 @@ bool t_command_parser_executor::print_blockchain_info(const std::vector<std::str
}
uint64_t start_index = 0;
uint64_t end_index = 0;
+ if (args[0][0] == '-')
+ {
+ int64_t nblocks;
+ if(!epee::string_tools::get_xtype_from_string(nblocks, args[0]))
+ {
+ std::cout << "wrong number of blocks" << std::endl;
+ return false;
+ }
+ return m_executor.print_blockchain_info(nblocks, (uint64_t)-nblocks);
+ }
if(!epee::string_tools::get_xtype_from_string(start_index, args[0]))
{
std::cout << "wrong starter block index parameter" << std::endl;
@@ -243,12 +253,15 @@ bool t_command_parser_executor::print_block(const std::vector<std::string>& args
bool t_command_parser_executor::print_transaction(const std::vector<std::string>& args)
{
+ bool include_metadata = false;
bool include_hex = false;
bool include_json = false;
// Assumes that optional flags come after mandatory argument <transaction_hash>
for (unsigned int i = 1; i < args.size(); ++i) {
- if (args[i] == "+hex")
+ if (args[i] == "+meta")
+ include_metadata = true;
+ else if (args[i] == "+hex")
include_hex = true;
else if (args[i] == "+json")
include_json = true;
@@ -260,7 +273,7 @@ bool t_command_parser_executor::print_transaction(const std::vector<std::string>
}
if (args.empty())
{
- std::cout << "expected: print_tx <transaction_hash> [+hex] [+json]" << std::endl;
+ std::cout << "expected: print_tx <transaction_hash> [+meta] [+hex] [+json]" << std::endl;
return true;
}
@@ -268,7 +281,7 @@ bool t_command_parser_executor::print_transaction(const std::vector<std::string>
crypto::hash tx_hash;
if (parse_hash256(str_hash, tx_hash))
{
- m_executor.print_transaction(tx_hash, include_hex, include_json);
+ m_executor.print_transaction(tx_hash, include_metadata, include_hex, include_json);
}
return true;
@@ -844,13 +857,27 @@ bool t_command_parser_executor::set_bootstrap_daemon(const std::vector<std::stri
bool t_command_parser_executor::flush_cache(const std::vector<std::string>& args)
{
+ bool bad_txs = false, bad_blocks = false;
+ std::string arg;
+
if (args.empty())
goto show_list;
- if (args[0] == "bad-txs")
- return m_executor.flush_cache(true);
+
+ for (size_t i = 0; i < args.size(); ++i)
+ {
+ arg = args[i];
+ if (arg == "bad-txs")
+ bad_txs = true;
+ else if (arg == "bad-blocks")
+ bad_blocks = true;
+ else
+ goto show_list;
+ }
+ return m_executor.flush_cache(bad_txs, bad_blocks);
show_list:
- std::cout << "Cache type needed: bad-txs" << std::endl;
+ std::cout << "Invalid cache type: " << arg << std::endl;
+ std::cout << "Cache types: bad-txs bad-blocks" << std::endl;
return true;
}
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index 8ec690631..7fae77c30 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -325,7 +325,7 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"flush_cache"
, std::bind(&t_command_parser_executor::flush_cache, &m_parser, p::_1)
- , "flush_cache bad-txs"
+ , "flush_cache [bad-txs] [bad-blocks]"
, "Flush the specified cache(s)."
);
}
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index cb96b37b6..056f2f320 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -78,13 +78,14 @@ public:
const auto restricted = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc);
const auto main_rpc_port = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port);
- rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, main_rpc_port, "core"});
+ const auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port;
+ const bool has_restricted_rpc_port_arg = !command_line::is_arg_defaulted(vm, restricted_rpc_port_arg);
+ rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, main_rpc_port, "core", !has_restricted_rpc_port_arg});
- auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port;
- if(!command_line::is_arg_defaulted(vm, restricted_rpc_port_arg))
+ if(has_restricted_rpc_port_arg)
{
auto restricted_rpc_port = command_line::get_arg(vm, restricted_rpc_port_arg);
- rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted"});
+ rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted", true});
}
}
};
diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h
index 213593aa7..6f545a3b7 100644
--- a/src/daemon/rpc.h
+++ b/src/daemon/rpc.h
@@ -56,12 +56,13 @@ public:
, const bool restricted
, const std::string & port
, const std::string & description
+ , bool allow_rpc_payment
)
: m_server{core.get(), p2p.get()}, m_description{description}
{
MGINFO("Initializing " << m_description << " RPC server...");
- if (!m_server.init(vm, restricted, port))
+ if (!m_server.init(vm, restricted, port, allow_rpc_payment))
{
throw std::runtime_error("Failed to initialize " + m_description + " RPC server.");
}
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index ef9fed177..086808f8e 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -665,7 +665,7 @@ bool t_rpc_command_executor::print_connections() {
<< std::setw(30) << std::left << address
<< std::setw(8) << (get_address_type_name((epee::net_utils::address_type)info.address_type))
<< std::setw(6) << (info.ssl ? "yes" : "no")
- << std::setw(20) << epee::string_tools::pad_string(info.peer_id, 16, '0', true)
+ << std::setw(20) << info.peer_id
<< std::setw(20) << info.support_flags
<< std::setw(30) << std::to_string(info.recv_count) + "(" + std::to_string(info.recv_idle_time) + ")/" + std::to_string(info.send_count) + "(" + std::to_string(info.send_idle_time) + ")"
<< std::setw(25) << info.state
@@ -744,17 +744,46 @@ bool t_rpc_command_executor::print_net_stats()
return true;
}
-bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, uint64_t end_block_index) {
+bool t_rpc_command_executor::print_blockchain_info(int64_t start_block_index, uint64_t end_block_index) {
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request req;
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response res;
epee::json_rpc::error error_resp;
+ std::string fail_message = "Problem fetching info";
+
+ // negative: relative to the end
+ if (start_block_index < 0)
+ {
+ cryptonote::COMMAND_RPC_GET_INFO::request ireq;
+ cryptonote::COMMAND_RPC_GET_INFO::response ires;
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->rpc_request(ireq, ires, "/getinfo", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_get_info(ireq, ires) || ires.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << make_error(fail_message, ires.status);
+ return true;
+ }
+ }
+ if (start_block_index < 0 && (uint64_t)-start_block_index >= ires.height)
+ {
+ tools::fail_msg_writer() << "start offset is larger than blockchain height";
+ return true;
+ }
+ start_block_index = ires.height + start_block_index;
+ end_block_index = start_block_index + end_block_index - 1;
+ }
req.start_height = start_block_index;
req.end_height = end_block_index;
req.fill_pow_hash = false;
- std::string fail_message = "Unsuccessful";
-
+ fail_message = "Failed calling getblockheadersrange";
if (m_is_rpc)
{
if (!m_rpc_client->json_rpc_request(req, res, "getblockheadersrange", fail_message.c_str()))
@@ -940,6 +969,7 @@ bool t_rpc_command_executor::print_block_by_height(uint64_t height, bool include
}
bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash,
+ bool include_metadata,
bool include_hex,
bool include_json) {
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
@@ -982,6 +1012,29 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash,
const std::string &as_hex = (1 == res.txs.size()) ? res.txs.front().as_hex : res.txs_as_hex.front();
const std::string &pruned_as_hex = (1 == res.txs.size()) ? res.txs.front().pruned_as_hex : "";
const std::string &prunable_as_hex = (1 == res.txs.size()) ? res.txs.front().prunable_as_hex : "";
+ // Print metadata if requested
+ if (include_metadata)
+ {
+ if (!res.txs.front().in_pool)
+ {
+ tools::msg_writer() << "Block timestamp: " << res.txs.front().block_timestamp << " (" << tools::get_human_readable_timestamp(res.txs.front().block_timestamp) << ")";
+ }
+ cryptonote::blobdata blob;
+ if (epee::string_tools::parse_hexstr_to_binbuff(pruned_as_hex + prunable_as_hex, blob))
+ {
+ cryptonote::transaction tx;
+ if (cryptonote::parse_and_validate_tx_from_blob(blob, tx))
+ {
+ tools::msg_writer() << "Size: " << blob.size();
+ tools::msg_writer() << "Weight: " << cryptonote::get_transaction_weight(tx);
+ }
+ else
+ tools::fail_msg_writer() << "Error parsing transaction blob";
+ }
+ else
+ tools::fail_msg_writer() << "Error parsing transaction from hex";
+ }
+
// Print raw hex if requested
if (include_hex)
{
@@ -1852,9 +1905,9 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou
tools::msg_writer() << "Sum of coinbase transactions between block heights ["
<< height << ", " << (height + count) << ") is "
- << cryptonote::print_money(res.emission_amount + res.fee_amount) << " "
- << "consisting of " << cryptonote::print_money(res.emission_amount)
- << " in emissions, and " << cryptonote::print_money(res.fee_amount) << " in fees";
+ << cryptonote::print_money(boost::multiprecision::uint128_t(res.wide_emission_amount) + boost::multiprecision::uint128_t(res.wide_fee_amount)) << " "
+ << "consisting of " << cryptonote::print_money(boost::multiprecision::uint128_t(res.wide_emission_amount))
+ << " in emissions, and " << cryptonote::print_money(boost::multiprecision::uint128_t(res.wide_fee_amount)) << " in fees";
return true;
}
@@ -2218,7 +2271,7 @@ bool t_rpc_command_executor::sync_info()
for (const auto &s: res.spans)
if (s.connection_id == p.info.connection_id)
nblocks += s.nblocks, size += s.size;
- tools::success_msg_writer() << address << " " << epee::string_tools::pad_string(p.info.peer_id, 16, '0', true) << " " <<
+ tools::success_msg_writer() << address << " " << p.info.peer_id << " " <<
epee::string_tools::pad_string(p.info.state, 16) << " " <<
epee::string_tools::pad_string(epee::string_tools::to_string_hex(p.info.pruning_seed), 8) << " " << p.info.height << " " <<
p.info.current_download << " kB/s, " << nblocks << " blocks / " << size/1e6 << " MB queued";
@@ -2374,7 +2427,7 @@ bool t_rpc_command_executor::set_bootstrap_daemon(
return true;
}
-bool t_rpc_command_executor::flush_cache(bool bad_txs)
+bool t_rpc_command_executor::flush_cache(bool bad_txs, bool bad_blocks)
{
cryptonote::COMMAND_RPC_FLUSH_CACHE::request req;
cryptonote::COMMAND_RPC_FLUSH_CACHE::response res;
@@ -2382,6 +2435,7 @@ bool t_rpc_command_executor::flush_cache(bool bad_txs)
epee::json_rpc::error error_resp;
req.bad_txs = bad_txs;
+ req.bad_blocks = bad_blocks;
if (m_is_rpc)
{
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index af55f0e22..66ada6f1f 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -85,7 +85,7 @@ public:
bool print_connections();
- bool print_blockchain_info(uint64_t start_block_index, uint64_t end_block_index);
+ bool print_blockchain_info(int64_t start_block_index, uint64_t end_block_index);
bool set_log_level(int8_t level);
@@ -97,7 +97,7 @@ public:
bool print_block_by_height(uint64_t height, bool include_hex);
- bool print_transaction(crypto::hash transaction_hash, bool include_hex, bool include_json);
+ bool print_transaction(crypto::hash transaction_hash, bool include_metadata, bool include_hex, bool include_json);
bool is_key_image_spent(const crypto::key_image &ki);
@@ -172,7 +172,7 @@ public:
bool rpc_payments();
- bool flush_cache(bool bad_txs);
+ bool flush_cache(bool bad_txs, bool invalid_blocks);
};
} // namespace daemonize
diff --git a/src/lmdb/table.h b/src/lmdb/table.h
index 41a3de296..4ded4ba54 100644
--- a/src/lmdb/table.h
+++ b/src/lmdb/table.h
@@ -15,8 +15,8 @@ namespace lmdb
{
char const* const name;
const unsigned flags;
- MDB_cmp_func const* const key_cmp;
- MDB_cmp_func const* const value_cmp;
+ MDB_cmp_func* const key_cmp;
+ MDB_cmp_func* const value_cmp;
//! \pre `name != nullptr` \return Open table.
expect<MDB_dbi> open(MDB_txn& write_txn) const noexcept;
diff --git a/src/net/i2p_address.cpp b/src/net/i2p_address.cpp
index cba829d3f..f4cc75fee 100644
--- a/src/net/i2p_address.cpp
+++ b/src/net/i2p_address.cpp
@@ -171,7 +171,8 @@ namespace net
bool i2p_address::less(const i2p_address& rhs) const noexcept
{
- return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port();
+ int res = std::strcmp(host_str(), rhs.host_str());
+ return res < 0 || (res == 0 && port() < rhs.port());
}
bool i2p_address::is_same_host(const i2p_address& rhs) const noexcept
diff --git a/src/net/tor_address.cpp b/src/net/tor_address.cpp
index 904a9a0fc..4414861e7 100644
--- a/src/net/tor_address.cpp
+++ b/src/net/tor_address.cpp
@@ -173,7 +173,8 @@ namespace net
bool tor_address::less(const tor_address& rhs) const noexcept
{
- return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port();
+ int res = std::strcmp(host_str(), rhs.host_str());
+ return res < 0 || (res == 0 && port() < rhs.port());
}
bool tor_address::is_same_host(const tor_address& rhs) const noexcept
diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
index 58c1717e0..58c5706de 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -161,6 +161,10 @@ namespace nodetool
const command_line::arg_descriptor<int64_t> arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", P2P_DEFAULT_LIMIT_RATE_DOWN};
const command_line::arg_descriptor<int64_t> arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1};
+ const command_line::arg_descriptor<bool> arg_pad_transactions = {
+ "pad-transactions", "Pad relayed transactions to help defend against traffic volume analysis", false
+ };
+
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)
{
namespace ip = boost::asio::ip;
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index ee70c2806..31d8aad3f 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -38,6 +38,7 @@
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/uuid/uuid.hpp>
+#include <chrono>
#include <functional>
#include <utility>
#include <vector>
@@ -109,11 +110,20 @@ namespace nodetool
template<class base_type>
struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base
{
- p2p_connection_context_t(): peer_id(0), support_flags(0), m_in_timedsync(false) {}
+ p2p_connection_context_t()
+ : fluff_txs(),
+ flush_time(std::chrono::steady_clock::time_point::max()),
+ peer_id(0),
+ support_flags(0),
+ m_in_timedsync(false)
+ {}
+ std::vector<cryptonote::blobdata> fluff_txs;
+ std::chrono::steady_clock::time_point flush_time;
peerid_type peer_id;
uint32_t support_flags;
bool m_in_timedsync;
+ std::set<epee::net_utils::network_address> sent_addresses;
};
template<class t_payload_net_handler>
@@ -291,8 +301,6 @@ namespace nodetool
bool islimitup=false;
bool islimitdown=false;
- typedef COMMAND_REQUEST_STAT_INFO_T<typename t_payload_net_handler::stat_info> COMMAND_REQUEST_STAT_INFO;
-
CHAIN_LEVIN_INVOKE_MAP2(p2p_connection_context); //move levin_commands_handler interface invoke(...) callbacks into invoke map
CHAIN_LEVIN_NOTIFY_MAP2(p2p_connection_context); //move levin_commands_handler interface notify(...) callbacks into nothing
@@ -303,11 +311,6 @@ namespace nodetool
HANDLE_INVOKE_T2(COMMAND_HANDSHAKE, &node_server::handle_handshake)
HANDLE_INVOKE_T2(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync)
HANDLE_INVOKE_T2(COMMAND_PING, &node_server::handle_ping)
-#ifdef ALLOW_DEBUG_COMMANDS
- HANDLE_INVOKE_T2(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info)
- HANDLE_INVOKE_T2(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state)
- HANDLE_INVOKE_T2(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id)
-#endif
HANDLE_INVOKE_T2(COMMAND_REQUEST_SUPPORT_FLAGS, &node_server::handle_get_support_flags)
CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&)
END_INVOKE_MAP2()
@@ -318,17 +321,11 @@ namespace nodetool
int handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context);
int handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context);
int handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context);
-#ifdef ALLOW_DEBUG_COMMANDS
- int handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context);
- int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context);
- int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context);
-#endif
int handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context);
bool init_config();
bool make_default_peer_id();
bool make_default_config();
bool store_config();
- bool check_trust(const proof_of_trust& tr, epee::net_utils::zone zone_type);
//----------------- levin_commands_handler -------------------------------------------------------------
@@ -337,7 +334,7 @@ namespace nodetool
virtual void callback(p2p_connection_context& context);
//----------------- i_p2p_endpoint -------------------------------------------------------------
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections);
- virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, bool pad_txs);
+ virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core);
virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context);
virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context);
virtual bool drop_connection(const epee::net_utils::connection_context_base& context);
@@ -353,7 +350,7 @@ namespace nodetool
const boost::program_options::variables_map& vm
);
bool idle_worker();
- bool handle_remote_peerlist(const std::vector<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context);
+ bool handle_remote_peerlist(const std::vector<peerlist_entry>& peerlist, const epee::net_utils::connection_context_base& context);
bool get_local_node_data(basic_node_data& node_data, const network_zone& zone);
//bool get_local_handshake_data(handshake_data& hshd);
@@ -383,7 +380,7 @@ namespace nodetool
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);
bool make_expected_connections_count(network_zone& zone, PeerType peer_type, size_t expected_connections);
- void cache_connect_fail_info(const epee::net_utils::network_address& addr);
+ void record_addr_failed(const epee::net_utils::network_address& addr);
bool is_addr_recently_failed(const epee::net_utils::network_address& addr);
bool is_priority_node(const epee::net_utils::network_address& na);
std::set<std::string> get_seed_nodes(cryptonote::network_type nettype) const;
@@ -404,7 +401,6 @@ namespace nodetool
bool set_rate_limit(const boost::program_options::variables_map& vm, int64_t limit);
bool has_too_many_connections(const epee::net_utils::network_address &address);
- uint64_t get_connections_count();
size_t get_incoming_connections_count();
size_t get_incoming_connections_count(network_zone&);
size_t get_outgoing_connections_count();
@@ -468,9 +464,6 @@ namespace nodetool
epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval;
epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval;
-#ifdef ALLOW_DEBUG_COMMANDS
- uint64_t m_last_stat_request_time;
-#endif
std::list<epee::net_utils::network_address> m_priority_peers;
std::vector<epee::net_utils::network_address> m_exclusive_peers;
std::vector<epee::net_utils::network_address> m_seed_nodes;
@@ -540,6 +533,7 @@ namespace nodetool
extern const command_line::arg_descriptor<int64_t> arg_limit_rate_up;
extern const command_line::arg_descriptor<int64_t> arg_limit_rate_down;
extern const command_line::arg_descriptor<int64_t> arg_limit_rate;
+ extern const command_line::arg_descriptor<bool> arg_pad_transactions;
}
POP_WARNINGS
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 82abee71b..9b6358dee 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -116,6 +116,7 @@ namespace nodetool
command_line::add_arg(desc, arg_limit_rate_up);
command_line::add_arg(desc, arg_limit_rate_down);
command_line::add_arg(desc, arg_limit_rate);
+ command_line::add_arg(desc, arg_pad_transactions);
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
@@ -340,6 +341,7 @@ namespace nodetool
{
bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
+ const bool pad_txs = command_line::get_arg(vm, arg_pad_transactions);
m_nettype = testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET;
network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
@@ -384,7 +386,7 @@ namespace nodetool
m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
m_require_ipv4 = !command_line::get_arg(vm, arg_p2p_ignore_ipv4);
public_zone.m_notifier = cryptonote::levin::notify{
- public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, true
+ public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, true, pad_txs
};
if (command_line::has_arg(vm, arg_p2p_add_peer))
@@ -495,7 +497,7 @@ namespace nodetool
}
zone.m_notifier = cryptonote::levin::notify{
- zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), false
+ zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), false, pad_txs
};
}
@@ -811,7 +813,6 @@ namespace nodetool
//only in case if we really sure that we have external visible ip
m_have_address = true;
- m_last_stat_request_time = 0;
//configure self
@@ -938,15 +939,6 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
- uint64_t node_server<t_payload_net_handler>::get_connections_count()
- {
- std::uint64_t count = 0;
- for (auto& zone : m_network_zones)
- count += zone.second.m_net_server.get_config_object().get_connections_count();
- return count;
- }
- //-----------------------------------------------------------------------------------
- template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::deinit()
{
kill();
@@ -1022,7 +1014,7 @@ namespace nodetool
epee::simple_event ev;
std::atomic<bool> hsh_result(false);
- bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_HANDSHAKE::response>(context_.m_connection_id, COMMAND_HANDSHAKE::ID, arg, zone.m_net_server.get_config_object(),
+ bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_HANDSHAKE::response>(context_, COMMAND_HANDSHAKE::ID, arg, zone.m_net_server.get_config_object(),
[this, &pi, &ev, &hsh_result, &just_take_peerlist, &context_](int code, const typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context)
{
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ev.raise();});
@@ -1039,7 +1031,7 @@ namespace nodetool
return;
}
- if(!handle_remote_peerlist(rsp.local_peerlist_new, rsp.node_data.local_time, context))
+ if(!handle_remote_peerlist(rsp.local_peerlist_new, context))
{
LOG_WARNING_CC(context, "COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection.");
add_host_fail(context.m_remote_address);
@@ -1089,7 +1081,7 @@ namespace nodetool
LOG_WARNING_CC(context_, "COMMAND_HANDSHAKE Failed");
m_network_zones.at(context_.m_remote_address.get_zone()).m_net_server.get_config_object().close(context_.m_connection_id);
}
- else
+ else if (!just_take_peerlist)
{
try_get_support_flags(context_, [](p2p_connection_context& flags_context, const uint32_t& support_flags)
{
@@ -1107,7 +1099,7 @@ namespace nodetool
m_payload_handler.get_payload_sync_data(arg.payload_data);
network_zone& zone = m_network_zones.at(context_.m_remote_address.get_zone());
- bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_TIMED_SYNC::response>(context_.m_connection_id, COMMAND_TIMED_SYNC::ID, arg, zone.m_net_server.get_config_object(),
+ bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_TIMED_SYNC::response>(context_, COMMAND_TIMED_SYNC::ID, arg, zone.m_net_server.get_config_object(),
[this](int code, const typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context)
{
context.m_in_timedsync = false;
@@ -1117,7 +1109,7 @@ namespace nodetool
return;
}
- if(!handle_remote_peerlist(rsp.local_peerlist_new, rsp.local_time, context))
+ if(!handle_remote_peerlist(rsp.local_peerlist_new, context))
{
LOG_WARNING_CC(context, "COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection.");
m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id );
@@ -1260,7 +1252,7 @@ namespace nodetool
bool is_priority = is_priority_node(na);
LOG_PRINT_CC_PRIORITY_NODE(is_priority, bool(con), "Connect failed to " << na.str()
/*<< ", try " << try_count*/);
- //m_peerlist.set_peer_unreachable(pe);
+ record_addr_failed(na);
return false;
}
@@ -1275,6 +1267,7 @@ namespace nodetool
<< na.str()
/*<< ", try " << try_count*/);
zone.m_net_server.get_config_object().close(con->m_connection_id);
+ record_addr_failed(na);
return false;
}
@@ -1325,6 +1318,7 @@ namespace nodetool
bool is_priority = is_priority_node(na);
LOG_PRINT_CC_PRIORITY_NODE(is_priority, p2p_connection_context{}, "Connect failed to " << na.str());
+ record_addr_failed(na);
return false;
}
@@ -1337,6 +1331,7 @@ namespace nodetool
LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str());
zone.m_net_server.get_config_object().close(con->m_connection_id);
+ record_addr_failed(na);
return false;
}
@@ -1351,6 +1346,13 @@ namespace nodetool
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::record_addr_failed(const epee::net_utils::network_address& addr)
+ {
+ CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock);
+ m_conn_fails_cache[addr.host_str()] = time(NULL);
+ }
+ //-----------------------------------------------------------------------------------
+ template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::is_addr_recently_failed(const epee::net_utils::network_address& addr)
{
CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock);
@@ -1368,7 +1370,7 @@ namespace nodetool
bool node_server<t_payload_net_handler>::make_new_connection_from_anchor_peerlist(const std::vector<anchor_peerlist_entry>& anchor_peerlist)
{
for (const auto& pe: anchor_peerlist) {
- _note("Considering connecting (out) to anchor peer: " << peerid_type(pe.id) << " " << pe.adr.str());
+ _note("Considering connecting (out) to anchor peer: " << peerid_to_string(pe.id) << " " << pe.adr.str());
if(is_peer_used(pe)) {
_note("Peer is used");
@@ -1432,10 +1434,10 @@ namespace nodetool
std::deque<size_t> filtered;
const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max();
- size_t idx = 0, skipped = 0;
for (int step = 0; step < 2; ++step)
{
bool skip_duplicate_class_B = step == 0;
+ size_t idx = 0, skipped = 0;
zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){
if (filtered.size() >= limit)
return false;
@@ -1541,6 +1543,7 @@ namespace nodetool
return true;
size_t try_count = 0;
+ bool is_connected_to_at_least_one_seed_node = false;
size_t current_index = crypto::rand_idx(m_seed_nodes.size());
const net_server& server = m_network_zones.at(epee::net_utils::zone::public_).m_net_server;
while(true)
@@ -1548,21 +1551,25 @@ namespace nodetool
if(server.is_stop_signal_sent())
return false;
- if(try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true))
+ peerlist_entry pe_seed{};
+ pe_seed.adr = m_seed_nodes[current_index];
+ if (is_peer_used(pe_seed))
+ is_connected_to_at_least_one_seed_node = true;
+ else if (try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true))
break;
if(++try_count > m_seed_nodes.size())
{
if (!m_fallback_seed_nodes_added)
{
MWARNING("Failed to connect to any of seed peers, trying fallback seeds");
- current_index = m_seed_nodes.size();
+ current_index = m_seed_nodes.size() - 1;
for (const auto &peer: get_seed_nodes(m_nettype))
{
MDEBUG("Fallback seed node: " << peer);
append_net_address(m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT);
}
m_fallback_seed_nodes_added = true;
- if (current_index == m_seed_nodes.size())
+ if (current_index == m_seed_nodes.size() - 1)
{
MWARNING("No fallback seeds, continuing without seeds");
break;
@@ -1571,7 +1578,8 @@ namespace nodetool
}
else
{
- MWARNING("Failed to connect to any of seed peers, continuing without seeds");
+ if (!is_connected_to_at_least_one_seed_node)
+ MWARNING("Failed to connect to any of seed peers, continuing without seeds");
break;
}
}
@@ -1892,7 +1900,7 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
- bool node_server<t_payload_net_handler>::handle_remote_peerlist(const std::vector<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context)
+ bool node_server<t_payload_net_handler>::handle_remote_peerlist(const std::vector<peerlist_entry>& peerlist, const epee::net_utils::connection_context_base& context)
{
std::vector<peerlist_entry> peerlist_ = peerlist;
if(!sanitize_peerlist(peerlist_))
@@ -1909,16 +1917,13 @@ namespace nodetool
}
LOG_DEBUG_CC(context, "REMOTE PEERLIST: remote peerlist size=" << peerlist_.size());
- LOG_DEBUG_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_));
- return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_);
+ LOG_TRACE_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_));
+ return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_, [this](const peerlist_entry &pe) { return !is_addr_recently_failed(pe.adr); });
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::get_local_node_data(basic_node_data& node_data, const network_zone& zone)
{
- time_t local_time;
- time(&local_time);
- node_data.local_time = local_time; // \TODO This can be an identifying value across zones (public internet to tor/i2p) ...
node_data.peer_id = zone.m_config.m_peer_id;
if(!m_hide_my_port && zone.m_can_pingback)
node_data.my_port = m_external_port ? m_external_port : m_listening_port;
@@ -1930,91 +1935,6 @@ namespace nodetool
return true;
}
//-----------------------------------------------------------------------------------
-#ifdef ALLOW_DEBUG_COMMANDS
- template<class t_payload_net_handler>
- bool node_server<t_payload_net_handler>::check_trust(const proof_of_trust& tr, const epee::net_utils::zone zone_type)
- {
- uint64_t local_time = time(NULL);
- uint64_t time_delata = local_time > tr.time ? local_time - tr.time: tr.time - local_time;
- if(time_delata > 24*60*60 )
- {
- MWARNING("check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time);
- return false;
- }
- if(m_last_stat_request_time >= tr.time )
- {
- MWARNING("check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time);
- return false;
- }
-
- const network_zone& zone = m_network_zones.at(zone_type);
- if(zone.m_config.m_peer_id != tr.peer_id)
- {
- MWARNING("check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << zone.m_config.m_peer_id<< ")");
- return false;
- }
- crypto::public_key pk = AUTO_VAL_INIT(pk);
- epee::string_tools::hex_to_pod(::config::P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY, pk);
- crypto::hash h = get_proof_of_trust_hash(tr);
- if(!crypto::check_signature(h, pk, tr.sign))
- {
- MWARNING("check_trust failed: sign check failed");
- return false;
- }
- //update last request time
- m_last_stat_request_time = tr.time;
- return true;
- }
- //-----------------------------------------------------------------------------------
- template<class t_payload_net_handler>
- int node_server<t_payload_net_handler>::handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context)
- {
- if(!check_trust(arg.tr, context.m_remote_address.get_zone()))
- {
- drop_connection(context);
- return 1;
- }
- rsp.connections_count = get_connections_count();
- rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count();
- rsp.version = MONERO_VERSION_FULL;
- rsp.os_version = tools::get_os_version_string();
- m_payload_handler.get_stat_info(rsp.payload_info);
- return 1;
- }
- //-----------------------------------------------------------------------------------
- template<class t_payload_net_handler>
- int node_server<t_payload_net_handler>::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context)
- {
- if(!check_trust(arg.tr, context.m_remote_address.get_zone()))
- {
- drop_connection(context);
- return 1;
- }
- m_network_zones.at(epee::net_utils::zone::public_).m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
- {
- connection_entry ce;
- ce.adr = cntxt.m_remote_address;
- ce.id = cntxt.peer_id;
- ce.is_income = cntxt.m_is_income;
- rsp.connections_list.push_back(ce);
- return true;
- });
-
- network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone());
- zone.m_peerlist.get_peerlist(rsp.local_peerlist_gray, rsp.local_peerlist_white);
- rsp.my_id = zone.m_config.m_peer_id;
- rsp.local_time = time(NULL);
- return 1;
- }
- //-----------------------------------------------------------------------------------
- template<class t_payload_net_handler>
- int node_server<t_payload_net_handler>::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context)
- {
- rsp.my_id = m_network_zones.at(context.m_remote_address.get_zone()).m_config.m_peer_id;
- return 1;
- }
-#endif
- //-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
int node_server<t_payload_net_handler>::handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context)
{
@@ -2054,18 +1974,18 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
- epee::net_utils::zone node_server<t_payload_net_handler>::send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, const bool pad_txs)
+ epee::net_utils::zone node_server<t_payload_net_handler>::send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core)
{
namespace enet = epee::net_utils;
- const auto send = [&txs, &source, &core, pad_txs] (std::pair<const enet::zone, network_zone>& network)
+ const auto send = [&txs, &source, &core] (std::pair<const enet::zone, network_zone>& network)
{
const bool is_public = (network.first == enet::zone::public_);
const cryptonote::relay_method tx_relay = is_public ?
- cryptonote::relay_method::flood : cryptonote::relay_method::local;
+ cryptonote::relay_method::fluff : cryptonote::relay_method::local;
core.on_transactions_relayed(epee::to_span(txs), tx_relay);
- if (network.second.m_notifier.send_txs(std::move(txs), source, (pad_txs || !is_public)))
+ if (network.second.m_notifier.send_txs(std::move(txs), source))
return network.first;
return enet::zone::invalid;
};
@@ -2214,7 +2134,7 @@ namespace nodetool
network_zone& zone = m_network_zones.at(address.get_zone());
- bool inv_call_res = epee::net_utils::async_invoke_remote_command2<COMMAND_PING::response>(ping_context.m_connection_id, COMMAND_PING::ID, req, zone.m_net_server.get_config_object(),
+ bool inv_call_res = epee::net_utils::async_invoke_remote_command2<COMMAND_PING::response>(ping_context, COMMAND_PING::ID, req, zone.m_net_server.get_config_object(),
[=](int code, const COMMAND_PING::response& rsp, p2p_connection_context& context)
{
if(code <= 0)
@@ -2226,7 +2146,7 @@ namespace nodetool
network_zone& zone = m_network_zones.at(address.get_zone());
if(rsp.status != PING_OK_RESPONSE_STATUS_TEXT || pr != rsp.peer_id)
{
- LOG_WARNING_CC(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << address.str() << ", hsh_peer_id=" << pr_ << ", rsp.peer_id=" << rsp.peer_id);
+ LOG_WARNING_CC(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << address.str() << ", hsh_peer_id=" << pr_ << ", rsp.peer_id=" << peerid_to_string(rsp.peer_id));
zone.m_net_server.get_config_object().close(ping_context.m_connection_id);
return;
}
@@ -2258,7 +2178,7 @@ namespace nodetool
COMMAND_REQUEST_SUPPORT_FLAGS::request support_flags_request;
bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_REQUEST_SUPPORT_FLAGS::response>
(
- context.m_connection_id,
+ context,
COMMAND_REQUEST_SUPPORT_FLAGS::ID,
support_flags_request,
m_network_zones.at(epee::net_utils::zone::public_).m_net_server.get_config_object(),
@@ -2289,12 +2209,20 @@ namespace nodetool
}
//fill response
- rsp.local_time = time(NULL);
-
const epee::net_utils::zone zone_type = context.m_remote_address.get_zone();
network_zone& zone = m_network_zones.at(zone_type);
- zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new, true);
+ std::vector<peerlist_entry> local_peerlist_new;
+ zone.m_peerlist.get_peerlist_head(local_peerlist_new, true, P2P_DEFAULT_PEERS_IN_HANDSHAKE);
+
+ //only include out peers we did not already send
+ rsp.local_peerlist_new.reserve(local_peerlist_new.size());
+ for (auto &pe: local_peerlist_new)
+ {
+ if (!context.sent_addresses.insert(pe.adr).second)
+ continue;
+ rsp.local_peerlist_new.push_back(std::move(pe));
+ }
m_payload_handler.get_payload_sync_data(rsp.payload_data);
/* Tor/I2P nodes receiving connections via forwarding (from tor/i2p daemon)
@@ -2416,6 +2344,8 @@ namespace nodetool
//fill response
zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new, true);
+ for (const auto &e: rsp.local_peerlist_new)
+ context.sent_addresses.insert(e.adr);
get_local_node_data(rsp.node_data, zone);
m_payload_handler.get_payload_sync_data(rsp.payload_data);
LOG_DEBUG_CC(context, "COMMAND_HANDSHAKE");
@@ -2459,7 +2389,7 @@ namespace nodetool
zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
{
ss << cntxt.m_remote_address.str()
- << " \t\tpeer_id " << cntxt.peer_id
+ << " \t\tpeer_id " << peerid_to_string(cntxt.peer_id)
<< " \t\tconn_id " << cntxt.m_connection_id << (cntxt.m_is_income ? " INC":" OUT")
<< std::endl;
return true;
@@ -2717,12 +2647,12 @@ namespace nodetool
if (!check_connection_and_handshake_with_peer(pe.adr, pe.last_seen))
{
zone.second.m_peerlist.remove_from_peer_gray(pe);
- LOG_PRINT_L2("PEER EVICTED FROM GRAY PEER LIST IP address: " << pe.adr.host_str() << " Peer ID: " << peerid_type(pe.id));
+ LOG_PRINT_L2("PEER EVICTED FROM GRAY PEER LIST: address: " << pe.adr.host_str() << " Peer ID: " << peerid_to_string(pe.id));
}
else
{
zone.second.m_peerlist.set_peer_just_seen(pe.id, pe.adr, pe.pruning_seed, pe.rpc_port, pe.rpc_credits_per_hash);
- LOG_PRINT_L2("PEER PROMOTED TO WHITE PEER LIST IP address: " << pe.adr.host_str() << " Peer ID: " << peerid_type(pe.id));
+ LOG_PRINT_L2("PEER PROMOTED TO WHITE PEER LIST IP address: " << pe.adr.host_str() << " Peer ID: " << peerid_to_string(pe.id));
}
}
return true;
diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h
index aa1b83b83..ed88aa28c 100644
--- a/src/p2p/net_node_common.h
+++ b/src/p2p/net_node_common.h
@@ -50,7 +50,7 @@ namespace nodetool
struct i_p2p_endpoint
{
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections)=0;
- virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, bool pad_txs)=0;
+ virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core)=0;
virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0;
virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context)=0;
virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0;
@@ -75,7 +75,7 @@ namespace nodetool
{
return false;
}
- virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, const bool pad_txs)
+ virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core)
{
return epee::net_utils::zone::invalid;
}
diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h
index 58b704f73..300181bbb 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -43,6 +43,7 @@
#include <boost/range/adaptor/reversed.hpp>
+#include "crypto/crypto.h"
#include "cryptonote_config.h"
#include "net/enums.h"
#include "net/local_ip.h"
@@ -101,7 +102,7 @@ namespace nodetool
bool init(peerlist_types&& peers, bool allow_local_ip);
size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();}
size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();}
- bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs);
+ bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs, const std::function<bool(const peerlist_entry&)> &f = NULL);
bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, bool anonymize, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE);
void get_peerlist(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white);
void get_peerlist(peerlist_types& peers);
@@ -112,7 +113,6 @@ namespace nodetool
bool append_with_peer_gray(const peerlist_entry& pr);
bool append_with_peer_anchor(const anchor_peerlist_entry& ple);
bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash);
- bool set_peer_unreachable(const peerlist_entry& pr);
bool is_host_allowed(const epee::net_utils::network_address &address);
bool get_random_gray_peer(peerlist_entry& pe);
bool remove_from_peer_gray(const peerlist_entry& pe);
@@ -213,12 +213,13 @@ namespace nodetool
}
//--------------------------------------------------------------------------------------------------
inline
- bool peerlist_manager::merge_peerlist(const std::vector<peerlist_entry>& outer_bs)
+ bool peerlist_manager::merge_peerlist(const std::vector<peerlist_entry>& outer_bs, const std::function<bool(const peerlist_entry&)> &f)
{
CRITICAL_REGION_LOCAL(m_peerlist_lock);
for(const peerlist_entry& be: outer_bs)
{
- append_with_peer_gray(be);
+ if (!f || f(be))
+ append_with_peer_gray(be);
}
// delete extra elements
trim_gray_peerlist();
@@ -269,19 +270,19 @@ namespace nodetool
peers_indexed::index<by_time>::type& by_time_index=m_peers_white.get<by_time>();
uint32_t cnt = 0;
- // picks a random set of peers within the first 120%, rather than a set of the first 100%.
+ // picks a random set of peers within the whole set, rather pick the first depth elements.
// The intent is that if someone asks twice, they can't easily tell:
// - this address was not in the first list, but is in the second, so the only way this can be
// is if its last_seen was recently reset, so this means the target node recently had a new
// connection to that address
// - this address was in the first list, and not in the second, which means either the address
- // was moved to the gray list (if it's not accessibe, which the attacker can check if
+ // was moved to the gray list (if it's not accessible, which the attacker can check if
// the address accepts incoming connections) or it was the oldest to still fit in the 250 items,
// so its last_seen is old.
//
// See Cao, Tong et al. "Exploring the Monero Peer-to-Peer Network". https://eprint.iacr.org/2019/411
//
- const uint32_t pick_depth = anonymize ? depth + depth / 5 : depth;
+ const uint32_t pick_depth = anonymize ? m_peers_white.size() : depth;
bs_head.reserve(pick_depth);
for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index))
{
diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h
index 44b278589..609661871 100644
--- a/src/p2p/p2p_protocol_defs.h
+++ b/src/p2p/p2p_protocol_defs.h
@@ -40,9 +40,6 @@
#include "string_tools.h"
#include "time_helper.h"
#include "cryptonote_config.h"
-#ifdef ALLOW_DEBUG_COMMANDS
-#include "crypto/crypto.h"
-#endif
namespace nodetool
{
@@ -82,8 +79,7 @@ namespace nodetool
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(adr)
KV_SERIALIZE(id)
- if (!is_store || this_ref.last_seen != 0)
- KV_SERIALIZE_OPT(last_seen, (int64_t)0)
+ KV_SERIALIZE_OPT(last_seen, (int64_t)0)
KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0)
KV_SERIALIZE_OPT(rpc_port, (uint16_t)0)
KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0)
@@ -132,7 +128,7 @@ namespace nodetool
ss << std::setfill ('0') << std::setw (8) << std::hex << std::noshowbase;
for(const peerlist_entry& pe: pl)
{
- ss << pe.id << "\t" << pe.adr.str()
+ ss << peerid_to_string(pe.id) << "\t" << pe.adr.str()
<< " \trpc port " << (pe.rpc_port > 0 ? std::to_string(pe.rpc_port) : "-")
<< " \trpc credits per hash " << (pe.rpc_credits_per_hash > 0 ? std::to_string(pe.rpc_credits_per_hash) : "-")
<< " \tpruning seed " << pe.pruning_seed
@@ -166,7 +162,6 @@ namespace nodetool
struct basic_node_data
{
uuid network_id;
- uint64_t local_time;
uint32_t my_port;
uint16_t rpc_port;
uint32_t rpc_credits_per_hash;
@@ -175,7 +170,6 @@ namespace nodetool
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_VAL_POD_AS_BLOB(network_id)
KV_SERIALIZE(peer_id)
- KV_SERIALIZE(local_time)
KV_SERIALIZE(my_port)
KV_SERIALIZE_OPT(rpc_port, (uint16_t)(0))
KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0)
@@ -214,35 +208,7 @@ namespace nodetool
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(node_data)
KV_SERIALIZE(payload_data)
- if (is_store)
- {
- // saving: save both, so old and new peers can understand it
- KV_SERIALIZE(local_peerlist_new)
- std::vector<peerlist_entry_base<network_address_old>> local_peerlist;
- for (const auto &p: this_ref.local_peerlist_new)
- {
- if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
- {
- const epee::net_utils::network_address &na = p.adr;
- const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
- local_peerlist.push_back(peerlist_entry_base<network_address_old>({{ipv4.ip(), ipv4.port()}, p.id, p.last_seen, p.pruning_seed, p.rpc_port, p.rpc_credits_per_hash}));
- }
- else
- MDEBUG("Not including in legacy peer list: " << p.adr.str());
- }
- epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist");
- }
- else
- {
- // loading: load old list only if there is no new one
- if (!epee::serialization::selector<is_store>::serialize(this_ref.local_peerlist_new, stg, hparent_section, "local_peerlist_new"))
- {
- std::vector<peerlist_entry_base<network_address_old>> local_peerlist;
- epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist");
- for (const auto &p: local_peerlist)
- ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen, p.pruning_seed, p.rpc_port, p.rpc_credits_per_hash}));
- }
- }
+ KV_SERIALIZE(local_peerlist_new)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -268,42 +234,12 @@ namespace nodetool
struct response_t
{
- uint64_t local_time;
t_playload_type payload_data;
std::vector<peerlist_entry> local_peerlist_new;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(local_time)
KV_SERIALIZE(payload_data)
- if (is_store)
- {
- // saving: save both, so old and new peers can understand it
- KV_SERIALIZE(local_peerlist_new)
- std::vector<peerlist_entry_base<network_address_old>> local_peerlist;
- for (const auto &p: this_ref.local_peerlist_new)
- {
- if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
- {
- const epee::net_utils::network_address &na = p.adr;
- const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
- local_peerlist.push_back(peerlist_entry_base<network_address_old>({{ipv4.ip(), ipv4.port()}, p.id, p.last_seen}));
- }
- else
- MDEBUG("Not including in legacy peer list: " << p.adr.str());
- }
- epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist");
- }
- else
- {
- // loading: load old list only if there is no new one
- if (!epee::serialization::selector<is_store>::serialize(this_ref.local_peerlist_new, stg, hparent_section, "local_peerlist_new"))
- {
- std::vector<peerlist_entry_base<network_address_old>> local_peerlist;
- epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist");
- for (const auto &p: local_peerlist)
- ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen}));
- }
- }
+ KV_SERIALIZE(local_peerlist_new)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -347,117 +283,6 @@ namespace nodetool
};
-#ifdef ALLOW_DEBUG_COMMANDS
- //These commands are considered as insecure, and made in debug purposes for a limited lifetime.
- //Anyone who feel unsafe with this commands can disable the ALLOW_GET_STAT_COMMAND macro.
-
- struct proof_of_trust
- {
- peerid_type peer_id;
- uint64_t time;
- crypto::signature sign;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(peer_id)
- KV_SERIALIZE(time)
- KV_SERIALIZE_VAL_POD_AS_BLOB(sign)
- END_KV_SERIALIZE_MAP()
- };
-
-
- template<class payload_stat_info>
- struct COMMAND_REQUEST_STAT_INFO_T
- {
- const static int ID = P2P_COMMANDS_POOL_BASE + 4;
-
- struct request_t
- {
- proof_of_trust tr;
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(tr)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- std::string version;
- std::string os_version;
- uint64_t connections_count;
- uint64_t incoming_connections_count;
- payload_stat_info payload_info;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(version)
- KV_SERIALIZE(os_version)
- KV_SERIALIZE(connections_count)
- KV_SERIALIZE(incoming_connections_count)
- KV_SERIALIZE(payload_info)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
-
- /************************************************************************/
- /* */
- /************************************************************************/
- struct COMMAND_REQUEST_NETWORK_STATE
- {
- const static int ID = P2P_COMMANDS_POOL_BASE + 5;
-
- struct request_t
- {
- proof_of_trust tr;
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(tr)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- std::vector<peerlist_entry> local_peerlist_white;
- std::vector<peerlist_entry> local_peerlist_gray;
- std::vector<connection_entry> connections_list;
- peerid_type my_id;
- uint64_t local_time;
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_white)
- KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_gray)
- KV_SERIALIZE_CONTAINER_POD_AS_BLOB(connections_list)
- KV_SERIALIZE(my_id)
- KV_SERIALIZE(local_time)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
- /************************************************************************/
- /* */
- /************************************************************************/
- struct COMMAND_REQUEST_PEER_ID
- {
- const static int ID = P2P_COMMANDS_POOL_BASE + 6;
-
- struct request_t
- {
- BEGIN_KV_SERIALIZE_MAP()
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- peerid_type my_id;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(my_id)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
/************************************************************************/
/* */
/************************************************************************/
@@ -482,16 +307,4 @@ namespace nodetool
};
typedef epee::misc_utils::struct_init<response_t> response;
};
-
-#endif
-
-
- inline crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot)
- {
- std::string s;
- s.append(reinterpret_cast<const char*>(&pot.peer_id), sizeof(pot.peer_id));
- s.append(reinterpret_cast<const char*>(&pot.time), sizeof(pot.time));
- return crypto::cn_fast_hash(s.data(), s.size());
- }
-
}
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index dc93e7023..23ade21a2 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -87,10 +87,14 @@ namespace
RPCTracker(const char *rpc, tools::LoggingPerformanceTimer &timer): rpc(rpc), timer(timer) {
}
~RPCTracker() {
- boost::unique_lock<boost::mutex> lock(mutex);
- auto &e = tracker[rpc];
- ++e.count;
- e.time += timer.value();
+ try
+ {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ auto &e = tracker[rpc];
+ ++e.count;
+ e.time += timer.value();
+ }
+ catch (...) { /* ignore */ }
}
void pay(uint64_t amount) {
boost::unique_lock<boost::mutex> lock(mutex);
@@ -121,11 +125,16 @@ namespace
return (value + quantum - 1) / quantum * quantum;
}
+ void store_128(boost::multiprecision::uint128_t value, uint64_t &slow64, std::string &swide, uint64_t &stop64)
+ {
+ slow64 = (value & 0xffffffffffffffff).convert_to<uint64_t>();
+ swide = cryptonote::hex(value);
+ stop64 = ((value >> 64) & 0xffffffffffffffff).convert_to<uint64_t>();
+ }
+
void store_difficulty(cryptonote::difficulty_type difficulty, uint64_t &sdiff, std::string &swdiff, uint64_t &stop64)
{
- sdiff = (difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
- swdiff = cryptonote::hex(difficulty);
- stop64 = ((difficulty >> 64) & 0xffffffffffffffff).convert_to<uint64_t>();
+ store_128(difficulty, sdiff, swdiff, stop64);
}
}
@@ -144,6 +153,7 @@ namespace cryptonote
command_line::add_arg(desc, arg_rpc_payment_address);
command_line::add_arg(desc, arg_rpc_payment_difficulty);
command_line::add_arg(desc, arg_rpc_payment_credits);
+ command_line::add_arg(desc, arg_rpc_payment_allow_free_loopback);
}
//------------------------------------------------------------------------------------------------------------------------------
core_rpc_server::core_rpc_server(
@@ -153,6 +163,8 @@ namespace cryptonote
: m_core(cr)
, m_p2p(p2p)
, m_was_bootstrap_ever_used(false)
+ , disable_rpc_ban(false)
+ , m_rpc_payment_allow_free_loopback(false)
{}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const std::string &username_password)
@@ -232,6 +244,7 @@ namespace cryptonote
const boost::program_options::variables_map& vm
, const bool restricted
, const std::string& port
+ , bool allow_rpc_payment
)
{
m_restricted = restricted;
@@ -242,8 +255,9 @@ namespace cryptonote
if (!rpc_config)
return false;
+ disable_rpc_ban = rpc_config->disable_rpc_ban;
std::string address = command_line::get_arg(vm, arg_rpc_payment_address);
- if (!address.empty())
+ if (!address.empty() && allow_rpc_payment)
{
if (!m_restricted && nettype() != FAKECHAIN)
{
@@ -268,6 +282,7 @@ namespace cryptonote
MERROR("Payments difficulty and/or payments credits are 0, but a payment address was given");
return false;
}
+ m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback);
m_rpc_payment.reset(new rpc_payment(info.address, diff, credits));
m_rpc_payment->load(command_line::get_arg(vm, cryptonote::arg_data_dir));
m_p2p.set_rpc_credits_per_hash(RPC_CREDITS_PER_HASH_SCALE * (credits / (float)diff));
@@ -341,7 +356,7 @@ namespace cryptonote
#define CHECK_PAYMENT_BASE(req, res, payment, same_ts) do { if (!ctx) break; uint64_t P = (uint64_t)payment; if (P > 0 && !check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0)
#define CHECK_PAYMENT(req, res, payment) CHECK_PAYMENT_BASE(req, res, payment, false)
#define CHECK_PAYMENT_SAME_TS(req, res, payment) CHECK_PAYMENT_BASE(req, res, payment, true)
-#define CHECK_PAYMENT_MIN1(req, res, payment, same_ts) do { if (!ctx) break; uint64_t P = (uint64_t)payment; if (P == 0) P = 1; if(!check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0)
+#define CHECK_PAYMENT_MIN1(req, res, payment, same_ts) do { if (!ctx || (m_rpc_payment_allow_free_loopback && ctx->m_remote_address.is_loopback())) break; uint64_t P = (uint64_t)payment; if (P == 0) P = 1; if(!check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0)
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::check_core_ready()
{
@@ -354,7 +369,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::add_host_fail(const connection_context *ctx, unsigned int score)
{
- if(!ctx || !ctx->m_remote_address.is_blockable())
+ if(!ctx || !ctx->m_remote_address.is_blockable() || disable_rpc_ban)
return false;
CRITICAL_REGION_LOCAL(m_host_fails_score_lock);
@@ -539,7 +554,7 @@ namespace cryptonote
CHECK_PAYMENT_SAME_TS(req, res, bs.size() * COST_PER_BLOCK);
- size_t pruned_size = 0, unpruned_size = 0, ntxes = 0;
+ size_t size = 0, ntxes = 0;
res.blocks.reserve(bs.size());
res.output_indices.reserve(bs.size());
for(auto& bd: bs)
@@ -547,8 +562,7 @@ namespace cryptonote
res.blocks.resize(res.blocks.size()+1);
res.blocks.back().pruned = req.prune;
res.blocks.back().block = bd.first.first;
- pruned_size += bd.first.first.size();
- unpruned_size += bd.first.first.size();
+ size += bd.first.first.size();
res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices());
ntxes += bd.second.size();
res.output_indices.back().indices.reserve(1 + bd.second.size());
@@ -557,11 +571,10 @@ namespace cryptonote
res.blocks.back().txs.reserve(bd.second.size());
for (std::vector<std::pair<crypto::hash, cryptonote::blobdata>>::iterator i = bd.second.begin(); i != bd.second.end(); ++i)
{
- unpruned_size += i->second.size();
res.blocks.back().txs.push_back({std::move(i->second), crypto::null_hash});
i->second.clear();
i->second.shrink_to_fit();
- pruned_size += res.blocks.back().txs.back().blob.size();
+ size += res.blocks.back().txs.back().blob.size();
}
const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1);
@@ -584,7 +597,7 @@ namespace cryptonote
}
}
- MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, pruned size " << pruned_size << ", unpruned size " << unpruned_size);
+ MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, size " << size);
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -954,18 +967,21 @@ namespace cryptonote
{
e.double_spend_seen = it->second.double_spend_seen;
e.relayed = it->second.relayed;
+ e.received_timestamp = it->second.receive_time;
}
else
{
MERROR("Failed to determine pool info for " << tx_hash);
e.double_spend_seen = false;
e.relayed = false;
+ e.received_timestamp = 0;
}
}
else
{
e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash);
e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height);
+ e.received_timestamp = 0;
e.double_spend_seen = false;
e.relayed = false;
}
@@ -1087,7 +1103,7 @@ namespace cryptonote
return true;
}
- if (req.do_sanity_checks && !cryptonote::tx_sanity_check(m_core.get_blockchain_storage(), tx_blob))
+ if (req.do_sanity_checks && !cryptonote::tx_sanity_check(tx_blob, m_core.get_blockchain_storage().get_num_mature_outputs(0)))
{
res.status = "Failed";
res.reason = "Sanity check failed";
@@ -1958,7 +1974,11 @@ namespace cryptonote
m_was_bootstrap_ever_used = true;
}
- r = r && res.status == CORE_RPC_STATUS_OK;
+ if (r && res.status != CORE_RPC_STATUS_PAYMENT_REQUIRED && res.status != CORE_RPC_STATUS_OK)
+ {
+ MINFO("Failing RPC " << command_name << " due to peer return status " << res.status);
+ r = false;
+ }
res.untrusted = true;
return true;
}
@@ -2199,6 +2219,7 @@ namespace cryptonote
error_resp.message = "Internal error: can't produce valid response.";
return false;
}
+ res.miner_tx_hash = res.block_header.miner_tx_hash;
for (size_t n = 0; n < blk.tx_hashes.size(); ++n)
{
res.tx_hashes.push_back(epee::string_tools::pod_to_hex(blk.tx_hashes[n]));
@@ -2222,8 +2243,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- on_get_info(req, res, ctx);
- if (res.status != CORE_RPC_STATUS_OK)
+ if (!on_get_info(req, res, ctx) || res.status != CORE_RPC_STATUS_OK)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = res.status;
@@ -2486,9 +2506,9 @@ namespace cryptonote
return true;
}
CHECK_PAYMENT_MIN1(req, res, COST_PER_COINBASE_TX_SUM_BLOCK * req.count, false);
- std::pair<uint64_t, uint64_t> amounts = m_core.get_coinbase_tx_sum(req.height, req.count);
- res.emission_amount = amounts.first;
- res.fee_amount = amounts.second;
+ std::pair<boost::multiprecision::uint128_t, boost::multiprecision::uint128_t> amounts = m_core.get_coinbase_tx_sum(req.height, req.count);
+ store_128(amounts.first, res.emission_amount, res.wide_emission_amount, res.emission_amount_top64);
+ store_128(amounts.second, res.fee_amount, res.wide_fee_amount, res.fee_amount_top64);
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -2617,6 +2637,7 @@ namespace cryptonote
{
RPC_TRACKER(update);
+ res.update = false;
if (m_core.offline())
{
res.status = "Daemon is running offline";
@@ -2938,7 +2959,7 @@ namespace cryptonote
RPC_TRACKER(rpc_access_info);
bool r;
- if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_INFO>(invoke_http_mode::JON, "rpc_access_info", req, res, r))
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_INFO>(invoke_http_mode::JON_RPC, "rpc_access_info", req, res, r))
return r;
// if RPC payment is not enabled
@@ -3002,6 +3023,8 @@ namespace cryptonote
RPC_TRACKER(flush_cache);
if (req.bad_txs)
m_core.flush_bad_txs_cache();
+ if (req.bad_blocks)
+ m_core.flush_invalid_blocks();
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -3010,7 +3033,7 @@ namespace cryptonote
{
RPC_TRACKER(rpc_access_submit_nonce);
bool r;
- if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_SUBMIT_NONCE>(invoke_http_mode::JON, "rpc_access_submit_nonce", req, res, r))
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_SUBMIT_NONCE>(invoke_http_mode::JON_RPC, "rpc_access_submit_nonce", req, res, r))
return r;
// if RPC payment is not enabled
@@ -3069,7 +3092,7 @@ namespace cryptonote
RPC_TRACKER(rpc_access_pay);
bool r;
- if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_PAY>(invoke_http_mode::JON, "rpc_access_pay", req, res, r))
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_PAY>(invoke_http_mode::JON_RPC, "rpc_access_pay", req, res, r))
return r;
// if RPC payment is not enabled
@@ -3128,7 +3151,7 @@ namespace cryptonote
RPC_TRACKER(rpc_access_data);
bool r;
- if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_DATA>(invoke_http_mode::JON, "rpc_access_data", req, res, r))
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_DATA>(invoke_http_mode::JON_RPC, "rpc_access_data", req, res, r))
return r;
if (!m_rpc_payment)
@@ -3156,7 +3179,7 @@ namespace cryptonote
RPC_TRACKER(rpc_access_account);
bool r;
- if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_ACCOUNT>(invoke_http_mode::JON, "rpc_access_account", req, res, r))
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_ACCOUNT>(invoke_http_mode::JON_RPC, "rpc_access_account", req, res, r))
return r;
if (!m_rpc_payment)
@@ -3235,4 +3258,10 @@ namespace cryptonote
, "Restrict RPC to clients sending micropayment, yields that many credits per payment"
, DEFAULT_PAYMENT_CREDITS_PER_HASH
};
+
+ const command_line::arg_descriptor<bool> core_rpc_server::arg_rpc_payment_allow_free_loopback = {
+ "rpc-payment-allow-free-loopback"
+ , "Allow free access from the loopback address (ie, the local host)"
+ , false
+ };
} // namespace cryptonote
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 23c611470..d82ab6af4 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -75,6 +75,7 @@ namespace cryptonote
static const command_line::arg_descriptor<std::string> arg_rpc_payment_address;
static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_difficulty;
static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_credits;
+ static const command_line::arg_descriptor<bool> arg_rpc_payment_allow_free_loopback;
typedef epee::net_utils::connection_context_base connection_context;
@@ -88,7 +89,8 @@ namespace cryptonote
bool init(
const boost::program_options::variables_map& vm,
const bool restricted,
- const std::string& port
+ const std::string& port,
+ bool allow_rpc_payment
);
network_type nettype() const { return m_core.get_nettype(); }
@@ -285,6 +287,8 @@ private:
epee::critical_section m_host_fails_score_lock;
std::map<std::string, uint64_t> m_host_fails_score;
std::unique_ptr<rpc_payment> m_rpc_payment;
+ bool disable_rpc_ban;
+ bool m_rpc_payment_allow_free_loopback;
};
}
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 855ea854c..dbb1d4472 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -88,7 +88,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 3
-#define CORE_RPC_VERSION_MINOR 0
+#define CORE_RPC_VERSION_MINOR 1
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -351,6 +351,7 @@ namespace cryptonote
bool double_spend_seen;
uint64_t block_height;
uint64_t block_timestamp;
+ uint64_t received_timestamp;
std::vector<uint64_t> output_indices;
bool relayed;
@@ -372,6 +373,7 @@ namespace cryptonote
else
{
KV_SERIALIZE(relayed)
+ KV_SERIALIZE(received_timestamp)
}
END_KV_SERIALIZE_MAP()
};
@@ -2021,12 +2023,20 @@ namespace cryptonote
struct response_t: public rpc_access_response_base
{
uint64_t emission_amount;
+ std::string wide_emission_amount;
+ uint64_t emission_amount_top64;
uint64_t fee_amount;
+ std::string wide_fee_amount;
+ uint64_t fee_amount_top64;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(emission_amount)
+ KV_SERIALIZE(wide_emission_amount)
+ KV_SERIALIZE(emission_amount_top64)
KV_SERIALIZE(fee_amount)
+ KV_SERIALIZE(wide_fee_amount)
+ KV_SERIALIZE(fee_amount_top64)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -2556,22 +2566,23 @@ namespace cryptonote
struct COMMAND_RPC_FLUSH_CACHE
{
- struct request_t
+ struct request_t: public rpc_request_base
{
bool bad_txs;
+ bool bad_blocks;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE_OPT(bad_txs, false)
+ KV_SERIALIZE_OPT(bad_blocks, false)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 24800ff20..125688ba5 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -28,6 +28,10 @@
#include "daemon_handler.h"
+#include <algorithm>
+#include <cstring>
+#include <stdexcept>
+
#include <boost/uuid/nil_generator.hpp>
// likely included by daemon_handler.h's includes,
// but including here for clarity
@@ -42,6 +46,74 @@ namespace cryptonote
namespace rpc
{
+ namespace
+ {
+ using handler_function = std::string(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& msg);
+ struct handler_map
+ {
+ const char* method_name;
+ handler_function* call;
+ };
+
+ bool operator<(const handler_map& lhs, const handler_map& rhs) noexcept
+ {
+ return std::strcmp(lhs.method_name, rhs.method_name) < 0;
+ }
+
+ bool operator<(const handler_map& lhs, const std::string& rhs) noexcept
+ {
+ return std::strcmp(lhs.method_name, rhs.c_str()) < 0;
+ }
+
+ template<typename Message>
+ std::string handle_message(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& parameters)
+ {
+ typename Message::Request request{};
+ request.fromJson(parameters);
+
+ typename Message::Response response{};
+ handler.handle(request, response);
+ return FullMessage::getResponse(response, id);
+ }
+
+ constexpr const handler_map handlers[] =
+ {
+ {u8"get_block_hash", handle_message<GetBlockHash>},
+ {u8"get_block_header_by_hash", handle_message<GetBlockHeaderByHash>},
+ {u8"get_block_header_by_height", handle_message<GetBlockHeaderByHeight>},
+ {u8"get_block_headers_by_height", handle_message<GetBlockHeadersByHeight>},
+ {u8"get_blocks_fast", handle_message<GetBlocksFast>},
+ {u8"get_dynamic_fee_estimate", handle_message<GetFeeEstimate>},
+ {u8"get_hashes_fast", handle_message<GetHashesFast>},
+ {u8"get_height", handle_message<GetHeight>},
+ {u8"get_info", handle_message<GetInfo>},
+ {u8"get_last_block_header", handle_message<GetLastBlockHeader>},
+ {u8"get_output_distribution", handle_message<GetOutputDistribution>},
+ {u8"get_output_histogram", handle_message<GetOutputHistogram>},
+ {u8"get_output_keys", handle_message<GetOutputKeys>},
+ {u8"get_peer_list", handle_message<GetPeerList>},
+ {u8"get_rpc_version", handle_message<GetRPCVersion>},
+ {u8"get_transaction_pool", handle_message<GetTransactionPool>},
+ {u8"get_transactions", handle_message<GetTransactions>},
+ {u8"get_tx_global_output_indices", handle_message<GetTxGlobalOutputIndices>},
+ {u8"hard_fork_info", handle_message<HardForkInfo>},
+ {u8"key_images_spent", handle_message<KeyImagesSpent>},
+ {u8"mining_status", handle_message<MiningStatus>},
+ {u8"save_bc", handle_message<SaveBC>},
+ {u8"send_raw_tx", handle_message<SendRawTxHex>},
+ {u8"set_log_level", handle_message<SetLogLevel>},
+ {u8"start_mining", handle_message<StartMining>},
+ {u8"stop_mining", handle_message<StopMining>}
+ };
+ } // anonymous
+
+ DaemonHandler::DaemonHandler(cryptonote::core& c, t_p2p& p2p)
+ : m_core(c), m_p2p(p2p)
+ {
+ const auto last_sorted = std::is_sorted_until(std::begin(handlers), std::end(handlers));
+ if (last_sorted != std::end(handlers))
+ throw std::logic_error{std::string{"ZMQ JSON-RPC handlers map is not properly sorted, see "} + last_sorted->method_name};
+ }
void DaemonHandler::handle(const GetHeight::Request& req, GetHeight::Response& res)
{
@@ -840,68 +912,21 @@ namespace rpc
{
MDEBUG("Handling RPC request: " << request);
- Message* resp_message = NULL;
-
try
{
FullMessage req_full(request, true);
- rapidjson::Value& req_json = req_full.getMessage();
-
const std::string request_type = req_full.getRequestType();
-
- // create correct Message subclass and call handle() on it
- REQ_RESP_TYPES_MACRO(request_type, GetHeight, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetBlocksFast, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetHashesFast, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetTransactions, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, SendRawTxHex, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, StopMining, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, MiningStatus, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, SaveBC, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetBlockHash, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetLastBlockHeader, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHash, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHeight, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetBlockHeadersByHeight, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetPeerList, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, SetLogLevel, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetTransactionPool, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, HardForkInfo, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetOutputHistogram, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetOutputKeys, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetRPCVersion, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetFeeEstimate, req_json, resp_message, handle);
- REQ_RESP_TYPES_MACRO(request_type, GetOutputDistribution, req_json, resp_message, handle);
-
- // if none of the request types matches
- if (resp_message == NULL)
- {
+ const auto matched_handler = std::lower_bound(std::begin(handlers), std::end(handlers), request_type);
+ if (matched_handler == std::end(handlers) || matched_handler->method_name != request_type)
return BAD_REQUEST(request_type, req_full.getID());
- }
-
- FullMessage resp_full = FullMessage::responseMessage(resp_message, req_full.getID());
-
- const std::string response = resp_full.getJson();
- delete resp_message;
- resp_message = NULL;
+ std::string response = matched_handler->call(*this, req_full.getID(), req_full.getMessage());
MDEBUG("Returning RPC response: " << response);
-
return response;
}
catch (const std::exception& e)
{
- if (resp_message)
- {
- delete resp_message;
- }
-
return BAD_JSON(e.what());
}
}
diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h
index 34723f676..c33f608ab 100644
--- a/src/rpc/daemon_handler.h
+++ b/src/rpc/daemon_handler.h
@@ -50,7 +50,7 @@ class DaemonHandler : public RpcHandler
{
public:
- DaemonHandler(cryptonote::core& c, t_p2p& p2p) : m_core(c), m_p2p(p2p) { }
+ DaemonHandler(cryptonote::core& c, t_p2p& p2p);
~DaemonHandler() { }
diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp
index cf0f5ece1..5c179408e 100644
--- a/src/rpc/daemon_messages.cpp
+++ b/src/rpc/daemon_messages.cpp
@@ -34,99 +34,47 @@ namespace cryptonote
namespace rpc
{
+void GetHeight::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
-const char* const GetHeight::name = "get_height";
-const char* const GetBlocksFast::name = "get_blocks_fast";
-const char* const GetHashesFast::name = "get_hashes_fast";
-const char* const GetTransactions::name = "get_transactions";
-const char* const KeyImagesSpent::name = "key_images_spent";
-const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices";
-const char* const SendRawTx::name = "send_raw_tx";
-const char* const SendRawTxHex::name = "send_raw_tx_hex";
-const char* const StartMining::name = "start_mining";
-const char* const StopMining::name = "stop_mining";
-const char* const MiningStatus::name = "mining_status";
-const char* const GetInfo::name = "get_info";
-const char* const SaveBC::name = "save_bc";
-const char* const GetBlockHash::name = "get_block_hash";
-const char* const GetLastBlockHeader::name = "get_last_block_header";
-const char* const GetBlockHeaderByHash::name = "get_block_header_by_hash";
-const char* const GetBlockHeaderByHeight::name = "get_block_header_by_height";
-const char* const GetBlockHeadersByHeight::name = "get_block_headers_by_height";
-const char* const GetPeerList::name = "get_peer_list";
-const char* const SetLogLevel::name = "set_log_level";
-const char* const GetTransactionPool::name = "get_transaction_pool";
-const char* const HardForkInfo::name = "hard_fork_info";
-const char* const GetOutputHistogram::name = "get_output_histogram";
-const char* const GetOutputKeys::name = "get_output_keys";
-const char* const GetRPCVersion::name = "get_rpc_version";
-const char* const GetFeeEstimate::name = "get_dynamic_fee_estimate";
-const char* const GetOutputDistribution::name = "get_output_distribution";
-
-
-
-
-rapidjson::Value GetHeight::Request::toJson(rapidjson::Document& doc) const
+void GetHeight::Request::fromJson(const rapidjson::Value& val)
{
- return Message::toJson(doc);
}
-void GetHeight::Request::fromJson(rapidjson::Value& val)
+void GetHeight::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
+ INSERT_INTO_JSON_OBJECT(dest, height, height);
}
-rapidjson::Value GetHeight::Response::toJson(rapidjson::Document& doc) const
-{
- auto val = Message::toJson(doc);
-
- auto& al = doc.GetAllocator();
-
- val.AddMember("height", height, al);
-
- return val;
-}
-
-void GetHeight::Response::fromJson(rapidjson::Value& val)
+void GetHeight::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, height, height);
}
-rapidjson::Value GetBlocksFast::Request::toJson(rapidjson::Document& doc) const
+void GetBlocksFast::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- auto& al = doc.GetAllocator();
-
- INSERT_INTO_JSON_OBJECT(val, doc, block_ids, block_ids);
- val.AddMember("start_height", start_height, al);
- val.AddMember("prune", prune, al);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, block_ids, block_ids);
+ INSERT_INTO_JSON_OBJECT(dest, start_height, start_height);
+ INSERT_INTO_JSON_OBJECT(dest, prune, prune);
}
-void GetBlocksFast::Request::fromJson(rapidjson::Value& val)
+void GetBlocksFast::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, block_ids, block_ids);
GET_FROM_JSON_OBJECT(val, start_height, start_height);
GET_FROM_JSON_OBJECT(val, prune, prune);
}
-rapidjson::Value GetBlocksFast::Response::toJson(rapidjson::Document& doc) const
+void GetBlocksFast::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- auto& al = doc.GetAllocator();
-
- INSERT_INTO_JSON_OBJECT(val, doc, blocks, blocks);
- val.AddMember("start_height", start_height, al);
- val.AddMember("current_height", current_height, al);
- INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, blocks, blocks);
+ INSERT_INTO_JSON_OBJECT(dest, start_height, start_height);
+ INSERT_INTO_JSON_OBJECT(dest, current_height, current_height);
+ INSERT_INTO_JSON_OBJECT(dest, output_indices, output_indices);
}
-void GetBlocksFast::Response::fromJson(rapidjson::Value& val)
+void GetBlocksFast::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, blocks, blocks);
GET_FROM_JSON_OBJECT(val, start_height, start_height);
@@ -135,38 +83,26 @@ void GetBlocksFast::Response::fromJson(rapidjson::Value& val)
}
-rapidjson::Value GetHashesFast::Request::toJson(rapidjson::Document& doc) const
+void GetHashesFast::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- auto& al = doc.GetAllocator();
-
- INSERT_INTO_JSON_OBJECT(val, doc, known_hashes, known_hashes);
- val.AddMember("start_height", start_height, al);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, known_hashes, known_hashes);
+ INSERT_INTO_JSON_OBJECT(dest, start_height, start_height);
}
-void GetHashesFast::Request::fromJson(rapidjson::Value& val)
+void GetHashesFast::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, known_hashes, known_hashes);
GET_FROM_JSON_OBJECT(val, start_height, start_height);
}
-rapidjson::Value GetHashesFast::Response::toJson(rapidjson::Document& doc) const
+void GetHashesFast::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- auto& al = doc.GetAllocator();
-
- INSERT_INTO_JSON_OBJECT(val, doc, hashes, hashes);
- val.AddMember("start_height", start_height, al);
- val.AddMember("current_height", current_height, al);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, hashes, hashes);
+ INSERT_INTO_JSON_OBJECT(dest, start_height, start_height);
+ INSERT_INTO_JSON_OBJECT(dest, current_height, current_height);
}
-void GetHashesFast::Response::fromJson(rapidjson::Value& val)
+void GetHashesFast::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, hashes, hashes);
GET_FROM_JSON_OBJECT(val, start_height, start_height);
@@ -174,154 +110,114 @@ void GetHashesFast::Response::fromJson(rapidjson::Value& val)
}
-rapidjson::Value GetTransactions::Request::toJson(rapidjson::Document& doc) const
+void GetTransactions::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, tx_hashes);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, tx_hashes, tx_hashes);
}
-void GetTransactions::Request::fromJson(rapidjson::Value& val)
+void GetTransactions::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, tx_hashes, tx_hashes);
}
-rapidjson::Value GetTransactions::Response::toJson(rapidjson::Document& doc) const
+void GetTransactions::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- rapidjson::Value val(rapidjson::kObjectType);
-
- INSERT_INTO_JSON_OBJECT(val, doc, txs, txs);
- INSERT_INTO_JSON_OBJECT(val, doc, missed_hashes, missed_hashes);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, txs, txs);
+ INSERT_INTO_JSON_OBJECT(dest, missed_hashes, missed_hashes);
}
-void GetTransactions::Response::fromJson(rapidjson::Value& val)
+void GetTransactions::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, txs, txs);
GET_FROM_JSON_OBJECT(val, missed_hashes, missed_hashes);
}
-rapidjson::Value KeyImagesSpent::Request::toJson(rapidjson::Document& doc) const
+void KeyImagesSpent::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, key_images, key_images);
}
-void KeyImagesSpent::Request::fromJson(rapidjson::Value& val)
+void KeyImagesSpent::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, key_images, key_images);
}
-rapidjson::Value KeyImagesSpent::Response::toJson(rapidjson::Document& doc) const
+void KeyImagesSpent::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, spent_status, spent_status);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, spent_status, spent_status);
}
-void KeyImagesSpent::Response::fromJson(rapidjson::Value& val)
+void KeyImagesSpent::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, spent_status, spent_status);
}
-rapidjson::Value GetTxGlobalOutputIndices::Request::toJson(rapidjson::Document& doc) const
+void GetTxGlobalOutputIndices::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx_hash);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, tx_hash, tx_hash);
}
-void GetTxGlobalOutputIndices::Request::fromJson(rapidjson::Value& val)
+void GetTxGlobalOutputIndices::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, tx_hash, tx_hash);
}
-rapidjson::Value GetTxGlobalOutputIndices::Response::toJson(rapidjson::Document& doc) const
+void GetTxGlobalOutputIndices::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, output_indices, output_indices);
}
-void GetTxGlobalOutputIndices::Response::fromJson(rapidjson::Value& val)
+void GetTxGlobalOutputIndices::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, output_indices, output_indices);
}
-rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const
+void SendRawTx::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, tx, tx);
- INSERT_INTO_JSON_OBJECT(val, doc, relay, relay);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, tx, tx);
+ INSERT_INTO_JSON_OBJECT(dest, relay, relay);
}
-void SendRawTx::Request::fromJson(rapidjson::Value& val)
+void SendRawTx::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, tx, tx);
GET_FROM_JSON_OBJECT(val, relay, relay);
}
-rapidjson::Value SendRawTx::Response::toJson(rapidjson::Document& doc) const
+void SendRawTx::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, relayed, relayed);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, relayed, relayed);
}
-void SendRawTx::Response::fromJson(rapidjson::Value& val)
+void SendRawTx::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, relayed, relayed);
}
-rapidjson::Value SendRawTxHex::Request::toJson(rapidjson::Document& doc) const
+void SendRawTxHex::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, tx_as_hex, tx_as_hex);
- INSERT_INTO_JSON_OBJECT(val, doc, relay, relay);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, tx_as_hex, tx_as_hex);
+ INSERT_INTO_JSON_OBJECT(dest, relay, relay);
}
-void SendRawTxHex::Request::fromJson(rapidjson::Value& val)
+void SendRawTxHex::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, tx_as_hex, tx_as_hex);
GET_FROM_JSON_OBJECT(val, relay, relay);
}
-rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const
+void StartMining::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, miner_address, miner_address);
- INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count);
- INSERT_INTO_JSON_OBJECT(val, doc, do_background_mining, do_background_mining);
- INSERT_INTO_JSON_OBJECT(val, doc, ignore_battery, ignore_battery);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, miner_address, miner_address);
+ INSERT_INTO_JSON_OBJECT(dest, threads_count, threads_count);
+ INSERT_INTO_JSON_OBJECT(dest, do_background_mining, do_background_mining);
+ INSERT_INTO_JSON_OBJECT(dest, ignore_battery, ignore_battery);
}
-void StartMining::Request::fromJson(rapidjson::Value& val)
+void StartMining::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, miner_address, miner_address);
GET_FROM_JSON_OBJECT(val, threads_count, threads_count);
@@ -329,58 +225,46 @@ void StartMining::Request::fromJson(rapidjson::Value& val)
GET_FROM_JSON_OBJECT(val, ignore_battery, ignore_battery);
}
-rapidjson::Value StartMining::Response::toJson(rapidjson::Document& doc) const
-{
- return Message::toJson(doc);
-}
+void StartMining::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
-void StartMining::Response::fromJson(rapidjson::Value& val)
+void StartMining::Response::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value StopMining::Request::toJson(rapidjson::Document& doc) const
-{
- return Message::toJson(doc);
-}
+void StopMining::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
-void StopMining::Request::fromJson(rapidjson::Value& val)
+void StopMining::Request::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value StopMining::Response::toJson(rapidjson::Document& doc) const
-{
- return Message::toJson(doc);
-}
+void StopMining::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
-void StopMining::Response::fromJson(rapidjson::Value& val)
+void StopMining::Response::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value MiningStatus::Request::toJson(rapidjson::Document& doc) const
-{
- return Message::toJson(doc);
-}
+void MiningStatus::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
-void MiningStatus::Request::fromJson(rapidjson::Value& val)
+void MiningStatus::Request::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value MiningStatus::Response::toJson(rapidjson::Document& doc) const
+void MiningStatus::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, active, active);
- INSERT_INTO_JSON_OBJECT(val, doc, speed, speed);
- INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count);
- INSERT_INTO_JSON_OBJECT(val, doc, address, address);
- INSERT_INTO_JSON_OBJECT(val, doc, is_background_mining_enabled, is_background_mining_enabled);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, active, active);
+ INSERT_INTO_JSON_OBJECT(dest, speed, speed);
+ INSERT_INTO_JSON_OBJECT(dest, threads_count, threads_count);
+ INSERT_INTO_JSON_OBJECT(dest, address, address);
+ INSERT_INTO_JSON_OBJECT(dest, is_background_mining_enabled, is_background_mining_enabled);
}
-void MiningStatus::Response::fromJson(rapidjson::Value& val)
+void MiningStatus::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, active, active);
GET_FROM_JSON_OBJECT(val, speed, speed);
@@ -390,318 +274,230 @@ void MiningStatus::Response::fromJson(rapidjson::Value& val)
}
-rapidjson::Value GetInfo::Request::toJson(rapidjson::Document& doc) const
-{
- return Message::toJson(doc);
-}
+void GetInfo::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
-void GetInfo::Request::fromJson(rapidjson::Value& val)
+void GetInfo::Request::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value GetInfo::Response::toJson(rapidjson::Document& doc) const
+void GetInfo::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, info, info);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, info, info);
}
-void GetInfo::Response::fromJson(rapidjson::Value& val)
+void GetInfo::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, info, info);
}
-rapidjson::Value SaveBC::Request::toJson(rapidjson::Document& doc) const
-{
- auto val = Message::toJson(doc);
+void SaveBC::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
- return val;
-}
-
-void SaveBC::Request::fromJson(rapidjson::Value& val)
+void SaveBC::Request::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value SaveBC::Response::toJson(rapidjson::Document& doc) const
-{
- auto val = Message::toJson(doc);
+void SaveBC::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
- return val;
-}
-
-void SaveBC::Response::fromJson(rapidjson::Value& val)
+void SaveBC::Response::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value GetBlockHash::Request::toJson(rapidjson::Document& doc) const
+void GetBlockHash::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, height, height);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, height, height);
}
-void GetBlockHash::Request::fromJson(rapidjson::Value& val)
+void GetBlockHash::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, height, height);
}
-rapidjson::Value GetBlockHash::Response::toJson(rapidjson::Document& doc) const
+void GetBlockHash::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, hash, hash);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, hash, hash);
}
-void GetBlockHash::Response::fromJson(rapidjson::Value& val)
+void GetBlockHash::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, hash, hash);
}
-rapidjson::Value GetLastBlockHeader::Request::toJson(rapidjson::Document& doc) const
-{
- auto val = Message::toJson(doc);
-
- return val;
-}
+void GetLastBlockHeader::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
-void GetLastBlockHeader::Request::fromJson(rapidjson::Value& val)
+void GetLastBlockHeader::Request::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value GetLastBlockHeader::Response::toJson(rapidjson::Document& doc) const
+void GetLastBlockHeader::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, header, header);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, header, header);
}
-void GetLastBlockHeader::Response::fromJson(rapidjson::Value& val)
+void GetLastBlockHeader::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, header, header);
}
-rapidjson::Value GetBlockHeaderByHash::Request::toJson(rapidjson::Document& doc) const
+void GetBlockHeaderByHash::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, hash, hash);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, hash, hash);
}
-void GetBlockHeaderByHash::Request::fromJson(rapidjson::Value& val)
+void GetBlockHeaderByHash::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, hash, hash);
}
-rapidjson::Value GetBlockHeaderByHash::Response::toJson(rapidjson::Document& doc) const
+void GetBlockHeaderByHash::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, header, header);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, header, header);
}
-void GetBlockHeaderByHash::Response::fromJson(rapidjson::Value& val)
+void GetBlockHeaderByHash::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, header, header);
}
-rapidjson::Value GetBlockHeaderByHeight::Request::toJson(rapidjson::Document& doc) const
+void GetBlockHeaderByHeight::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, height, height);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, height, height);
}
-void GetBlockHeaderByHeight::Request::fromJson(rapidjson::Value& val)
+void GetBlockHeaderByHeight::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, height, height);
}
-rapidjson::Value GetBlockHeaderByHeight::Response::toJson(rapidjson::Document& doc) const
+void GetBlockHeaderByHeight::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, header, header);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, header, header);
}
-void GetBlockHeaderByHeight::Response::fromJson(rapidjson::Value& val)
+void GetBlockHeaderByHeight::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, header, header);
}
-rapidjson::Value GetBlockHeadersByHeight::Request::toJson(rapidjson::Document& doc) const
+void GetBlockHeadersByHeight::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, heights, heights);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, heights, heights);
}
-void GetBlockHeadersByHeight::Request::fromJson(rapidjson::Value& val)
+void GetBlockHeadersByHeight::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, heights, heights);
}
-rapidjson::Value GetBlockHeadersByHeight::Response::toJson(rapidjson::Document& doc) const
+void GetBlockHeadersByHeight::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, headers, headers);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, headers, headers);
}
-void GetBlockHeadersByHeight::Response::fromJson(rapidjson::Value& val)
+void GetBlockHeadersByHeight::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, headers, headers);
}
-rapidjson::Value GetPeerList::Request::toJson(rapidjson::Document& doc) const
-{
- auto val = Message::toJson(doc);
+void GetPeerList::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
- return val;
-}
-
-void GetPeerList::Request::fromJson(rapidjson::Value& val)
+void GetPeerList::Request::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value GetPeerList::Response::toJson(rapidjson::Document& doc) const
+void GetPeerList::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, white_list, white_list);
- INSERT_INTO_JSON_OBJECT(val, doc, gray_list, gray_list);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, white_list, white_list);
+ INSERT_INTO_JSON_OBJECT(dest, gray_list, gray_list);
}
-void GetPeerList::Response::fromJson(rapidjson::Value& val)
+void GetPeerList::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, white_list, white_list);
GET_FROM_JSON_OBJECT(val, gray_list, gray_list);
}
-rapidjson::Value SetLogLevel::Request::toJson(rapidjson::Document& doc) const
+void SetLogLevel::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- auto& al = doc.GetAllocator();
-
- val.AddMember("level", level, al);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, level, level);
}
-void SetLogLevel::Request::fromJson(rapidjson::Value& val)
+void SetLogLevel::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, level, level);
}
-rapidjson::Value SetLogLevel::Response::toJson(rapidjson::Document& doc) const
-{
- return Message::toJson(doc);
-}
+void SetLogLevel::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
-void SetLogLevel::Response::fromJson(rapidjson::Value& val)
+void SetLogLevel::Response::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value GetTransactionPool::Request::toJson(rapidjson::Document& doc) const
-{
- return Message::toJson(doc);
-}
+void GetTransactionPool::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
-void GetTransactionPool::Request::fromJson(rapidjson::Value& val)
+void GetTransactionPool::Request::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value GetTransactionPool::Response::toJson(rapidjson::Document& doc) const
+void GetTransactionPool::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, transactions, transactions);
- INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, transactions, transactions);
+ INSERT_INTO_JSON_OBJECT(dest, key_images, key_images);
}
-void GetTransactionPool::Response::fromJson(rapidjson::Value& val)
+void GetTransactionPool::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, transactions, transactions);
GET_FROM_JSON_OBJECT(val, key_images, key_images);
}
-rapidjson::Value HardForkInfo::Request::toJson(rapidjson::Document& doc) const
+void HardForkInfo::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, version, version);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, version, version);
}
-void HardForkInfo::Request::fromJson(rapidjson::Value& val)
+void HardForkInfo::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, version, version);
}
-rapidjson::Value HardForkInfo::Response::toJson(rapidjson::Document& doc) const
+void HardForkInfo::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, info, info);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, info, info);
}
-void HardForkInfo::Response::fromJson(rapidjson::Value& val)
+void HardForkInfo::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, info, info);
}
-rapidjson::Value GetOutputHistogram::Request::toJson(rapidjson::Document& doc) const
+void GetOutputHistogram::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
- INSERT_INTO_JSON_OBJECT(val, doc, min_count, min_count);
- INSERT_INTO_JSON_OBJECT(val, doc, max_count, max_count);
- INSERT_INTO_JSON_OBJECT(val, doc, unlocked, unlocked);
- INSERT_INTO_JSON_OBJECT(val, doc, recent_cutoff, recent_cutoff);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, amounts, amounts);
+ INSERT_INTO_JSON_OBJECT(dest, min_count, min_count);
+ INSERT_INTO_JSON_OBJECT(dest, max_count, max_count);
+ INSERT_INTO_JSON_OBJECT(dest, unlocked, unlocked);
+ INSERT_INTO_JSON_OBJECT(dest, recent_cutoff, recent_cutoff);
}
-void GetOutputHistogram::Request::fromJson(rapidjson::Value& val)
+void GetOutputHistogram::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, amounts, amounts);
GET_FROM_JSON_OBJECT(val, min_count, min_count);
@@ -710,100 +506,74 @@ void GetOutputHistogram::Request::fromJson(rapidjson::Value& val)
GET_FROM_JSON_OBJECT(val, recent_cutoff, recent_cutoff);
}
-rapidjson::Value GetOutputHistogram::Response::toJson(rapidjson::Document& doc) const
+void GetOutputHistogram::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, histogram, histogram);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, histogram, histogram);
}
-void GetOutputHistogram::Response::fromJson(rapidjson::Value& val)
+void GetOutputHistogram::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, histogram, histogram);
}
-rapidjson::Value GetOutputKeys::Request::toJson(rapidjson::Document& doc) const
+void GetOutputKeys::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, outputs, outputs);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, outputs, outputs);
}
-void GetOutputKeys::Request::fromJson(rapidjson::Value& val)
+void GetOutputKeys::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, outputs, outputs);
}
-rapidjson::Value GetOutputKeys::Response::toJson(rapidjson::Document& doc) const
+void GetOutputKeys::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, keys, keys);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, keys, keys);
}
-void GetOutputKeys::Response::fromJson(rapidjson::Value& val)
+void GetOutputKeys::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, keys, keys);
}
-rapidjson::Value GetRPCVersion::Request::toJson(rapidjson::Document& doc) const
-{
- return Message::toJson(doc);
-}
+void GetRPCVersion::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+{}
-void GetRPCVersion::Request::fromJson(rapidjson::Value& val)
+void GetRPCVersion::Request::fromJson(const rapidjson::Value& val)
{
}
-rapidjson::Value GetRPCVersion::Response::toJson(rapidjson::Document& doc) const
+void GetRPCVersion::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, version, version);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, version, version);
}
-void GetRPCVersion::Response::fromJson(rapidjson::Value& val)
+void GetRPCVersion::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, version, version);
}
-rapidjson::Value GetFeeEstimate::Request::toJson(rapidjson::Document& doc) const
+void GetFeeEstimate::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, num_grace_blocks, num_grace_blocks);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, num_grace_blocks, num_grace_blocks);
}
-void GetFeeEstimate::Request::fromJson(rapidjson::Value& val)
+void GetFeeEstimate::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, num_grace_blocks, num_grace_blocks);
}
-rapidjson::Value GetFeeEstimate::Response::toJson(rapidjson::Document& doc) const
+void GetFeeEstimate::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, estimated_base_fee, estimated_base_fee);
- INSERT_INTO_JSON_OBJECT(val, doc, fee_mask, fee_mask);
- INSERT_INTO_JSON_OBJECT(val, doc, size_scale, size_scale);
- INSERT_INTO_JSON_OBJECT(val, doc, hard_fork_version, hard_fork_version);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, estimated_base_fee, estimated_base_fee);
+ INSERT_INTO_JSON_OBJECT(dest, fee_mask, fee_mask);
+ INSERT_INTO_JSON_OBJECT(dest, size_scale, size_scale);
+ INSERT_INTO_JSON_OBJECT(dest, hard_fork_version, hard_fork_version);
}
-void GetFeeEstimate::Response::fromJson(rapidjson::Value& val)
+void GetFeeEstimate::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, estimated_base_fee, estimated_base_fee);
GET_FROM_JSON_OBJECT(val, fee_mask, fee_mask);
@@ -811,19 +581,15 @@ void GetFeeEstimate::Response::fromJson(rapidjson::Value& val)
GET_FROM_JSON_OBJECT(val, hard_fork_version, hard_fork_version);
}
-rapidjson::Value GetOutputDistribution::Request::toJson(rapidjson::Document& doc) const
+void GetOutputDistribution::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
- INSERT_INTO_JSON_OBJECT(val, doc, from_height, from_height);
- INSERT_INTO_JSON_OBJECT(val, doc, to_height, to_height);
- INSERT_INTO_JSON_OBJECT(val, doc, cumulative, cumulative);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, amounts, amounts);
+ INSERT_INTO_JSON_OBJECT(dest, from_height, from_height);
+ INSERT_INTO_JSON_OBJECT(dest, to_height, to_height);
+ INSERT_INTO_JSON_OBJECT(dest, cumulative, cumulative);
}
-void GetOutputDistribution::Request::fromJson(rapidjson::Value& val)
+void GetOutputDistribution::Request::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, amounts, amounts);
GET_FROM_JSON_OBJECT(val, from_height, from_height);
@@ -831,17 +597,13 @@ void GetOutputDistribution::Request::fromJson(rapidjson::Value& val)
GET_FROM_JSON_OBJECT(val, cumulative, cumulative);
}
-rapidjson::Value GetOutputDistribution::Response::toJson(rapidjson::Document& doc) const
+void GetOutputDistribution::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- auto val = Message::toJson(doc);
-
- INSERT_INTO_JSON_OBJECT(val, doc, status, status);
- INSERT_INTO_JSON_OBJECT(val, doc, distributions, distributions);
-
- return val;
+ INSERT_INTO_JSON_OBJECT(dest, status, status);
+ INSERT_INTO_JSON_OBJECT(dest, distributions, distributions);
}
-void GetOutputDistribution::Response::fromJson(rapidjson::Value& val)
+void GetOutputDistribution::Response::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, status, status);
GET_FROM_JSON_OBJECT(val, distributions, distributions);
diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h
index c0d9aed0a..bb5059cdc 100644
--- a/src/rpc/daemon_messages.h
+++ b/src/rpc/daemon_messages.h
@@ -28,6 +28,8 @@
#pragma once
+#include <rapidjson/stringbuffer.h>
+#include <rapidjson/writer.h>
#include <unordered_map>
#include <vector>
@@ -40,26 +42,25 @@
#define BEGIN_RPC_MESSAGE_CLASS(classname) \
class classname \
{ \
- public: \
- static const char* const name;
+ public:
#define BEGIN_RPC_MESSAGE_REQUEST \
- class Request : public Message \
+ class Request final : public Message \
{ \
public: \
Request() { } \
~Request() { } \
- rapidjson::Value toJson(rapidjson::Document& doc) const; \
- void fromJson(rapidjson::Value& val);
+ void doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const override final; \
+ void fromJson(const rapidjson::Value& val) override final;
#define BEGIN_RPC_MESSAGE_RESPONSE \
- class Response : public Message \
+ class Response final : public Message \
{ \
public: \
Response() { } \
~Response() { } \
- rapidjson::Value toJson(rapidjson::Document& doc) const; \
- void fromJson(rapidjson::Value& val);
+ void doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const override final; \
+ void fromJson(const rapidjson::Value& val) override final;
#define END_RPC_MESSAGE_REQUEST };
#define END_RPC_MESSAGE_RESPONSE };
diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp
index 158b58005..a3df7fb56 100644
--- a/src/rpc/message.cpp
+++ b/src/rpc/message.cpp
@@ -27,12 +27,10 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "message.h"
+
#include "daemon_rpc_version.h"
#include "serialization/json_object.h"
-#include "rapidjson/writer.h"
-#include "rapidjson/stringbuffer.h"
-
namespace cryptonote
{
@@ -54,60 +52,23 @@ constexpr const char params_field[] = "params";
constexpr const char result_field[] = "result";
}
-rapidjson::Value Message::toJson(rapidjson::Document& doc) const
+void Message::toJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
{
- rapidjson::Value val(rapidjson::kObjectType);
-
- auto& al = doc.GetAllocator();
-
- val.AddMember("status", rapidjson::StringRef(status.c_str()), al);
- val.AddMember("error_details", rapidjson::StringRef(error_details.c_str()), al);
- INSERT_INTO_JSON_OBJECT(val, doc, rpc_version, DAEMON_RPC_VERSION_ZMQ);
-
- return val;
+ dest.StartObject();
+ INSERT_INTO_JSON_OBJECT(dest, status, status);
+ INSERT_INTO_JSON_OBJECT(dest, error_details, error_details);
+ INSERT_INTO_JSON_OBJECT(dest, rpc_version, DAEMON_RPC_VERSION_ZMQ);
+ doToJson(dest);
+ dest.EndObject();
}
-void Message::fromJson(rapidjson::Value& val)
+void Message::fromJson(const rapidjson::Value& val)
{
GET_FROM_JSON_OBJECT(val, status, status);
GET_FROM_JSON_OBJECT(val, error_details, error_details);
GET_FROM_JSON_OBJECT(val, rpc_version, rpc_version);
}
-
-FullMessage::FullMessage(const std::string& request, Message* message)
-{
- doc.SetObject();
-
- doc.AddMember(method_field, rapidjson::StringRef(request.c_str()), doc.GetAllocator());
- doc.AddMember(params_field, message->toJson(doc), doc.GetAllocator());
-
- // required by JSON-RPC 2.0 spec
- doc.AddMember("jsonrpc", rapidjson::Value("2.0"), doc.GetAllocator());
-}
-
-FullMessage::FullMessage(Message* message)
-{
- doc.SetObject();
-
- // required by JSON-RPC 2.0 spec
- doc.AddMember("jsonrpc", "2.0", doc.GetAllocator());
-
- if (message->status == Message::STATUS_OK)
- {
- doc.AddMember(result_field, message->toJson(doc), doc.GetAllocator());
- }
- else
- {
- cryptonote::rpc::error err;
-
- err.error_str = message->status;
- err.message = message->error_details;
-
- INSERT_INTO_JSON_OBJECT(doc, doc, error, err);
- }
-}
-
FullMessage::FullMessage(const std::string& json_string, bool request)
{
doc.Parse(json_string.c_str());
@@ -132,30 +93,13 @@ FullMessage::FullMessage(const std::string& json_string, bool request)
}
}
-std::string FullMessage::getJson()
-{
-
- if (!doc.HasMember(id_field))
- {
- doc.AddMember(id_field, rapidjson::Value("unused"), doc.GetAllocator());
- }
-
- rapidjson::StringBuffer buf;
-
- rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
-
- doc.Accept(writer);
-
- return std::string(buf.GetString(), buf.GetSize());
-}
-
std::string FullMessage::getRequestType() const
{
OBJECT_HAS_MEMBER_OR_THROW(doc, method_field)
return doc[method_field].GetString();
}
-rapidjson::Value& FullMessage::getMessage()
+const rapidjson::Value& FullMessage::getMessage() const
{
if (doc.HasMember(params_field))
{
@@ -174,30 +118,15 @@ rapidjson::Value& FullMessage::getMessage()
rapidjson::Value FullMessage::getMessageCopy()
{
- rapidjson::Value& val = getMessage();
-
- return rapidjson::Value(val, doc.GetAllocator());
+ return rapidjson::Value(getMessage(), doc.GetAllocator());
}
-rapidjson::Value& FullMessage::getID()
+const rapidjson::Value& FullMessage::getID() const
{
OBJECT_HAS_MEMBER_OR_THROW(doc, id_field)
return doc[id_field];
}
-void FullMessage::setID(rapidjson::Value& id)
-{
- auto itr = doc.FindMember(id_field);
- if (itr != doc.MemberEnd())
- {
- itr->value = id;
- }
- else
- {
- doc.AddMember(id_field, id, doc.GetAllocator());
- }
-}
-
cryptonote::rpc::error FullMessage::getError()
{
cryptonote::rpc::error err;
@@ -211,82 +140,89 @@ cryptonote::rpc::error FullMessage::getError()
return err;
}
-FullMessage FullMessage::requestMessage(const std::string& request, Message* message)
+std::string FullMessage::getRequest(const std::string& request, const Message& message, const unsigned id)
{
- return FullMessage(request, message);
-}
+ rapidjson::StringBuffer buffer;
+ {
+ rapidjson::Writer<rapidjson::StringBuffer> dest{buffer};
-FullMessage FullMessage::requestMessage(const std::string& request, Message* message, rapidjson::Value& id)
-{
- auto mes = requestMessage(request, message);
- mes.setID(id);
- return mes;
-}
+ dest.StartObject();
+ INSERT_INTO_JSON_OBJECT(dest, jsonrpc, (boost::string_ref{"2.0", 3}));
-FullMessage FullMessage::responseMessage(Message* message)
-{
- return FullMessage(message);
-}
+ dest.Key(id_field);
+ json::toJsonValue(dest, id);
-FullMessage FullMessage::responseMessage(Message* message, rapidjson::Value& id)
-{
- auto mes = responseMessage(message);
- mes.setID(id);
- return mes;
+ dest.Key(method_field);
+ json::toJsonValue(dest, request);
+
+ dest.Key(params_field);
+ message.toJson(dest);
+
+ dest.EndObject();
+
+ if (!dest.IsComplete())
+ throw std::logic_error{"Invalid JSON tree generated"};
+ }
+ return std::string{buffer.GetString(), buffer.GetSize()};
}
-FullMessage* FullMessage::timeoutMessage()
+
+std::string FullMessage::getResponse(const Message& message, const rapidjson::Value& id)
{
- auto *full_message = new FullMessage();
+ rapidjson::StringBuffer buffer;
+ {
+ rapidjson::Writer<rapidjson::StringBuffer> dest{buffer};
- auto& doc = full_message->doc;
- auto& al = full_message->doc.GetAllocator();
+ dest.StartObject();
+ INSERT_INTO_JSON_OBJECT(dest, jsonrpc, (boost::string_ref{"2.0", 3}));
- doc.SetObject();
+ dest.Key(id_field);
+ json::toJsonValue(dest, id);
- // required by JSON-RPC 2.0 spec
- doc.AddMember("jsonrpc", "2.0", al);
+ if (message.status == Message::STATUS_OK)
+ {
+ dest.Key(result_field);
+ message.toJson(dest);
+ }
+ else
+ {
+ cryptonote::rpc::error err;
- cryptonote::rpc::error err;
+ err.error_str = message.status;
+ err.message = message.error_details;
- err.error_str = "RPC request timed out.";
- INSERT_INTO_JSON_OBJECT(doc, doc, err, err);
+ INSERT_INTO_JSON_OBJECT(dest, error, err);
+ }
+ dest.EndObject();
- return full_message;
+ if (!dest.IsComplete())
+ throw std::logic_error{"Invalid JSON tree generated"};
+ }
+ return std::string{buffer.GetString(), buffer.GetSize()};
}
// convenience functions for bad input
std::string BAD_REQUEST(const std::string& request)
{
- Message fail;
- fail.status = Message::STATUS_BAD_REQUEST;
- fail.error_details = std::string("\"") + request + "\" is not a valid request.";
-
- FullMessage fail_response = FullMessage::responseMessage(&fail);
-
- return fail_response.getJson();
+ rapidjson::Value invalid;
+ return BAD_REQUEST(request, invalid);
}
-std::string BAD_REQUEST(const std::string& request, rapidjson::Value& id)
+std::string BAD_REQUEST(const std::string& request, const rapidjson::Value& id)
{
Message fail;
fail.status = Message::STATUS_BAD_REQUEST;
fail.error_details = std::string("\"") + request + "\" is not a valid request.";
-
- FullMessage fail_response = FullMessage::responseMessage(&fail, id);
-
- return fail_response.getJson();
+ return FullMessage::getResponse(fail, id);
}
std::string BAD_JSON(const std::string& error_details)
{
+ rapidjson::Value invalid;
Message fail;
fail.status = Message::STATUS_BAD_JSON;
fail.error_details = error_details;
-
- FullMessage fail_response = FullMessage::responseMessage(&fail);
-
- return fail_response.getJson();
+ return FullMessage::getResponse(fail, invalid);
}
diff --git a/src/rpc/message.h b/src/rpc/message.h
index 2b7b61ab3..4cbc84fe4 100644
--- a/src/rpc/message.h
+++ b/src/rpc/message.h
@@ -28,27 +28,12 @@
#pragma once
-#include "rapidjson/document.h"
-#include "rpc/message_data_structs.h"
+#include <rapidjson/document.h>
+#include <rapidjson/stringbuffer.h>
+#include <rapidjson/writer.h>
#include <string>
-/* I normally hate using macros, but in this case it would be untenably
- * verbose to not use a macro. This macro saves the trouble of explicitly
- * writing the below if block for every single RPC call.
- */
-#define REQ_RESP_TYPES_MACRO( runtime_str, type, reqjson, resp_message_ptr, handler) \
- \
- if (runtime_str == type::name) \
- { \
- type::Request reqvar; \
- type::Response *respvar = new type::Response(); \
- \
- reqvar.fromJson(reqjson); \
- \
- handler(reqvar, *respvar); \
- \
- resp_message_ptr = respvar; \
- }
+#include "rpc/message_data_structs.h"
namespace cryptonote
{
@@ -58,6 +43,9 @@ namespace rpc
class Message
{
+ virtual void doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
+ {}
+
public:
static const char* STATUS_OK;
static const char* STATUS_RETRY;
@@ -69,9 +57,9 @@ namespace rpc
virtual ~Message() { }
- virtual rapidjson::Value toJson(rapidjson::Document& doc) const;
+ void toJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const;
- virtual void fromJson(rapidjson::Value& val);
+ virtual void fromJson(const rapidjson::Value& val);
std::string status;
std::string error_details;
@@ -87,27 +75,18 @@ namespace rpc
FullMessage(const std::string& json_string, bool request=false);
- std::string getJson();
-
std::string getRequestType() const;
- rapidjson::Value& getMessage();
+ const rapidjson::Value& getMessage() const;
rapidjson::Value getMessageCopy();
- rapidjson::Value& getID();
-
- void setID(rapidjson::Value& id);
+ const rapidjson::Value& getID() const;
cryptonote::rpc::error getError();
- static FullMessage requestMessage(const std::string& request, Message* message);
- static FullMessage requestMessage(const std::string& request, Message* message, rapidjson::Value& id);
-
- static FullMessage responseMessage(Message* message);
- static FullMessage responseMessage(Message* message, rapidjson::Value& id);
-
- static FullMessage* timeoutMessage();
+ static std::string getRequest(const std::string& request, const Message& message, unsigned id);
+ static std::string getResponse(const Message& message, const rapidjson::Value& id);
private:
FullMessage() = default;
@@ -121,7 +100,7 @@ namespace rpc
// convenience functions for bad input
std::string BAD_REQUEST(const std::string& request);
- std::string BAD_REQUEST(const std::string& request, rapidjson::Value& id);
+ std::string BAD_REQUEST(const std::string& request, const rapidjson::Value& id);
std::string BAD_JSON(const std::string& error_details);
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index 0eaa0ef0e..dcb804d3e 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -103,6 +103,7 @@ namespace cryptonote
, rpc_ssl_allowed_fingerprints({"rpc-ssl-allowed-fingerprints", rpc_args::tr("List of certificate fingerprints to allow")})
, rpc_ssl_allow_chained({"rpc-ssl-allow-chained", rpc_args::tr("Allow user (via --rpc-ssl-certificates) chain certificates"), false})
, rpc_ssl_allow_any_cert({"rpc-ssl-allow-any-cert", rpc_args::tr("Allow any peer certificate"), false})
+ , disable_rpc_ban({"disable-rpc-ban", rpc_args::tr("Do not ban hosts on RPC errors"), false, false})
{}
const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); }
@@ -123,6 +124,7 @@ namespace cryptonote
command_line::add_arg(desc, arg.rpc_ssl_ca_certificates);
command_line::add_arg(desc, arg.rpc_ssl_allowed_fingerprints);
command_line::add_arg(desc, arg.rpc_ssl_allow_chained);
+ command_line::add_arg(desc, arg.disable_rpc_ban);
if (any_cert_option)
command_line::add_arg(desc, arg.rpc_ssl_allow_any_cert);
}
@@ -136,6 +138,7 @@ namespace cryptonote
config.bind_ipv6_address = command_line::get_arg(vm, arg.rpc_bind_ipv6_address);
config.use_ipv6 = command_line::get_arg(vm, arg.rpc_use_ipv6);
config.require_ipv4 = !command_line::get_arg(vm, arg.rpc_ignore_ipv4);
+ config.disable_rpc_ban = command_line::get_arg(vm, arg.disable_rpc_ban);
if (!config.bind_ip.empty())
{
// always parse IP here for error consistency
diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h
index bdb9c70d5..ac6eb2744 100644
--- a/src/rpc/rpc_args.h
+++ b/src/rpc/rpc_args.h
@@ -65,6 +65,7 @@ namespace cryptonote
const command_line::arg_descriptor<std::vector<std::string>> rpc_ssl_allowed_fingerprints;
const command_line::arg_descriptor<bool> rpc_ssl_allow_chained;
const command_line::arg_descriptor<bool> rpc_ssl_allow_any_cert;
+ const command_line::arg_descriptor<bool> disable_rpc_ban;
};
// `allow_any_cert` bool toggles `--rpc-ssl-allow-any-cert` configuration
@@ -85,5 +86,6 @@ namespace cryptonote
std::vector<std::string> access_control_origins;
boost::optional<tools::login> login; // currently `boost::none` if unspecified by user
epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
+ bool disable_rpc_ban = false;
};
}
diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp
index 0637db728..b363c27b2 100644
--- a/src/rpc/rpc_payment.cpp
+++ b/src/rpc/rpc_payment.cpp
@@ -59,6 +59,10 @@
namespace cryptonote
{
rpc_payment::client_info::client_info():
+ previous_seed_height(0),
+ seed_height(0),
+ previous_seed_hash(crypto::null_hash),
+ seed_hash(crypto::null_hash),
cookie(0),
top(crypto::null_hash),
previous_top(crypto::null_hash),
diff --git a/src/serialization/CMakeLists.txt b/src/serialization/CMakeLists.txt
index 28b775a37..0b231f156 100644
--- a/src/serialization/CMakeLists.txt
+++ b/src/serialization/CMakeLists.txt
@@ -44,6 +44,7 @@ target_link_libraries(serialization
LINK_PRIVATE
cryptonote_core
cryptonote_protocol
+ epee
${Boost_CHRONO_LIBRARY}
${Boost_REGEX_LIBRARY}
${Boost_SYSTEM_LIBRARY}
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index ea67209dc..e98ba0483 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -109,9 +109,14 @@ namespace
}
}
-void toJsonValue(rapidjson::Document& doc, const std::string& i, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rapidjson::Value& src)
{
- val = rapidjson::Value(i.c_str(), doc.GetAllocator());
+ src.Accept(dest);
+}
+
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const boost::string_ref i)
+{
+ dest.String(i.data(), i.size());
}
void fromJsonValue(const rapidjson::Value& val, std::string& str)
@@ -124,9 +129,9 @@ void fromJsonValue(const rapidjson::Value& val, std::string& str)
str = val.GetString();
}
-void toJsonValue(rapidjson::Document& doc, bool i, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, bool i)
{
- val.SetBool(i);
+ dest.Bool(i);
}
void fromJsonValue(const rapidjson::Value& val, bool& b)
@@ -163,9 +168,9 @@ void fromJsonValue(const rapidjson::Value& val, short& i)
to_int(val, i);
}
-void toJsonValue(rapidjson::Document& doc, const unsigned int i, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned int i)
{
- val = rapidjson::Value(i);
+ dest.Uint(i);
}
void fromJsonValue(const rapidjson::Value& val, unsigned int& i)
@@ -173,9 +178,9 @@ void fromJsonValue(const rapidjson::Value& val, unsigned int& i)
to_uint(val, i);
}
-void toJsonValue(rapidjson::Document& doc, const int i, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const int i)
{
- val = rapidjson::Value(i);
+ dest.Int(i);
}
void fromJsonValue(const rapidjson::Value& val, int& i)
@@ -183,10 +188,10 @@ void fromJsonValue(const rapidjson::Value& val, int& i)
to_int(val, i);
}
-void toJsonValue(rapidjson::Document& doc, const unsigned long long i, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned long long i)
{
- static_assert(!precision_loss<unsigned long long, std::uint64_t>(), "precision loss");
- val = rapidjson::Value(std::uint64_t(i));
+ static_assert(std::numeric_limits<unsigned long long>::max() <= std::numeric_limits<std::uint64_t>::max(), "bad uint64 conversion");
+ dest.Uint64(i);
}
void fromJsonValue(const rapidjson::Value& val, unsigned long long& i)
@@ -194,10 +199,11 @@ void fromJsonValue(const rapidjson::Value& val, unsigned long long& i)
to_uint64(val, i);
}
-void toJsonValue(rapidjson::Document& doc, const long long i, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const long long i)
{
- static_assert(!precision_loss<long long, std::int64_t>(), "precision loss");
- val = rapidjson::Value(std::int64_t(i));
+ static_assert(std::numeric_limits<std::uint64_t>::min() <= std::numeric_limits<long long>::min(), "bad int64 conversion");
+ static_assert(std::numeric_limits<long long>::max() <= std::numeric_limits<std::uint64_t>::max(), "bad int64 conversion");
+ dest.Int64(i);
}
void fromJsonValue(const rapidjson::Value& val, long long& i)
@@ -215,17 +221,19 @@ void fromJsonValue(const rapidjson::Value& val, long& i)
to_int64(val, i);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::transaction& tx)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, version, tx.version);
- INSERT_INTO_JSON_OBJECT(val, doc, unlock_time, tx.unlock_time);
- INSERT_INTO_JSON_OBJECT(val, doc, inputs, tx.vin);
- INSERT_INTO_JSON_OBJECT(val, doc, outputs, tx.vout);
- INSERT_INTO_JSON_OBJECT(val, doc, extra, tx.extra);
- INSERT_INTO_JSON_OBJECT(val, doc, signatures, tx.signatures);
- INSERT_INTO_JSON_OBJECT(val, doc, ringct, tx.rct_signatures);
+ INSERT_INTO_JSON_OBJECT(dest, version, tx.version);
+ INSERT_INTO_JSON_OBJECT(dest, unlock_time, tx.unlock_time);
+ INSERT_INTO_JSON_OBJECT(dest, inputs, tx.vin);
+ INSERT_INTO_JSON_OBJECT(dest, outputs, tx.vout);
+ INSERT_INTO_JSON_OBJECT(dest, extra, tx.extra);
+ INSERT_INTO_JSON_OBJECT(dest, signatures, tx.signatures);
+ INSERT_INTO_JSON_OBJECT(dest, ringct, tx.rct_signatures);
+
+ dest.EndObject();
}
@@ -245,17 +253,19 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx)
GET_FROM_JSON_OBJECT(val, tx.rct_signatures, ringct);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block& b)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, major_version, b.major_version);
+ INSERT_INTO_JSON_OBJECT(dest, minor_version, b.minor_version);
+ INSERT_INTO_JSON_OBJECT(dest, timestamp, b.timestamp);
+ INSERT_INTO_JSON_OBJECT(dest, prev_id, b.prev_id);
+ INSERT_INTO_JSON_OBJECT(dest, nonce, b.nonce);
+ INSERT_INTO_JSON_OBJECT(dest, miner_tx, b.miner_tx);
+ INSERT_INTO_JSON_OBJECT(dest, tx_hashes, b.tx_hashes);
- INSERT_INTO_JSON_OBJECT(val, doc, major_version, b.major_version);
- INSERT_INTO_JSON_OBJECT(val, doc, minor_version, b.minor_version);
- INSERT_INTO_JSON_OBJECT(val, doc, timestamp, b.timestamp);
- INSERT_INTO_JSON_OBJECT(val, doc, prev_id, b.prev_id);
- INSERT_INTO_JSON_OBJECT(val, doc, nonce, b.nonce);
- INSERT_INTO_JSON_OBJECT(val, doc, miner_tx, b.miner_tx);
- INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, b.tx_hashes);
+ dest.EndObject();
}
@@ -275,35 +285,34 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b)
GET_FROM_JSON_OBJECT(val, b.tx_hashes, tx_hashes);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_v& txin)
{
- val.SetObject();
-
+ dest.StartObject();
struct add_input
{
using result_type = void;
- rapidjson::Document& doc;
- rapidjson::Value& val;
+ rapidjson::Writer<rapidjson::StringBuffer>& dest;
void operator()(cryptonote::txin_to_key const& input) const
{
- INSERT_INTO_JSON_OBJECT(val, doc, to_key, input);
+ INSERT_INTO_JSON_OBJECT(dest, to_key, input);
}
void operator()(cryptonote::txin_gen const& input) const
{
- INSERT_INTO_JSON_OBJECT(val, doc, gen, input);
+ INSERT_INTO_JSON_OBJECT(dest, gen, input);
}
void operator()(cryptonote::txin_to_script const& input) const
{
- INSERT_INTO_JSON_OBJECT(val, doc, to_script, input);
+ INSERT_INTO_JSON_OBJECT(dest, to_script, input);
}
void operator()(cryptonote::txin_to_scripthash const& input) const
{
- INSERT_INTO_JSON_OBJECT(val, doc, to_scripthash, input);
+ INSERT_INTO_JSON_OBJECT(dest, to_scripthash, input);
}
};
- boost::apply_visitor(add_input{doc, val}, txin);
+ boost::apply_visitor(add_input{dest}, txin);
+ dest.EndObject();
}
@@ -348,13 +357,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin)
}
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_gen& txin, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_gen& txin)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, height, txin.height);
-}
+ INSERT_INTO_JSON_OBJECT(dest, height, txin.height);
+ dest.EndObject();
+}
void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin)
{
@@ -366,13 +376,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin)
GET_FROM_JSON_OBJECT(val, txin.height, height);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_script& txin, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_script& txin)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, prev, txin.prev);
+ INSERT_INTO_JSON_OBJECT(dest, prevout, txin.prevout);
+ INSERT_INTO_JSON_OBJECT(dest, sigset, txin.sigset);
- INSERT_INTO_JSON_OBJECT(val, doc, prev, txin.prev);
- INSERT_INTO_JSON_OBJECT(val, doc, prevout, txin.prevout);
- INSERT_INTO_JSON_OBJECT(val, doc, sigset, txin.sigset);
+ dest.EndObject();
}
@@ -388,14 +400,17 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_script& txin
GET_FROM_JSON_OBJECT(val, txin.sigset, sigset);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_scripthash& txin, rapidjson::Value& val)
+
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_scripthash& txin)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, prev, txin.prev);
+ INSERT_INTO_JSON_OBJECT(dest, prevout, txin.prevout);
+ INSERT_INTO_JSON_OBJECT(dest, script, txin.script);
+ INSERT_INTO_JSON_OBJECT(dest, sigset, txin.sigset);
- INSERT_INTO_JSON_OBJECT(val, doc, prev, txin.prev);
- INSERT_INTO_JSON_OBJECT(val, doc, prevout, txin.prevout);
- INSERT_INTO_JSON_OBJECT(val, doc, script, txin.script);
- INSERT_INTO_JSON_OBJECT(val, doc, sigset, txin.sigset);
+ dest.EndObject();
}
@@ -412,15 +427,16 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash&
GET_FROM_JSON_OBJECT(val, txin.sigset, sigset);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_key& txin, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_key& txin)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, amount, txin.amount);
- INSERT_INTO_JSON_OBJECT(val, doc, key_offsets, txin.key_offsets);
- INSERT_INTO_JSON_OBJECT(val, doc, key_image, txin.k_image);
-}
+ INSERT_INTO_JSON_OBJECT(dest, amount, txin.amount);
+ INSERT_INTO_JSON_OBJECT(dest, key_offsets, txin.key_offsets);
+ INSERT_INTO_JSON_OBJECT(dest, key_image, txin.k_image);
+ dest.EndObject();
+}
void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin)
{
@@ -434,14 +450,16 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin)
GET_FROM_JSON_OBJECT(val, txin.k_image, key_image);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_script& txout, rapidjson::Value& val)
+
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_script& txout)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, keys, txout.keys);
- INSERT_INTO_JSON_OBJECT(val, doc, script, txout.script);
-}
+ INSERT_INTO_JSON_OBJECT(dest, keys, txout.keys);
+ INSERT_INTO_JSON_OBJECT(dest, script, txout.script);
+ dest.EndObject();
+}
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txout)
{
@@ -454,13 +472,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txo
GET_FROM_JSON_OBJECT(val, txout.script, script);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_scripthash& txout, rapidjson::Value& val)
+
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_scripthash& txout)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, hash, txout.hash);
-}
+ INSERT_INTO_JSON_OBJECT(dest, hash, txout.hash);
+ dest.EndObject();
+}
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& txout)
{
@@ -472,13 +492,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash&
GET_FROM_JSON_OBJECT(val, txout.hash, hash);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_key& txout, rapidjson::Value& val)
+
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_key& txout)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, key, txout.key);
-}
+ INSERT_INTO_JSON_OBJECT(dest, key, txout.key);
+ dest.EndObject();
+}
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout)
{
@@ -490,33 +512,32 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout)
GET_FROM_JSON_OBJECT(val, txout.key, key);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_out& txout, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_out& txout)
{
- val.SetObject();
-
- INSERT_INTO_JSON_OBJECT(val, doc, amount, txout.amount);
+ dest.StartObject();
+ INSERT_INTO_JSON_OBJECT(dest, amount, txout.amount);
struct add_output
{
using result_type = void;
- rapidjson::Document& doc;
- rapidjson::Value& val;
+ rapidjson::Writer<rapidjson::StringBuffer>& dest;
void operator()(cryptonote::txout_to_key const& output) const
{
- INSERT_INTO_JSON_OBJECT(val, doc, to_key, output);
+ INSERT_INTO_JSON_OBJECT(dest, to_key, output);
}
void operator()(cryptonote::txout_to_script const& output) const
{
- INSERT_INTO_JSON_OBJECT(val, doc, to_script, output);
+ INSERT_INTO_JSON_OBJECT(dest, to_script, output);
}
void operator()(cryptonote::txout_to_scripthash const& output) const
{
- INSERT_INTO_JSON_OBJECT(val, doc, to_scripthash, output);
+ INSERT_INTO_JSON_OBJECT(dest, to_scripthash, output);
}
};
- boost::apply_visitor(add_output{doc, val}, txout.target);
+ boost::apply_visitor(add_output{dest}, txout.target);
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout)
@@ -559,37 +580,39 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout)
}
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::connection_info& info)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, incoming, info.incoming);
+ INSERT_INTO_JSON_OBJECT(dest, localhost, info.localhost);
+ INSERT_INTO_JSON_OBJECT(dest, local_ip, info.local_ip);
+ INSERT_INTO_JSON_OBJECT(dest, address_type, info.address_type);
- INSERT_INTO_JSON_OBJECT(val, doc, incoming, info.incoming);
- INSERT_INTO_JSON_OBJECT(val, doc, localhost, info.localhost);
- INSERT_INTO_JSON_OBJECT(val, doc, local_ip, info.local_ip);
- INSERT_INTO_JSON_OBJECT(val, doc, address_type, info.address_type);
+ INSERT_INTO_JSON_OBJECT(dest, ip, info.ip);
+ INSERT_INTO_JSON_OBJECT(dest, port, info.port);
+ INSERT_INTO_JSON_OBJECT(dest, rpc_port, info.rpc_port);
+ INSERT_INTO_JSON_OBJECT(dest, rpc_credits_per_hash, info.rpc_credits_per_hash);
- INSERT_INTO_JSON_OBJECT(val, doc, ip, info.ip);
- INSERT_INTO_JSON_OBJECT(val, doc, port, info.port);
- INSERT_INTO_JSON_OBJECT(val, doc, rpc_port, info.rpc_port);
- INSERT_INTO_JSON_OBJECT(val, doc, rpc_credits_per_hash, info.rpc_credits_per_hash);
+ INSERT_INTO_JSON_OBJECT(dest, peer_id, info.peer_id);
- INSERT_INTO_JSON_OBJECT(val, doc, peer_id, info.peer_id);
+ INSERT_INTO_JSON_OBJECT(dest, recv_count, info.recv_count);
+ INSERT_INTO_JSON_OBJECT(dest, recv_idle_time, info.recv_idle_time);
- INSERT_INTO_JSON_OBJECT(val, doc, recv_count, info.recv_count);
- INSERT_INTO_JSON_OBJECT(val, doc, recv_idle_time, info.recv_idle_time);
+ INSERT_INTO_JSON_OBJECT(dest, send_count, info.send_count);
+ INSERT_INTO_JSON_OBJECT(dest, send_idle_time, info.send_idle_time);
- INSERT_INTO_JSON_OBJECT(val, doc, send_count, info.send_count);
- INSERT_INTO_JSON_OBJECT(val, doc, send_idle_time, info.send_idle_time);
+ INSERT_INTO_JSON_OBJECT(dest, state, info.state);
- INSERT_INTO_JSON_OBJECT(val, doc, state, info.state);
+ INSERT_INTO_JSON_OBJECT(dest, live_time, info.live_time);
- INSERT_INTO_JSON_OBJECT(val, doc, live_time, info.live_time);
+ INSERT_INTO_JSON_OBJECT(dest, avg_download, info.avg_download);
+ INSERT_INTO_JSON_OBJECT(dest, current_download, info.current_download);
- INSERT_INTO_JSON_OBJECT(val, doc, avg_download, info.avg_download);
- INSERT_INTO_JSON_OBJECT(val, doc, current_download, info.current_download);
+ INSERT_INTO_JSON_OBJECT(dest, avg_upload, info.avg_upload);
+ INSERT_INTO_JSON_OBJECT(dest, current_upload, info.current_upload);
- INSERT_INTO_JSON_OBJECT(val, doc, avg_upload, info.avg_upload);
- INSERT_INTO_JSON_OBJECT(val, doc, current_upload, info.current_upload);
+ dest.EndObject();
}
@@ -629,12 +652,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& inf
GET_FROM_JSON_OBJECT(val, info.current_upload, current_upload);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_blob_entry& tx, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_blob_entry& tx)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, blob, tx.blob);
+ INSERT_INTO_JSON_OBJECT(dest, prunable_hash, tx.prunable_hash);
- INSERT_INTO_JSON_OBJECT(val, doc, blob, tx.blob);
- INSERT_INTO_JSON_OBJECT(val, doc, prunable_hash, tx.prunable_hash);
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_blob_entry& tx)
@@ -648,12 +673,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_blob_entry& tx)
GET_FROM_JSON_OBJECT(val, tx.prunable_hash, prunable_hash);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block_complete_entry& blk)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block);
- INSERT_INTO_JSON_OBJECT(val, doc, transactions, blk.txs);
+ INSERT_INTO_JSON_OBJECT(dest, block, blk.block);
+ INSERT_INTO_JSON_OBJECT(dest, transactions, blk.txs);
+
+ dest.EndObject();
}
@@ -668,12 +695,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry
GET_FROM_JSON_OBJECT(val, blk.txs, transactions);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::block_with_transactions& blk, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::block_with_transactions& blk)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, block, blk.block);
+ INSERT_INTO_JSON_OBJECT(dest, transactions, blk.transactions);
- INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block);
- INSERT_INTO_JSON_OBJECT(val, doc, transactions, blk.transactions);
+ dest.EndObject();
}
@@ -688,13 +717,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::block_with_tran
GET_FROM_JSON_OBJECT(val, blk.transactions, transactions);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::transaction_info& tx_info, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::transaction_info& tx_info)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, height, tx_info.height);
+ INSERT_INTO_JSON_OBJECT(dest, in_pool, tx_info.in_pool);
+ INSERT_INTO_JSON_OBJECT(dest, transaction, tx_info.transaction);
- INSERT_INTO_JSON_OBJECT(val, doc, height, tx_info.height);
- INSERT_INTO_JSON_OBJECT(val, doc, in_pool, tx_info.in_pool);
- INSERT_INTO_JSON_OBJECT(val, doc, transaction, tx_info.transaction);
+ dest.EndObject();
}
@@ -710,12 +741,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::transaction_inf
GET_FROM_JSON_OBJECT(val, tx_info.transaction, transaction);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_and_amount_index& out, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_and_amount_index& out)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, amount_index, out.amount_index);
- INSERT_INTO_JSON_OBJECT(val, doc, key, out.key);
+ INSERT_INTO_JSON_OBJECT(dest, amount_index, out.amount_index);
+ INSERT_INTO_JSON_OBJECT(dest, key, out.key);
+
+ dest.EndObject();
}
@@ -730,12 +763,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_and_
GET_FROM_JSON_OBJECT(val, out.key, key);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::amount_with_random_outputs& out, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::amount_with_random_outputs& out)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, amount, out.amount);
+ INSERT_INTO_JSON_OBJECT(dest, outputs, out.outputs);
- INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount);
- INSERT_INTO_JSON_OBJECT(val, doc, outputs, out.outputs);
+ dest.EndObject();
}
@@ -750,17 +785,19 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::amount_with_ran
GET_FROM_JSON_OBJECT(val, out.outputs, outputs);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::peer& peer, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::peer& peer)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, id, peer.id);
+ INSERT_INTO_JSON_OBJECT(dest, ip, peer.ip);
+ INSERT_INTO_JSON_OBJECT(dest, port, peer.port);
+ INSERT_INTO_JSON_OBJECT(dest, rpc_port, peer.rpc_port);
+ INSERT_INTO_JSON_OBJECT(dest, rpc_credits_per_hash, peer.rpc_credits_per_hash);
+ INSERT_INTO_JSON_OBJECT(dest, last_seen, peer.last_seen);
+ INSERT_INTO_JSON_OBJECT(dest, pruning_seed, peer.pruning_seed);
- INSERT_INTO_JSON_OBJECT(val, doc, id, peer.id);
- INSERT_INTO_JSON_OBJECT(val, doc, ip, peer.ip);
- INSERT_INTO_JSON_OBJECT(val, doc, port, peer.port);
- INSERT_INTO_JSON_OBJECT(val, doc, rpc_port, peer.rpc_port);
- INSERT_INTO_JSON_OBJECT(val, doc, rpc_credits_per_hash, peer.rpc_credits_per_hash);
- INSERT_INTO_JSON_OBJECT(val, doc, last_seen, peer.last_seen);
- INSERT_INTO_JSON_OBJECT(val, doc, pruning_seed, peer.pruning_seed);
+ dest.EndObject();
}
@@ -780,25 +817,27 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer)
GET_FROM_JSON_OBJECT(val, peer.pruning_seed, pruning_seed);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::tx_in_pool& tx)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, tx, tx.tx);
- INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx.tx_hash);
- INSERT_INTO_JSON_OBJECT(val, doc, blob_size, tx.blob_size);
- INSERT_INTO_JSON_OBJECT(val, doc, weight, tx.weight);
- INSERT_INTO_JSON_OBJECT(val, doc, fee, tx.fee);
- INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_hash, tx.max_used_block_hash);
- INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_height, tx.max_used_block_height);
- INSERT_INTO_JSON_OBJECT(val, doc, kept_by_block, tx.kept_by_block);
- INSERT_INTO_JSON_OBJECT(val, doc, last_failed_block_hash, tx.last_failed_block_hash);
- INSERT_INTO_JSON_OBJECT(val, doc, last_failed_block_height, tx.last_failed_block_height);
- INSERT_INTO_JSON_OBJECT(val, doc, receive_time, tx.receive_time);
- INSERT_INTO_JSON_OBJECT(val, doc, last_relayed_time, tx.last_relayed_time);
- INSERT_INTO_JSON_OBJECT(val, doc, relayed, tx.relayed);
- INSERT_INTO_JSON_OBJECT(val, doc, do_not_relay, tx.do_not_relay);
- INSERT_INTO_JSON_OBJECT(val, doc, double_spend_seen, tx.double_spend_seen);
+ INSERT_INTO_JSON_OBJECT(dest, tx, tx.tx);
+ INSERT_INTO_JSON_OBJECT(dest, tx_hash, tx.tx_hash);
+ INSERT_INTO_JSON_OBJECT(dest, blob_size, tx.blob_size);
+ INSERT_INTO_JSON_OBJECT(dest, weight, tx.weight);
+ INSERT_INTO_JSON_OBJECT(dest, fee, tx.fee);
+ INSERT_INTO_JSON_OBJECT(dest, max_used_block_hash, tx.max_used_block_hash);
+ INSERT_INTO_JSON_OBJECT(dest, max_used_block_height, tx.max_used_block_height);
+ INSERT_INTO_JSON_OBJECT(dest, kept_by_block, tx.kept_by_block);
+ INSERT_INTO_JSON_OBJECT(dest, last_failed_block_hash, tx.last_failed_block_hash);
+ INSERT_INTO_JSON_OBJECT(dest, last_failed_block_height, tx.last_failed_block_height);
+ INSERT_INTO_JSON_OBJECT(dest, receive_time, tx.receive_time);
+ INSERT_INTO_JSON_OBJECT(dest, last_relayed_time, tx.last_relayed_time);
+ INSERT_INTO_JSON_OBJECT(dest, relayed, tx.relayed);
+ INSERT_INTO_JSON_OBJECT(dest, do_not_relay, tx.do_not_relay);
+ INSERT_INTO_JSON_OBJECT(dest, double_spend_seen, tx.double_spend_seen);
+
+ dest.EndObject();
}
@@ -825,18 +864,20 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx)
GET_FROM_JSON_OBJECT(val, tx.double_spend_seen, double_spend_seen);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::hard_fork_info& info, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::hard_fork_info& info)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, version, info.version);
+ INSERT_INTO_JSON_OBJECT(dest, enabled, info.enabled);
+ INSERT_INTO_JSON_OBJECT(dest, window, info.window);
+ INSERT_INTO_JSON_OBJECT(dest, votes, info.votes);
+ INSERT_INTO_JSON_OBJECT(dest, threshold, info.threshold);
+ INSERT_INTO_JSON_OBJECT(dest, voting, info.voting);
+ INSERT_INTO_JSON_OBJECT(dest, state, info.state);
+ INSERT_INTO_JSON_OBJECT(dest, earliest_height, info.earliest_height);
- INSERT_INTO_JSON_OBJECT(val, doc, version, info.version);
- INSERT_INTO_JSON_OBJECT(val, doc, enabled, info.enabled);
- INSERT_INTO_JSON_OBJECT(val, doc, window, info.window);
- INSERT_INTO_JSON_OBJECT(val, doc, votes, info.votes);
- INSERT_INTO_JSON_OBJECT(val, doc, threshold, info.threshold);
- INSERT_INTO_JSON_OBJECT(val, doc, voting, info.voting);
- INSERT_INTO_JSON_OBJECT(val, doc, state, info.state);
- INSERT_INTO_JSON_OBJECT(val, doc, earliest_height, info.earliest_height);
+ dest.EndObject();
}
@@ -857,14 +898,16 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::hard_fork_info&
GET_FROM_JSON_OBJECT(val, info.earliest_height, earliest_height);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_count& out, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_count& out)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount);
- INSERT_INTO_JSON_OBJECT(val, doc, total_count, out.total_count);
- INSERT_INTO_JSON_OBJECT(val, doc, unlocked_count, out.unlocked_count);
- INSERT_INTO_JSON_OBJECT(val, doc, recent_count, out.recent_count);
+ INSERT_INTO_JSON_OBJECT(dest, amount, out.amount);
+ INSERT_INTO_JSON_OBJECT(dest, total_count, out.total_count);
+ INSERT_INTO_JSON_OBJECT(dest, unlocked_count, out.unlocked_count);
+ INSERT_INTO_JSON_OBJECT(dest, recent_count, out.recent_count);
+
+ dest.EndObject();
}
@@ -881,12 +924,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_c
GET_FROM_JSON_OBJECT(val, out.recent_count, recent_count);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_and_index& out, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_and_index& out)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, amount, out.amount);
+ INSERT_INTO_JSON_OBJECT(dest, index, out.index);
- INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount);
- INSERT_INTO_JSON_OBJECT(val, doc, index, out.index);
+ dest.EndObject();
}
@@ -901,13 +946,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_a
GET_FROM_JSON_OBJECT(val, out.index, index);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_mask_unlocked& out, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_mask_unlocked& out)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, key, out.key);
+ INSERT_INTO_JSON_OBJECT(dest, mask, out.mask);
+ INSERT_INTO_JSON_OBJECT(dest, unlocked, out.unlocked);
- INSERT_INTO_JSON_OBJECT(val, doc, key, out.key);
- INSERT_INTO_JSON_OBJECT(val, doc, mask, out.mask);
- INSERT_INTO_JSON_OBJECT(val, doc, unlocked, out.unlocked);
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask_unlocked& out)
@@ -922,13 +969,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask
GET_FROM_JSON_OBJECT(val, out.unlocked, unlocked);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::error& err, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::error& err)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, code, err.code);
- INSERT_INTO_JSON_OBJECT(val, doc, error_str, err.error_str);
- INSERT_INTO_JSON_OBJECT(val, doc, message, err.message);
+ INSERT_INTO_JSON_OBJECT(dest, code, err.code);
+ INSERT_INTO_JSON_OBJECT(dest, error_str, err.error_str);
+ INSERT_INTO_JSON_OBJECT(dest, message, err.message);
+
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error)
@@ -943,20 +992,22 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error)
GET_FROM_JSON_OBJECT(val, error.message, message);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::BlockHeaderResponse& response, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::BlockHeaderResponse& response)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, major_version, response.major_version);
+ INSERT_INTO_JSON_OBJECT(dest, minor_version, response.minor_version);
+ INSERT_INTO_JSON_OBJECT(dest, timestamp, response.timestamp);
+ INSERT_INTO_JSON_OBJECT(dest, prev_id, response.prev_id);
+ INSERT_INTO_JSON_OBJECT(dest, nonce, response.nonce);
+ INSERT_INTO_JSON_OBJECT(dest, height, response.height);
+ INSERT_INTO_JSON_OBJECT(dest, depth, response.depth);
+ INSERT_INTO_JSON_OBJECT(dest, hash, response.hash);
+ INSERT_INTO_JSON_OBJECT(dest, difficulty, response.difficulty);
+ INSERT_INTO_JSON_OBJECT(dest, reward, response.reward);
- INSERT_INTO_JSON_OBJECT(val, doc, major_version, response.major_version);
- INSERT_INTO_JSON_OBJECT(val, doc, minor_version, response.minor_version);
- INSERT_INTO_JSON_OBJECT(val, doc, timestamp, response.timestamp);
- INSERT_INTO_JSON_OBJECT(val, doc, prev_id, response.prev_id);
- INSERT_INTO_JSON_OBJECT(val, doc, nonce, response.nonce);
- INSERT_INTO_JSON_OBJECT(val, doc, height, response.height);
- INSERT_INTO_JSON_OBJECT(val, doc, depth, response.depth);
- INSERT_INTO_JSON_OBJECT(val, doc, hash, response.hash);
- INSERT_INTO_JSON_OBJECT(val, doc, difficulty, response.difficulty);
- INSERT_INTO_JSON_OBJECT(val, doc, reward, response.reward);
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response)
@@ -978,34 +1029,36 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResp
GET_FROM_JSON_OBJECT(val, response.reward, reward);
}
-void toJsonValue(rapidjson::Document& doc, const rct::rctSig& sig, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::rctSig& sig)
{
using boost::adaptors::transform;
- val.SetObject();
+ dest.StartObject();
const auto just_mask = [] (rct::ctkey const& key) -> rct::key const&
{
return key.mask;
};
- INSERT_INTO_JSON_OBJECT(val, doc, type, sig.type);
- INSERT_INTO_JSON_OBJECT(val, doc, encrypted, sig.ecdhInfo);
- INSERT_INTO_JSON_OBJECT(val, doc, commitments, transform(sig.outPk, just_mask));
- INSERT_INTO_JSON_OBJECT(val, doc, fee, sig.txnFee);
+ INSERT_INTO_JSON_OBJECT(dest, type, sig.type);
+ INSERT_INTO_JSON_OBJECT(dest, encrypted, sig.ecdhInfo);
+ INSERT_INTO_JSON_OBJECT(dest, commitments, transform(sig.outPk, just_mask));
+ INSERT_INTO_JSON_OBJECT(dest, fee, sig.txnFee);
// prunable
{
- rapidjson::Value prunable;
- prunable.SetObject();
+ dest.Key("prunable");
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(prunable, doc, range_proofs, sig.p.rangeSigs);
- INSERT_INTO_JSON_OBJECT(prunable, doc, bulletproofs, sig.p.bulletproofs);
- INSERT_INTO_JSON_OBJECT(prunable, doc, mlsags, sig.p.MGs);
- INSERT_INTO_JSON_OBJECT(prunable, doc, pseudo_outs, sig.get_pseudo_outs());
+ INSERT_INTO_JSON_OBJECT(dest, range_proofs, sig.p.rangeSigs);
+ INSERT_INTO_JSON_OBJECT(dest, bulletproofs, sig.p.bulletproofs);
+ INSERT_INTO_JSON_OBJECT(dest, mlsags, sig.p.MGs);
+ INSERT_INTO_JSON_OBJECT(dest, pseudo_outs, sig.get_pseudo_outs());
- val.AddMember("prunable", prunable, doc.GetAllocator());
+ dest.EndObject();
}
+
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig)
@@ -1046,12 +1099,12 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig)
}
}
-void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::ecdhTuple& tuple)
{
- val.SetObject();
-
- INSERT_INTO_JSON_OBJECT(val, doc, mask, tuple.mask);
- INSERT_INTO_JSON_OBJECT(val, doc, amount, tuple.amount);
+ dest.StartObject();
+ INSERT_INTO_JSON_OBJECT(dest, mask, tuple.mask);
+ INSERT_INTO_JSON_OBJECT(dest, amount, tuple.amount);
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple)
@@ -1065,14 +1118,14 @@ void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple)
GET_FROM_JSON_OBJECT(val, tuple.amount, amount);
}
-void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::rangeSig& sig)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, asig, sig.asig);
+ INSERT_INTO_JSON_OBJECT(dest, asig, sig.asig);
+ INSERT_INTO_JSON_OBJECT(dest, Ci, epee::span<const rct::key>{sig.Ci});
- std::vector<rct::key> keyVector(sig.Ci, std::end(sig.Ci));
- INSERT_INTO_JSON_OBJECT(val, doc, Ci, keyVector);
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig)
@@ -1102,22 +1155,24 @@ void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig)
}
}
-void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::Bulletproof& p)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, V, p.V);
+ INSERT_INTO_JSON_OBJECT(dest, A, p.A);
+ INSERT_INTO_JSON_OBJECT(dest, S, p.S);
+ INSERT_INTO_JSON_OBJECT(dest, T1, p.T1);
+ INSERT_INTO_JSON_OBJECT(dest, T2, p.T2);
+ INSERT_INTO_JSON_OBJECT(dest, taux, p.taux);
+ INSERT_INTO_JSON_OBJECT(dest, mu, p.mu);
+ INSERT_INTO_JSON_OBJECT(dest, L, p.L);
+ INSERT_INTO_JSON_OBJECT(dest, R, p.R);
+ INSERT_INTO_JSON_OBJECT(dest, a, p.a);
+ INSERT_INTO_JSON_OBJECT(dest, b, p.b);
+ INSERT_INTO_JSON_OBJECT(dest, t, p.t);
- INSERT_INTO_JSON_OBJECT(val, doc, V, p.V);
- INSERT_INTO_JSON_OBJECT(val, doc, A, p.A);
- INSERT_INTO_JSON_OBJECT(val, doc, S, p.S);
- INSERT_INTO_JSON_OBJECT(val, doc, T1, p.T1);
- INSERT_INTO_JSON_OBJECT(val, doc, T2, p.T2);
- INSERT_INTO_JSON_OBJECT(val, doc, taux, p.taux);
- INSERT_INTO_JSON_OBJECT(val, doc, mu, p.mu);
- INSERT_INTO_JSON_OBJECT(val, doc, L, p.L);
- INSERT_INTO_JSON_OBJECT(val, doc, R, p.R);
- INSERT_INTO_JSON_OBJECT(val, doc, a, p.a);
- INSERT_INTO_JSON_OBJECT(val, doc, b, p.b);
- INSERT_INTO_JSON_OBJECT(val, doc, t, p.t);
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p)
@@ -1141,17 +1196,15 @@ void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p)
GET_FROM_JSON_OBJECT(val, p.t, t);
}
-void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::boroSig& sig)
{
- val.SetObject();
-
- std::vector<rct::key> keyVector(sig.s0, std::end(sig.s0));
- INSERT_INTO_JSON_OBJECT(val, doc, s0, keyVector);
+ dest.StartObject();
- keyVector.assign(sig.s1, std::end(sig.s1));
- INSERT_INTO_JSON_OBJECT(val, doc, s1, keyVector);
+ INSERT_INTO_JSON_OBJECT(dest, s0, epee::span<const rct::key>{sig.s0});
+ INSERT_INTO_JSON_OBJECT(dest, s1, epee::span<const rct::key>{sig.s1});
+ INSERT_INTO_JSON_OBJECT(dest, ee, sig.ee);
- INSERT_INTO_JSON_OBJECT(val, doc, ee, sig.ee);
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig)
@@ -1188,12 +1241,14 @@ void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig)
GET_FROM_JSON_OBJECT(val, sig.ee, ee);
}
-void toJsonValue(rapidjson::Document& doc, const rct::mgSig& sig, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::mgSig& sig)
{
- val.SetObject();
+ dest.StartObject();
- INSERT_INTO_JSON_OBJECT(val, doc, ss, sig.ss);
- INSERT_INTO_JSON_OBJECT(val, doc, cc, sig.cc);
+ INSERT_INTO_JSON_OBJECT(dest, ss, sig.ss);
+ INSERT_INTO_JSON_OBJECT(dest, cc, sig.cc);
+
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig)
@@ -1207,32 +1262,34 @@ void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig)
GET_FROM_JSON_OBJECT(val, sig.cc, cc);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& info, rapidjson::Value& val)
-{
- val.SetObject();
-
- INSERT_INTO_JSON_OBJECT(val, doc, height, info.height);
- INSERT_INTO_JSON_OBJECT(val, doc, target_height, info.target_height);
- INSERT_INTO_JSON_OBJECT(val, doc, difficulty, info.difficulty);
- INSERT_INTO_JSON_OBJECT(val, doc, target, info.target);
- INSERT_INTO_JSON_OBJECT(val, doc, tx_count, info.tx_count);
- INSERT_INTO_JSON_OBJECT(val, doc, tx_pool_size, info.tx_pool_size);
- INSERT_INTO_JSON_OBJECT(val, doc, alt_blocks_count, info.alt_blocks_count);
- INSERT_INTO_JSON_OBJECT(val, doc, outgoing_connections_count, info.outgoing_connections_count);
- INSERT_INTO_JSON_OBJECT(val, doc, incoming_connections_count, info.incoming_connections_count);
- INSERT_INTO_JSON_OBJECT(val, doc, white_peerlist_size, info.white_peerlist_size);
- INSERT_INTO_JSON_OBJECT(val, doc, grey_peerlist_size, info.grey_peerlist_size);
- INSERT_INTO_JSON_OBJECT(val, doc, mainnet, info.mainnet);
- INSERT_INTO_JSON_OBJECT(val, doc, testnet, info.testnet);
- INSERT_INTO_JSON_OBJECT(val, doc, stagenet, info.stagenet);
- INSERT_INTO_JSON_OBJECT(val, doc, nettype, info.nettype);
- INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash);
- INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty);
- INSERT_INTO_JSON_OBJECT(val, doc, block_size_limit, info.block_size_limit);
- INSERT_INTO_JSON_OBJECT(val, doc, block_weight_limit, info.block_weight_limit);
- INSERT_INTO_JSON_OBJECT(val, doc, block_size_median, info.block_size_median);
- INSERT_INTO_JSON_OBJECT(val, doc, block_weight_median, info.block_weight_median);
- INSERT_INTO_JSON_OBJECT(val, doc, start_time, info.start_time);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::DaemonInfo& info)
+{
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, height, info.height);
+ INSERT_INTO_JSON_OBJECT(dest, target_height, info.target_height);
+ INSERT_INTO_JSON_OBJECT(dest, difficulty, info.difficulty);
+ INSERT_INTO_JSON_OBJECT(dest, target, info.target);
+ INSERT_INTO_JSON_OBJECT(dest, tx_count, info.tx_count);
+ INSERT_INTO_JSON_OBJECT(dest, tx_pool_size, info.tx_pool_size);
+ INSERT_INTO_JSON_OBJECT(dest, alt_blocks_count, info.alt_blocks_count);
+ INSERT_INTO_JSON_OBJECT(dest, outgoing_connections_count, info.outgoing_connections_count);
+ INSERT_INTO_JSON_OBJECT(dest, incoming_connections_count, info.incoming_connections_count);
+ INSERT_INTO_JSON_OBJECT(dest, white_peerlist_size, info.white_peerlist_size);
+ INSERT_INTO_JSON_OBJECT(dest, grey_peerlist_size, info.grey_peerlist_size);
+ INSERT_INTO_JSON_OBJECT(dest, mainnet, info.mainnet);
+ INSERT_INTO_JSON_OBJECT(dest, testnet, info.testnet);
+ INSERT_INTO_JSON_OBJECT(dest, stagenet, info.stagenet);
+ INSERT_INTO_JSON_OBJECT(dest, nettype, info.nettype);
+ INSERT_INTO_JSON_OBJECT(dest, top_block_hash, info.top_block_hash);
+ INSERT_INTO_JSON_OBJECT(dest, cumulative_difficulty, info.cumulative_difficulty);
+ INSERT_INTO_JSON_OBJECT(dest, block_size_limit, info.block_size_limit);
+ INSERT_INTO_JSON_OBJECT(dest, block_weight_limit, info.block_weight_limit);
+ INSERT_INTO_JSON_OBJECT(dest, block_size_median, info.block_size_median);
+ INSERT_INTO_JSON_OBJECT(dest, block_weight_median, info.block_weight_median);
+ INSERT_INTO_JSON_OBJECT(dest, start_time, info.start_time);
+
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info)
@@ -1266,14 +1323,16 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
GET_FROM_JSON_OBJECT(val, info.start_time, start_time);
}
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_distribution& dist, rapidjson::Value& val)
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_distribution& dist)
{
- val.SetObject();
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, distribution, dist.data.distribution);
+ INSERT_INTO_JSON_OBJECT(dest, amount, dist.amount);
+ INSERT_INTO_JSON_OBJECT(dest, start_height, dist.data.start_height);
+ INSERT_INTO_JSON_OBJECT(dest, base, dist.data.base);
- INSERT_INTO_JSON_OBJECT(val, doc, distribution, dist.data.distribution);
- INSERT_INTO_JSON_OBJECT(val, doc, amount, dist.amount);
- INSERT_INTO_JSON_OBJECT(val, doc, start_height, dist.data.start_height);
- INSERT_INTO_JSON_OBJECT(val, doc, base, dist.data.base);
+ dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribution& dist)
diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h
index 5ef75b863..a1a5105d5 100644
--- a/src/serialization/json_object.h
+++ b/src/serialization/json_object.h
@@ -28,8 +28,13 @@
#pragma once
-#include "string_tools.h"
-#include "rapidjson/document.h"
+#include <boost/utility/string_ref.hpp>
+#include <cstring>
+#include "string_tools.h" // out of order because windows.h GetObject macro conflicts with GenericValue<..>::GetObject()
+#include <rapidjson/document.h>
+#include <rapidjson/stringbuffer.h>
+#include <rapidjson/writer.h>
+
#include "cryptonote_basic/cryptonote_basic.h"
#include "rpc/message_data_structs.h"
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
@@ -44,10 +49,9 @@
} \
} while (0);
-#define INSERT_INTO_JSON_OBJECT(jsonVal, doc, key, source) \
- rapidjson::Value key##Val; \
- cryptonote::json::toJsonValue(doc, source, key##Val); \
- jsonVal.AddMember(#key, key##Val, doc.GetAllocator());
+#define INSERT_INTO_JSON_OBJECT(dest, key, source) \
+ dest.Key(#key, sizeof(#key) - 1); \
+ cryptonote::json::toJsonValue(dest, source);
#define GET_FROM_JSON_OBJECT(source, dst, key) \
OBJECT_HAS_MEMBER_OR_THROW(source, #key) \
@@ -114,16 +118,24 @@ inline constexpr bool is_to_hex()
return std::is_pod<Type>() && !std::is_integral<Type>();
}
+// POD to json key
+template <class Type>
+inline typename std::enable_if<is_to_hex<Type>()>::type toJsonKey(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Type& pod)
+{
+ const auto hex = epee::to_hex::array(pod);
+ dest.Key(hex.data(), hex.size());
+}
// POD to json value
template <class Type>
-typename std::enable_if<is_to_hex<Type>()>::type toJsonValue(rapidjson::Document& doc, const Type& pod, rapidjson::Value& value)
+inline typename std::enable_if<is_to_hex<Type>()>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Type& pod)
{
- value = rapidjson::Value(epee::string_tools::pod_to_hex(pod).c_str(), doc.GetAllocator());
+ const auto hex = epee::to_hex::array(pod);
+ dest.String(hex.data(), hex.size());
}
template <class Type>
-typename std::enable_if<is_to_hex<Type>()>::type fromJsonValue(const rapidjson::Value& val, Type& t)
+inline typename std::enable_if<is_to_hex<Type>()>::type fromJsonValue(const rapidjson::Value& val, Type& t)
{
if (!val.IsString())
{
@@ -139,10 +151,16 @@ typename std::enable_if<is_to_hex<Type>()>::type fromJsonValue(const rapidjson::
}
}
-void toJsonValue(rapidjson::Document& doc, const std::string& i, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rapidjson::Value& src);
+
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, boost::string_ref i);
+inline void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const std::string& i)
+{
+ toJsonValue(dest, boost::string_ref{i});
+}
void fromJsonValue(const rapidjson::Value& val, std::string& str);
-void toJsonValue(rapidjson::Document& doc, bool i, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, bool i);
void fromJsonValue(const rapidjson::Value& val, bool& b);
// integers overloads for toJsonValue are not needed for standard promotions
@@ -157,144 +175,144 @@ void fromJsonValue(const rapidjson::Value& val, unsigned short& i);
void fromJsonValue(const rapidjson::Value& val, short& i);
-void toJsonValue(rapidjson::Document& doc, const unsigned i, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned i);
void fromJsonValue(const rapidjson::Value& val, unsigned& i);
-void toJsonValue(rapidjson::Document& doc, const int, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const int);
void fromJsonValue(const rapidjson::Value& val, int& i);
-void toJsonValue(rapidjson::Document& doc, const unsigned long long i, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned long long i);
void fromJsonValue(const rapidjson::Value& val, unsigned long long& i);
-void toJsonValue(rapidjson::Document& doc, const long long i, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const long long i);
void fromJsonValue(const rapidjson::Value& val, long long& i);
-inline void toJsonValue(rapidjson::Document& doc, const unsigned long i, rapidjson::Value& val) {
- toJsonValue(doc, static_cast<unsigned long long>(i), val);
+inline void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned long i) {
+ toJsonValue(dest, static_cast<unsigned long long>(i));
}
void fromJsonValue(const rapidjson::Value& val, unsigned long& i);
-inline void toJsonValue(rapidjson::Document& doc, const long i, rapidjson::Value& val) {
- toJsonValue(doc, static_cast<long long>(i), val);
+inline void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const long i) {
+ toJsonValue(dest, static_cast<long long>(i));
}
void fromJsonValue(const rapidjson::Value& val, long& i);
// end integers
-void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::transaction& tx);
void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block& b);
void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_v& txin);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_gen& txin, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_gen& txin);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_script& txin, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_script& txin);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_script& txin);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_scripthash& txin, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_scripthash& txin);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash& txin);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_key& txin, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_key& txin);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_target_v& txout, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_target_v& txout);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_script& txout, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_script& txout);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txout);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_scripthash& txout, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_scripthash& txout);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& txout);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_key& txout, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_key& txout);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_out& txout, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_out& txout);
void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::connection_info& info);
void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& info);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_blob_entry& tx, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_blob_entry& tx);
void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_blob_entry& tx);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block_complete_entry& blk);
void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry& blk);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::block_with_transactions& blk, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::block_with_transactions& blk);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::block_with_transactions& blk);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::transaction_info& tx_info, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::transaction_info& tx_info);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::transaction_info& tx_info);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_and_amount_index& out, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_and_amount_index& out);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_and_amount_index& out);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::amount_with_random_outputs& out, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::amount_with_random_outputs& out);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::amount_with_random_outputs& out);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::peer& peer, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::peer& peer);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::tx_in_pool& tx);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::hard_fork_info& info, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::hard_fork_info& info);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::hard_fork_info& info);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_count& out, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_count& out);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_count& out);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_and_index& out, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_and_index& out);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_and_index& out);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_mask_unlocked& out, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_mask_unlocked& out);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask_unlocked& out);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::error& err, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::error& err);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::BlockHeaderResponse& response, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::BlockHeaderResponse& response);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response);
-void toJsonValue(rapidjson::Document& doc, const rct::rctSig& i, rapidjson::Value& val);
-void fromJsonValue(const rapidjson::Value& i, rct::rctSig& sig);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::rctSig& i);
+void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig);
-void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::ecdhTuple& tuple);
void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple);
-void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::rangeSig& sig);
void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig);
-void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::Bulletproof& p);
void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p);
-void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::boroSig& sig);
void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig);
-void toJsonValue(rapidjson::Document& doc, const rct::mgSig& sig, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::mgSig& sig);
void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& info, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::DaemonInfo& info);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info);
-void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_distribution& dist, rapidjson::Value& val);
+void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_distribution& dist);
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribution& dist);
template <typename Map>
-typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Document& doc, const Map& map, rapidjson::Value& val);
+typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Map& map);
template <typename Map>
typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map);
template <typename Vec>
-typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Document& doc, const Vec &vec, rapidjson::Value& val);
+typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Vec &vec);
template <typename Vec>
typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec);
@@ -304,24 +322,22 @@ typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJson
// unfortunately because of how templates work they have to be here.
template <typename Map>
-typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Document& doc, const Map& map, rapidjson::Value& val)
+inline typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Map& map)
{
- val.SetObject();
-
- auto& al = doc.GetAllocator();
+ using key_type = typename Map::key_type;
+ static_assert(std::is_same<std::string, key_type>() || is_to_hex<key_type>(), "invalid map key type");
+ dest.StartObject();
for (const auto& i : map)
{
- rapidjson::Value k;
- rapidjson::Value m;
- toJsonValue(doc, i.first, k);
- toJsonValue(doc, i.second, m);
- val.AddMember(k, m, al);
+ toJsonKey(dest, i.first);
+ toJsonValue(dest, i.second);
}
+ dest.EndObject();
}
template <typename Map>
-typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map)
+inline typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map)
{
if (!val.IsObject())
{
@@ -342,20 +358,16 @@ typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonVal
}
template <typename Vec>
-typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Document& doc, const Vec &vec, rapidjson::Value& val)
+inline typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Vec &vec)
{
- val.SetArray();
-
+ dest.StartArray();
for (const auto& t : vec)
- {
- rapidjson::Value v;
- toJsonValue(doc, t, v);
- val.PushBack(v, doc.GetAllocator());
- }
+ toJsonValue(dest, t);
+ dest.EndArray(vec.size());
}
template <typename Vec>
-typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec)
+inline typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec)
{
if (!val.IsArray())
{
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 87bbf62d3..d981a48b8 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -121,7 +121,7 @@ typedef cryptonote::simple_wallet sw;
#define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;)
-#define PRINT_USAGE(usage_help) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help;
+#define PRINT_USAGE(usage_help_advanced) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help_advanced;
#define LONG_PAYMENT_ID_SUPPORT_CHECK() \
do { \
@@ -180,8 +180,8 @@ namespace
const char* USAGE_PAYMENT_ID("payment_id");
const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]");
const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]");
- const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]");
- const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]");
+ const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]");
+ const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]");
const char* USAGE_SWEEP_BELOW("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id (obsolete)>]");
const char* USAGE_SWEEP_SINGLE("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id (obsolete)>]");
const char* USAGE_DONATE("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id (obsolete)>]");
@@ -196,7 +196,7 @@ namespace
" account tag_description <tag_name> <description>");
const char* USAGE_ADDRESS("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed> | device [<index>]]");
const char* USAGE_INTEGRATED_ADDRESS("integrated_address [device] [<payment_id> | <address>]");
- const char* USAGE_ADDRESS_BOOK("address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]");
+ const char* USAGE_ADDRESS_BOOK("address_book [(add (<address>|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]");
const char* USAGE_SET_VARIABLE("set <option> [<value>]");
const char* USAGE_GET_TX_KEY("get_tx_key <txid>");
const char* USAGE_SET_TX_KEY("set_tx_key <txid> <tx_key>");
@@ -214,7 +214,7 @@ namespace
const char* USAGE_GET_TX_NOTE("get_tx_note <txid>");
const char* USAGE_GET_DESCRIPTION("get_description");
const char* USAGE_SET_DESCRIPTION("set_description [free text note]");
- const char* USAGE_SIGN("sign <filename>");
+ const char* USAGE_SIGN("sign [<account_index>,<address_index>] <filename>");
const char* USAGE_VERIFY("verify <filename> <address> <signature>");
const char* USAGE_EXPORT_KEY_IMAGES("export_key_images [all] <filename>");
const char* USAGE_IMPORT_KEY_IMAGES("import_key_images <filename>");
@@ -268,7 +268,8 @@ namespace
const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc");
const char* USAGE_STOP_MINING_FOR_RPC("stop_mining_for_rpc");
const char* USAGE_VERSION("version");
- const char* USAGE_HELP("help [<command>]");
+ const char* USAGE_HELP_ADVANCED("help_advanced [<command>]");
+ const char* USAGE_HELP("help");
std::string input_line(const std::string& prompt, bool yesno = false)
{
@@ -1690,7 +1691,7 @@ bool simple_wallet::print_ring(const std::vector<std::string> &args)
rings.push_back({key_image, ring});
else if (!m_wallet->get_rings(txid, rings))
{
- fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0");
+ fail_msg_writer() << tr("Key image either not spent, or spent with ring size 1");
return true;
}
@@ -2305,7 +2306,7 @@ bool simple_wallet::on_unknown_command(const std::vector<std::string> &args)
{
if (args[0] == "exit" || args[0] == "q") // backward compat
return false;
- fail_msg_writer() << boost::format(tr("Unknown command '%s', try 'help'")) % args.front();
+ fail_msg_writer() << boost::format(tr("Unknown command '%s', try 'help_advanced'")) % args.front();
return true;
}
@@ -3034,13 +3035,37 @@ bool simple_wallet::set_export_format(const std::vector<std::string> &args/* = s
bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
+ message_writer() << "";
+ message_writer() << tr("Commands:");
+ message_writer() << "";
+ message_writer() << tr("\"welcome\" - Read welcome message.");
+ message_writer() << tr("\"donate <amount>\" - Donate XMR to the development team.");
+ message_writer() << tr("\"balance\" - Show balance.");
+ message_writer() << tr("\"address new\" - Create new subaddress.");
+ message_writer() << tr("\"address all\" - Show all addresses.");
+ message_writer() << tr("\"transfer <address> <amount>\" - Send XMR to an address.");
+ message_writer() << tr("\"show_transfers [in|out|pending|failed|pool]\" - Show transactions.");
+ message_writer() << tr("\"sweep_all <address>\" - Send whole balance to another wallet.");
+ message_writer() << tr("\"seed\" - Show secret 25 words that can be used to recover this wallet.");
+ message_writer() << tr("\"refresh\" - Synchronize wallet with the Monero network.");
+ message_writer() << tr("\"status\" - Check current status of wallet.");
+ message_writer() << tr("\"version\" - Check software version.");
+ message_writer() << tr("\"help_advanced\" - Show list with more available commands.");
+ message_writer() << tr("\"save\" - Save wallet.");
+ message_writer() << tr("\"exit\" - Exit wallet.");
+ message_writer() << "";
+ return true;
+}
+
+bool simple_wallet::help_advanced(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
if(args.empty())
{
success_msg_writer() << get_commands_str();
}
else if ((args.size() == 2) && (args.front() == "mms"))
{
- // Little hack to be able to do "help mms <subcommand>"
+ // Little hack to be able to do "help_advanced mms <subcommand>"
std::vector<std::string> mms_args(1, args.front() + " " + args.back());
success_msg_writer() << get_command_usage(mms_args);
}
@@ -3113,13 +3138,13 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("locked_sweep_all",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::locked_sweep_all,_1),
tr(USAGE_LOCKED_SWEEP_ALL),
- tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability."));
+ tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability."));
m_cmd_binder.set_handler("sweep_unmixable",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _1),
tr("Send all unmixable outputs to yourself with ring_size 1"));
m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1),
tr(USAGE_SWEEP_ALL),
- tr("Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs."));
+ tr("Send all unlocked balance to an address. If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs."));
m_cmd_binder.set_handler("sweep_below",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_below, _1),
tr(USAGE_SWEEP_BELOW),
@@ -3252,7 +3277,9 @@ simple_wallet::simple_wallet()
"auto-mine-for-rpc-payment-threshold <float>\n "
" Whether to automatically start mining for RPC payment if the daemon requires it.\n"
"credits-target <unsigned int>\n"
- " The RPC payment credits balance to target (0 for default)."));
+ " The RPC payment credits balance to target (0 for default).\n "
+ "inactivity-lock-timeout <unsigned int>\n "
+ " How many seconds to wait before locking the wallet (0 to disable)."));
m_cmd_binder.set_handler("encrypted_seed",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::encrypted_seed, _1),
tr("Display the encrypted Electrum-style mnemonic seed."));
@@ -3346,7 +3373,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("sign",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::sign, _1),
tr(USAGE_SIGN),
- tr("Sign the contents of a file."));
+ tr("Sign the contents of a file with the given subaddress (or the main address if not specified)"));
m_cmd_binder.set_handler("verify",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::verify, _1),
tr(USAGE_VERIFY),
@@ -3429,7 +3456,7 @@ simple_wallet::simple_wallet()
"<subcommand> is one of:\n"
" init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n"
" send_signer_config, start_auto_config, stop_auto_config, auto_config\n"
- "Get help about a subcommand with: help mms <subcommand>, or mms help <subcommand>"));
+ "Get help about a subcommand with: help_advanced mms <subcommand>"));
m_cmd_binder.set_handler("mms init",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_INIT),
@@ -3579,10 +3606,14 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::stop_mining_for_rpc, this, _1),
tr(USAGE_STOP_MINING_FOR_RPC),
tr("Stop mining to pay for RPC access"));
+ m_cmd_binder.set_handler("help_advanced",
+ boost::bind(&simple_wallet::on_command, this, &simple_wallet::help_advanced, _1),
+ tr(USAGE_HELP_ADVANCED),
+ tr("Show the help section or the documentation about a <command>."));
m_cmd_binder.set_handler("help",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::help, _1),
tr(USAGE_HELP),
- tr("Show the help section or the documentation about a <command>."));
+ tr("Show simplified list of available commands."));
m_cmd_binder.set_unknown_command_handler(boost::bind(&simple_wallet::on_command, this, &simple_wallet::on_unknown_command, _1));
m_cmd_binder.set_empty_command_handler(boost::bind(&simple_wallet::on_empty_command, this));
m_cmd_binder.set_cancel_handler(boost::bind(&simple_wallet::on_cancelled_command, this));
@@ -4734,8 +4765,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
"**********************************************************************\n" <<
tr("Your wallet has been generated!\n"
"To start synchronizing with the daemon, use the \"refresh\" command.\n"
- "Use the \"help\" command to see the list of available commands.\n"
- "Use \"help <command>\" to see a command's documentation.\n"
+ "Use the \"help\" command to see a simplified list of available commands.\n"
+ "Use the \"help_advanced\" command to see an advanced list of available commands.\n"
+ "Use \"help_advanced <command>\" to see a command's documentation.\n"
"Always use the \"exit\" command when closing monero-wallet-cli to save \n"
"your current session's state. Otherwise, you might need to synchronize \n"
"your wallet again (your wallet keys are NOT at risk in any case).\n")
@@ -4994,8 +5026,9 @@ boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::p
}
success_msg_writer() <<
"**********************************************************************\n" <<
- tr("Use the \"help\" command to see the list of available commands.\n") <<
- tr("Use \"help <command>\" to see a command's documentation.\n") <<
+ tr("Use the \"help\" command to see a simplified list of available commands.\n") <<
+ tr("Use the \"help_advanced\" command to see an advanced list of available commands.\n") <<
+ tr("Use \"help_advanced <command>\" to see a command's documentation.\n") <<
"**********************************************************************";
return password;
}
@@ -5436,20 +5469,28 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid,
std::vector<tx_extra_field> tx_extra_fields;
parse_tx_extra(tx.extra, tx_extra_fields); // failure ok
tx_extra_nonce extra_nonce;
- if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ tx_extra_pub_key extra_pub_key;
+ crypto::hash8 payment_id8 = crypto::null_hash8;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_pub_key))
{
- crypto::hash payment_id = crypto::null_hash;
- crypto::hash8 payment_id8 = crypto::null_hash8;
- if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ const crypto::public_key &tx_pub_key = extra_pub_key.pub_key;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
{
- if (payment_id8 != crypto::null_hash8)
- message_writer() <<
- tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead");
- }
- else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
- message_writer(console_color_red, false) <<
- tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete and ignored. Use subaddresses instead.");
- }
+ if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ {
+ m_wallet->get_account().get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_wallet->get_account().get_keys().m_view_secret_key);
+ }
+ }
+ }
+
+ if (payment_id8 != crypto::null_hash8)
+ message_writer() <<
+ tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead");
+
+ crypto::hash payment_id = crypto::null_hash;
+ if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ message_writer(console_color_red, false) <<
+ tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete and ignored. Use subaddresses instead.");
}
if (unlock_time && !cryptonote::is_coinbase(tx))
message_writer() << tr("NOTE: This transaction is locked, see details with: show_transfer ") + epee::string_tools::pod_to_hex(txid);
@@ -5543,6 +5584,14 @@ boost::optional<epee::wipeable_string> simple_wallet::on_device_passphrase_reque
//----------------------------------------------------------------------------------------------------
void simple_wallet::on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money)
{
+ const uint64_t rfbh = m_wallet->get_refresh_from_block_height();
+ std::string err;
+ const uint64_t dh = m_wallet->get_daemon_blockchain_height(err);
+ if (err.empty() && rfbh > dh)
+ {
+ message_writer(console_color_yellow, false) << tr("The wallet's refresh-from-block-height setting is higher than the daemon's height: this may mean your wallet will skip over transactions");
+ }
+
// Key image sync after the first refresh
if (!m_wallet->get_account().get_device().has_tx_cold_sign() || m_wallet->get_account().get_device().has_ki_live_refresh()) {
return;
@@ -5812,8 +5861,14 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
if (uses)
{
std::vector<uint64_t> heights;
- for (const auto &e: td.m_uses) heights.push_back(e.first);
- const std::pair<std::string, std::string> line = show_outputs_line(heights, blockchain_height, td.m_spent_height);
+ uint64_t idx = 0;
+ for (const auto &e: td.m_uses)
+ {
+ heights.push_back(e.first);
+ if (e.first < td.m_spent_height)
+ ++idx;
+ }
+ const std::pair<std::string, std::string> line = show_outputs_line(heights, blockchain_height, idx);
extra_string += std::string("\n ") + tr("Used at heights: ") + line.first + "\n " + line.second;
}
message_writer(td.m_spent ? console_color_magenta : console_color_green, false) <<
@@ -5982,7 +6037,7 @@ bool simple_wallet::rescan_spent(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
-std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height) const
+std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_idx) const
{
std::stringstream ostr;
@@ -5990,7 +6045,7 @@ std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::
blockchain_height = std::max(blockchain_height, h);
for (size_t j = 0; j < heights.size(); ++j)
- ostr << (heights[j] == highlight_height ? " *" : " ") << heights[j];
+ ostr << (j == highlight_idx ? " *" : " ") << heights[j];
// visualize the distribution, using the code by moneroexamples onion-monero-viewer
const uint64_t resolution = 79;
@@ -6000,9 +6055,9 @@ std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::
uint64_t pos = (heights[j] * resolution) / blockchain_height;
ring_str[pos] = 'o';
}
- if (highlight_height < blockchain_height)
+ if (highlight_idx < heights.size() && heights[highlight_idx] < blockchain_height)
{
- uint64_t pos = (highlight_height * resolution) / blockchain_height;
+ uint64_t pos = (heights[highlight_idx] * resolution) / blockchain_height;
ring_str[pos] = '*';
}
@@ -6062,6 +6117,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
req.outputs[j].index = absolute_offsets[j];
}
COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
+ req.get_txid = true;
req.client = cryptonote::make_rpc_payment_signature(m_wallet->get_rpc_client_secret_key());
bool r = m_wallet->invoke_http_bin("/get_outs.bin", req, res);
err = interpret_rpc_response(r, res.status);
@@ -6083,14 +6139,11 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
spent_key_height[i] = res.outs[source.real_output].height;
spent_key_txid [i] = res.outs[source.real_output].txid;
std::vector<uint64_t> heights(absolute_offsets.size(), 0);
- uint64_t highlight_height = std::numeric_limits<uint64_t>::max();
for (size_t j = 0; j < absolute_offsets.size(); ++j)
{
heights[j] = res.outs[j].height;
- if (j == source.real_output)
- highlight_height = heights[j];
}
- std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, highlight_height);
+ std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, source.real_output);
ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n");
}
// warn if rings contain keys originating from the same tx or temporally very close block heights
@@ -6165,7 +6218,7 @@ void simple_wallet::check_for_inactivity_lock(bool user)
m_in_command = true;
if (!user)
{
- const std::string speech = tr("I locked your Monero wallet to protect you while you were away");
+ const std::string speech = tr("I locked your Monero wallet to protect you while you were away\nsee \"help_advanced set\" to configure/disable");
std::vector<std::pair<std::string, size_t>> lines = tools::split_string_by_width(speech, 45);
size_t max_len = 0;
@@ -6185,7 +6238,8 @@ void simple_wallet::check_for_inactivity_lock(bool user)
}
while (1)
{
- tools::msg_writer() << tr("Locked due to inactivity. The wallet password is required to unlock the console.");
+ const char *inactivity_msg = user ? "" : tr("Locked due to inactivity.");
+ tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << tr("The wallet password is required to unlock the console.");
try
{
if (get_and_verify_password())
@@ -6818,7 +6872,12 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
std::set<uint32_t> subaddr_indices;
if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
{
- if (!parse_subaddress_indices(local_args[0], subaddr_indices))
+ if (local_args[0] == "index=all")
+ {
+ for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++i)
+ subaddr_indices.insert(i);
+ }
+ else if (!parse_subaddress_indices(local_args[0], subaddr_indices))
{
print_usage();
return true;
@@ -8331,7 +8390,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
m_in_manual_refresh.store(true, std::memory_order_relaxed);
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
- std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
+ std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
m_wallet->update_pool_state(process_txs);
if (!process_txs.empty())
m_wallet->process_pool_state(process_txs);
@@ -8761,6 +8820,8 @@ bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_)
}
}
+ m_in_manual_refresh.store(true, std::memory_order_relaxed);
+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
return refresh_main(start_height, reset_type, true);
}
//----------------------------------------------------------------------------------------------------
@@ -9385,29 +9446,7 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v
fail_msg_writer() << tr("failed to parse address");
return true;
}
- crypto::hash payment_id = crypto::null_hash;
size_t description_start = 2;
- if (info.has_payment_id)
- {
- memcpy(payment_id.data, info.payment_id.data, 8);
- }
- else if (!info.has_payment_id && args.size() >= 4 && args[2] == "pid")
- {
- if (tools::wallet2::parse_long_payment_id(args[3], payment_id))
- {
- LONG_PAYMENT_ID_SUPPORT_CHECK();
- }
- else if (tools::wallet2::parse_short_payment_id(args[3], info.payment_id))
- {
- fail_msg_writer() << tr("Short payment IDs are to be used within an integrated address only");
- return true;
- }
- else
- {
- fail_msg_writer() << tr("failed to parse payment ID");
- return true;
- }
- }
std::string description;
for (size_t i = description_start; i < args.size(); ++i)
{
@@ -9415,7 +9454,7 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v
description += " ";
description += args[i];
}
- m_wallet->add_address_book_row(info.address, payment_id, description, info.is_subaddress);
+ m_wallet->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : NULL, description, info.is_subaddress);
}
else
{
@@ -9437,8 +9476,12 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v
for (size_t i = 0; i < address_book.size(); ++i) {
auto& row = address_book[i];
success_msg_writer() << tr("Index: ") << i;
- success_msg_writer() << tr("Address: ") << get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address);
- success_msg_writer() << tr("Payment ID: ") << row.m_payment_id << " (OBSOLETE)";
+ std::string address;
+ if (row.m_has_payment_id)
+ address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), row.m_address, row.m_payment_id);
+ else
+ address = get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address);
+ success_msg_writer() << tr("Address: ") << address;
success_msg_writer() << tr("Description: ") << row.m_description << "\n";
}
}
@@ -9590,7 +9633,7 @@ bool simple_wallet::sign(const std::vector<std::string> &args)
fail_msg_writer() << tr("command not supported by HW wallet");
return true;
}
- if (args.size() != 1)
+ if (args.size() != 1 && args.size() != 2)
{
PRINT_USAGE(USAGE_SIGN);
return true;
@@ -9606,7 +9649,20 @@ bool simple_wallet::sign(const std::vector<std::string> &args)
return true;
}
- std::string filename = args[0];
+ subaddress_index index{0, 0};
+ if (args.size() == 2)
+ {
+ unsigned int a, b;
+ if (sscanf(args[0].c_str(), "%u,%u", &a, &b) != 2)
+ {
+ fail_msg_writer() << tr("Invalid subaddress index format");
+ return true;
+ }
+ index.major = a;
+ index.minor = b;
+ }
+
+ const std::string &filename = args.back();
std::string data;
bool r = m_wallet->load_from_file(filename, data);
if (!r)
@@ -9617,7 +9673,7 @@ bool simple_wallet::sign(const std::vector<std::string> &args)
SCOPED_WALLET_UNLOCK();
- std::string signature = m_wallet->sign(data);
+ std::string signature = m_wallet->sign(data, index);
success_msg_writer() << signature;
return true;
}
@@ -9999,7 +10055,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
try
{
- std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
+ std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
m_wallet->update_pool_state(process_txs);
if (!process_txs.empty())
m_wallet->process_pool_state(process_txs);
@@ -10990,7 +11046,7 @@ void simple_wallet::mms_help(const std::vector<std::string> &args)
{
if (args.size() > 1)
{
- fail_msg_writer() << tr("Usage: mms help [<subcommand>]");
+ fail_msg_writer() << tr("Usage: help_advanced mms [<subcommand>]");
return;
}
std::vector<std::string> help_args;
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 75bd893d5..c0416ecbb 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -154,6 +154,7 @@ namespace cryptonote
bool set_persistent_rpc_client_id(const std::vector<std::string> &args = std::vector<std::string>());
bool set_auto_mine_for_rpc_payment_threshold(const std::vector<std::string> &args = std::vector<std::string>());
bool set_credits_target(const std::vector<std::string> &args = std::vector<std::string>());
+ bool help_advanced(const std::vector<std::string> &args = std::vector<std::string>());
bool help(const std::vector<std::string> &args = std::vector<std::string>());
bool start_mining(const std::vector<std::string> &args);
bool stop_mining(const std::vector<std::string> &args);
@@ -274,7 +275,7 @@ namespace cryptonote
bool print_seed(bool encrypted);
void key_images_sync_intern();
void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money);
- std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const;
+ std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_idx = std::numeric_limits<uint64_t>::max()) const;
bool freeze_thaw(const std::vector<std::string>& args, bool freeze);
bool prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector);
bool on_command(bool (simple_wallet::*cmd)(const std::vector<std::string>&), const std::vector<std::string> &args);
diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp
index 7be78bba7..005ddf7ee 100644
--- a/src/wallet/api/address_book.cpp
+++ b/src/wallet/api/address_book.cpp
@@ -55,37 +55,14 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa
return false;
}
- crypto::hash payment_id = crypto::null_hash;
- bool has_long_pid = (payment_id_str.empty())? false : tools::wallet2::parse_long_payment_id(payment_id_str, payment_id);
-
- // Short payment id provided
- if(payment_id_str.length() == 16) {
- m_errorString = tr("Invalid payment ID. Short payment ID should only be used in an integrated address");
- m_errorCode = Invalid_Payment_Id;
- return false;
- }
-
- // long payment id provided but not valid
- if(!payment_id_str.empty() && !has_long_pid) {
- m_errorString = tr("Invalid payment ID");
- m_errorCode = Invalid_Payment_Id;
- return false;
- }
-
- // integrated + long payment id provided
- if(has_long_pid && info.has_payment_id) {
- m_errorString = tr("Integrated address and long payment ID can't be used at the same time");
+ if (!payment_id_str.empty())
+ {
+ m_errorString = tr("Payment ID supplied: this is obsolete");
m_errorCode = Invalid_Payment_Id;
return false;
}
- // Pad short pid with zeros
- if (info.has_payment_id)
- {
- memcpy(payment_id.data, info.payment_id.data, 8);
- }
-
- bool r = m_wallet->m_wallet->add_address_book_row(info.address,payment_id,description,info.is_subaddress);
+ bool r = m_wallet->m_wallet->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : NULL,description,info.is_subaddress);
if (r)
refresh();
else
@@ -104,19 +81,12 @@ void AddressBookImpl::refresh()
for (size_t i = 0; i < rows.size(); ++i) {
tools::wallet2::address_book_row * row = &rows.at(i);
- std::string payment_id = (row->m_payment_id == crypto::null_hash)? "" : epee::string_tools::pod_to_hex(row->m_payment_id);
- std::string address = cryptonote::get_account_address_as_str(m_wallet->m_wallet->nettype(), row->m_is_subaddress, row->m_address);
- // convert the zero padded short payment id to integrated address
- if (!row->m_is_subaddress && payment_id.length() > 16 && payment_id.substr(16).find_first_not_of('0') == std::string::npos) {
- payment_id = payment_id.substr(0,16);
- crypto::hash8 payment_id_short;
- if(tools::wallet2::parse_short_payment_id(payment_id, payment_id_short)) {
- address = cryptonote::get_account_integrated_address_as_str(m_wallet->m_wallet->nettype(), row->m_address, payment_id_short);
- // Don't show payment id when integrated address is used
- payment_id = "";
- }
- }
- AddressBookRow * abr = new AddressBookRow(i, address, payment_id, row->m_description);
+ std::string address;
+ if (row->m_has_payment_id)
+ address = cryptonote::get_account_integrated_address_as_str(m_wallet->m_wallet->nettype(), row->m_address, row->m_payment_id);
+ else
+ address = get_account_address_as_str(m_wallet->m_wallet->nettype(), row->m_is_subaddress, row->m_address);
+ AddressBookRow * abr = new AddressBookRow(i, address, "", row->m_description);
m_rows.push_back(abr);
}
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index 005b0bafa..f3698b599 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -78,6 +78,10 @@ void NodeRPCProxy::invalidate()
m_rpc_payment_seed_hash = crypto::null_hash;
m_rpc_payment_next_seed_hash = crypto::null_hash;
m_height_time = 0;
+ m_rpc_payment_diff = 0;
+ m_rpc_payment_credits_per_hash_found = 0;
+ m_rpc_payment_height = 0;
+ m_rpc_payment_cookie = 0;
}
boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version)
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index b7efdd75c..5e88ea788 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -39,6 +39,8 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb"
+#define V1TAG ((uint64_t)798237759845202)
+
static const char zerokey[8] = {0};
static const MDB_val zerokeyval = { sizeof(zerokey), (void *)zerokey };
@@ -63,15 +65,16 @@ static int compare_uint64(const MDB_val *a, const MDB_val *b)
return va < vb ? -1 : va > vb;
}
-static std::string compress_ring(const std::vector<uint64_t> &ring)
+static std::string compress_ring(const std::vector<uint64_t> &ring, uint64_t tag)
{
std::string s;
+ s += tools::get_varint_data(tag);
for (uint64_t out: ring)
s += tools::get_varint_data(out);
return s;
}
-static std::vector<uint64_t> decompress_ring(const std::string &s)
+static std::vector<uint64_t> decompress_ring(const std::string &s, uint64_t tag)
{
std::vector<uint64_t> ring;
int read = 0;
@@ -81,6 +84,13 @@ static std::vector<uint64_t> decompress_ring(const std::string &s)
std::string tmp(i, s.cend());
read = tools::read_varint(tmp.begin(), tmp.end(), out);
THROW_WALLET_EXCEPTION_IF(read <= 0 || read > 256, tools::error::wallet_internal_error, "Internal error decompressing ring");
+ if (tag)
+ {
+ if (tag != out)
+ return {};
+ tag = 0;
+ continue;
+ }
ring.push_back(out);
}
return ring;
@@ -93,25 +103,27 @@ std::string get_rings_filename(boost::filesystem::path filename)
return filename.string();
}
-static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key)
+static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
static const char salt[] = "ringdsb";
- uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt)];
+ uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt) + sizeof(field)];
memcpy(buffer, &key_image, sizeof(key_image));
memcpy(buffer + sizeof(key_image), &key, sizeof(key));
memcpy(buffer + sizeof(key_image) + sizeof(key), salt, sizeof(salt));
+ memcpy(buffer + sizeof(key_image) + sizeof(key) + sizeof(salt), &field, sizeof(field));
crypto::hash hash;
- crypto::cn_fast_hash(buffer, sizeof(buffer), hash.data);
+ // if field is 0, backward compat mode: hash without the field
+ crypto::cn_fast_hash(buffer, sizeof(buffer) - !field, hash.data);
static_assert(sizeof(hash) >= CHACHA_IV_SIZE, "Incompatible hash and chacha IV sizes");
crypto::chacha_iv iv;
memcpy(&iv, &hash, CHACHA_IV_SIZE);
return iv;
}
-static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key)
+static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
- const crypto::chacha_iv iv = make_iv(key_image, key);
+ const crypto::chacha_iv iv = make_iv(key_image, key, field);
std::string ciphertext;
ciphertext.resize(plaintext.size() + sizeof(iv));
crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]);
@@ -119,14 +131,14 @@ static std::string encrypt(const std::string &plaintext, const crypto::key_image
return ciphertext;
}
-static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key)
+static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
- return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key);
+ return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key, field);
}
-static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key)
+static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
- const crypto::chacha_iv iv = make_iv(key_image, key);
+ const crypto::chacha_iv iv = make_iv(key_image, key, field);
std::string plaintext;
THROW_WALLET_EXCEPTION_IF(ciphertext.size() < sizeof(iv), tools::error::wallet_internal_error, "Bad ciphertext text");
plaintext.resize(ciphertext.size() - sizeof(iv));
@@ -137,11 +149,11 @@ static std::string decrypt(const std::string &ciphertext, const crypto::key_imag
static void store_relative_ring(MDB_txn *txn, MDB_dbi &dbi, const crypto::key_image &key_image, const std::vector<uint64_t> &relative_ring, const crypto::chacha_key &chacha_key)
{
MDB_val key, data;
- std::string key_ciphertext = encrypt(key_image, chacha_key);
+ std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size();
- std::string compressed_ring = compress_ring(relative_ring);
- std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key);
+ std::string compressed_ring = compress_ring(relative_ring, V1TAG);
+ std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key, 1);
data.mv_size = data_ciphertext.size();
data.mv_data = (void*)data_ciphertext.c_str();
int dbr = mdb_put(txn, dbi, &key, &data, 0);
@@ -297,7 +309,7 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const std::vecto
for (const crypto::key_image &key_image: key_images)
{
MDB_val key, data;
- std::string key_ciphertext = encrypt(key_image, chacha_key);
+ std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size();
@@ -349,7 +361,7 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
tx_active = true;
MDB_val key, data;
- std::string key_ciphertext = encrypt(key_image, chacha_key);
+ std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size();
dbr = mdb_get(txn, dbi_rings, &key, &data);
@@ -358,8 +370,15 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
return false;
THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size");
- std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key);
- outs = decompress_ring(data_plaintext);
+ bool try_v0 = false;
+ std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 1);
+ try { outs = decompress_ring(data_plaintext, V1TAG); if (outs.empty()) try_v0 = true; }
+ catch(...) { try_v0 = true; }
+ if (try_v0)
+ {
+ data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 0);
+ outs = decompress_ring(data_plaintext, 0);
+ }
MDEBUG("Found ring for key image " << key_image << ":");
MDEBUG("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
outs = cryptonote::relative_output_offsets_to_absolute(outs);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 4d2ec5103..eaf185c63 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -44,6 +44,7 @@
using namespace epee;
#include "cryptonote_config.h"
+#include "cryptonote_core/tx_sanity_check.h"
#include "wallet_rpc_helpers.h"
#include "wallet2.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
@@ -149,6 +150,9 @@ static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1";
+boost::mutex tools::wallet2::default_daemon_address_lock;
+std::string tools::wallet2::default_daemon_address = "";
+
namespace
{
std::string get_default_ringdb_path()
@@ -412,6 +416,15 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
daemon_port = get_config(nettype).RPC_DEFAULT_PORT;
}
+ // if no daemon settings are given and we have a previous one, reuse that one
+ if (command_line::is_arg_defaulted(vm, opts.daemon_host) && command_line::is_arg_defaulted(vm, opts.daemon_port) && command_line::is_arg_defaulted(vm, opts.daemon_address))
+ {
+ // not a bug: taking a const ref to a temporary in this way is actually ok in a recent C++ standard
+ const std::string &def = tools::wallet2::get_default_daemon_address();
+ if (!def.empty())
+ daemon_address = def;
+ }
+
if (daemon_address.empty())
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
@@ -591,6 +604,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f
}
viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
crypto::public_key pkey;
+ if (viewkey == crypto::null_skey)
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view secret key may not be all zeroes"));
if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
}
@@ -607,6 +622,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f
}
spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
crypto::public_key pkey;
+ if (spendkey == crypto::null_skey)
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend secret key may not be all zeroes"));
if (!crypto::secret_key_to_public_key(spendkey, pkey)) {
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
}
@@ -1186,6 +1203,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_ringdb(),
m_last_block_reward(0),
m_encrypt_keys_after_refresh(boost::none),
+ m_decrypt_keys_lockers(0),
m_unattended(unattended),
m_devices_registered(false),
m_device_last_key_image_sync(0),
@@ -1312,8 +1330,15 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
m_node_rpc_proxy.invalidate();
}
- MINFO("setting daemon to " << get_daemon_address());
- return m_http_client.set_server(get_daemon_address(), get_daemon_login(), std::move(ssl_options));
+ const std::string address = get_daemon_address();
+ MINFO("setting daemon to " << address);
+ bool ret = m_http_client.set_server(address, get_daemon_login(), std::move(ssl_options));
+ if (ret)
+ {
+ CRITICAL_REGION_LOCAL(default_daemon_address_lock);
+ default_daemon_address = address;
+ }
+ return ret;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options)
@@ -1842,7 +1867,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// (that is, the prunable stuff may or may not be included)
if (!miner_tx && !pool)
process_unconfirmed(txid, tx, height);
- std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; // per receiving subaddress index
+
+ // per receiving subaddress index
+ std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs;
+ std::unordered_map<cryptonote::subaddress_index, amounts_container> tx_amounts_individual_outs;
+
crypto::public_key tx_pub_key = null_pkey;
bool notify = false;
@@ -1971,6 +2000,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations);
scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool);
+ if (!tx_scan_info[i].error)
+ {
+ tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered);
+ }
}
}
}
@@ -1994,6 +2027,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations);
scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool);
+ if (!tx_scan_info[i].error)
+ {
+ tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered);
+ }
}
}
}
@@ -2010,6 +2047,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
hwdev.set_mode(hw::device::NONE);
hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations);
scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool);
+ if (!tx_scan_info[i].error)
+ {
+ tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered);
+ }
}
}
}
@@ -2118,6 +2159,12 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount,
error::wallet_internal_error, "Unexpected values of new and old outputs");
tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount;
+
+ amounts_container& tx_amounts_this_out = tx_amounts_individual_outs[tx_scan_info[o].received->index]; // Only for readability on the following lines
+ auto amount_iterator = std::find(tx_amounts_this_out.begin(), tx_amounts_this_out.end(), tx_scan_info[o].amount);
+ THROW_WALLET_EXCEPTION_IF(amount_iterator == tx_amounts_this_out.end(),
+ error::wallet_internal_error, "Unexpected values of new and old outputs");
+ tx_amounts_this_out.erase(amount_iterator);
}
else
{
@@ -2183,6 +2230,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
}
+ THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs.size() != tx_amounts_individual_outs.size(), error::wallet_internal_error, "Inconsistent size of output arrays");
+
uint64_t tx_money_spent_in_ins = 0;
// The line below is equivalent to "boost::optional<uint32_t> subaddr_account;", but avoids the GCC warning: ‘*((void*)& subaddr_account +4)’ may be used uninitialized in this function
// It's a GCC bug with boost::optional, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47679
@@ -2286,6 +2335,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
if (subaddr_account && i->first.major == *subaddr_account)
{
sub_change += i->second;
+ tx_amounts_individual_outs.erase(i->first);
i = tx_money_got_in_outs.erase(i);
}
else
@@ -2363,6 +2413,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
payment.m_tx_hash = txid;
payment.m_fee = fee;
payment.m_amount = i.second;
+ payment.m_amounts = tx_amounts_individual_outs[i.first];
payment.m_block_height = height;
payment.m_unlock_time = tx.unlock_time;
payment.m_timestamp = ts;
@@ -2847,7 +2898,7 @@ void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashe
}
//----------------------------------------------------------------------------------------------------
-void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, bool>> &process_txs, bool refreshed)
+void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed)
{
MTRACE("update_pool_state start");
@@ -2907,7 +2958,6 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b
pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
// the inputs aren't spent anymore, since the tx failed
- remove_rings(pit->second.m_tx);
for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini)
{
if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key))
@@ -3037,7 +3087,7 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b
[tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
if (i != txids.end())
{
- process_txs.push_back(std::make_pair(tx, tx_entry.double_spend_seen));
+ process_txs.push_back(std::make_tuple(tx, tx_hash, tx_entry.double_spend_seen));
}
else
{
@@ -3068,14 +3118,14 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b
MTRACE("update_pool_state end");
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_pool_state(const std::vector<std::pair<cryptonote::transaction, bool>> &txs)
+void wallet2::process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs)
{
const time_t now = time(NULL);
for (const auto &e: txs)
{
- const cryptonote::transaction &tx = e.first;
- const bool double_spend_seen = e.second;
- const crypto::hash tx_hash = get_transaction_hash(tx);
+ const cryptonote::transaction &tx = std::get<0>(e);
+ const crypto::hash &tx_hash = std::get<1>(e);
+ const bool double_spend_seen = std::get<2>(e);
process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, 0, now, false, true, double_spend_seen, {});
m_scanned_pool_txs[0].insert(tx_hash);
if (m_scanned_pool_txs[0].size() > 5000)
@@ -3114,6 +3164,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
MERROR("Blocks start before blockchain offset: " << blocks_start_height << " " << m_blockchain.offset());
return;
}
+ current_index = blocks_start_height;
if (hashes.size() + current_index < stop_height) {
drop_from_short_history(short_chain_history, 3);
std::vector<crypto::hash>::iterator right = hashes.end();
@@ -3123,7 +3174,6 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
short_chain_history.push_front(*right);
}
}
- current_index = blocks_start_height;
for(auto& bl_id: hashes)
{
if(current_index >= m_blockchain.size())
@@ -3151,11 +3201,12 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
}
-bool wallet2::add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress)
+bool wallet2::add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress)
{
wallet2::address_book_row a;
a.m_address = address;
- a.m_payment_id = payment_id;
+ a.m_has_payment_id = !!payment_id;
+ a.m_payment_id = payment_id ? *payment_id : crypto::null_hash8;
a.m_description = description;
a.m_is_subaddress = is_subaddress;
@@ -3166,11 +3217,12 @@ bool wallet2::add_address_book_row(const cryptonote::account_public_address &add
return false;
}
-bool wallet2::set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress)
+bool wallet2::set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress)
{
wallet2::address_book_row a;
a.m_address = address;
- a.m_payment_id = payment_id;
+ a.m_has_payment_id = !!payment_id;
+ a.m_payment_id = payment_id ? *payment_id : crypto::null_hash8;
a.m_description = description;
a.m_is_subaddress = is_subaddress;
@@ -3294,7 +3346,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// since that might cause a password prompt, which would introduce a data
// leak allowing a passive adversary with traffic analysis capability to
// infer when we get an incoming output
- std::vector<std::pair<cryptonote::transaction, bool>> process_pool_txs;
+ std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_pool_txs;
update_pool_state(process_pool_txs, true);
bool first = true, last = false;
@@ -4254,7 +4306,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
if(!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD)
r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
- THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file);
if (r)
setup_keys(password);
@@ -4343,12 +4395,18 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
void wallet2::encrypt_keys(const crypto::chacha_key &key)
{
+ boost::lock_guard<boost::mutex> lock(m_decrypt_keys_lock);
+ if (--m_decrypt_keys_lockers) // another lock left ?
+ return;
m_account.encrypt_keys(key);
m_account.decrypt_viewkey(key);
}
void wallet2::decrypt_keys(const crypto::chacha_key &key)
{
+ boost::lock_guard<boost::mutex> lock(m_decrypt_keys_lock);
+ if (m_decrypt_keys_lockers++) // already unlocked ?
+ return;
m_account.encrypt_viewkey(key);
m_account.decrypt_keys(key);
}
@@ -5412,6 +5470,7 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
void wallet2::set_offline(bool offline)
{
m_offline = offline;
+ m_node_rpc_proxy.set_offline(offline);
m_http_client.set_auto_connect(!offline);
if (offline)
{
@@ -7573,6 +7632,8 @@ bool wallet2::is_output_blackballed(const std::pair<uint64_t, uint64_t> &output)
bool wallet2::lock_keys_file()
{
+ if (m_wallet_file.empty())
+ return true;
if (m_keys_file_locker)
{
MDEBUG(m_keys_file << " is already locked.");
@@ -7584,6 +7645,8 @@ bool wallet2::lock_keys_file()
bool wallet2::unlock_keys_file()
{
+ if (m_wallet_file.empty())
+ return true;
if (!m_keys_file_locker)
{
MDEBUG(m_keys_file << " is already unlocked.");
@@ -7595,6 +7658,8 @@ bool wallet2::unlock_keys_file()
bool wallet2::is_keys_file_locked() const
{
+ if (m_wallet_file.empty())
+ return false;
return m_keys_file_locker->locked();
}
@@ -7733,8 +7798,50 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
}
}
+std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs)
+{
+ std::set<uint64_t> unique;
+ size_t total = 0;
+
+ for (const auto &it : outs)
+ {
+ for (const auto &out : it)
+ {
+ const uint64_t global_index = std::get<0>(out);
+ unique.insert(global_index);
+ }
+ total += it.size();
+ }
+
+ return std::make_pair(std::move(unique), total);
+}
+
void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count)
{
+ std::vector<uint64_t> rct_offsets;
+ for (size_t attempts = 3; attempts > 0; --attempts)
+ {
+ get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets);
+
+ const auto unique = outs_unique(outs);
+ if (tx_sanity_check(unique.first, unique.second, rct_offsets.empty() ? 0 : rct_offsets.back()))
+ {
+ return;
+ }
+
+ std::vector<crypto::key_image> key_images;
+ key_images.reserve(selected_transfers.size());
+ std::for_each(selected_transfers.begin(), selected_transfers.end(), [this, &key_images](size_t index) {
+ key_images.push_back(m_transfers[index].m_key_image);
+ });
+ unset_ring(key_images);
+ }
+
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, tr("Transaction sanity check failed"));
+}
+
+void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets)
+{
LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
outs.clear();
@@ -7755,7 +7862,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// if we have at least one rct out, get the distribution, or fall back to the previous system
uint64_t rct_start_height;
- std::vector<uint64_t> rct_offsets;
bool has_rct = false;
uint64_t max_rct_index = 0;
for (size_t idx: selected_transfers)
@@ -7764,7 +7870,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
has_rct = true;
max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
}
- const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
+ const bool has_rct_distribution = has_rct && (!rct_offsets.empty() || get_rct_distribution(rct_start_height, rct_offsets));
if (has_rct_distribution)
{
// check we're clear enough of rct start, to avoid corner cases below
@@ -11820,13 +11926,27 @@ void wallet2::set_account_tag_description(const std::string& tag, const std::str
m_account_tags.first[tag] = description;
}
-std::string wallet2::sign(const std::string &data) const
+std::string wallet2::sign(const std::string &data, cryptonote::subaddress_index index) const
{
crypto::hash hash;
crypto::cn_fast_hash(data.data(), data.size(), hash);
const cryptonote::account_keys &keys = m_account.get_keys();
crypto::signature signature;
- crypto::generate_signature(hash, keys.m_account_address.m_spend_public_key, keys.m_spend_secret_key, signature);
+ crypto::secret_key skey;
+ crypto::public_key pkey;
+ if (index.is_zero())
+ {
+ skey = keys.m_spend_secret_key;
+ pkey = keys.m_account_address.m_spend_public_key;
+ }
+ else
+ {
+ skey = keys.m_spend_secret_key;
+ crypto::secret_key m = m_account.get_device().get_subaddress_secret_key(keys.m_view_secret_key, index);
+ sc_add((unsigned char*)&skey, (unsigned char*)&m, (unsigned char*)&skey);
+ secret_key_to_public_key(skey, pkey);
+ }
+ crypto::generate_signature(hash, pkey, skey, signature);
return std::string("SigV1") + tools::base58::encode(std::string((const char *)&signature, sizeof(signature)));
}
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 8f840a42d..4b69cae40 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -360,10 +360,12 @@ private:
END_SERIALIZE()
};
+ typedef std::vector<uint64_t> amounts_container;
struct payment_details
{
crypto::hash m_tx_hash;
uint64_t m_amount;
+ amounts_container m_amounts;
uint64_t m_fee;
uint64_t m_block_height;
uint64_t m_unlock_time;
@@ -546,9 +548,10 @@ private:
struct address_book_row
{
cryptonote::account_public_address m_address;
- crypto::hash m_payment_id;
+ crypto::hash8 m_payment_id;
std::string m_description;
bool m_is_subaddress;
+ bool m_has_payment_id;
};
struct reserve_proof_entry
@@ -1125,8 +1128,8 @@ private:
* \brief GUI Address book get/store
*/
std::vector<address_book_row> get_address_book() const { return m_address_book; }
- bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress);
- bool set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress);
+ bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress);
+ bool set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress);
bool delete_address_book_row(std::size_t row_id);
uint64_t get_num_rct_outputs();
@@ -1184,7 +1187,7 @@ private:
*/
void set_account_tag_description(const std::string& tag, const std::string& description);
- std::string sign(const std::string &data) const;
+ std::string sign(const std::string &data, cryptonote::subaddress_index index = {0, 0}) const;
bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const;
/*!
@@ -1221,8 +1224,8 @@ private:
bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false);
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
- void update_pool_state(std::vector<std::pair<cryptonote::transaction, bool>> &process_txs, bool refreshed = false);
- void process_pool_state(const std::vector<std::pair<cryptonote::transaction, bool>> &txs);
+ void update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed = false);
+ void process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs);
void remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes);
std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const;
@@ -1386,6 +1389,8 @@ private:
uint64_t credits() const { return m_rpc_payment_state.credits; }
void credit_report(uint64_t &expected_spent, uint64_t &discrepancy) const { expected_spent = m_rpc_payment_state.expected_spent; discrepancy = m_rpc_payment_state.discrepancy; }
+ static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; }
+
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -1437,6 +1442,7 @@ private:
bool is_spent(const transfer_details &td, bool strict = true) const;
bool is_spent(size_t idx, bool strict = true) const;
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
+ void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets);
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
@@ -1615,6 +1621,8 @@ private:
crypto::chacha_key m_cache_key;
boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh;
+ boost::mutex m_decrypt_keys_lock;
+ unsigned int m_decrypt_keys_lockers;
bool m_unattended;
bool m_devices_registered;
@@ -1623,6 +1631,9 @@ private:
std::unique_ptr<wallet_device_callback> m_device_callback;
ExportFormat m_export_format;
+
+ static boost::mutex default_daemon_address_lock;
+ static std::string default_daemon_address;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 29)
@@ -1630,11 +1641,11 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
-BOOST_CLASS_VERSION(tools::wallet2::payment_details, 4)
+BOOST_CLASS_VERSION(tools::wallet2::payment_details, 5)
BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6)
-BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17)
+BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 18)
BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0)
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1)
@@ -1941,6 +1952,9 @@ namespace boost
return;
}
a & x.m_coinbase;
+ if (ver < 5)
+ return;
+ a & x.m_amounts;
}
template <class Archive>
@@ -1954,7 +1968,26 @@ namespace boost
inline void serialize(Archive& a, tools::wallet2::address_book_row& x, const boost::serialization::version_type ver)
{
a & x.m_address;
- a & x.m_payment_id;
+ if (ver < 18)
+ {
+ crypto::hash payment_id;
+ a & payment_id;
+ x.m_has_payment_id = !(payment_id == crypto::null_hash);
+ if (x.m_has_payment_id)
+ {
+ bool is_long = false;
+ for (int i = 8; i < 32; ++i)
+ is_long |= payment_id.data[i];
+ if (is_long)
+ {
+ MWARNING("Long payment ID ignored on address book load");
+ x.m_payment_id = crypto::null_hash8;
+ x.m_has_payment_id = false;
+ }
+ else
+ memcpy(x.m_payment_id.data, payment_id.data, 8);
+ }
+ }
a & x.m_description;
if (ver < 17)
{
@@ -1962,6 +1995,11 @@ namespace boost
return;
}
a & x.m_is_subaddress;
+ if (ver < 18)
+ return;
+ a & x.m_has_payment_id;
+ if (x.m_has_payment_id)
+ a & x.m_payment_id;
}
template <class Archive>
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index eb89a96e9..db2e2344b 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -326,6 +326,7 @@ namespace tools
entry.height = pd.m_block_height;
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
+ entry.amounts = pd.m_amounts;
entry.unlock_time = pd.m_unlock_time;
entry.locked = !m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height);
entry.fee = pd.m_fee;
@@ -408,6 +409,7 @@ namespace tools
entry.height = 0;
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
+ entry.amounts = pd.m_amounts;
entry.unlock_time = pd.m_unlock_time;
entry.locked = true;
entry.fee = pd.m_fee;
@@ -863,7 +865,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
template<typename Ts, typename Tu>
bool wallet_rpc_server::fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
- bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay,
+ bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, Tu &weight, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay,
Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er)
{
for (const auto & ptx : ptx_vector)
@@ -878,6 +880,7 @@ namespace tools
// Compute amount leaving wallet in tx. By convention dests does not include change outputs
fill(amount, total_amount(ptx));
fill(fee, ptx.fee);
+ fill(weight, cryptonote::get_transaction_weight(ptx.tx));
}
if (m_wallet->multisig())
@@ -963,7 +966,7 @@ namespace tools
return false;
}
- return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
+ return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.weight, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er);
}
catch (const std::exception& e)
@@ -1009,7 +1012,7 @@ namespace tools
return false;
}
- return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
@@ -1177,7 +1180,6 @@ namespace tools
}
}
- std::vector<tools::wallet2::pending_tx> ptx;
try
{
// gather info to ask the user
@@ -1373,7 +1375,7 @@ namespace tools
{
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions();
- return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
@@ -1414,13 +1416,24 @@ namespace tools
return false;
}
+ std::set<uint32_t> subaddr_indices;
+ if (req.subaddr_indices_all)
+ {
+ for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(req.account_index); ++i)
+ subaddr_indices.insert(i);
+ }
+ else
+ {
+ subaddr_indices= req.subaddr_indices;
+ }
+
try
{
uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
uint32_t priority = m_wallet->adjust_priority(req.priority);
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, subaddr_indices);
- return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
@@ -1495,7 +1508,7 @@ namespace tools
return false;
}
- return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
+ return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.weight, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er);
}
catch (const std::exception& e)
@@ -1963,7 +1976,7 @@ namespace tools
return false;
}
- res.signature = m_wallet->sign(req.data);
+ res.signature = m_wallet->sign(req.data, {req.account_index, req.address_index});
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -2458,7 +2471,7 @@ namespace tools
if (req.pool)
{
- std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
+ std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
m_wallet->update_pool_state(process_txs);
if (!process_txs.empty())
m_wallet->process_pool_state(process_txs);
@@ -2541,7 +2554,7 @@ namespace tools
}
}
- std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
+ std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
m_wallet->update_pool_state(process_txs);
if (!process_txs.empty())
m_wallet->process_pool_state(process_txs);
@@ -2745,7 +2758,14 @@ namespace tools
{
uint64_t idx = 0;
for (const auto &entry: ab)
- res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
+ {
+ std::string address;
+ if (entry.m_has_payment_id)
+ address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id);
+ else
+ address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address);
+ res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, address, entry.m_description});
+ }
}
else
{
@@ -2758,7 +2778,12 @@ namespace tools
return false;
}
const auto &entry = ab[idx];
- res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
+ std::string address;
+ if (entry.m_has_payment_id)
+ address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id);
+ else
+ address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address);
+ res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, address, entry.m_description});
}
}
return true;
@@ -2775,7 +2800,6 @@ namespace tools
}
cryptonote::address_parse_info info;
- crypto::hash payment_id = crypto::null_hash;
er.message = "";
if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), req.address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
@@ -2797,39 +2821,7 @@ namespace tools
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
return false;
}
- if (info.has_payment_id)
- {
- memcpy(payment_id.data, info.payment_id.data, 8);
- memset(payment_id.data + 8, 0, 24);
- }
- if (!req.payment_id.empty())
- {
- if (info.has_payment_id)
- {
- er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
- er.message = "Separate payment ID given with integrated address";
- return false;
- }
-
- crypto::hash long_payment_id;
-
- if (!wallet2::parse_long_payment_id(req.payment_id, payment_id))
- {
- if (!wallet2::parse_short_payment_id(req.payment_id, info.payment_id))
- {
- er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
- er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 64 character string";
- return false;
- }
- else
- {
- er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
- er.message = "Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address";
- return false;
- }
- }
- }
- if (!m_wallet->add_address_book_row(info.address, payment_id, req.description, info.is_subaddress))
+ if (!m_wallet->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : NULL, req.description, info.is_subaddress))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to add address book entry";
@@ -2860,7 +2852,6 @@ namespace tools
tools::wallet2::address_book_row entry = ab[req.index];
cryptonote::address_parse_info info;
- crypto::hash payment_id = crypto::null_hash;
if (req.set_address)
{
er.message = "";
@@ -2887,52 +2878,13 @@ namespace tools
entry.m_address = info.address;
entry.m_is_subaddress = info.is_subaddress;
if (info.has_payment_id)
- {
- memcpy(entry.m_payment_id.data, info.payment_id.data, 8);
- memset(entry.m_payment_id.data + 8, 0, 24);
- }
- }
-
- if (req.set_payment_id)
- {
- if (req.payment_id.empty())
- {
- payment_id = crypto::null_hash;
- }
- else
- {
- if (req.set_address && info.has_payment_id)
- {
- er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
- er.message = "Separate payment ID given with integrated address";
- return false;
- }
-
- if (!wallet2::parse_long_payment_id(req.payment_id, payment_id))
- {
- crypto::hash8 spid;
- if (!wallet2::parse_short_payment_id(req.payment_id, spid))
- {
- er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
- er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 64 character string";
- return false;
- }
- else
- {
- er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
- er.message = "Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address";
- return false;
- }
- }
- }
-
- entry.m_payment_id = payment_id;
+ entry.m_payment_id = info.payment_id;
}
if (req.set_description)
entry.m_description = req.description;
- if (!m_wallet->set_address_book_row(req.index, entry.m_address, entry.m_payment_id, entry.m_description, entry.m_is_subaddress))
+ if (!m_wallet->set_address_book_row(req.index, entry.m_address, req.set_address && entry.m_has_payment_id ? &entry.m_payment_id : NULL, entry.m_description, entry.m_is_subaddress))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to edit address book entry";
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 41f6879ef..89bf3a924 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -257,7 +257,7 @@ namespace tools
template<typename Ts, typename Tu>
bool fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
- bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay,
+ bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, Tu &weight, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay,
Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er);
bool validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, bool at_least_one_destination, epee::json_rpc::error& er);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 8d601b050..a212b79e6 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -490,6 +490,7 @@ namespace wallet_rpc
std::string tx_key;
uint64_t amount;
uint64_t fee;
+ uint64_t weight;
std::string tx_blob;
std::string tx_metadata;
std::string multisig_txset;
@@ -500,6 +501,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_key)
KV_SERIALIZE(amount)
KV_SERIALIZE(fee)
+ KV_SERIALIZE(weight)
KV_SERIALIZE(tx_blob)
KV_SERIALIZE(tx_metadata)
KV_SERIALIZE(multisig_txset)
@@ -556,6 +558,7 @@ namespace wallet_rpc
std::list<std::string> tx_key_list;
std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
+ std::list<uint64_t> weight_list;
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
std::string multisig_txset;
@@ -566,6 +569,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
+ KV_SERIALIZE(weight_list)
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
KV_SERIALIZE(multisig_txset)
@@ -729,6 +733,7 @@ namespace wallet_rpc
std::list<std::string> tx_key_list;
std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
+ std::list<uint64_t> weight_list;
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
std::string multisig_txset;
@@ -739,6 +744,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
+ KV_SERIALIZE(weight_list)
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
KV_SERIALIZE(multisig_txset)
@@ -755,6 +761,7 @@ namespace wallet_rpc
std::string address;
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
+ bool subaddr_indices_all;
uint32_t priority;
uint64_t ring_size;
uint64_t outputs;
@@ -770,6 +777,7 @@ namespace wallet_rpc
KV_SERIALIZE(address)
KV_SERIALIZE(account_index)
KV_SERIALIZE(subaddr_indices)
+ KV_SERIALIZE_OPT(subaddr_indices_all, false)
KV_SERIALIZE(priority)
KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
KV_SERIALIZE_OPT(outputs, (uint64_t)1)
@@ -799,6 +807,7 @@ namespace wallet_rpc
std::list<std::string> tx_key_list;
std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
+ std::list<uint64_t> weight_list;
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
std::string multisig_txset;
@@ -809,6 +818,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
+ KV_SERIALIZE(weight_list)
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
KV_SERIALIZE(multisig_txset)
@@ -856,6 +866,7 @@ namespace wallet_rpc
std::string tx_key;
uint64_t amount;
uint64_t fee;
+ uint64_t weight;
std::string tx_blob;
std::string tx_metadata;
std::string multisig_txset;
@@ -866,6 +877,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_key)
KV_SERIALIZE(amount)
KV_SERIALIZE(fee)
+ KV_SERIALIZE(weight)
KV_SERIALIZE(tx_blob)
KV_SERIALIZE(tx_metadata)
KV_SERIALIZE(multisig_txset)
@@ -1360,6 +1372,7 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};
+ typedef std::vector<uint64_t> amounts_container;
struct transfer_entry
{
std::string txid;
@@ -1367,6 +1380,7 @@ namespace wallet_rpc
uint64_t height;
uint64_t timestamp;
uint64_t amount;
+ amounts_container amounts;
uint64_t fee;
std::string note;
std::list<transfer_destination> destinations;
@@ -1386,6 +1400,7 @@ namespace wallet_rpc
KV_SERIALIZE(height);
KV_SERIALIZE(timestamp);
KV_SERIALIZE(amount);
+ KV_SERIALIZE(amounts);
KV_SERIALIZE(fee);
KV_SERIALIZE(note);
KV_SERIALIZE(destinations);
@@ -1597,9 +1612,13 @@ namespace wallet_rpc
struct request_t
{
std::string data;
+ uint32_t account_index;
+ uint32_t address_index;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(data);
+ KV_SERIALIZE(data)
+ KV_SERIALIZE_OPT(account_index, 0u)
+ KV_SERIALIZE_OPT(address_index, 0u)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -1829,12 +1848,10 @@ namespace wallet_rpc
struct request_t
{
std::string address;
- std::string payment_id;
std::string description;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
- KV_SERIALIZE(payment_id)
KV_SERIALIZE(description)
END_KV_SERIALIZE_MAP()
};
@@ -1858,8 +1875,6 @@ namespace wallet_rpc
uint64_t index;
bool set_address;
std::string address;
- bool set_payment_id;
- std::string payment_id;
bool set_description;
std::string description;
@@ -1867,8 +1882,6 @@ namespace wallet_rpc
KV_SERIALIZE(index)
KV_SERIALIZE(set_address)
KV_SERIALIZE(address)
- KV_SERIALIZE(set_payment_id)
- KV_SERIALIZE(payment_id)
KV_SERIALIZE(set_description)
KV_SERIALIZE(description)
END_KV_SERIALIZE_MAP()
@@ -1899,13 +1912,11 @@ namespace wallet_rpc
{
uint64_t index;
std::string address;
- std::string payment_id;
std::string description;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(index)
KV_SERIALIZE(address)
- KV_SERIALIZE(payment_id)
KV_SERIALIZE(description)
END_KV_SERIALIZE_MAP()
};
diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h
index 8732c85cc..aad1bc962 100644
--- a/tests/core_proxy/core_proxy.h
+++ b/tests/core_proxy/core_proxy.h
@@ -73,7 +73,6 @@ namespace tests
bool init(const boost::program_options::variables_map& vm);
bool deinit(){return true;}
bool get_short_chain_history(std::list<crypto::hash>& ids);
- bool get_stat_info(cryptonote::core_stat_info& st_inf){return true;}
bool have_block(const crypto::hash& id);
void get_blockchain_top(uint64_t& height, crypto::hash& top_id);
bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed);
@@ -110,5 +109,7 @@ namespace tests
bool pad_transactions() const { return false; }
uint32_t get_blockchain_pruning_seed() const { return 0; }
bool prune_blockchain(uint32_t pruning_seed) const { return true; }
+ bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) { return false; }
+ bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const { return false; }
};
}
diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp
index fd3f5114b..0a5b98605 100644
--- a/tests/core_tests/bulletproofs.cpp
+++ b/tests/core_tests/bulletproofs.cpp
@@ -64,7 +64,6 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
false, "Failed to generate block");
events.push_back(blocks[n]);
prev_block = blocks + n;
- LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx));
}
// rewind
diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp
index c38ea614c..f935d4f64 100644
--- a/tests/core_tests/chaingen.cpp
+++ b/tests/core_tests/chaingen.cpp
@@ -393,11 +393,17 @@ void test_generator::fill_nonce(cryptonote::block& blk, const difficulty_type& d
const cryptonote::Blockchain *blockchain = nullptr;
std::unique_ptr<cryptonote::Blockchain> bc;
- if (blk.major_version >= RX_BLOCK_VERSION)
+ if (blk.major_version >= RX_BLOCK_VERSION && diffic > 1)
{
- CHECK_AND_ASSERT_THROW_MES(m_events != nullptr, "events not set, cannot compute valid RandomX PoW");
- bc = init_blockchain(*m_events, m_nettype);
- blockchain = bc.get();
+ if (m_events == nullptr)
+ {
+ MDEBUG("events not set, RandomX PoW can fail due to zero seed hash");
+ }
+ else
+ {
+ bc = init_blockchain(*m_events, m_nettype);
+ blockchain = bc.get();
+ }
}
blk.nonce = 0;
diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h
index 12effffcf..80ce7404b 100644
--- a/tests/core_tests/chaingen.h
+++ b/tests/core_tests/chaingen.h
@@ -517,7 +517,7 @@ public:
, m_events(events)
, m_validator(validator)
, m_ev_index(0)
- , m_tx_relay(cryptonote::relay_method::flood)
+ , m_tx_relay(cryptonote::relay_method::fluff)
{
}
@@ -550,7 +550,7 @@ public:
}
else
{
- m_tx_relay = cryptonote::relay_method::flood;
+ m_tx_relay = cryptonote::relay_method::fluff;
}
return true;
diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp
index e0c90423d..ba6e2d270 100644
--- a/tests/core_tests/multisig.cpp
+++ b/tests/core_tests/multisig.cpp
@@ -180,8 +180,6 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
false, "Failed to generate block");
events.push_back(blocks[n]);
prev_block = blocks + n;
- LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx));
- LOG_PRINT_L0("in block: " << obj_to_json_str(blocks[n]));
}
// rewind
diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp
index dc77e408f..23638f62d 100644
--- a/tests/core_tests/rct.cpp
+++ b/tests/core_tests/rct.cpp
@@ -63,7 +63,6 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry
false, "Failed to generate block");
events.push_back(blocks[n]);
prev_block = blocks + n;
- LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx));
}
// rewind
diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp
index 8c2b51acf..d00d9b6f4 100644
--- a/tests/core_tests/v2_tests.cpp
+++ b/tests/core_tests/v2_tests.cpp
@@ -85,7 +85,6 @@ bool gen_v2_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
tx_source_entry& src = sources.back();
src.amount = blocks[0].miner_tx.vout[out_idx[out_idx_idx]].amount;
- std::cout << "using " << print_money(src.amount) << " output at index " << out_idx[out_idx_idx] << std::endl;
for (int m = 0; m <= mixin; ++m) {
int idx;
if (is_valid_decomposed_amount(src.amount))
diff --git a/tests/functional_tests/address_book.py b/tests/functional_tests/address_book.py
index 8d8711ffc..f9ec217af 100755
--- a/tests/functional_tests/address_book.py
+++ b/tests/functional_tests/address_book.py
@@ -73,14 +73,13 @@ class AddressBookTest():
# add one
res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', description = 'self')
- assert res.index == 0
+ assert res.index == 0, res
for get_all in [True, False]:
res = wallet.get_address_book() if get_all else wallet.get_address_book([0])
assert len(res.entries) == 1
e = res.entries[0]
assert e.index == 0
- assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
- assert e.payment_id == '' or e.payment_id == '0' * 16 or e.payment_id == '0' * 64
+ assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', e
assert e.description == 'self'
# add a duplicate
@@ -91,7 +90,6 @@ class AddressBookTest():
assert res.entries[0].index == 0
assert res.entries[1].index == 1
assert res.entries[0].address == res.entries[1].address
- assert res.entries[0].payment_id == res.entries[1].payment_id
assert res.entries[0].description == res.entries[1].description
e = res.entries[1]
res = wallet.get_address_book([1])
@@ -118,7 +116,6 @@ class AddressBookTest():
assert len(res.entries) == 1
assert res.entries[0].index == 0
assert res.entries[0].address == e.address
- assert res.entries[0].payment_id == e.payment_id
assert res.entries[0].description == e.description
# delete (new) first
@@ -165,38 +162,13 @@ class AddressBookTest():
assert res.entries[0] == e
assert res.entries[1] == e
- # payment IDs
- res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 64)
- assert res.index == 2
- ok = False
- try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = 'x' * 64)
- except: ok = True
- assert ok
- ok = False
- try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 65)
- except: ok = True
- assert ok
- ok = False
- try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 63)
- except: ok = True
- assert ok
- ok = False
- try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 16)
- except: ok = True
- assert ok
-
# various address types
res = wallet.make_integrated_address()
integrated_address = res.integrated_address
- integrated_address_payment_id = res.payment_id
- ok = False
- try: res = wallet.add_address_book(integrated_address, payment_id = '0' * 64)
- except: ok = True
- assert ok
res = wallet.add_address_book(integrated_address)
- assert res.index == 3
+ assert res.index == 2
res = wallet.add_address_book('87KfgTZ8ER5D3Frefqnrqif11TjVsTPaTcp37kqqKMrdDRUhpJRczeR7KiBmSHF32UJLP3HHhKUDmEQyJrv2mV8yFDCq8eB')
- assert res.index == 4
+ assert res.index == 3
# get them back
res = wallet.get_address_book([0])
@@ -209,16 +181,9 @@ class AddressBookTest():
assert res.entries[0].description == u'あまやかす'
res = wallet.get_address_book([2])
assert len(res.entries) == 1
- assert res.entries[0].address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ assert res.entries[0].address == integrated_address
res = wallet.get_address_book([3])
assert len(res.entries) == 1
- if False: # for now, the address book splits integrated addresses
- assert res.entries[0].address == integrated_address
- else:
- assert res.entries[0].address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
- assert res.entries[0].payment_id == integrated_address_payment_id + '0' * 48
- res = wallet.get_address_book([4])
- assert len(res.entries) == 1
assert res.entries[0].address == '87KfgTZ8ER5D3Frefqnrqif11TjVsTPaTcp37kqqKMrdDRUhpJRczeR7KiBmSHF32UJLP3HHhKUDmEQyJrv2mV8yFDCq8eB'
# edit
@@ -227,15 +192,12 @@ class AddressBookTest():
e = res.entries[0]
assert e.index == 1
assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
- assert e.payment_id == '0' * 64
assert e.description == u'あまやかす'
- res = wallet.edit_address_book(1, payment_id = '1' * 64)
res = wallet.get_address_book([1])
assert len(res.entries) == 1
e = res.entries[0]
assert e.index == 1
assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
- assert e.payment_id == '1' * 64
assert e.description == u'あまやかす'
res = wallet.edit_address_book(1, description = '')
res = wallet.get_address_book([1])
@@ -243,7 +205,6 @@ class AddressBookTest():
e = res.entries[0]
assert e.index == 1
assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
- assert e.payment_id == '1' * 64
assert e.description == ''
res = wallet.edit_address_book(1, description = 'えんしゅう')
res = wallet.get_address_book([1])
@@ -251,7 +212,6 @@ class AddressBookTest():
e = res.entries[0]
assert e.index == 1
assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
- assert e.payment_id == '1' * 64
assert e.description == u'えんしゅう'
res = wallet.edit_address_book(1, address = '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A')
res = wallet.get_address_book([1])
@@ -259,25 +219,12 @@ class AddressBookTest():
e = res.entries[0]
assert e.index == 1
assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
- assert e.payment_id == '1' * 64
- assert e.description == u'えんしゅう'
- res = wallet.edit_address_book(1, payment_id = '')
- res = wallet.get_address_book([1])
- assert len(res.entries) == 1
- e = res.entries[0]
- assert e.index == 1
- assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
- assert e.payment_id == '0' * 64
assert e.description == u'えんしゅう'
ok = False
try: res = wallet.edit_address_book(1, address = '')
except: ok = True
assert ok
ok = False
- try: res = wallet.edit_address_book(1, payment_id = 'asdnd')
- except: ok = True
- assert ok
- ok = False
try: res = wallet.edit_address_book(1, address = 'address')
except: ok = True
assert ok
@@ -287,7 +234,6 @@ class AddressBookTest():
assert e == res.entries[0]
# empty
- wallet.delete_address_book(4)
wallet.delete_address_book(0)
res = wallet.get_address_book([0]) # entries above the deleted one collapse one slot up
assert len(res.entries) == 1
diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py
index 78e0d8952..b8f8bac1a 100755
--- a/tests/functional_tests/blockchain.py
+++ b/tests/functional_tests/blockchain.py
@@ -203,10 +203,15 @@ class BlockchainTest():
res_sum = daemon.get_coinbase_tx_sum(i, 1)
res_header = daemon.getblockheaderbyheight(i)
assert res_sum.emission_amount == res_header.block_header.reward
+ assert res_sum.emission_amount_top64 == 0
+ assert res_sum.emission_amount == int(res_sum.wide_emission_amount, 16)
+ assert res_sum.fee_amount == int(res_sum.wide_fee_amount, 16)
res = daemon.get_coinbase_tx_sum(0, 1)
assert res.emission_amount == 17592186044415
+ assert res.emission_amount_top64 == 0
assert res.fee_amount == 0
+ assert res.fee_amount_top64 == 0
sum_blocks = height + nblocks - 1
res = daemon.get_coinbase_tx_sum(0, sum_blocks)
extrapolated = 17592186044415 + 17592186044415 * 2 * (sum_blocks - 1)
diff --git a/tests/functional_tests/make_test_signature.cc b/tests/functional_tests/make_test_signature.cc
index 8c0333233..789523de5 100644
--- a/tests/functional_tests/make_test_signature.cc
+++ b/tests/functional_tests/make_test_signature.cc
@@ -32,6 +32,7 @@
int main(int argc, const char **argv)
{
+ TRY_ENTRY();
if (argc > 2)
{
fprintf(stderr, "usage: %s <secret_key>\n", argv[0]);
@@ -57,4 +58,5 @@ int main(int argc, const char **argv)
std::string signature = cryptonote::make_rpc_payment_signature(skey);
printf("%s\n", signature.c_str());
return 0;
+ CATCH_ENTRY_L0("main()", 1);
}
diff --git a/tests/functional_tests/sign_message.py b/tests/functional_tests/sign_message.py
index de8f0cee2..9dd70f8bc 100755
--- a/tests/functional_tests/sign_message.py
+++ b/tests/functional_tests/sign_message.py
@@ -43,7 +43,8 @@ from framework.wallet import Wallet
class MessageSigningTest():
def run_test(self):
self.create()
- self.check_signing()
+ self.check_signing(False)
+ self.check_signing(True)
def create(self):
print('Creating wallets')
@@ -65,20 +66,34 @@ class MessageSigningTest():
assert res.address == self.address[i]
assert res.seed == seeds[i]
- def check_signing(self):
- print('Signing/verifing messages')
+ def check_signing(self, subaddress):
+ print('Signing/verifing messages with ' + ('subaddress' if subaddress else 'standard address'))
messages = ['foo', '']
+ if subaddress:
+ address = []
+ for i in range(2):
+ res = self.wallet[i].create_account()
+ if i == 0:
+ account_index = res.account_index
+ res = self.wallet[i].create_address(account_index = account_index)
+ if i == 0:
+ address_index = res.address_index
+ address.append(res.address)
+ else:
+ address = [self.address[0], self.address[1]]
+ account_index = 0
+ address_index = 0
for message in messages:
- res = self.wallet[0].sign(message)
+ res = self.wallet[0].sign(message, account_index = account_index, address_index = address_index)
signature = res.signature
for i in range(2):
- res = self.wallet[i].verify(message, self.address[0], signature)
+ res = self.wallet[i].verify(message, address[0], signature)
assert res.good
- res = self.wallet[i].verify('different', self.address[0], signature)
+ res = self.wallet[i].verify('different', address[0], signature)
assert not res.good
- res = self.wallet[i].verify(message, self.address[1], signature)
+ res = self.wallet[i].verify(message, address[1], signature)
assert not res.good
- res = self.wallet[i].verify(message, self.address[0], signature + 'x')
+ res = self.wallet[i].verify(message, address[0], signature + 'x')
assert not res.good
if __name__ == '__main__':
diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py
index 0c942f48b..3bed69b98 100755
--- a/tests/functional_tests/transfer.py
+++ b/tests/functional_tests/transfer.py
@@ -169,7 +169,7 @@ class TransferTest():
assert e.subaddr_indices == [{'major': 0, 'minor': 0}]
assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert e.double_spend_seen == False
- assert e.confirmations == 0
+ assert not 'confirmations' in e or e.confirmations == 0
running_balances[0] -= 1000000000000 + fee
@@ -282,7 +282,7 @@ class TransferTest():
assert e.subaddr_indices == [{'major': 0, 'minor': 0}]
assert e.address == '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW'
assert e.double_spend_seen == False
- assert e.confirmations == 0
+ assert not 'confirmations' in e or e.confirmations == 0
assert e.amount == amount
assert e.fee == fee
diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp
index fc2280f23..e154363e7 100644
--- a/tests/net_load_tests/clt.cpp
+++ b/tests/net_load_tests/clt.cpp
@@ -202,11 +202,11 @@ namespace
// Connect to server
std::atomic<int> conn_status(0);
- m_cmd_conn_id = boost::uuids::nil_uuid();
+ m_context = {};
ASSERT_TRUE(m_tcp_server.connect_async("127.0.0.1", srv_port, CONNECTION_TIMEOUT, [&](const test_connection_context& context, const boost::system::error_code& ec) {
if (!ec)
{
- m_cmd_conn_id = context.m_connection_id;
+ m_context = context;
}
else
{
@@ -217,11 +217,11 @@ namespace
EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&]{ return 0 != conn_status.load(std::memory_order_seq_cst); })) << "connect_async timed out";
ASSERT_EQ(1, conn_status.load(std::memory_order_seq_cst));
- ASSERT_FALSE(m_cmd_conn_id.is_nil());
+ ASSERT_FALSE(m_context.m_connection_id.is_nil());
conn_status.store(0, std::memory_order_seq_cst);
CMD_RESET_STATISTICS::request req;
- ASSERT_TRUE(epee::net_utils::async_invoke_remote_command2<CMD_RESET_STATISTICS::response>(m_cmd_conn_id, CMD_RESET_STATISTICS::ID, req,
+ ASSERT_TRUE(epee::net_utils::async_invoke_remote_command2<CMD_RESET_STATISTICS::response>(m_context, CMD_RESET_STATISTICS::ID, req,
m_tcp_server.get_config_object(), [&](int code, const CMD_RESET_STATISTICS::response& rsp, const test_connection_context&) {
conn_status.store(code, std::memory_order_seq_cst);
}));
@@ -250,16 +250,16 @@ namespace
// Connect to server and invoke shutdown command
std::atomic<int> conn_status(0);
- boost::uuids::uuid cmd_conn_id = boost::uuids::nil_uuid();
+ test_connection_context cmd_context;
tcp_server.connect_async("127.0.0.1", srv_port, CONNECTION_TIMEOUT, [&](const test_connection_context& context, const boost::system::error_code& ec) {
- cmd_conn_id = context.m_connection_id;
+ cmd_context = context;
conn_status.store(!ec ? 1 : -1, std::memory_order_seq_cst);
});
if (!busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&]{ return 0 != conn_status.load(std::memory_order_seq_cst); })) return;
if (1 != conn_status.load(std::memory_order_seq_cst)) return;
- epee::net_utils::notify_remote_command2(cmd_conn_id, CMD_SHUTDOWN::ID, CMD_SHUTDOWN::request(), tcp_server.get_config_object());
+ epee::net_utils::notify_remote_command2(cmd_context, CMD_SHUTDOWN::ID, CMD_SHUTDOWN::request(), tcp_server.get_config_object());
busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&]{ return 0 != commands_handler.close_connection_counter(); });
}
@@ -299,7 +299,7 @@ namespace
{
std::atomic<int> req_status(0);
CMD_GET_STATISTICS::request req;
- ASSERT_TRUE(epee::net_utils::async_invoke_remote_command2<CMD_GET_STATISTICS::response>(m_cmd_conn_id, CMD_GET_STATISTICS::ID, req,
+ ASSERT_TRUE(epee::net_utils::async_invoke_remote_command2<CMD_GET_STATISTICS::response>(m_context, CMD_GET_STATISTICS::ID, req,
m_tcp_server.get_config_object(), [&](int code, const CMD_GET_STATISTICS::response& rsp, const test_connection_context&) {
if (0 < code)
{
@@ -338,14 +338,14 @@ namespace
{
CMD_SEND_DATA_REQUESTS::request req;
req.request_size = request_size;
- epee::net_utils::notify_remote_command2(m_cmd_conn_id, CMD_SEND_DATA_REQUESTS::ID, req, m_tcp_server.get_config_object());
+ epee::net_utils::notify_remote_command2(m_context, CMD_SEND_DATA_REQUESTS::ID, req, m_tcp_server.get_config_object());
}
protected:
test_tcp_server m_tcp_server;
test_levin_commands_handler m_commands_handler;
size_t m_thread_count;
- boost::uuids::uuid m_cmd_conn_id;
+ test_connection_context m_context;
};
}
@@ -434,7 +434,7 @@ TEST_F(net_load_test_clt, a_lot_of_client_connections_and_connections_closed_by_
// Close connections
CMD_CLOSE_ALL_CONNECTIONS::request req;
- ASSERT_TRUE(epee::net_utils::notify_remote_command2(m_cmd_conn_id, CMD_CLOSE_ALL_CONNECTIONS::ID, req, m_tcp_server.get_config_object()));
+ ASSERT_TRUE(epee::net_utils::notify_remote_command2(m_context, CMD_CLOSE_ALL_CONNECTIONS::ID, req, m_tcp_server.get_config_object()));
// Wait for all opened connections to close
busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&](){ return m_commands_handler.new_connection_counter() - RESERVED_CONN_CNT <= m_commands_handler.close_connection_counter(); });
@@ -455,10 +455,10 @@ TEST_F(net_load_test_clt, a_lot_of_client_connections_and_connections_closed_by_
// Close rest connections
m_tcp_server.get_config_object().foreach_connection([&](test_connection_context& ctx) {
- if (ctx.m_connection_id != m_cmd_conn_id)
+ if (ctx.m_connection_id != m_context.m_connection_id)
{
CMD_DATA_REQUEST::request req;
- bool r = epee::net_utils::async_invoke_remote_command2<CMD_DATA_REQUEST::response>(ctx.m_connection_id, CMD_DATA_REQUEST::ID, req,
+ bool r = epee::net_utils::async_invoke_remote_command2<CMD_DATA_REQUEST::response>(ctx, CMD_DATA_REQUEST::ID, req,
m_tcp_server.get_config_object(), [=](int code, const CMD_DATA_REQUEST::response& rsp, const test_connection_context&) {
if (code <= 0)
{
@@ -548,7 +548,7 @@ TEST_F(net_load_test_clt, permament_open_and_close_and_connections_closed_by_ser
CMD_START_OPEN_CLOSE_TEST::request req_start;
req_start.open_request_target = CONNECTION_COUNT;
req_start.max_opened_conn_count = MAX_OPENED_CONN_COUNT;
- ASSERT_TRUE(epee::net_utils::async_invoke_remote_command2<CMD_START_OPEN_CLOSE_TEST::response>(m_cmd_conn_id, CMD_START_OPEN_CLOSE_TEST::ID, req_start,
+ ASSERT_TRUE(epee::net_utils::async_invoke_remote_command2<CMD_START_OPEN_CLOSE_TEST::response>(m_context, CMD_START_OPEN_CLOSE_TEST::ID, req_start,
m_tcp_server.get_config_object(), [&](int code, const CMD_START_OPEN_CLOSE_TEST::response&, const test_connection_context&) {
test_state.store(0 < code ? 1 : -1, std::memory_order_seq_cst);
}));
@@ -582,7 +582,7 @@ TEST_F(net_load_test_clt, permament_open_and_close_and_connections_closed_by_ser
// Ask server to close rest connections
CMD_CLOSE_ALL_CONNECTIONS::request req;
- ASSERT_TRUE(epee::net_utils::notify_remote_command2(m_cmd_conn_id, CMD_CLOSE_ALL_CONNECTIONS::ID, req, m_tcp_server.get_config_object()));
+ ASSERT_TRUE(epee::net_utils::notify_remote_command2(m_context, CMD_CLOSE_ALL_CONNECTIONS::ID, req, m_tcp_server.get_config_object()));
// Wait for almost all connections to be closed by server
busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&](){ return m_commands_handler.new_connection_counter() <= m_commands_handler.close_connection_counter() + RESERVED_CONN_CNT; });
@@ -601,10 +601,10 @@ TEST_F(net_load_test_clt, permament_open_and_close_and_connections_closed_by_ser
// Close rest connections
m_tcp_server.get_config_object().foreach_connection([&](test_connection_context& ctx) {
- if (ctx.m_connection_id != m_cmd_conn_id)
+ if (ctx.m_connection_id != m_context.m_connection_id)
{
CMD_DATA_REQUEST::request req;
- bool r = epee::net_utils::async_invoke_remote_command2<CMD_DATA_REQUEST::response>(ctx.m_connection_id, CMD_DATA_REQUEST::ID, req,
+ bool r = epee::net_utils::async_invoke_remote_command2<CMD_DATA_REQUEST::response>(ctx, CMD_DATA_REQUEST::ID, req,
m_tcp_server.get_config_object(), [=](int code, const CMD_DATA_REQUEST::response& rsp, const test_connection_context&) {
if (code <= 0)
{
diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h
index cdc2d267a..4a76f2ec6 100644
--- a/tests/net_load_tests/net_load_tests.h
+++ b/tests/net_load_tests/net_load_tests.h
@@ -47,6 +47,7 @@ namespace net_load_tests
{
struct test_connection_context : epee::net_utils::connection_context_base
{
+ test_connection_context(): epee::net_utils::connection_context_base(boost::uuids::nil_uuid(), {}, false, false), m_closed(false) {}
volatile bool m_closed;
};
diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp
index fe32ec5cb..b42b1e1b0 100644
--- a/tests/net_load_tests/srv.cpp
+++ b/tests/net_load_tests/srv.cpp
@@ -147,7 +147,7 @@ namespace
CMD_DATA_REQUEST::request req2;
req2.data.resize(req.request_size);
- bool r = epee::net_utils::async_invoke_remote_command2<CMD_DATA_REQUEST::response>(ctx.m_connection_id, CMD_DATA_REQUEST::ID, req2,
+ bool r = epee::net_utils::async_invoke_remote_command2<CMD_DATA_REQUEST::response>(ctx, CMD_DATA_REQUEST::ID, req2,
m_tcp_server.get_config_object(), [=](int code, const CMD_DATA_REQUEST::response& rsp, const test_connection_context&) {
if (code <= 0)
{
diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp
index 760769551..a73b80848 100644
--- a/tests/unit_tests/bulletproofs.cpp
+++ b/tests/unit_tests/bulletproofs.cpp
@@ -179,15 +179,6 @@ TEST(bulletproofs, invalid_31)
ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
}
-TEST(bulletproofs, invalid_gamma_0)
-{
- rct::key invalid_amount = rct::zero();
- invalid_amount[8] = 1;
- rct::key gamma = rct::zero();
- rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, gamma);
- ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
-}
-
static const char * const torsion_elements[] =
{
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp
index 6f887afda..513c2227c 100644
--- a/tests/unit_tests/epee_utils.cpp
+++ b/tests/unit_tests/epee_utils.cpp
@@ -45,6 +45,7 @@
#include "boost/archive/portable_binary_iarchive.hpp"
#include "boost/archive/portable_binary_oarchive.hpp"
#include "byte_slice.h"
+#include "crypto/crypto.h"
#include "hex.h"
#include "net/net_utils_base.h"
#include "net/local_ip.h"
@@ -850,6 +851,17 @@ TEST(ToHex, Array)
);
}
+TEST(ToHex, ArrayFromPod)
+{
+ std::array<char, 64> expected{{'5', 'f', '2', 'b', '0', '1'}};
+ std::fill(expected.begin() + 6, expected.end(), '0');
+
+ EXPECT_EQ(
+ expected,
+ (epee::to_hex::array(crypto::ec_point{{0x5F, 0x2B, 0x01, 0x00}}))
+ );
+}
+
TEST(ToHex, Ostream)
{
std::stringstream out;
diff --git a/tests/unit_tests/json_serialization.cpp b/tests/unit_tests/json_serialization.cpp
index 53d9f84c7..6f98d854d 100644
--- a/tests/unit_tests/json_serialization.cpp
+++ b/tests/unit_tests/json_serialization.cpp
@@ -3,6 +3,8 @@
#include <boost/range/adaptor/indexed.hpp>
#include <gtest/gtest.h>
#include <rapidjson/document.h>
+#include <rapidjson/stringbuffer.h>
+#include <rapidjson/writer.h>
#include <vector>
#include "crypto/hash.h"
@@ -80,6 +82,27 @@ namespace
return tx;
}
+
+ template<typename T>
+ T test_json(const T& value)
+ {
+ rapidjson::StringBuffer buffer;
+ {
+ rapidjson::Writer<rapidjson::StringBuffer> dest{buffer};
+ cryptonote::json::toJsonValue(dest, value);
+ }
+
+ rapidjson::Document doc;
+ doc.Parse(buffer.GetString());
+ if (doc.HasParseError() || !doc.IsObject())
+ {
+ throw cryptonote::json::PARSE_FAIL();
+ }
+
+ T out{};
+ cryptonote::json::fromJsonValue(doc, out);
+ return out;
+ }
} // anonymous
TEST(JsonSerialization, MinerTransaction)
@@ -91,11 +114,7 @@ TEST(JsonSerialization, MinerTransaction)
crypto::hash tx_hash{};
ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx, tx_hash));
- rapidjson::Document doc;
- cryptonote::json::toJsonValue(doc, miner_tx, doc);
-
- cryptonote::transaction miner_tx_copy;
- cryptonote::json::fromJsonValue(doc, miner_tx_copy);
+ cryptonote::transaction miner_tx_copy = test_json(miner_tx);
crypto::hash tx_copy_hash{};
ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx_copy, tx_copy_hash));
@@ -126,11 +145,7 @@ TEST(JsonSerialization, RegularTransaction)
crypto::hash tx_hash{};
ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash));
- rapidjson::Document doc;
- cryptonote::json::toJsonValue(doc, tx, doc);
-
- cryptonote::transaction tx_copy;
- cryptonote::json::fromJsonValue(doc, tx_copy);
+ cryptonote::transaction tx_copy = test_json(tx);
crypto::hash tx_copy_hash{};
ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash));
@@ -161,11 +176,7 @@ TEST(JsonSerialization, RingctTransaction)
crypto::hash tx_hash{};
ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash));
- rapidjson::Document doc;
- cryptonote::json::toJsonValue(doc, tx, doc);
-
- cryptonote::transaction tx_copy;
- cryptonote::json::fromJsonValue(doc, tx_copy);
+ cryptonote::transaction tx_copy = test_json(tx);
crypto::hash tx_copy_hash{};
ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash));
@@ -196,11 +207,7 @@ TEST(JsonSerialization, BulletproofTransaction)
crypto::hash tx_hash{};
ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash));
- rapidjson::Document doc;
- cryptonote::json::toJsonValue(doc, tx, doc);
-
- cryptonote::transaction tx_copy;
- cryptonote::json::fromJsonValue(doc, tx_copy);
+ cryptonote::transaction tx_copy = test_json(tx);
crypto::hash tx_copy_hash{};
ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash));
diff --git a/tests/unit_tests/levin.cpp b/tests/unit_tests/levin.cpp
index e5ca4e41e..38707f075 100644
--- a/tests/unit_tests/levin.cpp
+++ b/tests/unit_tests/levin.cpp
@@ -271,12 +271,12 @@ namespace
EXPECT_EQ(connection_ids_.size(), connections_->get_connections_count());
}
- cryptonote::levin::notify make_notifier(const std::size_t noise_size, bool is_public)
+ cryptonote::levin::notify make_notifier(const std::size_t noise_size, bool is_public, bool pad_txs)
{
epee::byte_slice noise = nullptr;
if (noise_size)
noise = epee::levin::make_noise_notify(noise_size);
- return cryptonote::levin::notify{io_service_, connections_, std::move(noise), is_public};
+ return cryptonote::levin::notify{io_service_, connections_, std::move(noise), is_public, pad_txs};
}
boost::uuids::random_generator random_generator_;
@@ -434,12 +434,16 @@ TEST_F(levin_notify, defaulted)
EXPECT_FALSE(status.has_noise);
EXPECT_FALSE(status.connections_filled);
}
- EXPECT_FALSE(notifier.send_txs({}, random_generator_(), false));
+ EXPECT_TRUE(notifier.send_txs({}, random_generator_()));
+
+ std::vector<cryptonote::blobdata> txs(2);
+ txs[0].resize(100, 'e');
+ EXPECT_FALSE(notifier.send_txs(std::move(txs), random_generator_()));
}
-TEST_F(levin_notify, flood)
+TEST_F(levin_notify, fluff_without_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, true);
+ cryptonote::levin::notify notifier = make_notifier(0, true, false);
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -464,10 +468,13 @@ TEST_F(levin_notify, flood)
ASSERT_EQ(10u, contexts_.size());
{
auto context = contexts_.begin();
- EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), false));
+ EXPECT_TRUE(notifier.send_txs(txs, context->get_id()));
io_service_.reset();
ASSERT_LT(0u, io_service_.poll());
+ notifier.run_fluff();
+ ASSERT_LT(0u, io_service_.poll());
+
EXPECT_EQ(0u, context->process_send_queue());
for (++context; context != contexts_.end(); ++context)
EXPECT_EQ(1u, context->process_send_queue());
@@ -480,14 +487,42 @@ TEST_F(levin_notify, flood)
EXPECT_TRUE(notification._.empty());
}
}
+}
+
+TEST_F(levin_notify, fluff_with_padding)
+{
+ cryptonote::levin::notify notifier = make_notifier(0, true, true);
+
+ for (unsigned count = 0; count < 10; ++count)
+ add_connection(count % 2 == 0);
+
+ {
+ const auto status = notifier.get_status();
+ EXPECT_FALSE(status.has_noise);
+ EXPECT_FALSE(status.connections_filled);
+ }
+ notifier.new_out_connection();
+ io_service_.poll();
+ {
+ const auto status = notifier.get_status();
+ EXPECT_FALSE(status.has_noise);
+ EXPECT_FALSE(status.connections_filled); // not tracked
+ }
+
+ std::vector<cryptonote::blobdata> txs(2);
+ txs[0].resize(100, 'e');
+ txs[1].resize(200, 'f');
ASSERT_EQ(10u, contexts_.size());
{
auto context = contexts_.begin();
- EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), true));
+ EXPECT_TRUE(notifier.send_txs(txs, context->get_id()));
io_service_.reset();
ASSERT_LT(0u, io_service_.poll());
+ notifier.run_fluff();
+ ASSERT_LT(0u, io_service_.poll());
+
EXPECT_EQ(0u, context->process_send_queue());
for (++context; context != contexts_.end(); ++context)
EXPECT_EQ(1u, context->process_send_queue());
@@ -502,9 +537,9 @@ TEST_F(levin_notify, flood)
}
}
-TEST_F(levin_notify, private_flood)
+TEST_F(levin_notify, private_fluff_without_padding)
{
- cryptonote::levin::notify notifier = make_notifier(0, false);
+ cryptonote::levin::notify notifier = make_notifier(0, false, false);
for (unsigned count = 0; count < 10; ++count)
add_connection(count % 2 == 0);
@@ -529,10 +564,14 @@ TEST_F(levin_notify, private_flood)
ASSERT_EQ(10u, contexts_.size());
{
auto context = contexts_.begin();
- EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), false));
+ EXPECT_TRUE(notifier.send_txs(txs, context->get_id()));
io_service_.reset();
ASSERT_LT(0u, io_service_.poll());
+ notifier.run_fluff();
+ io_service_.reset();
+ ASSERT_LT(0u, io_service_.poll());
+
EXPECT_EQ(0u, context->process_send_queue());
for (++context; context != contexts_.end(); ++context)
{
@@ -548,14 +587,43 @@ TEST_F(levin_notify, private_flood)
EXPECT_TRUE(notification._.empty());
}
}
+}
+
+TEST_F(levin_notify, private_fluff_with_padding)
+{
+ cryptonote::levin::notify notifier = make_notifier(0, false, true);
+
+ for (unsigned count = 0; count < 10; ++count)
+ add_connection(count % 2 == 0);
+
+ {
+ const auto status = notifier.get_status();
+ EXPECT_FALSE(status.has_noise);
+ EXPECT_FALSE(status.connections_filled);
+ }
+ notifier.new_out_connection();
+ io_service_.poll();
+ {
+ const auto status = notifier.get_status();
+ EXPECT_FALSE(status.has_noise);
+ EXPECT_FALSE(status.connections_filled); // not tracked
+ }
+
+ std::vector<cryptonote::blobdata> txs(2);
+ txs[0].resize(100, 'e');
+ txs[1].resize(200, 'f');
ASSERT_EQ(10u, contexts_.size());
{
auto context = contexts_.begin();
- EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), true));
+ EXPECT_TRUE(notifier.send_txs(txs, context->get_id()));
io_service_.reset();
ASSERT_LT(0u, io_service_.poll());
+ notifier.run_fluff();
+ io_service_.reset();
+ ASSERT_LT(0u, io_service_.poll());
+
EXPECT_EQ(0u, context->process_send_queue());
for (++context; context != contexts_.end(); ++context)
{
@@ -582,7 +650,7 @@ TEST_F(levin_notify, noise)
txs[0].resize(1900, 'h');
const boost::uuids::uuid incoming_id = random_generator_();
- cryptonote::levin::notify notifier = make_notifier(2048, false);
+ cryptonote::levin::notify notifier = make_notifier(2048, false, true);
{
const auto status = notifier.get_status();
@@ -608,7 +676,7 @@ TEST_F(levin_notify, noise)
EXPECT_EQ(0u, receiver_.notified_size());
}
- EXPECT_TRUE(notifier.send_txs(txs, incoming_id, false));
+ EXPECT_TRUE(notifier.send_txs(txs, incoming_id));
notifier.run_stems();
io_service_.reset();
ASSERT_LT(0u, io_service_.poll());
@@ -627,7 +695,7 @@ TEST_F(levin_notify, noise)
}
txs[0].resize(3000, 'r');
- EXPECT_TRUE(notifier.send_txs(txs, incoming_id, true));
+ EXPECT_TRUE(notifier.send_txs(txs, incoming_id));
notifier.run_stems();
io_service_.reset();
ASSERT_LT(0u, io_service_.poll());
diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp
index 262541bd2..36cb28ae0 100644
--- a/tests/unit_tests/net.cpp
+++ b/tests/unit_tests/net.cpp
@@ -53,8 +53,10 @@
#include <memory>
#include <type_traits>
+#include "crypto/crypto.h"
#include "net/dandelionpp.h"
#include "net/error.h"
+#include "net/i2p_address.h"
#include "net/net_utils_base.h"
#include "net/socks.h"
#include "net/socks_connect.h"
@@ -177,22 +179,33 @@ TEST(tor_address, valid)
EXPECT_FALSE(address2.less(*address1));
EXPECT_TRUE(address1->less(address2));
- address2 = MONERO_UNWRAP(net::tor_address::make(std::string{v3_onion} + ":", 65535));
-
- EXPECT_EQ(65535, address2.port());
- EXPECT_STREQ(v3_onion, address2.host_str());
- EXPECT_EQ(std::string{v3_onion} + ":65535", address2.str().c_str());
- EXPECT_TRUE(address2.is_blockable());
- EXPECT_FALSE(address2.equal(*address1));
- EXPECT_FALSE(address1->equal(address2));
- EXPECT_FALSE(address2 == *address1);
- EXPECT_FALSE(*address1 == address2);
- EXPECT_TRUE(address2 != *address1);
- EXPECT_TRUE(*address1 != address2);
- EXPECT_TRUE(address2.is_same_host(*address1));
- EXPECT_TRUE(address1->is_same_host(address2));
- EXPECT_FALSE(address2.less(*address1));
- EXPECT_TRUE(address1->less(address2));
+ net::tor_address address3 = MONERO_UNWRAP(net::tor_address::make(std::string{v3_onion} + ":", 65535));
+
+ EXPECT_EQ(65535, address3.port());
+ EXPECT_STREQ(v3_onion, address3.host_str());
+ EXPECT_EQ(std::string{v3_onion} + ":65535", address3.str().c_str());
+ EXPECT_TRUE(address3.is_blockable());
+ EXPECT_FALSE(address3.equal(*address1));
+ EXPECT_FALSE(address1->equal(address3));
+ EXPECT_FALSE(address3 == *address1);
+ EXPECT_FALSE(*address1 == address3);
+ EXPECT_TRUE(address3 != *address1);
+ EXPECT_TRUE(*address1 != address3);
+ EXPECT_TRUE(address3.is_same_host(*address1));
+ EXPECT_TRUE(address1->is_same_host(address3));
+ EXPECT_FALSE(address3.less(*address1));
+ EXPECT_TRUE(address1->less(address3));
+
+ EXPECT_FALSE(address3.equal(address2));
+ EXPECT_FALSE(address2.equal(address3));
+ EXPECT_FALSE(address3 == address2);
+ EXPECT_FALSE(address2 == address3);
+ EXPECT_TRUE(address3 != address2);
+ EXPECT_TRUE(address2 != address3);
+ EXPECT_FALSE(address3.is_same_host(address2));
+ EXPECT_FALSE(address2.is_same_host(address3));
+ EXPECT_TRUE(address3.less(address2));
+ EXPECT_FALSE(address2.less(address3));
}
TEST(tor_address, generic_network_address)
@@ -220,7 +233,7 @@ TEST(tor_address, generic_network_address)
namespace
{
- struct test_command
+ struct test_command_tor
{
net::tor_address tor;
@@ -234,7 +247,7 @@ TEST(tor_address, epee_serializev_v2)
{
std::string buffer{};
{
- test_command command{MONERO_UNWRAP(net::tor_address::make(v2_onion, 10))};
+ test_command_tor command{MONERO_UNWRAP(net::tor_address::make(v2_onion, 10))};
EXPECT_FALSE(command.tor.is_unknown());
EXPECT_NE(net::tor_address{}, command.tor);
EXPECT_STREQ(v2_onion, command.tor.host_str());
@@ -245,7 +258,7 @@ TEST(tor_address, epee_serializev_v2)
EXPECT_TRUE(stg.store_to_binary(buffer));
}
- test_command command{};
+ test_command_tor command{};
{
EXPECT_TRUE(command.tor.is_unknown());
EXPECT_EQ(net::tor_address{}, command.tor);
@@ -271,7 +284,7 @@ TEST(tor_address, epee_serializev_v2)
EXPECT_EQ(std::strlen(v2_onion), host.size());
host.push_back('k');
- EXPECT_TRUE(stg.set_value("host", host, stg.open_section("tor", nullptr, false)));
+ EXPECT_TRUE(stg.set_value("host", std::move(host), stg.open_section("tor", nullptr, false)));
EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE`
}
@@ -285,7 +298,7 @@ TEST(tor_address, epee_serializev_v3)
{
std::string buffer{};
{
- test_command command{MONERO_UNWRAP(net::tor_address::make(v3_onion, 10))};
+ test_command_tor command{MONERO_UNWRAP(net::tor_address::make(v3_onion, 10))};
EXPECT_FALSE(command.tor.is_unknown());
EXPECT_NE(net::tor_address{}, command.tor);
EXPECT_STREQ(v3_onion, command.tor.host_str());
@@ -296,7 +309,7 @@ TEST(tor_address, epee_serializev_v3)
EXPECT_TRUE(stg.store_to_binary(buffer));
}
- test_command command{};
+ test_command_tor command{};
{
EXPECT_TRUE(command.tor.is_unknown());
EXPECT_EQ(net::tor_address{}, command.tor);
@@ -322,7 +335,7 @@ TEST(tor_address, epee_serializev_v3)
EXPECT_EQ(std::strlen(v3_onion), host.size());
host.push_back('k');
- EXPECT_TRUE(stg.set_value("host", host, stg.open_section("tor", nullptr, false)));
+ EXPECT_TRUE(stg.set_value("host", std::move(host), stg.open_section("tor", nullptr, false)));
EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE`
}
@@ -336,7 +349,7 @@ TEST(tor_address, epee_serialize_unknown)
{
std::string buffer{};
{
- test_command command{net::tor_address::unknown()};
+ test_command_tor command{net::tor_address::unknown()};
EXPECT_TRUE(command.tor.is_unknown());
EXPECT_EQ(net::tor_address{}, command.tor);
EXPECT_STREQ(net::tor_address::unknown_str(), command.tor.host_str());
@@ -347,7 +360,7 @@ TEST(tor_address, epee_serialize_unknown)
EXPECT_TRUE(stg.store_to_binary(buffer));
}
- test_command command{};
+ test_command_tor command{};
{
EXPECT_TRUE(command.tor.is_unknown());
EXPECT_EQ(net::tor_address{}, command.tor);
@@ -373,7 +386,7 @@ TEST(tor_address, epee_serialize_unknown)
EXPECT_EQ(std::strlen(net::tor_address::unknown_str()), host.size());
host.push_back('k');
- EXPECT_TRUE(stg.set_value("host", host, stg.open_section("tor", nullptr, false)));
+ EXPECT_TRUE(stg.set_value("host", std::move(host), stg.open_section("tor", nullptr, false)));
EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE`
}
@@ -513,6 +526,374 @@ TEST(get_network_address, onion)
EXPECT_EQ(net::error::invalid_port, address);
}
+namespace
+{
+ static constexpr const char b32_i2p[] =
+ "vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopn.b32.i2p";
+ static constexpr const char b32_i2p_2[] =
+ "xmrto2bturnore26xmrto2bturnore26xmrto2bturnore26xmr2.b32.i2p";
+}
+
+TEST(i2p_address, constants)
+{
+ static_assert(!net::i2p_address::is_local(), "bad is_local() response");
+ static_assert(!net::i2p_address::is_loopback(), "bad is_loopback() response");
+ static_assert(net::i2p_address::get_type_id() == epee::net_utils::address_type::i2p, "bad get_type_id() response");
+
+ EXPECT_FALSE(net::i2p_address::is_local());
+ EXPECT_FALSE(net::i2p_address::is_loopback());
+ EXPECT_EQ(epee::net_utils::address_type::i2p, net::i2p_address::get_type_id());
+ EXPECT_EQ(epee::net_utils::address_type::i2p, net::i2p_address::get_type_id());
+}
+
+TEST(i2p_address, invalid)
+{
+ EXPECT_TRUE(net::i2p_address::make("").has_error());
+ EXPECT_TRUE(net::i2p_address::make(":").has_error());
+ EXPECT_TRUE(net::i2p_address::make(".b32.i2p").has_error());
+ EXPECT_TRUE(net::i2p_address::make(".b32.i2p:").has_error());
+ EXPECT_TRUE(net::i2p_address::make(b32_i2p + 1).has_error());
+ EXPECT_TRUE(net::i2p_address::make(boost::string_ref{b32_i2p, sizeof(b32_i2p) - 2}).has_error());
+ EXPECT_TRUE(net::i2p_address::make(std::string{b32_i2p} + ":65536").has_error());
+ EXPECT_TRUE(net::i2p_address::make(std::string{b32_i2p} + ":-1").has_error());
+
+ std::string i2p{b32_i2p};
+ i2p.at(10) = 1;
+ EXPECT_TRUE(net::i2p_address::make(i2p).has_error());
+}
+
+TEST(i2p_address, unblockable_types)
+{
+ net::i2p_address i2p{};
+
+ ASSERT_NE(nullptr, i2p.host_str());
+ EXPECT_STREQ("<unknown i2p host>", i2p.host_str());
+ EXPECT_STREQ("<unknown i2p host>", i2p.str().c_str());
+ EXPECT_EQ(0u, i2p.port());
+ EXPECT_TRUE(i2p.is_unknown());
+ EXPECT_FALSE(i2p.is_local());
+ EXPECT_FALSE(i2p.is_loopback());
+ EXPECT_EQ(epee::net_utils::address_type::i2p, i2p.get_type_id());
+ EXPECT_EQ(epee::net_utils::zone::i2p, i2p.get_zone());
+
+ i2p = net::i2p_address::unknown();
+ ASSERT_NE(nullptr, i2p.host_str());
+ EXPECT_STREQ("<unknown i2p host>", i2p.host_str());
+ EXPECT_STREQ("<unknown i2p host>", i2p.str().c_str());
+ EXPECT_EQ(0u, i2p.port());
+ EXPECT_TRUE(i2p.is_unknown());
+ EXPECT_FALSE(i2p.is_local());
+ EXPECT_FALSE(i2p.is_loopback());
+ EXPECT_EQ(epee::net_utils::address_type::i2p, i2p.get_type_id());
+ EXPECT_EQ(epee::net_utils::zone::i2p, i2p.get_zone());
+
+ EXPECT_EQ(net::i2p_address{}, net::i2p_address::unknown());
+}
+
+TEST(i2p_address, valid)
+{
+ const auto address1 = net::i2p_address::make(b32_i2p);
+
+ ASSERT_TRUE(address1.has_value());
+ EXPECT_EQ(0u, address1->port());
+ EXPECT_STREQ(b32_i2p, address1->host_str());
+ EXPECT_STREQ(b32_i2p, address1->str().c_str());
+ EXPECT_TRUE(address1->is_blockable());
+
+ net::i2p_address address2{*address1};
+
+ EXPECT_EQ(0u, address2.port());
+ EXPECT_STREQ(b32_i2p, address2.host_str());
+ EXPECT_STREQ(b32_i2p, address2.str().c_str());
+ EXPECT_TRUE(address2.is_blockable());
+ EXPECT_TRUE(address2.equal(*address1));
+ EXPECT_TRUE(address1->equal(address2));
+ EXPECT_TRUE(address2 == *address1);
+ EXPECT_TRUE(*address1 == address2);
+ EXPECT_FALSE(address2 != *address1);
+ EXPECT_FALSE(*address1 != address2);
+ EXPECT_TRUE(address2.is_same_host(*address1));
+ EXPECT_TRUE(address1->is_same_host(address2));
+ EXPECT_FALSE(address2.less(*address1));
+ EXPECT_FALSE(address1->less(address2));
+
+ address2 = MONERO_UNWRAP(net::i2p_address::make(std::string{b32_i2p_2} + ":6545"));
+
+ EXPECT_EQ(6545, address2.port());
+ EXPECT_STREQ(b32_i2p_2, address2.host_str());
+ EXPECT_EQ(std::string{b32_i2p_2} + ":6545", address2.str().c_str());
+ EXPECT_TRUE(address2.is_blockable());
+ EXPECT_FALSE(address2.equal(*address1));
+ EXPECT_FALSE(address1->equal(address2));
+ EXPECT_FALSE(address2 == *address1);
+ EXPECT_FALSE(*address1 == address2);
+ EXPECT_TRUE(address2 != *address1);
+ EXPECT_TRUE(*address1 != address2);
+ EXPECT_FALSE(address2.is_same_host(*address1));
+ EXPECT_FALSE(address1->is_same_host(address2));
+ EXPECT_FALSE(address2.less(*address1));
+ EXPECT_TRUE(address1->less(address2));
+
+ net::i2p_address address3 = MONERO_UNWRAP(net::i2p_address::make(std::string{b32_i2p} + ":", 65535));
+
+ EXPECT_EQ(65535, address3.port());
+ EXPECT_STREQ(b32_i2p, address3.host_str());
+ EXPECT_EQ(std::string{b32_i2p} + ":65535", address3.str().c_str());
+ EXPECT_TRUE(address3.is_blockable());
+ EXPECT_FALSE(address3.equal(*address1));
+ EXPECT_FALSE(address1->equal(address3));
+ EXPECT_FALSE(address3 == *address1);
+ EXPECT_FALSE(*address1 == address3);
+ EXPECT_TRUE(address3 != *address1);
+ EXPECT_TRUE(*address1 != address3);
+ EXPECT_TRUE(address3.is_same_host(*address1));
+ EXPECT_TRUE(address1->is_same_host(address3));
+ EXPECT_FALSE(address3.less(*address1));
+ EXPECT_TRUE(address1->less(address3));
+
+ EXPECT_FALSE(address3.equal(address2));
+ EXPECT_FALSE(address2.equal(address3));
+ EXPECT_FALSE(address3 == address2);
+ EXPECT_FALSE(address2 == address3);
+ EXPECT_TRUE(address3 != address2);
+ EXPECT_TRUE(address2 != address3);
+ EXPECT_FALSE(address3.is_same_host(address2));
+ EXPECT_FALSE(address2.is_same_host(address3));
+ EXPECT_TRUE(address3.less(address2));
+ EXPECT_FALSE(address2.less(address3));
+}
+
+TEST(i2p_address, generic_network_address)
+{
+ const epee::net_utils::network_address i2p1{MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 8080))};
+ const epee::net_utils::network_address i2p2{MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 8080))};
+ const epee::net_utils::network_address ip{epee::net_utils::ipv4_network_address{100, 200}};
+
+ EXPECT_EQ(i2p1, i2p2);
+ EXPECT_NE(ip, i2p1);
+ EXPECT_LT(ip, i2p1);
+
+ EXPECT_STREQ(b32_i2p, i2p1.host_str().c_str());
+ EXPECT_EQ(std::string{b32_i2p} + ":8080", i2p1.str());
+ EXPECT_EQ(epee::net_utils::address_type::i2p, i2p1.get_type_id());
+ EXPECT_EQ(epee::net_utils::address_type::i2p, i2p2.get_type_id());
+ EXPECT_EQ(epee::net_utils::address_type::ipv4, ip.get_type_id());
+ EXPECT_EQ(epee::net_utils::zone::i2p, i2p1.get_zone());
+ EXPECT_EQ(epee::net_utils::zone::i2p, i2p2.get_zone());
+ EXPECT_EQ(epee::net_utils::zone::public_, ip.get_zone());
+ EXPECT_TRUE(i2p1.is_blockable());
+ EXPECT_TRUE(i2p2.is_blockable());
+ EXPECT_TRUE(ip.is_blockable());
+}
+
+namespace
+{
+ struct test_command_i2p
+ {
+ net::i2p_address i2p;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(i2p);
+ END_KV_SERIALIZE_MAP()
+ };
+}
+
+TEST(i2p_address, epee_serializev_b32)
+{
+ std::string buffer{};
+ {
+ test_command_i2p command{MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 10))};
+ EXPECT_FALSE(command.i2p.is_unknown());
+ EXPECT_NE(net::i2p_address{}, command.i2p);
+ EXPECT_STREQ(b32_i2p, command.i2p.host_str());
+ EXPECT_EQ(10u, command.i2p.port());
+
+ epee::serialization::portable_storage stg{};
+ EXPECT_TRUE(command.store(stg));
+ EXPECT_TRUE(stg.store_to_binary(buffer));
+ }
+
+ test_command_i2p command{};
+ {
+ EXPECT_TRUE(command.i2p.is_unknown());
+ EXPECT_EQ(net::i2p_address{}, command.i2p);
+ EXPECT_STREQ(net::i2p_address::unknown_str(), command.i2p.host_str());
+ EXPECT_EQ(0u, command.i2p.port());
+
+ epee::serialization::portable_storage stg{};
+ EXPECT_TRUE(stg.load_from_binary(buffer));
+ EXPECT_TRUE(command.load(stg));
+ }
+ EXPECT_FALSE(command.i2p.is_unknown());
+ EXPECT_NE(net::i2p_address{}, command.i2p);
+ EXPECT_STREQ(b32_i2p, command.i2p.host_str());
+ EXPECT_EQ(10u, command.i2p.port());
+
+ // make sure that exceeding max buffer doesn't destroy i2p_address::_load
+ {
+ epee::serialization::portable_storage stg{};
+ stg.load_from_binary(buffer);
+
+ std::string host{};
+ ASSERT_TRUE(stg.get_value("host", host, stg.open_section("i2p", nullptr, false)));
+ EXPECT_EQ(std::strlen(b32_i2p), host.size());
+
+ host.push_back('k');
+ EXPECT_TRUE(stg.set_value("host", std::string{host}, stg.open_section("i2p", nullptr, false)));
+ EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE`
+ }
+
+ EXPECT_TRUE(command.i2p.is_unknown());
+ EXPECT_EQ(net::i2p_address{}, command.i2p);
+ EXPECT_STRNE(b32_i2p, command.i2p.host_str());
+ EXPECT_EQ(0u, command.i2p.port());
+}
+
+TEST(i2p_address, epee_serialize_unknown)
+{
+ std::string buffer{};
+ {
+ test_command_i2p command{net::i2p_address::unknown()};
+ EXPECT_TRUE(command.i2p.is_unknown());
+ EXPECT_EQ(net::i2p_address{}, command.i2p);
+ EXPECT_STREQ(net::i2p_address::unknown_str(), command.i2p.host_str());
+ EXPECT_EQ(0u, command.i2p.port());
+
+ epee::serialization::portable_storage stg{};
+ EXPECT_TRUE(command.store(stg));
+ EXPECT_TRUE(stg.store_to_binary(buffer));
+ }
+
+ test_command_i2p command{};
+ {
+ EXPECT_TRUE(command.i2p.is_unknown());
+ EXPECT_EQ(net::i2p_address{}, command.i2p);
+ EXPECT_STRNE(b32_i2p, command.i2p.host_str());
+ EXPECT_EQ(0u, command.i2p.port());
+
+ epee::serialization::portable_storage stg{};
+ EXPECT_TRUE(stg.load_from_binary(buffer));
+ EXPECT_TRUE(command.load(stg));
+ }
+ EXPECT_TRUE(command.i2p.is_unknown());
+ EXPECT_EQ(net::i2p_address{}, command.i2p);
+ EXPECT_STREQ(net::i2p_address::unknown_str(), command.i2p.host_str());
+ EXPECT_EQ(0u, command.i2p.port());
+
+ // make sure that exceeding max buffer doesn't destroy i2p_address::_load
+ {
+ epee::serialization::portable_storage stg{};
+ stg.load_from_binary(buffer);
+
+ std::string host{};
+ ASSERT_TRUE(stg.get_value("host", host, stg.open_section("i2p", nullptr, false)));
+ EXPECT_EQ(std::strlen(net::i2p_address::unknown_str()), host.size());
+
+ host.push_back('k');
+ EXPECT_TRUE(stg.set_value("host", std::string{host}, stg.open_section("i2p", nullptr, false)));
+ EXPECT_TRUE(command.load(stg)); // poor error reporting from `KV_SERIALIZE`
+ }
+
+ EXPECT_TRUE(command.i2p.is_unknown());
+ EXPECT_EQ(net::i2p_address{}, command.i2p);
+ EXPECT_STRNE(b32_i2p, command.i2p.host_str());
+ EXPECT_EQ(0u, command.i2p.port());
+}
+
+TEST(i2p_address, boost_serialize_b32)
+{
+ std::string buffer{};
+ {
+ const net::i2p_address i2p = MONERO_UNWRAP(net::i2p_address::make(b32_i2p, 10));
+ EXPECT_FALSE(i2p.is_unknown());
+ EXPECT_NE(net::i2p_address{}, i2p);
+ EXPECT_STREQ(b32_i2p, i2p.host_str());
+ EXPECT_EQ(10u, i2p.port());
+
+ std::ostringstream stream{};
+ {
+ boost::archive::portable_binary_oarchive archive{stream};
+ archive << i2p;
+ }
+ buffer = stream.str();
+ }
+
+ net::i2p_address i2p{};
+ {
+ EXPECT_TRUE(i2p.is_unknown());
+ EXPECT_EQ(net::i2p_address{}, i2p);
+ EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str());
+ EXPECT_EQ(0u, i2p.port());
+
+ std::istringstream stream{buffer};
+ boost::archive::portable_binary_iarchive archive{stream};
+ archive >> i2p;
+ }
+ EXPECT_FALSE(i2p.is_unknown());
+ EXPECT_NE(net::i2p_address{}, i2p);
+ EXPECT_STREQ(b32_i2p, i2p.host_str());
+ EXPECT_EQ(10u, i2p.port());
+}
+
+TEST(i2p_address, boost_serialize_unknown)
+{
+ std::string buffer{};
+ {
+ const net::i2p_address i2p{};
+ EXPECT_TRUE(i2p.is_unknown());
+ EXPECT_EQ(net::i2p_address::unknown(), i2p);
+ EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str());
+ EXPECT_EQ(0u, i2p.port());
+
+ std::ostringstream stream{};
+ {
+ boost::archive::portable_binary_oarchive archive{stream};
+ archive << i2p;
+ }
+ buffer = stream.str();
+ }
+
+ net::i2p_address i2p{};
+ {
+ EXPECT_TRUE(i2p.is_unknown());
+ EXPECT_EQ(net::i2p_address{}, i2p);
+ EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str());
+ EXPECT_EQ(0u, i2p.port());
+
+ std::istringstream stream{buffer};
+ boost::archive::portable_binary_iarchive archive{stream};
+ archive >> i2p;
+ }
+ EXPECT_TRUE(i2p.is_unknown());
+ EXPECT_EQ(net::i2p_address::unknown(), i2p);
+ EXPECT_STREQ(net::i2p_address::unknown_str(), i2p.host_str());
+ EXPECT_EQ(0u, i2p.port());
+}
+
+TEST(get_network_address, i2p)
+{
+ expect<epee::net_utils::network_address> address =
+ net::get_network_address("i2p", 0);
+ EXPECT_EQ(net::error::unsupported_address, address);
+
+ address = net::get_network_address(".b32.i2p", 0);
+ EXPECT_EQ(net::error::invalid_i2p_address, address);
+
+ address = net::get_network_address(b32_i2p, 1000);
+ ASSERT_TRUE(bool(address));
+ EXPECT_EQ(epee::net_utils::address_type::i2p, address->get_type_id());
+ EXPECT_STREQ(b32_i2p, address->host_str().c_str());
+ EXPECT_EQ(std::string{b32_i2p} + ":1000", address->str());
+
+ address = net::get_network_address(std::string{b32_i2p} + ":2000", 1000);
+ ASSERT_TRUE(bool(address));
+ EXPECT_EQ(epee::net_utils::address_type::i2p, address->get_type_id());
+ EXPECT_STREQ(b32_i2p, address->host_str().c_str());
+ EXPECT_EQ(std::string{b32_i2p} + ":2000", address->str());
+
+ address = net::get_network_address(std::string{b32_i2p} + ":65536", 1000);
+ EXPECT_EQ(net::error::invalid_port, address);
+}
TEST(get_network_address, ipv4)
{
diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp
index c92f70b97..b656c4858 100644
--- a/tests/unit_tests/node_server.cpp
+++ b/tests/unit_tests/node_server.cpp
@@ -54,7 +54,6 @@ public:
bool init(const boost::program_options::variables_map& vm) {return true ;}
bool deinit(){return true;}
bool get_short_chain_history(std::list<crypto::hash>& ids) const { return true; }
- bool get_stat_info(cryptonote::core_stat_info& st_inf) const {return true;}
bool have_block(const crypto::hash& id) const {return true;}
void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;}
bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed) { return true; }
@@ -91,6 +90,8 @@ public:
bool prune_blockchain(uint32_t pruning_seed = 0) { return true; }
bool is_within_compiled_block_hash_area(uint64_t height) const { return false; }
bool has_block_weights(uint64_t height, uint64_t nblocks) const { return false; }
+ bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) { return false; }
+ bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const { return false; }
void stop() {}
};
diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp
index 23f028464..b711526e6 100644
--- a/tests/unit_tests/serialization.cpp
+++ b/tests/unit_tests/serialization.cpp
@@ -735,7 +735,6 @@ TEST(Serialization, portability_wallet)
auto address_book_row = w.m_address_book.begin();
ASSERT_TRUE(epee::string_tools::pod_to_hex(address_book_row->m_address.m_spend_public_key) == "9bc53a6ff7b0831c9470f71b6b972dbe5ad1e8606f72682868b1dda64e119fb3");
ASSERT_TRUE(epee::string_tools::pod_to_hex(address_book_row->m_address.m_view_public_key) == "49fece1ef97dc0c0f7a5e2106e75e96edd910f7e86b56e1e308cd0cf734df191");
- ASSERT_TRUE(epee::string_tools::pod_to_hex(address_book_row->m_payment_id) == "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
ASSERT_TRUE(address_book_row->m_description == "testnet wallet 9y52S6");
}
}
diff --git a/utils/gpg_keys/thecharlatan.asc b/utils/gpg_keys/thecharlatan.asc
new file mode 100644
index 000000000..a10a1fb54
--- /dev/null
+++ b/utils/gpg_keys/thecharlatan.asc
@@ -0,0 +1,316 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+=abJr
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/utils/gpg_keys/xiphon.asc b/utils/gpg_keys/xiphon.asc
new file mode 100644
index 000000000..01ad7fb77
--- /dev/null
+++ b/utils/gpg_keys/xiphon.asc
@@ -0,0 +1,41 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+JezqaExCLJDmDVnOj/t8V7dUv4o84A==
+=y+nZ
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/utils/logs/levin-traffic.awk b/utils/logs/levin-traffic.awk
new file mode 100755
index 000000000..25c1c3ed1
--- /dev/null
+++ b/utils/logs/levin-traffic.awk
@@ -0,0 +1,73 @@
+#!/bin/awk -f
+
+function max(a, b) { if (a < b) return b; return a; }
+function bytes_str(b) { if (b < 1024) return b " bytes"; if (b < 1024 * 1024) return b/1024 " kB"; return b/1024/1024 " MB"; }
+function time_str(b) { if (b < 120) return b " sec"; if (b < 3600) return b/60 " min"; if (b < 86400) return b/3600 " hours"; return b/86400 " days"}
+
+BEGIN {
+commands["command-1001"] = "HANDSHAKE"
+commands["command-1002"] = "TIMED_SYNC"
+commands["command-1003"] = "PING"
+commands["command-1004"] = "REQUEST_STAT_INFO"
+commands["command-1005"] = "REQUEST_NETWORK_STATE"
+commands["command-1006"] = "REQUEST_PEER_ID"
+commands["command-1007"] = "REQUEST_SUPPORT_FLAGS"
+commands["command-2001"] = "NOTIFY_NEW_BLOCK"
+commands["command-2002"] = "NOTIFY_NEW_TRANSACTIONS"
+commands["command-2003"] = "REQUEST_GET_OBJECTS"
+commands["command-2004"] = "RESPONSE_GET_OBJECTS"
+commands["command-2006"] = "NOTIFY_REQUEST_CHAIN"
+commands["command-2007"] = "RESPONSE_CHAIN_ENTRY"
+commands["command-2008"] = "NOTIFY_NEW_FLUFFY_BLOCK"
+commands["command-2009"] = "NOTIFY_REQUEST_FLUFFY_MISSING_TX"
+}
+
+/ net.p2p.traffic / {
+ date=gensub(/-/, " ", "g", $1)
+ time=gensub(/\..*/, "", "g", gensub(/:/, " ", "g", $2))
+ ip=gensub(/\[/, "", "g", $7)
+ outin=gensub(/]/, "", "g", $8)
+ timestamp=date " " time
+ timestamp=mktime(timestamp)
+ if (!t0)
+ t0 = timestamp
+ if (!t0ip[ip])
+ t0ip[ip] = timestamp
+ t1 = timestamp
+ t1ip[ip] = timestamp
+ bytes=$9
+ dir=$11
+ command=$14
+ initiator=$17
+
+ bytes_by_command[command] += bytes
+ bytes_by_ip[ip] += bytes
+ if (dir == "sent")
+ bytes_sent_by_ip[ip] += bytes
+ else
+ bytes_received_by_ip[ip] += bytes
+ bytes_by_outin[outin] += bytes
+ bytes_by_direction[dir] += bytes
+ bytes_by_initiator[initiator] += bytes
+}
+
+END {
+ dt = t1 - t0
+ print "Running time:", time_str(dt)
+ for (command in bytes_by_command) {
+ category = command
+ if (commands[command])
+ category = commands[command];
+ print "Category", category ":", bytes_str(bytes_by_command[command])
+ }
+ for (direction in bytes_by_direction) print direction ":", bytes_str(bytes_by_direction[direction])
+ for (initiator in bytes_by_initiator) print "Initiating from", initiator ":", bytes_str(bytes_by_initiator[initiator])
+ for (outin in bytes_by_outin) print "With", outin, "peers:", bytes_str(bytes_by_outin[outin])
+ for (ip in bytes_by_ip) print "IP", ip ":", bytes_str(bytes_by_ip[ip])
+ print "Download rate:", bytes_str(bytes_by_direction["received"] / max(dt, 1)) "/s"
+ for (ip in bytes_received_by_ip)
+ print " ", ip ":", bytes_str(bytes_received_by_ip[ip] / max(t1ip[ip] - t0ip[ip], 1)) "/s over", time_str(t1ip[ip] - t0ip[ip])
+ print "Upload rate:", bytes_str(bytes_by_direction["sent"] / max(dt, 1)) "/s"
+ for (ip in bytes_sent_by_ip)
+ print " ", ip ":", bytes_str(bytes_sent_by_ip[ip] / max(t1ip[ip] - t0ip[ip], 1)) "/s over", time_str(t1ip[ip] - t0ip[ip])
+}
diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py
index 703261a4e..ac9ba2d3a 100644
--- a/utils/python-rpc/framework/wallet.py
+++ b/utils/python-rpc/framework/wallet.py
@@ -706,11 +706,13 @@ class Wallet(object):
}
return self.rpc.send_json_rpc_request(check_reserve_proof)
- def sign(self, data):
+ def sign(self, data, account_index = 0, address_index = 0):
sign = {
'method': 'sign',
'params' : {
'data': data,
+ 'account_index': account_index,
+ 'address_index': address_index,
},
'jsonrpc': '2.0',
'id': '0'