Compare commits
182 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b089f9ee69 | |||
| 7cb69fa6bc | |||
| c7d4bf491e | |||
| c6d17a0b39 | |||
| 753e4683af | |||
| 2676e5ae85 | |||
| bedfa83ea0 | |||
| dffa6766e7 | |||
| 1f733fb868 | |||
| 956d55c35f | |||
| 76feeb64b7 | |||
| 61db6497f7 | |||
| 6664e3e3b1 | |||
| ebe3fdaff3 | |||
| 886595b540 | |||
| b9fddc08b2 | |||
| c5ad937cc1 | |||
| e875a587e2 | |||
| 8c254e42e3 | |||
| 8dbc361e98 | |||
| 738f5038bf | |||
| 24ccaba6ef | |||
| 1ec7eae036 | |||
| 2f0503d7f3 | |||
| b13e597e8e | |||
| 19fa7dceac | |||
| f48fc72fb6 | |||
| 5930557a94 | |||
| b85f320738 | |||
| 026be65bf5 | |||
| 1123ae9043 | |||
| 755dddd2bc | |||
| dd47d03cf2 | |||
| 4634b8b539 | |||
| 100a7d06ee | |||
| 71567885e6 | |||
| 7686af7acf | |||
| 8703b8a4cb | |||
| d6b35e97be | |||
| 81d4db08eb | |||
| 31a7f12d55 | |||
| 453a82fd44 | |||
| 1744fada96 | |||
| 1b7de24e90 | |||
| 97e3ce5f18 | |||
| c7cf489585 | |||
| 1a931ecc83 | |||
| ef3e18b51b | |||
| b5b72ae05c | |||
| 5eb3fc29bb | |||
| c225a1f25b | |||
| ff15cb2f04 | |||
| eeb7c7c546 | |||
| 36ee12bd8d | |||
| 32b3a56313 | |||
| b23116424d | |||
| 7807f569e4 | |||
| 68e40ea2a7 | |||
| c6ff0d3820 | |||
| 0d2f515ecc | |||
| 522d82276e | |||
| 4f6f6d9e27 | |||
| 3872753202 | |||
| 13ed9d501b | |||
| b335433204 | |||
| 8a1e49664e | |||
| 2f912f8a58 | |||
| 81f113dd8c | |||
| f9a7f2a136 | |||
| c97c2ec01c | |||
| 51d7a6921c | |||
| f2360a725e | |||
| 0cc8f7aaa3 | |||
| b987870553 | |||
| 3aabfcfce5 | |||
| 8322f9c4f5 | |||
| fbd0b19fc8 | |||
| 33e3f72d24 | |||
| 052df1b28c | |||
| 98ee46f249 | |||
| f5b86342e8 | |||
| dfb990e8bb | |||
| 8eab181fe1 | |||
| 9a70f43440 | |||
| 47d8899c90 | |||
| c09062087e | |||
| 9a5f8431b4 | |||
| 79107ff68f | |||
| 646c3fb0d9 | |||
| c193c5e85d | |||
| d5c667a5ad | |||
| 2fe5a5e073 | |||
| 95e4fc3602 | |||
| e7d51e5583 | |||
| fe746dca4e | |||
| fe47806afb | |||
| 6c38c21dfd | |||
| 5435202450 | |||
| d9b765a3af | |||
| 72d2a610cd | |||
| 2e9542d01a | |||
| 0f75585f64 | |||
| eb4df0aa1c | |||
| eae62a07e0 | |||
| 14ae81246d | |||
| 5b0c27430f | |||
| 09a88cc00e | |||
| bdebf680bd | |||
| e0b2123c32 | |||
| 2656cdf505 | |||
| 1c12d305d6 | |||
| 83d0d2338f | |||
| 69de381526 | |||
| 810f6a6cd2 | |||
| fbcd8da082 | |||
| 03d51b7cc4 | |||
| f9b81a589e | |||
| 41157dbc82 | |||
| 9a89e2d9e4 | |||
| 1df5630f23 | |||
| 3f9140e754 | |||
| 205c80427b | |||
| 0c04018718 | |||
| 533bbc3208 | |||
| 6e7bd68b18 | |||
| 031d318ca2 | |||
| 61e664a258 | |||
| 64ed9385a2 | |||
| ba98269ca5 | |||
| 7dbb14b02a | |||
| 356e6877dc | |||
| 633e1b7359 | |||
| eac1b86bb2 | |||
| 3bebcc4a7d | |||
| 9d5c5b5634 | |||
| 894adef295 | |||
| 6c7640eb74 | |||
| 78348bcddd | |||
| b51f4a9244 | |||
| ed05ac6872 | |||
| f137a35984 | |||
| 23f782b211 | |||
| ab826008d6 | |||
| 4dc727b3f6 | |||
| 1eb1162923 | |||
| 3be6c1389e | |||
| 5a99b2dfbe | |||
| bd962882d1 | |||
| f173bf6e72 | |||
| a41453c256 | |||
| 842478c5a9 | |||
| 17ea7665d7 | |||
| 9f8ae9649a | |||
| 11b5139506 | |||
| 54f0f9eb96 | |||
| 5c900bb69f | |||
| 60e9426ef2 | |||
| 835896ea24 | |||
| 62bb95b25f | |||
| 1924c170d4 | |||
| aed36a25d6 | |||
| c6530d2f5d | |||
| dc24312bc3 | |||
| 438554e1ab | |||
| 26025cb294 | |||
| cfc62277c0 | |||
| aa139f0334 | |||
| a4a58eb886 | |||
| 8dc4abdafe | |||
| 1ce32d8536 | |||
| 1fad8cc919 | |||
| f983ac7780 | |||
| 1d1d5fb74c | |||
| 2f45d5c615 | |||
| e06129bb4d | |||
| a371e60a30 | |||
| 2f62dd5b78 | |||
| 059b975388 | |||
| c742fa4c6e | |||
| 4f1262bae9 | |||
| 65e13dbef1 | |||
| ad80f1b357 |
@@ -151,7 +151,7 @@ jobs:
|
||||
- name: install monero dependencies
|
||||
run: ${{env.APT_INSTALL_LINUX}}
|
||||
- name: install Python dependencies
|
||||
run: pip install requests psutil monotonic zmq
|
||||
run: pip install requests psutil monotonic zmq deepdiff
|
||||
- name: tests
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: ON
|
||||
|
||||
@@ -48,7 +48,7 @@ all: release-all
|
||||
|
||||
depends:
|
||||
cd contrib/depends && $(MAKE) HOST=$(target) && cd ../.. && mkdir -p build/$(target)/release
|
||||
cd build/$(target)/release && cmake -DCMAKE_TOOLCHAIN_FILE=$(CURDIR)/contrib/depends/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
|
||||
cd build/$(target)/release && USE_DEVICE_TREZOR_MANDATORY=1 cmake -DCMAKE_TOOLCHAIN_FILE=$(CURDIR)/contrib/depends/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
|
||||
|
||||
cmake-debug:
|
||||
mkdir -p $(builddir)/debug
|
||||
@@ -104,7 +104,7 @@ release-all:
|
||||
|
||||
release-static:
|
||||
mkdir -p $(builddir)/release
|
||||
cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
|
||||
cd $(builddir)/release && cmake -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
|
||||
|
||||
coverage:
|
||||
mkdir -p $(builddir)/debug
|
||||
|
||||
@@ -138,8 +138,8 @@ Dates are provided in the format YYYY-MM-DD.
|
||||
| 1978433 | 2019-11-30 | v12 | v0.15.0.0 | v0.16.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
|
||||
| 2210000 | 2020-10-17 | v13 | v0.17.0.0 | v0.17.3.2 | New CLSAG transaction format
|
||||
| 2210720 | 2020-10-18 | v14 | v0.17.1.1 | v0.17.3.2 | forbid old MLSAG transaction format
|
||||
| 2688888 | 2022-08-13 | v15 | v0.18.0.0 | v0.18.2.1 | ringsize = 16, bulletproofs+, view tags, adjusted dynamic block weight algorithm
|
||||
| 2689608 | 2022-08-14 | v16 | v0.18.0.0 | v0.18.2.1 | forbid old v14 transaction format
|
||||
| 2688888 | 2022-08-13 | v15 | v0.18.0.0 | v0.18.3.4 | ringsize = 16, bulletproofs+, view tags, adjusted dynamic block weight algorithm
|
||||
| 2689608 | 2022-08-14 | v16 | v0.18.0.0 | v0.18.3.4 | forbid old v14 transaction format
|
||||
| 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.
|
||||
@@ -344,7 +344,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
|
||||
```bash
|
||||
git clone https://github.com/monero-project/monero.git
|
||||
cd monero
|
||||
git checkout v0.18.2.1
|
||||
git checkout v0.18.3.4
|
||||
```
|
||||
|
||||
* Build:
|
||||
@@ -463,10 +463,10 @@ application.
|
||||
cd monero
|
||||
```
|
||||
|
||||
* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.18.2.1'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.18.3.4'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
|
||||
```bash
|
||||
git checkout v0.18.2.1
|
||||
git checkout v0.18.3.4
|
||||
```
|
||||
|
||||
* If you are on a 64-bit system, run:
|
||||
|
||||
@@ -145,7 +145,7 @@ $(1)_build_env+=PATH="$(build_prefix)/bin:$(PATH)"
|
||||
$(1)_stage_env+=PATH="$(build_prefix)/bin:$(PATH)"
|
||||
$(1)_autoconf=./configure --host=$($($(1)_type)_host) --prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)"
|
||||
|
||||
ifneq ($(1),libusb)
|
||||
ifeq ($(filter $(1),libusb unbound),)
|
||||
$(1)_autoconf += --disable-dependency-tracking
|
||||
endif
|
||||
ifneq ($($(1)_nm),)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package=expat
|
||||
$(package)_version=2.4.1
|
||||
$(package)_download_path=https://github.com/libexpat/libexpat/releases/download/R_2_4_1
|
||||
$(package)_version=2.6.0
|
||||
$(package)_download_path=https://github.com/libexpat/libexpat/releases/download/R_$(subst .,_,$($(package)_version))/
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
|
||||
$(package)_sha256_hash=2f9b6a580b94577b150a7d5617ad4643a4301a6616ff459307df3e225bcfbf40
|
||||
$(package)_sha256_hash=ff60e6a6b6ce570ae012dc7b73169c7fdf4b6bf08c12ed0ec6f55736b78d85ba
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--enable-static
|
||||
$(package)_config_opts=--disable-shared
|
||||
$(package)_config_opts=--disable-shared --without-docbook --without-tests --without-examples
|
||||
$(package)_config_opts+=--enable-option-checking --without-xmlwf --with-pic
|
||||
$(package)_config_opts+=--prefix=$(host_prefix)
|
||||
endef
|
||||
|
||||
@@ -23,6 +23,6 @@ define $(package)_stage_cmds
|
||||
endef
|
||||
|
||||
define $(package)_postprocess_cmds
|
||||
rm lib/*.la
|
||||
rm -rf share lib/cmake lib/*.la
|
||||
endef
|
||||
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
package=openssl
|
||||
$(package)_version=1.1.1t
|
||||
$(package)_version=3.0.13
|
||||
$(package)_download_path=https://www.openssl.org/source
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=8dee9b24bdb1dcbf0c3d1e9b02fb8f6bf22165e807f45adeb7c9677536859d3b
|
||||
$(package)_sha256_hash=88525753f79d3bec27d2fa7c66aa0b92b3aa9498dafd93d7cfa4b3780cdae313
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_env=AR="$($(package)_ar)" ARFLAGS=$($(package)_arflags) RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)"
|
||||
$(package)_config_env_android=ANDROID_NDK_HOME="$(host_prefix)/native" PATH="$(host_prefix)/native/bin" CC=clang AR=ar RANLIB=ranlib
|
||||
$(package)_build_env_android=ANDROID_NDK_HOME="$(host_prefix)/native"
|
||||
$(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl
|
||||
$(package)_config_env_android=ANDROID_NDK_ROOT="$(host_prefix)/native" PATH="$(host_prefix)/native/bin" CC=clang AR=ar RANLIB=ranlib
|
||||
$(package)_build_env_android=ANDROID_NDK_ROOT="$(host_prefix)/native"
|
||||
$(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl --libdir=$(host_prefix)/lib
|
||||
$(package)_config_opts+=no-capieng
|
||||
$(package)_config_opts+=no-dso
|
||||
$(package)_config_opts+=no-dtls1
|
||||
$(package)_config_opts+=no-ec_nistp_64_gcc_128
|
||||
$(package)_config_opts+=no-gost
|
||||
$(package)_config_opts+=no-heartbeats
|
||||
$(package)_config_opts+=no-md2
|
||||
$(package)_config_opts+=no-rc5
|
||||
$(package)_config_opts+=no-rdrand
|
||||
@@ -22,8 +21,8 @@ $(package)_config_opts+=no-rfc3779
|
||||
$(package)_config_opts+=no-sctp
|
||||
$(package)_config_opts+=no-shared
|
||||
$(package)_config_opts+=no-ssl-trace
|
||||
$(package)_config_opts+=no-ssl2
|
||||
$(package)_config_opts+=no-ssl3
|
||||
$(package)_config_opts+=no-tests
|
||||
$(package)_config_opts+=no-unit-test
|
||||
$(package)_config_opts+=no-weak-ssl-ciphers
|
||||
$(package)_config_opts+=no-zlib
|
||||
@@ -49,7 +48,7 @@ $(package)_config_opts_x86_64_freebsd=BSD-x86_64
|
||||
endef
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
sed -i.old 's|"engines", "apps", "test", "util", "tools", "fuzz"|"engines", "tools"|' Configure
|
||||
sed -i.old 's|crypto ssl apps util tools fuzz providers doc|crypto ssl util tools providers|' build.info
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
package=unbound
|
||||
$(package)_version=1.15.0
|
||||
$(package)_version=1.19.1
|
||||
$(package)_download_path=https://www.nlnetlabs.nl/downloads/$(package)/
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=a480dc6c8937447b98d161fe911ffc76cfaffa2da18788781314e81339f1126f
|
||||
$(package)_sha256_hash=bc1d576f3dd846a0739adc41ffaa702404c6767d2b6082deb9f2f97cbb24a3a9
|
||||
$(package)_dependencies=openssl expat
|
||||
$(package)_patches=disable-glibc-reallocarray.patch
|
||||
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--disable-shared --enable-static --without-pyunbound --prefix=$(host_prefix) --with-libexpat=$(host_prefix) --with-ssl=$(host_prefix) --with-libevent=no --without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only
|
||||
$(package)_config_opts=--disable-shared --enable-static --without-pyunbound --prefix=$(host_prefix)
|
||||
$(package)_config_opts+=--with-libexpat=$(host_prefix) --with-ssl=$(host_prefix) --with-libevent=no
|
||||
$(package)_config_opts+=--without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only
|
||||
$(package)_config_opts_linux=--with-pic
|
||||
$(package)_config_opts_w64=--enable-static-exe --sysconfdir=/etc --prefix=$(host_prefix) --target=$(host_prefix)
|
||||
$(package)_config_opts_x86_64_darwin=ac_cv_func_SHA384_Init=yes
|
||||
$(package)_build_opts_mingw32=LDFLAGS="$($(package)_ldflags) -lpthread"
|
||||
$(package)_cflags_mingw32+="-D_WIN32_WINNT=0x600"
|
||||
endef
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
@@ -30,6 +34,3 @@ endef
|
||||
define $(package)_stage_cmds
|
||||
$(MAKE) DESTDIR=$($(package)_staging_dir) install
|
||||
endef
|
||||
|
||||
define $(package)_postprocess_cmds
|
||||
endef
|
||||
|
||||
@@ -144,8 +144,11 @@ elseif(ARCHITECTURE STREQUAL "aarch64")
|
||||
endif()
|
||||
|
||||
if(ARCHITECTURE STREQUAL "riscv64")
|
||||
set(NO_AES ON)
|
||||
set(ARCH "rv64imafdc")
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(BUILD_TAG "linux-riscv64")
|
||||
endif()
|
||||
set(ARCH_ID "riscv64")
|
||||
set(ARCH "rv64gc")
|
||||
endif()
|
||||
|
||||
if(ARCHITECTURE STREQUAL "i686")
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
#include <cstdint>
|
||||
|
||||
namespace epee
|
||||
{
|
||||
|
||||
@@ -583,11 +583,8 @@ namespace net_utils
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ec.value())
|
||||
terminate();
|
||||
else {
|
||||
cancel_timer();
|
||||
on_interrupted();
|
||||
terminate();
|
||||
}
|
||||
};
|
||||
m_strand.post(
|
||||
|
||||
@@ -147,6 +147,16 @@ namespace epee
|
||||
return {reinterpret_cast<const std::uint8_t*>(src.data()), src.size_bytes()};
|
||||
}
|
||||
|
||||
//! \return `span<std::uint8_t>` from a STL compatible `src`.
|
||||
template<typename T>
|
||||
constexpr span<std::uint8_t> to_mut_byte_span(T& src)
|
||||
{
|
||||
using value_type = typename T::value_type;
|
||||
static_assert(!std::is_empty<value_type>(), "empty value types will not work -> sizeof == 1");
|
||||
static_assert(!has_padding<value_type>(), "source value type may have padding");
|
||||
return {reinterpret_cast<std::uint8_t*>(src.data()), src.size() * sizeof(value_type)};
|
||||
}
|
||||
|
||||
//! \return `span<const std::uint8_t>` which represents the bytes at `&src`.
|
||||
template<typename T>
|
||||
span<const std::uint8_t> as_byte_span(const T& src) noexcept
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <boost/utility/string_ref_fwd.hpp>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace epee
|
||||
{
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
#include "portable_storage_base.h"
|
||||
#include "portable_storage_bin_utils.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "serialization"
|
||||
|
||||
#ifdef EPEE_PORTABLE_STORAGE_RECURSION_LIMIT
|
||||
#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL EPEE_PORTABLE_STORAGE_RECURSION_LIMIT
|
||||
#else
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
#include "parserse_base_utils.h"
|
||||
#include "file_io_utils.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "serialization"
|
||||
|
||||
#define EPEE_JSON_RECURSION_LIMIT_INTERNAL 100
|
||||
|
||||
namespace epee
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "misc_log_ex.h"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/numeric/conversion/bounds.hpp>
|
||||
#include <typeinfo>
|
||||
#include <iomanip>
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "mlocker.h"
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
@@ -69,23 +70,19 @@ namespace string_tools
|
||||
#ifdef _WIN32
|
||||
std::string get_current_module_path();
|
||||
#endif
|
||||
bool set_module_name_and_folder(const std::string& path_to_process_);
|
||||
bool trim_left(std::string& str);
|
||||
bool trim_right(std::string& str);
|
||||
void set_module_name_and_folder(const std::string& path_to_process_);
|
||||
void trim_left(std::string& str);
|
||||
void trim_right(std::string& str);
|
||||
//----------------------------------------------------------------------------
|
||||
inline std::string& trim(std::string& str)
|
||||
{
|
||||
trim_left(str);
|
||||
trim_right(str);
|
||||
boost::trim(str);
|
||||
return str;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
inline std::string trim(const std::string& str_)
|
||||
inline std::string trim(const std::string& str)
|
||||
{
|
||||
std::string str = str_;
|
||||
trim_left(str);
|
||||
trim_right(str);
|
||||
return str;
|
||||
return boost::trim_copy(str);
|
||||
}
|
||||
std::string pad_string(std::string s, size_t n, char c = ' ', bool prepend = false);
|
||||
|
||||
|
||||
@@ -176,11 +176,12 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s
|
||||
std::vector<boost::filesystem::path> found_files;
|
||||
const boost::filesystem::directory_iterator end_itr;
|
||||
const boost::filesystem::path filename_base_path(filename_base);
|
||||
const std::string filename_base_name = filename_base_path.filename().string();
|
||||
const boost::filesystem::path parent_path = filename_base_path.has_parent_path() ? filename_base_path.parent_path() : ".";
|
||||
for (boost::filesystem::directory_iterator iter(parent_path); iter != end_itr; ++iter)
|
||||
{
|
||||
const std::string filename = iter->path().string();
|
||||
if (filename.size() >= filename_base.size() && std::memcmp(filename.data(), filename_base.data(), filename_base.size()) == 0)
|
||||
const std::string filename = iter->path().filename().string();
|
||||
if (filename.size() >= filename_base_name.size() && std::memcmp(filename.data(), filename_base_name.data(), filename_base_name.size()) == 0)
|
||||
{
|
||||
found_files.push_back(iter->path());
|
||||
}
|
||||
@@ -338,11 +339,21 @@ bool is_stdout_a_tty()
|
||||
return is_a_tty.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
static bool is_nocolor()
|
||||
{
|
||||
static const char *no_color_var = getenv("NO_COLOR");
|
||||
static const bool no_color = no_color_var && *no_color_var; // apparently, NO_COLOR=0 means no color too (as per no-color.org)
|
||||
return no_color;
|
||||
}
|
||||
|
||||
void set_console_color(int color, bool bright)
|
||||
{
|
||||
if (!is_stdout_a_tty())
|
||||
return;
|
||||
|
||||
if (is_nocolor())
|
||||
return;
|
||||
|
||||
switch(color)
|
||||
{
|
||||
case console_color_default:
|
||||
@@ -461,6 +472,9 @@ void reset_console_color() {
|
||||
if (!is_stdout_a_tty())
|
||||
return;
|
||||
|
||||
if (is_nocolor())
|
||||
return;
|
||||
|
||||
#ifdef WIN32
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
|
||||
@@ -496,6 +496,13 @@ void ssl_options_t::configure(
|
||||
const std::string& host) const
|
||||
{
|
||||
socket.next_layer().set_option(boost::asio::ip::tcp::no_delay(true));
|
||||
{
|
||||
// in case server is doing "virtual" domains, set hostname
|
||||
SSL* const ssl_ctx = socket.native_handle();
|
||||
if (type == boost::asio::ssl::stream_base::client && !host.empty() && ssl_ctx)
|
||||
SSL_set_tlsext_host_name(ssl_ctx, host.c_str());
|
||||
}
|
||||
|
||||
|
||||
/* Using system-wide CA store for client verification is funky - there is
|
||||
no expected hostname for server to verify against. If server doesn't have
|
||||
@@ -513,11 +520,7 @@ void ssl_options_t::configure(
|
||||
{
|
||||
socket.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert);
|
||||
|
||||
// in case server is doing "virtual" domains, set hostname
|
||||
SSL* const ssl_ctx = socket.native_handle();
|
||||
if (type == boost::asio::ssl::stream_base::client && !host.empty() && ssl_ctx)
|
||||
SSL_set_tlsext_host_name(ssl_ctx, host.c_str());
|
||||
|
||||
|
||||
socket.set_verify_callback([&](const bool preverified, boost::asio::ssl::verify_context &ctx)
|
||||
{
|
||||
// preverified means it passed system or user CA check. System CA is never loaded
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "readline_buffer.h"
|
||||
#include "string_tools.h"
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#include <iostream>
|
||||
@@ -173,7 +174,7 @@ static void handle_line(char* line)
|
||||
line_stat = rdln::full;
|
||||
the_line = line;
|
||||
std::string test_line = line;
|
||||
boost::trim_right(test_line);
|
||||
epee::string_tools::trim_right(test_line);
|
||||
if(!test_line.empty())
|
||||
{
|
||||
if (!same_as_last_line(test_line))
|
||||
@@ -238,6 +239,10 @@ static char** attempted_completion(const char* text, int start, int end)
|
||||
|
||||
static void install_line_handler()
|
||||
{
|
||||
#if RL_READLINE_VERSION >= 0x0801
|
||||
rl_variable_bind("enable-bracketed-paste", "off");
|
||||
#endif
|
||||
|
||||
rl_attempted_completion_function = attempted_completion;
|
||||
rl_callback_handler_install("", handle_line);
|
||||
stifle_history(500);
|
||||
|
||||
@@ -38,9 +38,12 @@
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <system_error>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "misc_log_ex.h"
|
||||
#include "storages/parserse_base_utils.h"
|
||||
#include "hex.h"
|
||||
@@ -157,46 +160,33 @@ namespace string_tools
|
||||
return pname;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool set_module_name_and_folder(const std::string& path_to_process_)
|
||||
{
|
||||
std::string path_to_process = path_to_process_;
|
||||
|
||||
void set_module_name_and_folder(const std::string& path_to_process_)
|
||||
{
|
||||
boost::filesystem::path path_to_process = path_to_process_;
|
||||
|
||||
#ifdef _WIN32
|
||||
path_to_process = get_current_module_path();
|
||||
#endif
|
||||
std::string::size_type a = path_to_process.rfind( '\\' );
|
||||
if(a == std::string::npos )
|
||||
{
|
||||
a = path_to_process.rfind( '/' );
|
||||
}
|
||||
if ( a != std::string::npos )
|
||||
{
|
||||
get_current_module_name() = path_to_process.substr(a+1, path_to_process.size());
|
||||
get_current_module_folder() = path_to_process.substr(0, a);
|
||||
return true;
|
||||
}else
|
||||
return false;
|
||||
|
||||
}
|
||||
get_current_module_name() = path_to_process.filename().string();
|
||||
get_current_module_folder() = path_to_process.parent_path().string();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool trim_left(std::string& str)
|
||||
{
|
||||
for(std::string::iterator it = str.begin(); it!= str.end() && isspace(static_cast<unsigned char>(*it));)
|
||||
str.erase(str.begin());
|
||||
|
||||
return true;
|
||||
}
|
||||
void trim_left(std::string& str)
|
||||
{
|
||||
boost::trim_left(str);
|
||||
return;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool trim_right(std::string& str)
|
||||
{
|
||||
void trim_right(std::string& str)
|
||||
{
|
||||
boost::trim_right(str);
|
||||
return;
|
||||
}
|
||||
|
||||
for(std::string::reverse_iterator it = str.rbegin(); it!= str.rend() && isspace(static_cast<unsigned char>(*it));)
|
||||
str.erase( --((it++).base()));
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
std::string pad_string(std::string s, size_t n, char c, bool prepend)
|
||||
{
|
||||
if (s.size() < n)
|
||||
@@ -209,28 +199,22 @@ namespace string_tools
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string get_extension(const std::string& str)
|
||||
{
|
||||
std::string res;
|
||||
std::string::size_type pos = str.rfind('.');
|
||||
if(std::string::npos == pos)
|
||||
return res;
|
||||
|
||||
res = str.substr(pos+1, str.size()-pos);
|
||||
return res;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
std::string cut_off_extension(const std::string& str)
|
||||
{
|
||||
std::string res;
|
||||
std::string::size_type pos = str.rfind('.');
|
||||
if(std::string::npos == pos)
|
||||
return str;
|
||||
std::string get_extension(const std::string& str)
|
||||
{
|
||||
std::string ext_with_dot = boost::filesystem::path(str).extension().string();
|
||||
|
||||
if (ext_with_dot.empty())
|
||||
return {};
|
||||
|
||||
return ext_with_dot.erase(0, 1);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
std::string cut_off_extension(const std::string& str)
|
||||
{
|
||||
return boost::filesystem::path(str).replace_extension("").string();
|
||||
}
|
||||
|
||||
res = str.substr(0, pos);
|
||||
return res;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
#ifdef _WIN32
|
||||
std::wstring utf8_to_utf16(const std::string& str)
|
||||
{
|
||||
|
||||
@@ -57,7 +57,7 @@ The dockrun.sh script will do everything to build the binaries. Just specify the
|
||||
version to build as its only argument, e.g.
|
||||
|
||||
```bash
|
||||
VERSION=v0.18.2.1
|
||||
VERSION=v0.18.3.4
|
||||
./dockrun.sh $VERSION
|
||||
```
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ Common setup part:
|
||||
su - gitianuser
|
||||
|
||||
GH_USER=YOUR_GITHUB_USER_NAME
|
||||
VERSION=v0.18.2.1
|
||||
VERSION=v0.18.3.4
|
||||
```
|
||||
|
||||
Where `GH_USER` is your GitHub user name and `VERSION` is the version tag you want to build.
|
||||
|
||||
@@ -21,6 +21,7 @@ packages:
|
||||
- "g++-7-arm-linux-gnueabihf"
|
||||
- "gcc-arm-linux-gnueabihf"
|
||||
- "g++-arm-linux-gnueabihf"
|
||||
- "g++-riscv64-linux-gnu"
|
||||
- "g++-7-multilib"
|
||||
- "gcc-7-multilib"
|
||||
- "binutils-arm-linux-gnueabihf"
|
||||
@@ -43,7 +44,7 @@ files: []
|
||||
script: |
|
||||
|
||||
WRAP_DIR=$HOME/wrapped
|
||||
HOSTS="x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu i686-linux-gnu"
|
||||
HOSTS="x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu i686-linux-gnu riscv64-linux-gnu"
|
||||
FAKETIME_HOST_PROGS=""
|
||||
FAKETIME_PROGS="date"
|
||||
HOST_CFLAGS="-O2 -g"
|
||||
@@ -159,7 +160,13 @@ script: |
|
||||
fi
|
||||
export C_INCLUDE_PATH="$EXTRA_INCLUDES"
|
||||
export CPLUS_INCLUDE_PATH="$EXTRA_INCLUDES"
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DBACKCOMPAT=ON -DCMAKE_SKIP_RPATH=ON
|
||||
# glibc only added riscv support in 2.27, disable backwards compatibility
|
||||
if [ "$i" == "riscv64-linux-gnu" ]; then
|
||||
BACKCOMPAT_OPTION=OFF
|
||||
else
|
||||
BACKCOMPAT_OPTION=ON
|
||||
fi
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DBACKCOMPAT=${BACKCOMPAT_OPTION} -DCMAKE_SKIP_RPATH=ON
|
||||
make ${MAKEOPTS}
|
||||
chmod 755 bin/*
|
||||
cp ../LICENSE ../README.md ../docs/ANONYMITY_NETWORKS.md bin
|
||||
|
||||
@@ -71,13 +71,13 @@ type, and max connections:
|
||||
|
||||
```
|
||||
--anonymous-inbound rveahdfho7wo4b2m.onion:28083,127.0.0.1:28083,25
|
||||
--anonymous-inbound cmeua5767mz2q5jsaelk2rxhf67agrwuetaso5dzbenyzwlbkg2q.b32.i2p:5000,127.0.0.1:30000
|
||||
--anonymous-inbound cmeua5767mz2q5jsaelk2rxhf67agrwuetaso5dzbenyzwlbkg2q.b32.i2p,127.0.0.1:30000
|
||||
```
|
||||
|
||||
which tells `monerod` that a max of 25 inbound Tor connections are being
|
||||
received at address "rveahdfho7wo4b2m.onion:28083" and forwarded to `monerod`
|
||||
localhost port 28083, and a default max I2P connections are being received at
|
||||
address "cmeua5767mz2q5jsaelk2rxhf67agrwuetaso5dzbenyzwlbkg2q.b32.i2p:5000" and
|
||||
address "cmeua5767mz2q5jsaelk2rxhf67agrwuetaso5dzbenyzwlbkg2q.b32.i2p" and
|
||||
forwarded to `monerod` localhost port 30000.
|
||||
These addresses will be shared with outgoing peers, over the same network type,
|
||||
otherwise the peer will not be notified of the peer address by the proxy.
|
||||
|
||||
+5
@@ -149,6 +149,11 @@ static el::Color colorFromLevel(el::Level level)
|
||||
|
||||
static void setConsoleColor(el::Color color, bool bright)
|
||||
{
|
||||
static const char *no_color_var = getenv("NO_COLOR");
|
||||
static const bool no_color = no_color_var && *no_color_var; // apparently, NO_COLOR=0 means no color too (as per no-color.org)
|
||||
if (no_color)
|
||||
return;
|
||||
|
||||
#if ELPP_OS_WINDOWS
|
||||
HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
switch (color)
|
||||
|
||||
Vendored
+1
-1
Submodule external/randomx updated: 261d58c77f...102f8acf90
Binary file not shown.
@@ -245,6 +245,12 @@ namespace cryptonote
|
||||
ADD_CHECKPOINT2(2720000, "b19fb41dff15bd1016afbee9f8469f05aab715c9e5d1b974466a11fd58ecbb86", "0x3216b5851ddbb61");
|
||||
ADD_CHECKPOINT2(2817000, "39726d19ccaac01d150bec827b877ffae710b516bd633503662036ef4422e577", "0x3900669561954c1");
|
||||
ADD_CHECKPOINT2(2844000, "28fc7b446dfef5b469f5778eb72ddf32a307a5f5a9823d1c394e772349e05d40", "0x3af384ec0e97d12");
|
||||
ADD_CHECKPOINT2(2851000, "5bf0e47fc782263191a33f63a67db6c711781dc2a3c442e17ed901ec401be5c9", "0x3b6cd8a8ed610e8");
|
||||
ADD_CHECKPOINT2(2971000, "3d4cac5ac515eeabd18769ab943af85f36db51d28720def0d0e6effc2c8f5ce3", "0x436e532738b8b5b");
|
||||
ADD_CHECKPOINT2(2985000, "08f5e6b7301c1b6ed88268a28f8677a06e8ff943b3f9e48d3080f71f9c134bfb", "0x444b7b42a633c96");
|
||||
ADD_CHECKPOINT2(3088000, "bddf8ca09110d33d6d497f13a113630c2b6af1c84d4f3a6f35cb1446f2604ade", "0x4aed3615c2f8c3e");
|
||||
ADD_CHECKPOINT2(3102800, "083f4a34f9490403b564286e7f13fd1ed45c52c86fa47195f151594e5bc87504", "0x4bbed52d4da5dfb");
|
||||
ADD_CHECKPOINT2(3198000, "1d685b39be51e4e84e0af69fa78e023c7cb21de7d33acd012d0371d5f78712d5", "0x517d415fee3a816");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <boost/archive/portable_binary_iarchive.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
||||
#include "common/util.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
@@ -110,7 +111,7 @@ namespace tools
|
||||
catch(...)
|
||||
{
|
||||
// if failed, try reading in unportable mode
|
||||
boost::filesystem::copy_file(file_path, file_path + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
|
||||
tools::copy_file(file_path, file_path + ".unportable");
|
||||
data_file.close();
|
||||
data_file.open( file_path, std::ios_base::binary | std::ios_base::in);
|
||||
if(data_file.fail())
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
|
||||
namespace tools {
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace tools
|
||||
while (1)
|
||||
{
|
||||
t1 = epee::misc_utils::get_ns_count();
|
||||
if (t1 - t0 > 1*1000000000) break; // work one second
|
||||
if (t1 - t0 > 1*100000000) break; // work 0.1 seconds
|
||||
}
|
||||
|
||||
uint64_t r1 = get_tick_count();
|
||||
|
||||
+53
-23
@@ -115,6 +115,24 @@ static int flock_exnb(int fd)
|
||||
|
||||
namespace tools
|
||||
{
|
||||
|
||||
void copy_file(const std::string& from, const std::string& to)
|
||||
{
|
||||
using boost::filesystem::path;
|
||||
#if BOOST_VERSION < 107400
|
||||
// Remove this preprocessor if/else when we are bumping the boost version.
|
||||
boost::filesystem::copy_file(
|
||||
path(from),
|
||||
path(to),
|
||||
boost::filesystem::copy_option::overwrite_if_exists);
|
||||
#else
|
||||
boost::filesystem::copy_file(
|
||||
path(from),
|
||||
path(to),
|
||||
boost::filesystem::copy_options::overwrite_existing);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::function<void(int)> signal_handler::m_handler;
|
||||
|
||||
private_file::private_file() noexcept : m_handle(), m_filename() {}
|
||||
@@ -122,7 +140,7 @@ namespace tools
|
||||
private_file::private_file(std::FILE* handle, std::string&& filename) noexcept
|
||||
: m_handle(handle), m_filename(std::move(filename)) {}
|
||||
|
||||
private_file private_file::create(std::string name)
|
||||
private_file private_file::create(std::string name, uint32_t extra_flags)
|
||||
{
|
||||
#ifdef WIN32
|
||||
struct close_handle
|
||||
@@ -175,7 +193,7 @@ namespace tools
|
||||
name.c_str(),
|
||||
GENERIC_WRITE, FILE_SHARE_READ,
|
||||
std::addressof(attributes),
|
||||
CREATE_NEW, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE),
|
||||
CREATE_NEW, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | extra_flags),
|
||||
nullptr
|
||||
)
|
||||
};
|
||||
@@ -194,7 +212,7 @@ namespace tools
|
||||
}
|
||||
}
|
||||
#else
|
||||
const int fdr = open(name.c_str(), (O_RDONLY | O_CREAT), S_IRUSR);
|
||||
const int fdr = open(name.c_str(), (O_RDONLY | O_CREAT | extra_flags), S_IRUSR);
|
||||
if (0 <= fdr)
|
||||
{
|
||||
struct stat rstats = {};
|
||||
@@ -225,6 +243,23 @@ namespace tools
|
||||
return {};
|
||||
}
|
||||
|
||||
private_file private_file::drop_and_recreate(std::string filename)
|
||||
{
|
||||
if (epee::file_io_utils::is_file_exist(filename)) {
|
||||
boost::system::error_code ec{};
|
||||
boost::filesystem::remove(filename, ec);
|
||||
if (ec) {
|
||||
MERROR("Failed to remove " << filename << ": " << ec.message());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
#ifdef WIN32
|
||||
return create(filename);
|
||||
#else
|
||||
return create(filename, O_EXCL);
|
||||
#endif
|
||||
}
|
||||
|
||||
private_file::~private_file() noexcept
|
||||
{
|
||||
try
|
||||
@@ -882,13 +917,6 @@ std::string get_nix_version_display_string()
|
||||
|
||||
bool is_local_address(const std::string &address)
|
||||
{
|
||||
// always assume Tor/I2P addresses to be untrusted by default
|
||||
if (is_privacy_preserving_network(address))
|
||||
{
|
||||
MDEBUG("Address '" << address << "' is Tor/I2P, non local");
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract host
|
||||
epee::net_utils::http::url_content u_c;
|
||||
if (!epee::net_utils::parse_url(address, u_c))
|
||||
@@ -902,20 +930,22 @@ std::string get_nix_version_display_string()
|
||||
return false;
|
||||
}
|
||||
|
||||
// resolve to IP
|
||||
boost::asio::io_service io_service;
|
||||
boost::asio::ip::tcp::resolver resolver(io_service);
|
||||
boost::asio::ip::tcp::resolver::query query(u_c.host, "");
|
||||
boost::asio::ip::tcp::resolver::iterator i = resolver.resolve(query);
|
||||
while (i != boost::asio::ip::tcp::resolver::iterator())
|
||||
if (u_c.host == "localhost" || boost::ends_with(u_c.host, ".localhost")) { // RFC 6761 (6.3)
|
||||
MDEBUG("Address '" << address << "' is local");
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
const auto parsed_ip = boost::asio::ip::address::from_string(u_c.host, ec);
|
||||
if (ec) {
|
||||
MDEBUG("Failed to parse '" << address << "' as IP address: " << ec.message() << ". Considering it not local");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parsed_ip.is_loopback())
|
||||
{
|
||||
const boost::asio::ip::tcp::endpoint &ep = *i;
|
||||
if (ep.address().is_loopback())
|
||||
{
|
||||
MDEBUG("Address '" << address << "' is local");
|
||||
return true;
|
||||
}
|
||||
++i;
|
||||
MDEBUG("Address '" << address << "' is local");
|
||||
return true;
|
||||
}
|
||||
|
||||
MDEBUG("Address '" << address << "' is not local");
|
||||
|
||||
+7
-1
@@ -67,6 +67,8 @@ namespace tools
|
||||
}
|
||||
};
|
||||
|
||||
void copy_file(const std::string& from, const std::string& to);
|
||||
|
||||
//! A file restricted to process owner AND process. Deletes file on destruction.
|
||||
class private_file {
|
||||
std::unique_ptr<std::FILE, close_file> m_handle;
|
||||
@@ -80,7 +82,11 @@ namespace tools
|
||||
|
||||
/*! \return File only readable by owner and only used by this process
|
||||
OR `private_file{}` on error. */
|
||||
static private_file create(std::string filename);
|
||||
static private_file create(std::string filename, uint32_t extra_flags = 0);
|
||||
|
||||
/*! \return Drop and create file only readable by owner and only used
|
||||
by this process OR `private_file{}` on error. */
|
||||
static private_file drop_and_recreate(std::string filename);
|
||||
|
||||
private_file(private_file&&) = default;
|
||||
private_file& operator=(private_file&&) = default;
|
||||
|
||||
@@ -42,10 +42,11 @@
|
||||
#define CTHR_RWLOCK_TRYLOCK_READ(x) TryAcquireSRWLockShared(&x)
|
||||
|
||||
#define CTHR_THREAD_TYPE HANDLE
|
||||
#define CTHR_THREAD_RTYPE void
|
||||
#define CTHR_THREAD_RETURN return
|
||||
#define CTHR_THREAD_CREATE(thr, func, arg) ((thr = (HANDLE)_beginthread(func, 0, arg)) != -1L)
|
||||
#define CTHR_THREAD_JOIN(thr) WaitForSingleObject((HANDLE)thr, INFINITE)
|
||||
#define CTHR_THREAD_RTYPE unsigned __stdcall
|
||||
#define CTHR_THREAD_RETURN _endthreadex(0); return 0;
|
||||
#define CTHR_THREAD_CREATE(thr, func, arg) ((thr = (HANDLE)_beginthreadex(0, 0, func, arg, 0, 0)) != 0L)
|
||||
#define CTHR_THREAD_JOIN(thr) do { WaitForSingleObject(thr, INFINITE); CloseHandle(thr); } while(0)
|
||||
#define CTHR_THREAD_CLOSE(thr) CloseHandle((HANDLE)thr);
|
||||
|
||||
#else
|
||||
|
||||
@@ -64,5 +65,6 @@
|
||||
#define CTHR_THREAD_RETURN return NULL
|
||||
#define CTHR_THREAD_CREATE(thr, func, arg) (pthread_create(&thr, NULL, func, arg) == 0)
|
||||
#define CTHR_THREAD_JOIN(thr) pthread_join(thr, NULL)
|
||||
#define CTHR_THREAD_CLOSE(thr)
|
||||
|
||||
#endif
|
||||
|
||||
+13
-4
@@ -34,7 +34,7 @@ typedef struct {
|
||||
unsigned long long databitlen; /*the message size in bits*/
|
||||
unsigned long long datasize_in_buffer; /*the size of the message remained in buffer; assumed to be multiple of 8bits except for the last partial block at the end of the message*/
|
||||
DATA_ALIGN16(uint64 x[8][2]); /*the 1024-bit state, ( x[i][0] || x[i][1] ) is the ith row of the state in the pseudocode*/
|
||||
unsigned char buffer[64]; /*the 512-bit message block to be hashed;*/
|
||||
DATA_ALIGN16(unsigned char buffer[64]); /*the 512-bit message block to be hashed;*/
|
||||
} hashState;
|
||||
|
||||
|
||||
@@ -213,16 +213,24 @@ static void E8(hashState *state)
|
||||
/*The compression function F8 */
|
||||
static void F8(hashState *state)
|
||||
{
|
||||
uint64 i;
|
||||
uint64_t* x = (uint64_t*)state->x;
|
||||
|
||||
/*xor the 512-bit message with the fist half of the 1024-bit hash state*/
|
||||
for (i = 0; i < 8; i++) state->x[i >> 1][i & 1] ^= ((uint64*)state->buffer)[i];
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
uint64 b;
|
||||
memcpy(&b, &state->buffer[i << 3], sizeof(b));
|
||||
x[i] ^= b;
|
||||
}
|
||||
|
||||
/*the bijective function E8 */
|
||||
E8(state);
|
||||
|
||||
/*xor the 512-bit message with the second half of the 1024-bit hash state*/
|
||||
for (i = 0; i < 8; i++) state->x[(8+i) >> 1][(8+i) & 1] ^= ((uint64*)state->buffer)[i];
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
uint64 b;
|
||||
memcpy(&b, &state->buffer[i << 3], sizeof(b));
|
||||
x[i + 8] ^= b;
|
||||
}
|
||||
}
|
||||
|
||||
/*before hashing a message, initialize the hash state as H0 */
|
||||
@@ -240,6 +248,7 @@ static HashReturn Init(hashState *state, int hashbitlen)
|
||||
case 224: memcpy(state->x,JH224_H0,128); break;
|
||||
case 256: memcpy(state->x,JH256_H0,128); break;
|
||||
case 384: memcpy(state->x,JH384_H0,128); break;
|
||||
default:
|
||||
case 512: memcpy(state->x,JH512_H0,128); break;
|
||||
}
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@ static void rx_init_dataset(size_t max_threads) {
|
||||
local_abort("Couldn't start RandomX seed thread");
|
||||
}
|
||||
}
|
||||
rx_seedthread(&si[n1]);
|
||||
randomx_init_dataset(main_dataset, si[n1].si_cache, si[n1].si_start, si[n1].si_count);
|
||||
for (size_t i = 0; i < n1; ++i) CTHR_THREAD_JOIN(st[i]);
|
||||
CTHR_RWLOCK_UNLOCK_READ(main_cache_lock);
|
||||
|
||||
@@ -402,6 +402,7 @@ void rx_set_main_seedhash(const char *seedhash, size_t max_dataset_init_threads)
|
||||
if (!CTHR_THREAD_CREATE(t, rx_set_main_seedhash_thread, info)) {
|
||||
local_abort("Couldn't start RandomX seed thread");
|
||||
}
|
||||
CTHR_THREAD_CLOSE(t);
|
||||
}
|
||||
|
||||
void rx_slow_hash(const char *seedhash, const void *data, size_t length, char *result_hash) {
|
||||
|
||||
@@ -1229,7 +1229,7 @@ namespace cryptonote
|
||||
char *end = NULL;
|
||||
errno = 0;
|
||||
const unsigned long long ull = strtoull(buf, &end, 10);
|
||||
CHECK_AND_ASSERT_THROW_MES(ull != ULONG_MAX || errno == 0, "Failed to parse rounded amount: " << buf);
|
||||
CHECK_AND_ASSERT_THROW_MES(ull != ULLONG_MAX || errno == 0, "Failed to parse rounded amount: " << buf);
|
||||
CHECK_AND_ASSERT_THROW_MES(ull != 0 || amount == 0, "Overflow in rounding");
|
||||
return ull;
|
||||
}
|
||||
|
||||
@@ -523,7 +523,7 @@ namespace cryptonote
|
||||
bool miner::worker_thread()
|
||||
{
|
||||
const uint32_t th_local_index = m_thread_index++; // atomically increment, getting value before increment
|
||||
crypto::rx_set_miner_thread(th_local_index, tools::get_max_concurrency());
|
||||
bool rx_set = false;
|
||||
|
||||
MLOG_SET_THREAD_NAME(std::string("[miner ") + std::to_string(th_local_index) + "]");
|
||||
MGINFO("Miner thread was started ["<< th_local_index << "]");
|
||||
@@ -575,6 +575,13 @@ namespace cryptonote
|
||||
|
||||
b.nonce = nonce;
|
||||
crypto::hash h;
|
||||
|
||||
if ((b.major_version >= RX_BLOCK_VERSION) && !rx_set)
|
||||
{
|
||||
crypto::rx_set_miner_thread(th_local_index, tools::get_max_concurrency());
|
||||
rx_set = true;
|
||||
}
|
||||
|
||||
m_gbh(b, height, NULL, tools::get_max_concurrency(), h);
|
||||
|
||||
if(check_hash(h, local_diff))
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace cryptonote
|
||||
|
||||
// load
|
||||
template <template <bool> class Archive>
|
||||
bool do_serialize(Archive<false>& ar)
|
||||
bool member_do_serialize(Archive<false>& ar)
|
||||
{
|
||||
// size - 1 - because of variant tag
|
||||
for (size = 1; size <= TX_EXTRA_PADDING_MAX_COUNT; ++size)
|
||||
@@ -73,7 +73,7 @@ namespace cryptonote
|
||||
|
||||
// store
|
||||
template <template <bool> class Archive>
|
||||
bool do_serialize(Archive<true>& ar)
|
||||
bool member_do_serialize(Archive<true>& ar)
|
||||
{
|
||||
if(TX_EXTRA_PADDING_MAX_COUNT < size)
|
||||
return false;
|
||||
@@ -129,7 +129,7 @@ namespace cryptonote
|
||||
|
||||
// load
|
||||
template <template <bool> class Archive>
|
||||
bool do_serialize(Archive<false>& ar)
|
||||
bool member_do_serialize(Archive<false>& ar)
|
||||
{
|
||||
std::string field;
|
||||
if(!::do_serialize(ar, field))
|
||||
@@ -142,7 +142,7 @@ namespace cryptonote
|
||||
|
||||
// store
|
||||
template <template <bool> class Archive>
|
||||
bool do_serialize(Archive<true>& ar)
|
||||
bool member_do_serialize(Archive<true>& ar)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
binary_archive<true> oar(oss);
|
||||
|
||||
@@ -42,7 +42,12 @@ namespace cryptonote
|
||||
static_assert(unsigned(relay_method::none) == 0, "default m_relay initialization is not to relay_method::none");
|
||||
|
||||
relay_method m_relay; // gives indication on how tx should be relayed (if at all)
|
||||
bool m_verifivation_failed; //bad tx, should drop connection
|
||||
bool m_verifivation_failed; //bad tx, tx should not enter mempool and connection should be dropped unless m_no_drop_offense
|
||||
// Do not add to mempool, do not relay, but also do not punish the peer for sending or drop
|
||||
// connections to them. Used for low fees, tx_extra too big, "relay-only rules". Not to be
|
||||
// confused with breaking soft fork rules, because tx could be later added to the chain if mined
|
||||
// because it does not violate consensus rules.
|
||||
bool m_no_drop_offense;
|
||||
bool m_verifivation_impossible; //the transaction is related with an alternative blockchain
|
||||
bool m_added_to_pool;
|
||||
bool m_low_mixin;
|
||||
@@ -54,6 +59,7 @@ namespace cryptonote
|
||||
bool m_fee_too_low;
|
||||
bool m_too_few_outputs;
|
||||
bool m_tx_extra_too_big;
|
||||
bool m_nonzero_unlock_time;
|
||||
};
|
||||
|
||||
struct block_verification_context
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
|
||||
@@ -2065,7 +2065,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
||||
cryptonote::blobdata blob;
|
||||
if (m_tx_pool.have_tx(txid, relay_category::legacy))
|
||||
{
|
||||
if (m_tx_pool.get_transaction_info(txid, td))
|
||||
if (m_tx_pool.get_transaction_info(txid, td, true/*include_sensitive_data*/))
|
||||
{
|
||||
bei.block_cumulative_weight += td.weight;
|
||||
}
|
||||
@@ -3710,7 +3710,7 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b
|
||||
div128_64(hi, lo, median_block_weight, &hi, &lo, NULL, NULL);
|
||||
assert(hi == 0);
|
||||
lo -= lo / 20;
|
||||
return lo;
|
||||
return lo == 0 ? 1 : lo;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3832,6 +3832,8 @@ void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_block
|
||||
const uint8_t version = get_current_hard_fork_version();
|
||||
const uint64_t db_height = m_db->height();
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(grace_blocks <= CRYPTONOTE_REWARD_BLOCKS_WINDOW, "Grace blocks invalid In 2021 fee scaling estimate.");
|
||||
|
||||
// we want Mlw = median of max((min(Mbw, 1.7 * Ml), Zm), Ml / 1.7)
|
||||
// Mbw: block weight for the last 99990 blocks, 0 for the next 10
|
||||
// Ml: penalty free zone (dynamic), aka long_term_median, aka median of max((min(Mb, 1.7 * Ml), Zm), Ml / 1.7)
|
||||
@@ -3845,7 +3847,6 @@ void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_block
|
||||
const uint64_t Mlw_penalty_free_zone_for_wallet = std::max<uint64_t>(rm.median(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5);
|
||||
|
||||
// Msw: median over [100 - grace blocks] past + [grace blocks] future blocks
|
||||
CHECK_AND_ASSERT_THROW_MES(grace_blocks <= 100, "Grace blocks invalid In 2021 fee scaling estimate.");
|
||||
std::vector<uint64_t> weights;
|
||||
get_last_n_blocks_weights(weights, 100 - grace_blocks);
|
||||
weights.reserve(100);
|
||||
@@ -4616,40 +4617,9 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint64_t block_weight = m_db->get_block_weight(db_height - 1);
|
||||
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
|
||||
const uint64_t long_term_median = get_long_term_block_weight_median(db_height - nblocks, nblocks);
|
||||
|
||||
uint64_t long_term_median;
|
||||
if (db_height == 1)
|
||||
{
|
||||
long_term_median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
|
||||
if (nblocks == db_height)
|
||||
--nblocks;
|
||||
long_term_median = get_long_term_block_weight_median(db_height - nblocks - 1, nblocks);
|
||||
}
|
||||
|
||||
m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
|
||||
|
||||
uint64_t short_term_constraint = m_long_term_effective_median_block_weight;
|
||||
if (hf_version >= HF_VERSION_2021_SCALING)
|
||||
short_term_constraint += m_long_term_effective_median_block_weight * 7 / 10;
|
||||
else
|
||||
short_term_constraint += m_long_term_effective_median_block_weight * 2 / 5;
|
||||
uint64_t long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint);
|
||||
|
||||
if (db_height == 1)
|
||||
{
|
||||
long_term_median = long_term_block_weight;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_long_term_block_weights_cache_tip_hash = m_db->get_block_hash_from_height(db_height - 1);
|
||||
m_long_term_block_weights_cache_rolling_median.insert(long_term_block_weight);
|
||||
long_term_median = m_long_term_block_weights_cache_rolling_median.median();
|
||||
}
|
||||
m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
|
||||
|
||||
std::vector<uint64_t> weights;
|
||||
@@ -5582,7 +5552,7 @@ void Blockchain::cancel()
|
||||
}
|
||||
|
||||
#if defined(PER_BLOCK_CHECKPOINT)
|
||||
static const char expected_block_hashes_hash[] = "af0467d5d8ac1ad7232ebfd8610ec775df3438cc3b7e8033c10ad71874b72e15";
|
||||
static const char expected_block_hashes_hash[] = "8ada865350270fd008397684d978dac75ea4029a8a1ffcaa9975c43be119ec19";
|
||||
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
|
||||
{
|
||||
if (get_checkpoints == nullptr || !m_fast_sync)
|
||||
|
||||
@@ -1099,7 +1099,7 @@ namespace cryptonote
|
||||
else if(tvc[i].m_verifivation_impossible)
|
||||
{MERROR_VER("Transaction verification impossible: " << results[i].hash);}
|
||||
|
||||
if(tvc[i].m_added_to_pool)
|
||||
if(tvc[i].m_added_to_pool && results[i].tx.extra.size() <= MAX_TX_EXTRA_SIZE)
|
||||
{
|
||||
MDEBUG("tx added: " << results[i].hash);
|
||||
valid_events = true;
|
||||
@@ -1727,6 +1727,11 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_pool_transactions_info(const std::vector<crypto::hash>& txids, std::vector<std::pair<crypto::hash, tx_memory_pool::tx_details>>& txs, bool include_sensitive_txes) const
|
||||
{
|
||||
return m_mempool.get_transactions_info(txids, txs, include_sensitive_txes);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_pool_transactions(std::vector<transaction>& txs, bool include_sensitive_data) const
|
||||
{
|
||||
m_mempool.get_transactions(txs, include_sensitive_data);
|
||||
@@ -1739,6 +1744,11 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_pool_info(time_t start_time, bool include_sensitive_txes, size_t max_tx_count, std::vector<std::pair<crypto::hash, tx_memory_pool::tx_details>>& added_txs, std::vector<crypto::hash>& remaining_added_txids, std::vector<crypto::hash>& removed_txs, bool& incremental) const
|
||||
{
|
||||
return m_mempool.get_pool_info(start_time, include_sensitive_txes, max_tx_count, added_txs, remaining_added_txids, removed_txs, incremental);
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool core::get_pool_transaction_stats(struct txpool_stats& stats, bool include_sensitive_data) const
|
||||
{
|
||||
m_mempool.get_transaction_stats(stats, include_sensitive_data);
|
||||
|
||||
@@ -510,6 +510,23 @@ namespace cryptonote
|
||||
bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_sensitive_txes = false) const;
|
||||
|
||||
/**
|
||||
* @copydoc tx_memory_pool::get_pool_transactions_info
|
||||
* @param include_sensitive_txes include private transactions
|
||||
*
|
||||
* @note see tx_memory_pool::get_pool_transactions_info
|
||||
*/
|
||||
bool get_pool_transactions_info(const std::vector<crypto::hash>& txids, std::vector<std::pair<crypto::hash, tx_memory_pool::tx_details>>& txs, bool include_sensitive_txes = false) const;
|
||||
|
||||
/**
|
||||
* @copydoc tx_memory_pool::get_pool_info
|
||||
* @param include_sensitive_txes include private transactions
|
||||
* @param max_tx_count max allowed added_txs in response
|
||||
*
|
||||
* @note see tx_memory_pool::get_pool_info
|
||||
*/
|
||||
bool get_pool_info(time_t start_time, bool include_sensitive_txes, size_t max_tx_count, std::vector<std::pair<crypto::hash, tx_memory_pool::tx_details>>& added_txs, std::vector<crypto::hash>& remaining_added_txids, std::vector<crypto::hash>& removed_txs, bool& incremental) const;
|
||||
|
||||
/**
|
||||
* @copydoc tx_memory_pool::get_transactions
|
||||
* @param include_sensitive_txes include private transactions
|
||||
*
|
||||
|
||||
@@ -203,7 +203,7 @@ namespace cryptonote
|
||||
return addr.m_view_public_key;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, bool shuffle_outs, bool use_view_tags)
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, bool shuffle_outs, bool use_view_tags)
|
||||
{
|
||||
hw::device &hwdev = sender_account_keys.get_device();
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace cryptonote
|
||||
amount_keys.clear();
|
||||
|
||||
tx.version = rct ? 2 : 1;
|
||||
tx.unlock_time = unlock_time;
|
||||
tx.unlock_time = 0;
|
||||
|
||||
tx.extra = extra;
|
||||
crypto::public_key txkey_pub;
|
||||
@@ -606,7 +606,7 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, bool use_view_tags)
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, bool use_view_tags)
|
||||
{
|
||||
hw::device &hwdev = sender_account_keys.get_device();
|
||||
hwdev.open_tx(tx_key);
|
||||
@@ -627,7 +627,7 @@ namespace cryptonote
|
||||
}
|
||||
|
||||
bool shuffle_outs = true;
|
||||
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, shuffle_outs, use_view_tags);
|
||||
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, tx_key, additional_tx_keys, rct, rct_config, shuffle_outs, use_view_tags);
|
||||
hwdev.close_tx();
|
||||
return r;
|
||||
} catch(...) {
|
||||
@@ -636,14 +636,14 @@ namespace cryptonote
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time)
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx)
|
||||
{
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||
subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0};
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
std::vector<tx_destination_entry> destinations_copy = destinations;
|
||||
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0});
|
||||
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0});
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool generate_genesis_block(
|
||||
|
||||
@@ -118,9 +118,9 @@ namespace cryptonote
|
||||
|
||||
//---------------------------------------------------------------
|
||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time);
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool shuffle_outs = true, bool use_view_tags = false);
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool use_view_tags = false);
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx);
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool shuffle_outs = true, bool use_view_tags = false);
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool use_view_tags = false);
|
||||
bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
|
||||
const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index,
|
||||
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
|
||||
|
||||
+259
-24
@@ -133,6 +133,12 @@ namespace cryptonote
|
||||
// class code expects unsigned values throughout
|
||||
if (m_next_check < time_t(0))
|
||||
throw std::runtime_error{"Unexpected time_t (system clock) value"};
|
||||
|
||||
m_added_txs_start_time = (time_t)0;
|
||||
m_removed_txs_start_time = (time_t)0;
|
||||
// We don't set these to "now" already here as we don't know how long it takes from construction
|
||||
// of the pool until it "goes to work". It's safer to set when the first actual txs enter the
|
||||
// corresponding lists.
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version)
|
||||
@@ -207,6 +213,7 @@ namespace cryptonote
|
||||
{
|
||||
tvc.m_verifivation_failed = true;
|
||||
tvc.m_fee_too_low = true;
|
||||
tvc.m_no_drop_offense = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -225,6 +232,16 @@ namespace cryptonote
|
||||
LOG_PRINT_L1("transaction tx-extra is too big: " << tx_extra_size << " bytes, the limit is: " << MAX_TX_EXTRA_SIZE);
|
||||
tvc.m_verifivation_failed = true;
|
||||
tvc.m_tx_extra_too_big = true;
|
||||
tvc.m_no_drop_offense = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!kept_by_block && tx.unlock_time)
|
||||
{
|
||||
LOG_PRINT_L1("transaction unlock time is not zero: " << tx.unlock_time);
|
||||
tvc.m_verifivation_failed = true;
|
||||
tvc.m_nonzero_unlock_time = true;
|
||||
tvc.m_no_drop_offense = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -239,6 +256,7 @@ namespace cryptonote
|
||||
LOG_PRINT_L1("Transaction with id= "<< id << " used already spent key images");
|
||||
tvc.m_verifivation_failed = true;
|
||||
tvc.m_double_spend = true;
|
||||
tvc.m_no_drop_offense = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -290,7 +308,7 @@ namespace cryptonote
|
||||
return false;
|
||||
|
||||
m_blockchain.add_txpool_tx(id, blob, meta);
|
||||
m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)(tx_weight ? tx_weight : 1), receive_time), id);
|
||||
add_tx_to_transient_lists(id, fee / (double)(tx_weight ? tx_weight : 1), receive_time);
|
||||
lock.commit();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
@@ -361,7 +379,7 @@ namespace cryptonote
|
||||
|
||||
m_blockchain.remove_txpool_tx(id);
|
||||
m_blockchain.add_txpool_tx(id, blob, meta);
|
||||
m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)(tx_weight ? tx_weight : 1), receive_time), id);
|
||||
add_tx_to_transient_lists(id, meta.fee / (double)(tx_weight ? tx_weight : 1), receive_time);
|
||||
}
|
||||
lock.commit();
|
||||
}
|
||||
@@ -382,7 +400,7 @@ namespace cryptonote
|
||||
|
||||
++m_cookie;
|
||||
|
||||
MINFO("Transaction added to pool: txid " << id << " weight: " << tx_weight << " fee/byte: " << (fee / (double)(tx_weight ? tx_weight : 1)));
|
||||
MINFO("Transaction added to pool: txid " << id << " weight: " << tx_weight << " fee/byte: " << (fee / (double)(tx_weight ? tx_weight : 1)) << ", count: " << m_added_txs_by_id.size());
|
||||
|
||||
prune(m_txpool_max_weight);
|
||||
|
||||
@@ -427,8 +445,14 @@ namespace cryptonote
|
||||
void tx_memory_pool::prune(size_t bytes)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
|
||||
// Nothing to do if already empty
|
||||
if (m_txs_by_fee_and_receive_time.empty())
|
||||
return;
|
||||
|
||||
if (bytes == 0)
|
||||
bytes = m_txpool_max_weight;
|
||||
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
LockedTXN lock(m_blockchain.get_db());
|
||||
bool changed = false;
|
||||
@@ -473,7 +497,13 @@ namespace cryptonote
|
||||
reduce_txpool_weight(meta.weight);
|
||||
remove_transaction_keyimages(tx, txid);
|
||||
MINFO("Pruned tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->first.first);
|
||||
m_txs_by_fee_and_receive_time.erase(it--);
|
||||
|
||||
auto it_prev = it;
|
||||
--it_prev;
|
||||
|
||||
remove_tx_from_transient_lists(it, txid, !meta.matches(relay_category::broadcasted));
|
||||
it = it_prev;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
@@ -555,8 +585,7 @@ namespace cryptonote
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
|
||||
auto sorted_it = find_tx_in_sorted_container(id);
|
||||
|
||||
bool sensitive = false;
|
||||
try
|
||||
{
|
||||
LockedTXN lock(m_blockchain.get_db());
|
||||
@@ -587,6 +616,7 @@ namespace cryptonote
|
||||
do_not_relay = meta.do_not_relay;
|
||||
double_spend_seen = meta.double_spend_seen;
|
||||
pruned = meta.pruned;
|
||||
sensitive = !meta.matches(relay_category::broadcasted);
|
||||
|
||||
// remove first, in case this throws, so key images aren't removed
|
||||
m_blockchain.remove_txpool_tx(id);
|
||||
@@ -600,13 +630,12 @@ namespace cryptonote
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sorted_it != m_txs_by_fee_and_receive_time.end())
|
||||
m_txs_by_fee_and_receive_time.erase(sorted_it);
|
||||
remove_tx_from_transient_lists(find_tx_in_sorted_container(id), id, sensitive);
|
||||
++m_cookie;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::get_transaction_info(const crypto::hash &txid, tx_details &td) const
|
||||
bool tx_memory_pool::get_transaction_info(const crypto::hash &txid, tx_details &td, bool include_sensitive_data, bool include_blob) const
|
||||
{
|
||||
PERF_TIMER(get_transaction_info);
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
@@ -618,7 +647,12 @@ namespace cryptonote
|
||||
txpool_tx_meta_t meta;
|
||||
if (!m_blockchain.get_txpool_tx_meta(txid, meta))
|
||||
{
|
||||
MERROR("Failed to find tx in txpool");
|
||||
LOG_PRINT_L2("Failed to find tx in txpool: " << txid);
|
||||
return false;
|
||||
}
|
||||
if (!include_sensitive_data && !meta.matches(relay_category::broadcasted))
|
||||
{
|
||||
// We don't want sensitive data && the tx is sensitive, so no need to return it
|
||||
return false;
|
||||
}
|
||||
cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid, relay_category::all);
|
||||
@@ -644,11 +678,13 @@ namespace cryptonote
|
||||
td.kept_by_block = meta.kept_by_block;
|
||||
td.last_failed_height = meta.last_failed_height;
|
||||
td.last_failed_id = meta.last_failed_id;
|
||||
td.receive_time = meta.receive_time;
|
||||
td.last_relayed_time = meta.dandelionpp_stem ? 0 : meta.last_relayed_time;
|
||||
td.receive_time = include_sensitive_data ? meta.receive_time : 0;
|
||||
td.last_relayed_time = (include_sensitive_data && !meta.dandelionpp_stem) ? meta.last_relayed_time : 0;
|
||||
td.relayed = meta.relayed;
|
||||
td.do_not_relay = meta.do_not_relay;
|
||||
td.double_spend_seen = meta.double_spend_seen;
|
||||
if (include_blob)
|
||||
td.tx_blob = std::move(txblob);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
@@ -658,6 +694,25 @@ namespace cryptonote
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool tx_memory_pool::get_transactions_info(const std::vector<crypto::hash>& txids, std::vector<std::pair<crypto::hash, tx_details>>& txs, bool include_sensitive) const
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
|
||||
txs.clear();
|
||||
|
||||
for (const auto &it: txids)
|
||||
{
|
||||
tx_details details;
|
||||
bool success = get_transaction_info(it, details, include_sensitive, true/*include_blob*/);
|
||||
if (success)
|
||||
{
|
||||
txs.push_back(std::make_pair(it, std::move(details)));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::get_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) const
|
||||
{
|
||||
@@ -719,15 +774,7 @@ namespace cryptonote
|
||||
(tx_age > CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME && meta.kept_by_block) )
|
||||
{
|
||||
LOG_PRINT_L1("Tx " << txid << " removed from tx pool due to outdated, age: " << tx_age );
|
||||
auto sorted_it = find_tx_in_sorted_container(txid);
|
||||
if (sorted_it == m_txs_by_fee_and_receive_time.end())
|
||||
{
|
||||
LOG_PRINT_L1("Removing tx " << txid << " from tx pool, but it was not found in the sorted txs container!");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_txs_by_fee_and_receive_time.erase(sorted_it);
|
||||
}
|
||||
remove_tx_from_transient_lists(find_tx_in_sorted_container(txid), txid, !meta.matches(relay_category::broadcasted));
|
||||
m_timed_out_transactions.insert(txid);
|
||||
remove.push_back(std::make_pair(txid, meta.weight));
|
||||
}
|
||||
@@ -881,9 +928,12 @@ namespace cryptonote
|
||||
meta.last_relayed_time = std::chrono::system_clock::to_time_t(now);
|
||||
|
||||
m_blockchain.update_txpool_tx(hash, meta);
|
||||
|
||||
// wait until db update succeeds to ensure tx is visible in the pool
|
||||
was_just_broadcasted = !already_broadcasted && meta.matches(relay_category::broadcasted);
|
||||
|
||||
if (was_just_broadcasted)
|
||||
// Make sure the tx gets re-added with an updated time
|
||||
add_tx_to_transient_lists(hash, meta.fee / (double)meta.weight, std::chrono::system_clock::to_time_t(now));
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
@@ -936,6 +986,81 @@ namespace cryptonote
|
||||
}, false, category);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool tx_memory_pool::get_pool_info(time_t start_time, bool include_sensitive, size_t max_tx_count, std::vector<std::pair<crypto::hash, tx_details>>& added_txs, std::vector<crypto::hash>& remaining_added_txids, std::vector<crypto::hash>& removed_txs, bool& incremental) const
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
|
||||
incremental = true;
|
||||
if (start_time == (time_t)0)
|
||||
{
|
||||
// Giving no start time means give back whole pool
|
||||
incremental = false;
|
||||
}
|
||||
else if ((m_added_txs_start_time != (time_t)0) && (m_removed_txs_start_time != (time_t)0))
|
||||
{
|
||||
if ((start_time <= m_added_txs_start_time) || (start_time <= m_removed_txs_start_time))
|
||||
{
|
||||
// If either of the two lists do not go back far enough it's not possible to
|
||||
// deliver incremental pool info
|
||||
incremental = false;
|
||||
}
|
||||
// The check uses "<=": We cannot be sure to have ALL txs exactly at start_time, only AFTER that time
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some incremental info still missing completely
|
||||
incremental = false;
|
||||
}
|
||||
|
||||
added_txs.clear();
|
||||
remaining_added_txids.clear();
|
||||
removed_txs.clear();
|
||||
|
||||
std::vector<crypto::hash> txids;
|
||||
if (!incremental)
|
||||
{
|
||||
LOG_PRINT_L2("Giving back the whole pool");
|
||||
// Give back the whole pool in 'added_txs'; because calling 'get_transaction_info' right inside the
|
||||
// anonymous method somehow results in an LMDB error with transactions we have to build a list of
|
||||
// ids first and get the full info afterwards
|
||||
get_transaction_hashes(txids, include_sensitive);
|
||||
if (txids.size() > max_tx_count)
|
||||
{
|
||||
remaining_added_txids = std::vector<crypto::hash>(txids.begin() + max_tx_count, txids.end());
|
||||
txids.erase(txids.begin() + max_tx_count, txids.end());
|
||||
}
|
||||
get_transactions_info(txids, added_txs, include_sensitive);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Give back incrementally, based on time of entry into the map
|
||||
for (const auto &pit : m_added_txs_by_id)
|
||||
{
|
||||
if (pit.second >= start_time)
|
||||
txids.push_back(pit.first);
|
||||
}
|
||||
get_transactions_info(txids, added_txs, include_sensitive);
|
||||
if (added_txs.size() > max_tx_count)
|
||||
{
|
||||
remaining_added_txids.reserve(added_txs.size() - max_tx_count);
|
||||
for (size_t i = max_tx_count; i < added_txs.size(); ++i)
|
||||
remaining_added_txids.push_back(added_txs[i].first);
|
||||
added_txs.erase(added_txs.begin() + max_tx_count, added_txs.end());
|
||||
}
|
||||
|
||||
std::multimap<time_t, removed_tx_info>::const_iterator rit = m_removed_txs_by_time.lower_bound(start_time);
|
||||
while (rit != m_removed_txs_by_time.end())
|
||||
{
|
||||
if (include_sensitive || !rit->second.sensitive)
|
||||
{
|
||||
removed_txs.push_back(rit->second.txid);
|
||||
}
|
||||
++rit;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_sensitive) const
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
@@ -970,7 +1095,7 @@ namespace cryptonote
|
||||
|
||||
// If the total weight is too high, choose the best paying transactions
|
||||
if (total_weight > max_backlog_weight)
|
||||
std::sort(tmp.begin(), tmp.end(), [](const auto& a, const auto& b){ return a.fee * b.weight > b.fee * a.weight; });
|
||||
std::stable_sort(tmp.begin(), tmp.end(), [](const auto& a, const auto& b){ return a.fee * b.weight > b.fee * a.weight; });
|
||||
|
||||
backlog.clear();
|
||||
uint64_t w = 0;
|
||||
@@ -1640,6 +1765,12 @@ namespace cryptonote
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
|
||||
// Simply throw away incremental info, too difficult to update
|
||||
m_added_txs_by_id.clear();
|
||||
m_added_txs_start_time = (time_t)0;
|
||||
m_removed_txs_by_time.clear();
|
||||
m_removed_txs_start_time = (time_t)0;
|
||||
|
||||
MINFO("Validating txpool contents for v" << (unsigned)version);
|
||||
|
||||
LockedTXN lock(m_blockchain.get_db());
|
||||
@@ -1697,6 +1828,106 @@ namespace cryptonote
|
||||
return n_removed;
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
void tx_memory_pool::add_tx_to_transient_lists(const crypto::hash& txid, double fee, time_t receive_time)
|
||||
{
|
||||
|
||||
time_t now = time(NULL);
|
||||
const std::unordered_map<crypto::hash, time_t>::iterator it = m_added_txs_by_id.find(txid);
|
||||
if (it == m_added_txs_by_id.end())
|
||||
{
|
||||
m_added_txs_by_id.insert(std::make_pair(txid, now));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This tx was already added to the map earlier, probably because then it was in the "stem"
|
||||
// phase of Dandelion++ and now is in the "fluff" phase i.e. got broadcasted: We have to set
|
||||
// a new time for clients that are not allowed to see sensitive txs to make sure they will
|
||||
// see it now if they query incrementally
|
||||
it->second = now;
|
||||
|
||||
auto sorted_it = find_tx_in_sorted_container(txid);
|
||||
if (sorted_it == m_txs_by_fee_and_receive_time.end())
|
||||
{
|
||||
MDEBUG("Re-adding tx " << txid << " to tx pool, but it was not found in the sorted txs container");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_txs_by_fee_and_receive_time.erase(sorted_it);
|
||||
}
|
||||
}
|
||||
m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(fee, receive_time), txid);
|
||||
|
||||
// Don't check for "resurrected" txs in case of reorgs i.e. don't check in 'm_removed_txs_by_time'
|
||||
// whether we have that txid there and if yes remove it; this results in possible duplicates
|
||||
// where we return certain txids as deleted AND in the pool at the same time which requires
|
||||
// clients to process deleted ones BEFORE processing pool txs
|
||||
if (m_added_txs_start_time == (time_t)0)
|
||||
{
|
||||
m_added_txs_start_time = now;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
void tx_memory_pool::remove_tx_from_transient_lists(const cryptonote::sorted_tx_container::iterator& sorted_it, const crypto::hash& txid, bool sensitive)
|
||||
{
|
||||
if (sorted_it == m_txs_by_fee_and_receive_time.end())
|
||||
{
|
||||
LOG_PRINT_L1("Removing tx " << txid << " from tx pool, but it was not found in the sorted txs container!");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_txs_by_fee_and_receive_time.erase(sorted_it);
|
||||
}
|
||||
|
||||
const std::unordered_map<crypto::hash, time_t>::iterator it = m_added_txs_by_id.find(txid);
|
||||
if (it != m_added_txs_by_id.end())
|
||||
{
|
||||
m_added_txs_by_id.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
MDEBUG("Removing tx " << txid << " from tx pool, but it was not found in the map of added txs");
|
||||
}
|
||||
track_removed_tx(txid, sensitive);
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
void tx_memory_pool::track_removed_tx(const crypto::hash& txid, bool sensitive)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
m_removed_txs_by_time.insert(std::make_pair(now, removed_tx_info{txid, sensitive}));
|
||||
MDEBUG("Transaction removed from pool: txid " << txid << ", total entries in removed list now " << m_removed_txs_by_time.size());
|
||||
if (m_removed_txs_start_time == (time_t)0)
|
||||
{
|
||||
m_removed_txs_start_time = now;
|
||||
}
|
||||
|
||||
// Simple system to make sure the list of removed ids does not swell to an unmanageable size: Set
|
||||
// an absolute size limit plus delete entries that are x minutes old (which is ok because clients
|
||||
// will sync with sensible time intervalls and should not ask for incremental info e.g. 1 hour back)
|
||||
const int MAX_REMOVED = 20000;
|
||||
if (m_removed_txs_by_time.size() > MAX_REMOVED)
|
||||
{
|
||||
auto erase_it = m_removed_txs_by_time.begin();
|
||||
std::advance(erase_it, MAX_REMOVED / 4 + 1);
|
||||
m_removed_txs_by_time.erase(m_removed_txs_by_time.begin(), erase_it);
|
||||
m_removed_txs_start_time = m_removed_txs_by_time.begin()->first;
|
||||
MDEBUG("Erased old transactions from big removed list, leaving " << m_removed_txs_by_time.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
time_t earliest = now - (30 * 60); // 30 minutes
|
||||
std::map<time_t, removed_tx_info>::iterator from, to;
|
||||
from = m_removed_txs_by_time.begin();
|
||||
to = m_removed_txs_by_time.lower_bound(earliest);
|
||||
int distance = std::distance(from, to);
|
||||
if (distance > 0)
|
||||
{
|
||||
m_removed_txs_by_time.erase(from, to);
|
||||
m_removed_txs_start_time = earliest;
|
||||
MDEBUG("Erased " << distance << " old transactions from removed list, leaving " << m_removed_txs_by_time.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::init(size_t max_txpool_weight, bool mine_stem_txes)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
@@ -1704,6 +1935,10 @@ namespace cryptonote
|
||||
|
||||
m_txpool_max_weight = max_txpool_weight ? max_txpool_weight : DEFAULT_TXPOOL_MAX_WEIGHT;
|
||||
m_txs_by_fee_and_receive_time.clear();
|
||||
m_added_txs_by_id.clear();
|
||||
m_added_txs_start_time = (time_t)0;
|
||||
m_removed_txs_by_time.clear();
|
||||
m_removed_txs_start_time = (time_t)0;
|
||||
m_spent_key_images.clear();
|
||||
m_txpool_weight = 0;
|
||||
std::vector<crypto::hash> remove;
|
||||
@@ -1728,7 +1963,7 @@ namespace cryptonote
|
||||
MFATAL("Failed to insert key images from txpool tx");
|
||||
return false;
|
||||
}
|
||||
m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.weight, meta.receive_time), txid);
|
||||
add_tx_to_transient_lists(txid, meta.fee / (double)meta.weight, meta.receive_time);
|
||||
m_txpool_weight += meta.weight;
|
||||
return true;
|
||||
}, true, relay_category::all);
|
||||
|
||||
@@ -69,11 +69,12 @@ namespace cryptonote
|
||||
{
|
||||
// sort by greatest first, not least
|
||||
if (a.first.first > b.first.first) return true;
|
||||
else if (a.first.first < b.first.first) return false;
|
||||
else if (a.first.second < b.first.second) return true;
|
||||
else if (a.first.second > b.first.second) return false;
|
||||
else if (a.second != b.second) return true;
|
||||
else return false;
|
||||
if (a.first.first < b.first.first) return false;
|
||||
|
||||
if (a.first.second < b.first.second) return true;
|
||||
if (a.first.second > b.first.second) return false;
|
||||
|
||||
return memcmp(a.second.data, b.second.data, sizeof(crypto::hash)) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -428,6 +429,7 @@ namespace cryptonote
|
||||
struct tx_details
|
||||
{
|
||||
transaction tx; //!< the transaction
|
||||
cryptonote::blobdata tx_blob; //!< the transaction's binary blob
|
||||
size_t blob_size; //!< the transaction's size
|
||||
size_t weight; //!< the transaction's weight
|
||||
uint64_t fee; //!< the transaction's fee amount
|
||||
@@ -466,13 +468,25 @@ namespace cryptonote
|
||||
/**
|
||||
* @brief get infornation about a single transaction
|
||||
*/
|
||||
bool get_transaction_info(const crypto::hash &txid, tx_details &td) const;
|
||||
bool get_transaction_info(const crypto::hash &txid, tx_details &td, bool include_sensitive_data, bool include_blob = false) const;
|
||||
|
||||
/**
|
||||
* @brief get information about multiple transactions
|
||||
*/
|
||||
bool get_transactions_info(const std::vector<crypto::hash>& txids, std::vector<std::pair<crypto::hash, tx_details>>& txs, bool include_sensitive_data = false) const;
|
||||
|
||||
/**
|
||||
* @brief get transactions not in the passed set
|
||||
*/
|
||||
bool get_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) const;
|
||||
|
||||
/**
|
||||
* @brief get info necessary for update of pool-related info in a wallet, preferably incremental
|
||||
*
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
bool get_pool_info(time_t start_time, bool include_sensitive, size_t max_tx_count, std::vector<std::pair<crypto::hash, tx_details>>& added_txs, std::vector<crypto::hash>& remaining_added_txids, std::vector<crypto::hash>& removed_txs, bool& incremental) const;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
@@ -577,6 +591,10 @@ namespace cryptonote
|
||||
*/
|
||||
void prune(size_t bytes = 0);
|
||||
|
||||
void add_tx_to_transient_lists(const crypto::hash& txid, double fee, time_t receive_time);
|
||||
void remove_tx_from_transient_lists(const cryptonote::sorted_tx_container::iterator& sorted_it, const crypto::hash& txid, bool sensitive);
|
||||
void track_removed_tx(const crypto::hash& txid, bool sensitive);
|
||||
|
||||
//TODO: confirm the below comments and investigate whether or not this
|
||||
// is the desired behavior
|
||||
//! map key images to transactions which spent them
|
||||
@@ -609,6 +627,26 @@ private:
|
||||
|
||||
std::atomic<uint64_t> m_cookie; //!< incremented at each change
|
||||
|
||||
// Info when transactions entered the pool, accessible by txid
|
||||
std::unordered_map<crypto::hash, time_t> m_added_txs_by_id;
|
||||
|
||||
// Info at what time the pool started to track the adding of transactions
|
||||
time_t m_added_txs_start_time;
|
||||
|
||||
struct removed_tx_info
|
||||
{
|
||||
crypto::hash txid;
|
||||
bool sensitive;
|
||||
};
|
||||
|
||||
// Info about transactions that were removed from the pool, ordered by the time
|
||||
// of deletion
|
||||
std::multimap<time_t, removed_tx_info> m_removed_txs_by_time;
|
||||
|
||||
// Info how far back in time the list of removed tx ids currently reaches
|
||||
// (it gets shorted periodically to prevent overflow)
|
||||
time_t m_removed_txs_start_time;
|
||||
|
||||
/**
|
||||
* @brief get an iterator to a transaction in the sorted container
|
||||
*
|
||||
|
||||
@@ -979,8 +979,18 @@ namespace cryptonote
|
||||
int t_cryptonote_protocol_handler<t_core>::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context)
|
||||
{
|
||||
MLOG_P2P_MESSAGE("Received NOTIFY_NEW_TRANSACTIONS (" << arg.txs.size() << " txes)");
|
||||
std::unordered_set<blobdata> seen;
|
||||
for (const auto &blob: arg.txs)
|
||||
{
|
||||
MLOGIF_P2P_MESSAGE(cryptonote::transaction tx; crypto::hash hash; bool ret = cryptonote::parse_and_validate_tx_from_blob(blob, tx, hash);, ret, "Including transaction " << hash);
|
||||
if (seen.find(blob) != seen.end())
|
||||
{
|
||||
LOG_PRINT_CCONTEXT_L1("Duplicate transaction in notification, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
return 1;
|
||||
}
|
||||
seen.insert(blob);
|
||||
}
|
||||
|
||||
if(context.m_state != cryptonote_connection_context::state_normal)
|
||||
return 1;
|
||||
@@ -1020,7 +1030,7 @@ namespace cryptonote
|
||||
for (auto& tx : arg.txs)
|
||||
{
|
||||
tx_verification_context tvc{};
|
||||
if (!m_core.handle_incoming_tx({tx, crypto::null_hash}, tvc, tx_relay, true))
|
||||
if (!m_core.handle_incoming_tx({tx, crypto::null_hash}, tvc, tx_relay, true) && !tvc.m_no_drop_offense)
|
||||
{
|
||||
LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection");
|
||||
drop_connection(context, false, false);
|
||||
|
||||
@@ -396,6 +396,8 @@ namespace levin
|
||||
for (auto& connection : connections)
|
||||
{
|
||||
std::sort(connection.first.begin(), connection.first.end()); // don't leak receive order
|
||||
connection.first.erase(std::unique(connection.first.begin(), connection.first.end()),
|
||||
connection.first.end());
|
||||
make_payload_send_txs(*zone_->p2p, std::move(connection.first), connection.second, zone_->pad_txs, true);
|
||||
}
|
||||
|
||||
@@ -741,9 +743,14 @@ namespace levin
|
||||
notify::status notify::get_status() const noexcept
|
||||
{
|
||||
if (!zone_)
|
||||
return {false, false};
|
||||
return {false, false, false};
|
||||
|
||||
return {!zone_->noise.empty(), CRYPTONOTE_NOISE_CHANNELS <= zone_->connection_count};
|
||||
// `connection_count` is only set when `!noise.empty()`.
|
||||
const std::size_t connection_count = zone_->connection_count;
|
||||
bool has_outgoing = connection_count;
|
||||
if (zone_->noise.empty())
|
||||
has_outgoing = zone_->p2p->get_out_connections_count();
|
||||
return {!zone_->noise.empty(), CRYPTONOTE_NOISE_CHANNELS <= connection_count, has_outgoing};
|
||||
}
|
||||
|
||||
void notify::new_out_connection()
|
||||
|
||||
@@ -75,7 +75,8 @@ namespace levin
|
||||
struct status
|
||||
{
|
||||
bool has_noise;
|
||||
bool connections_filled;
|
||||
bool connections_filled; //!< True when has zone has `CRYPTONOTE_NOISE_CHANNELS` outgoing noise channels
|
||||
bool has_outgoing; //!< True when zone has outgoing connections
|
||||
};
|
||||
|
||||
//! Construct an instance that cannot notify.
|
||||
|
||||
@@ -219,6 +219,19 @@ int main(int argc, char const * argv[])
|
||||
{
|
||||
po::store(po::parse_config_file<char>(config_path.string<std::string>().c_str(), core_settings), vm);
|
||||
}
|
||||
catch (const po::unknown_option &e)
|
||||
{
|
||||
std::string unrecognized_option = e.get_option_name();
|
||||
if (all_options.find_nothrow(unrecognized_option, false))
|
||||
{
|
||||
std::cerr << "Option '" << unrecognized_option << "' is not allowed in the config file, please use it as a command line flag." << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Unrecognized option '" << unrecognized_option << "' in config file." << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
// log system isn't initialized yet
|
||||
|
||||
@@ -527,6 +527,7 @@ namespace hw {
|
||||
{0x2c97, 0x0004, 0, 0xffa0},
|
||||
{0x2c97, 0x0005, 0, 0xffa0},
|
||||
{0x2c97, 0x0006, 0, 0xffa0},
|
||||
{0x2c97, 0x0007, 0, 0xffa0},
|
||||
};
|
||||
|
||||
bool device_ledger::connect(void) {
|
||||
|
||||
@@ -206,8 +206,13 @@ namespace multisig
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void multisig_kex_msg::parse_and_validate_msg()
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(MULTISIG_KEX_MSG_V2_MAGIC_1.size() == MULTISIG_KEX_MSG_V2_MAGIC_N.size(),
|
||||
"Multisig kex msg magic inconsistency.");
|
||||
CHECK_AND_ASSERT_THROW_MES(MULTISIG_KEX_MSG_V2_MAGIC_1.size() >= MULTISIG_KEX_V1_MAGIC.size(),
|
||||
"Multisig kex msg magic inconsistency.");
|
||||
|
||||
// check message type
|
||||
CHECK_AND_ASSERT_THROW_MES(m_msg.size() > 0, "Kex message unexpectedly empty.");
|
||||
CHECK_AND_ASSERT_THROW_MES(m_msg.size() >= MULTISIG_KEX_MSG_V2_MAGIC_1.size(), "Kex message unexpectedly small.");
|
||||
CHECK_AND_ASSERT_THROW_MES(m_msg.substr(0, MULTISIG_KEX_V1_MAGIC.size()) != MULTISIG_KEX_V1_MAGIC,
|
||||
"V1 multisig kex messages are deprecated (unsafe).");
|
||||
CHECK_AND_ASSERT_THROW_MES(m_msg.substr(0, MULTISIG_KEX_MSG_V1_MAGIC.size()) != MULTISIG_KEX_MSG_V1_MAGIC,
|
||||
@@ -215,8 +220,6 @@ namespace multisig
|
||||
|
||||
// deserialize the message
|
||||
std::string msg_no_magic;
|
||||
CHECK_AND_ASSERT_THROW_MES(MULTISIG_KEX_MSG_V2_MAGIC_1.size() == MULTISIG_KEX_MSG_V2_MAGIC_N.size(),
|
||||
"Multisig kex msg magic inconsistency.");
|
||||
CHECK_AND_ASSERT_THROW_MES(tools::base58::decode(m_msg.substr(MULTISIG_KEX_MSG_V2_MAGIC_1.size()), msg_no_magic),
|
||||
"Multisig kex msg decoding error.");
|
||||
binary_archive<false> b_archive{epee::strspan<std::uint8_t>(msg_no_magic)};
|
||||
|
||||
@@ -820,7 +820,6 @@ tx_builder_ringct_t::~tx_builder_ringct_t()
|
||||
bool tx_builder_ringct_t::init(
|
||||
const cryptonote::account_keys& account_keys,
|
||||
const std::vector<std::uint8_t>& extra,
|
||||
const std::uint64_t unlock_time,
|
||||
const std::uint32_t subaddr_account,
|
||||
const std::set<std::uint32_t>& subaddr_minor_indices,
|
||||
std::vector<cryptonote::tx_source_entry>& sources,
|
||||
@@ -854,7 +853,7 @@ bool tx_builder_ringct_t::init(
|
||||
|
||||
// misc. fields
|
||||
unsigned_tx.version = 2; //rct = 2
|
||||
unsigned_tx.unlock_time = unlock_time;
|
||||
unsigned_tx.unlock_time = 0;
|
||||
|
||||
// sort inputs
|
||||
sort_sources(sources);
|
||||
|
||||
@@ -71,7 +71,6 @@ public:
|
||||
bool init(
|
||||
const cryptonote::account_keys& account_keys,
|
||||
const std::vector<std::uint8_t>& extra,
|
||||
const std::uint64_t unlock_time,
|
||||
const std::uint32_t subaddr_account,
|
||||
const std::set<std::uint32_t>& subaddr_minor_indices,
|
||||
std::vector<cryptonote::tx_source_entry>& sources,
|
||||
|
||||
+9
-36
@@ -71,7 +71,7 @@ namespace net
|
||||
struct i2p_serialized
|
||||
{
|
||||
std::string host;
|
||||
std::uint16_t port;
|
||||
std::uint16_t port; //! Leave for compatability with older clients
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(host)
|
||||
@@ -80,8 +80,7 @@ namespace net
|
||||
};
|
||||
}
|
||||
|
||||
i2p_address::i2p_address(const boost::string_ref host, const std::uint16_t port) noexcept
|
||||
: port_(port)
|
||||
i2p_address::i2p_address(const boost::string_ref host) noexcept
|
||||
{
|
||||
// this is a private constructor, throw if moved to public
|
||||
assert(host.size() < sizeof(host_));
|
||||
@@ -97,27 +96,19 @@ namespace net
|
||||
}
|
||||
|
||||
i2p_address::i2p_address() noexcept
|
||||
: port_(0)
|
||||
{
|
||||
static_assert(sizeof(unknown_host) <= sizeof(host_), "bad buffer size");
|
||||
std::memcpy(host_, unknown_host, sizeof(unknown_host));
|
||||
std::memset(host_ + sizeof(unknown_host), 0, sizeof(host_) - sizeof(unknown_host));
|
||||
}
|
||||
|
||||
expect<i2p_address> i2p_address::make(const boost::string_ref address, const std::uint16_t default_port)
|
||||
expect<i2p_address> i2p_address::make(const boost::string_ref address)
|
||||
{
|
||||
boost::string_ref host = address.substr(0, address.rfind(':'));
|
||||
const boost::string_ref port =
|
||||
address.substr(host.size() + (host.size() == address.size() ? 0 : 1));
|
||||
|
||||
MONERO_CHECK(host_check(host));
|
||||
|
||||
std::uint16_t porti = default_port;
|
||||
if (!port.empty() && !epee::string_tools::get_xtype_from_string(porti, std::string{port}))
|
||||
return {net::error::invalid_port};
|
||||
|
||||
static_assert(b32_length + sizeof(tld) == sizeof(i2p_address::host_), "bad internal host size");
|
||||
return i2p_address{host, porti};
|
||||
return i2p_address{host};
|
||||
}
|
||||
|
||||
bool i2p_address::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
|
||||
@@ -127,23 +118,21 @@ namespace net
|
||||
{
|
||||
std::memcpy(host_, in.host.data(), in.host.size());
|
||||
std::memset(host_ + in.host.size(), 0, sizeof(host_) - in.host.size());
|
||||
port_ = in.port;
|
||||
return true;
|
||||
}
|
||||
static_assert(sizeof(unknown_host) <= sizeof(host_), "bad buffer size");
|
||||
std::memcpy(host_, unknown_host, sizeof(unknown_host)); // include null terminator
|
||||
port_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool i2p_address::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const
|
||||
{
|
||||
const i2p_serialized out{std::string{host_}, port_};
|
||||
// Set port to 1 for backwards compatability; zero is invalid port
|
||||
const i2p_serialized out{std::string{host_}, 1};
|
||||
return out.store(dest, hparent);
|
||||
}
|
||||
|
||||
i2p_address::i2p_address(const i2p_address& rhs) noexcept
|
||||
: port_(rhs.port_)
|
||||
{
|
||||
std::memcpy(host_, rhs.host_, sizeof(host_));
|
||||
}
|
||||
@@ -152,7 +141,6 @@ namespace net
|
||||
{
|
||||
if (this != std::addressof(rhs))
|
||||
{
|
||||
port_ = rhs.port_;
|
||||
std::memcpy(host_, rhs.host_, sizeof(host_));
|
||||
}
|
||||
return *this;
|
||||
@@ -166,13 +154,12 @@ namespace net
|
||||
|
||||
bool i2p_address::equal(const i2p_address& rhs) const noexcept
|
||||
{
|
||||
return port_ == rhs.port_ && is_same_host(rhs);
|
||||
return is_same_host(rhs);
|
||||
}
|
||||
|
||||
bool i2p_address::less(const i2p_address& rhs) const noexcept
|
||||
{
|
||||
int res = std::strcmp(host_str(), rhs.host_str());
|
||||
return res < 0 || (res == 0 && port() < rhs.port());
|
||||
return std::strcmp(host_str(), rhs.host_str()) < 0;
|
||||
}
|
||||
|
||||
bool i2p_address::is_same_host(const i2p_address& rhs) const noexcept
|
||||
@@ -182,20 +169,6 @@ namespace net
|
||||
|
||||
std::string i2p_address::str() const
|
||||
{
|
||||
const std::size_t host_length = std::strlen(host_str());
|
||||
const std::size_t port_length =
|
||||
port_ == 0 ? 0 : std::numeric_limits<std::uint16_t>::digits10 + 2;
|
||||
|
||||
std::string out{};
|
||||
out.reserve(host_length + port_length);
|
||||
out.assign(host_str(), host_length);
|
||||
|
||||
if (port_ != 0)
|
||||
{
|
||||
out.push_back(':');
|
||||
namespace karma = boost::spirit::karma;
|
||||
karma::generate(std::back_inserter(out), karma::ushort_, port());
|
||||
}
|
||||
return out;
|
||||
return host_str();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,11 +50,10 @@ namespace net
|
||||
//! b32 i2p address; internal format not condensed/decoded.
|
||||
class i2p_address
|
||||
{
|
||||
std::uint16_t port_;
|
||||
char host_[61]; // null-terminated
|
||||
|
||||
//! Keep in private, `host.size()` has no runtime check
|
||||
i2p_address(boost::string_ref host, std::uint16_t port) noexcept;
|
||||
i2p_address(boost::string_ref host) noexcept;
|
||||
|
||||
public:
|
||||
//! \return Size of internal buffer for host.
|
||||
@@ -74,7 +73,7 @@ namespace net
|
||||
with `default_port` being used if port is not specified in
|
||||
`address`.
|
||||
*/
|
||||
static expect<i2p_address> make(boost::string_ref address, std::uint16_t default_port = 0);
|
||||
static expect<i2p_address> make(boost::string_ref address);
|
||||
|
||||
//! Load from epee p2p format, and \return false if not valid tor address
|
||||
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
|
||||
@@ -103,8 +102,8 @@ namespace net
|
||||
//! \return Null-terminated `x.b32.i2p` value or `unknown_str()`.
|
||||
const char* host_str() const noexcept { return host_; }
|
||||
|
||||
//! \return Port value or `0` if unspecified.
|
||||
std::uint16_t port() const noexcept { return port_; }
|
||||
//! \return `1` to work with I2P socks which considers `0` error.
|
||||
std::uint16_t port() const noexcept { return 1; }
|
||||
|
||||
static constexpr bool is_loopback() noexcept { return false; }
|
||||
static constexpr bool is_local() noexcept { return false; }
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ namespace net
|
||||
if (host_str_ref.ends_with(".onion"))
|
||||
return tor_address::make(address, default_port);
|
||||
if (host_str_ref.ends_with(".i2p"))
|
||||
return i2p_address::make(address, default_port);
|
||||
return i2p_address::make(address);
|
||||
|
||||
boost::system::error_code ec;
|
||||
boost::asio::ip::address_v6 v6 = boost::asio::ip::address_v6::from_string(host_str, ec);
|
||||
|
||||
+11
-11
@@ -706,18 +706,18 @@ namespace nodetool
|
||||
if (m_nettype == cryptonote::TESTNET)
|
||||
{
|
||||
full_addrs.insert("176.9.0.187:28080");
|
||||
full_addrs.insert("88.99.173.38:28080");
|
||||
full_addrs.insert("51.79.173.165:28080");
|
||||
full_addrs.insert("192.99.8.110:28080");
|
||||
full_addrs.insert("37.187.74.171:28080");
|
||||
full_addrs.insert("77.172.183.193:28080");
|
||||
}
|
||||
else if (m_nettype == cryptonote::STAGENET)
|
||||
{
|
||||
full_addrs.insert("176.9.0.187:38080");
|
||||
full_addrs.insert("88.99.173.38:38080");
|
||||
full_addrs.insert("51.79.173.165:38080");
|
||||
full_addrs.insert("192.99.8.110:38080");
|
||||
full_addrs.insert("37.187.74.171:38080");
|
||||
full_addrs.insert("77.172.183.193:38080");
|
||||
}
|
||||
else if (m_nettype == cryptonote::FAKECHAIN)
|
||||
{
|
||||
@@ -727,10 +727,10 @@ namespace nodetool
|
||||
full_addrs.insert("176.9.0.187:18080");
|
||||
full_addrs.insert("88.198.163.90:18080");
|
||||
full_addrs.insert("66.85.74.134:18080");
|
||||
full_addrs.insert("88.99.173.38:18080");
|
||||
full_addrs.insert("51.79.173.165:18080");
|
||||
full_addrs.insert("192.99.8.110:18080");
|
||||
full_addrs.insert("37.187.74.171:18080");
|
||||
full_addrs.insert("77.172.183.193:18080");
|
||||
}
|
||||
return full_addrs;
|
||||
}
|
||||
@@ -861,12 +861,12 @@ namespace nodetool
|
||||
if (m_nettype == cryptonote::MAINNET)
|
||||
{
|
||||
return {
|
||||
"xwvz3ekocr3dkyxfkmgm2hvbpzx2ysqmaxgter7znnqrhoicygkfswid.onion:18083",
|
||||
"4pixvbejrvihnkxmduo2agsnmc3rrulrqc7s3cbwwrep6h6hrzsibeqd.onion:18083",
|
||||
"zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083",
|
||||
"qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083",
|
||||
"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083",
|
||||
"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083",
|
||||
"plowsofe6cleftfmk2raiw5h2x66atrik3nja4bfd3zrfa2hdlgworad.onion:18083",
|
||||
"aclc4e2jhhtr44guufbnwk5bzwhaecinax4yip4wr4tjn27sjsfg6zqd.onion:18083",
|
||||
};
|
||||
}
|
||||
return {};
|
||||
@@ -874,10 +874,9 @@ namespace nodetool
|
||||
if (m_nettype == cryptonote::MAINNET)
|
||||
{
|
||||
return {
|
||||
"s3l6ke4ed3df466khuebb4poienoingwof7oxtbo6j4n56sghe3a.b32.i2p:18080",
|
||||
"sel36x6fibfzujwvt4hf5gxolz6kd3jpvbjqg6o3ud2xtionyl2q.b32.i2p:18080",
|
||||
"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p:18080",
|
||||
"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p:18080",
|
||||
"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p",
|
||||
"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p",
|
||||
"ugnlcdciyhghh2zert7c3kl4biwkirc43ke33jiy5slnd3mv2trq.b32.i2p",
|
||||
};
|
||||
}
|
||||
return {};
|
||||
@@ -2300,11 +2299,12 @@ namespace nodetool
|
||||
if (enet::zone::tor < network->first)
|
||||
break; // unknown network
|
||||
|
||||
if (network->second.m_connect)
|
||||
const auto status = network->second.m_notifier.get_status();
|
||||
if (network->second.m_connect && status.has_outgoing)
|
||||
return send(*network);
|
||||
}
|
||||
|
||||
// configuration should not allow this scenario
|
||||
MWARNING("Unable to send " << txs.size() << " transaction(s): anonymity networks had no outgoing connections");
|
||||
return enet::zone::invalid;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include <boost/serialization/version.hpp>
|
||||
|
||||
#include "net_peerlist_boost_serialization.h"
|
||||
#include "common/util.h"
|
||||
|
||||
|
||||
namespace nodetool
|
||||
@@ -200,7 +201,7 @@ namespace nodetool
|
||||
if (!out)
|
||||
{
|
||||
// if failed, try reading in unportable mode
|
||||
boost::filesystem::copy_file(path, path + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
|
||||
tools::copy_file(path, path + ".unportable");
|
||||
src_file.close();
|
||||
src_file.open( path , std::ios_base::binary | std::ios_base::in);
|
||||
if(src_file.fail())
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace boost
|
||||
if (std::strcmp(host, net::i2p_address::unknown_str()) == 0)
|
||||
na = net::i2p_address::unknown();
|
||||
else
|
||||
na = MONERO_UNWRAP(net::i2p_address::make(host, port));
|
||||
na = MONERO_UNWRAP(net::i2p_address::make(host));
|
||||
}
|
||||
|
||||
template <class Archive, class ver_type>
|
||||
|
||||
+13
-3
@@ -325,6 +325,10 @@ namespace rct {
|
||||
ctkeyV outPk;
|
||||
xmr_amount txnFee; // contains b
|
||||
|
||||
rctSigBase() :
|
||||
type(RCTTypeNull), message{}, mixRing{}, pseudoOuts{}, ecdhInfo{}, outPk{}, txnFee(0)
|
||||
{}
|
||||
|
||||
template<bool W, template <bool> class Archive>
|
||||
bool serialize_rctsig_base(Archive<W> &ar, size_t inputs, size_t outputs)
|
||||
{
|
||||
@@ -362,11 +366,17 @@ namespace rct {
|
||||
{
|
||||
if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus)
|
||||
{
|
||||
// Since RCTTypeBulletproof2 enote types, we don't serialize the blinding factor, and only serialize the
|
||||
// first 8 bytes of ecdhInfo[i].amount
|
||||
ar.begin_object();
|
||||
if (!typename Archive<W>::is_saving())
|
||||
crypto::hash8 trunc_amount; // placeholder variable needed to maintain "strict aliasing"
|
||||
if (!typename Archive<W>::is_saving()) // loading
|
||||
memset(ecdhInfo[i].amount.bytes, 0, sizeof(ecdhInfo[i].amount.bytes));
|
||||
crypto::hash8 &amount = (crypto::hash8&)ecdhInfo[i].amount;
|
||||
FIELD(amount);
|
||||
else // saving
|
||||
memcpy(trunc_amount.data, ecdhInfo[i].amount.bytes, sizeof(trunc_amount));
|
||||
FIELD_N("amount", trunc_amount);
|
||||
if (!typename Archive<W>::is_saving()) // loading
|
||||
memcpy(ecdhInfo[i].amount.bytes, trunc_amount.data, sizeof(trunc_amount));
|
||||
ar.end_object();
|
||||
}
|
||||
else
|
||||
|
||||
+157
-90
@@ -598,88 +598,165 @@ namespace cryptonote
|
||||
|
||||
CHECK_PAYMENT(req, res, 1);
|
||||
|
||||
// quick check for noop
|
||||
if (!req.block_ids.empty())
|
||||
res.daemon_time = (uint64_t)time(NULL);
|
||||
// Always set daemon time, and set it early rather than late, as delivering some incremental pool
|
||||
// info twice because of slightly overlapping time intervals is no problem, whereas producing gaps
|
||||
// and never delivering something is
|
||||
|
||||
bool get_blocks = false;
|
||||
bool get_pool = false;
|
||||
switch (req.requested_info)
|
||||
{
|
||||
uint64_t last_block_height;
|
||||
crypto::hash last_block_hash;
|
||||
m_core.get_blockchain_top(last_block_height, last_block_hash);
|
||||
if (last_block_hash == req.block_ids.front())
|
||||
case COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY:
|
||||
// Compatibility value 0: Clients that do not set 'requested_info' want blocks, and only blocks
|
||||
get_blocks = true;
|
||||
break;
|
||||
case COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL:
|
||||
get_blocks = true;
|
||||
get_pool = true;
|
||||
break;
|
||||
case COMMAND_RPC_GET_BLOCKS_FAST::POOL_ONLY:
|
||||
get_pool = true;
|
||||
break;
|
||||
default:
|
||||
res.status = "Failed, wrong requested info";
|
||||
return true;
|
||||
}
|
||||
|
||||
res.pool_info_extent = COMMAND_RPC_GET_BLOCKS_FAST::NONE;
|
||||
|
||||
if (get_pool)
|
||||
{
|
||||
const bool restricted = m_restricted && ctx;
|
||||
const bool request_has_rpc_origin = ctx != NULL;
|
||||
const bool allow_sensitive = !request_has_rpc_origin || !restricted;
|
||||
const size_t max_tx_count = restricted ? RESTRICTED_TRANSACTIONS_COUNT : std::numeric_limits<size_t>::max();
|
||||
|
||||
bool incremental;
|
||||
std::vector<std::pair<crypto::hash, tx_memory_pool::tx_details>> added_pool_txs;
|
||||
bool success = m_core.get_pool_info((time_t)req.pool_info_since, allow_sensitive, max_tx_count, added_pool_txs, res.remaining_added_pool_txids, res.removed_pool_txids, incremental);
|
||||
if (success)
|
||||
{
|
||||
res.start_height = 0;
|
||||
res.current_height = m_core.get_current_blockchain_height();
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
res.added_pool_txs.clear();
|
||||
if (m_rpc_payment)
|
||||
{
|
||||
CHECK_PAYMENT_SAME_TS(req, res, added_pool_txs.size() * COST_PER_TX + (res.remaining_added_pool_txids.size() + res.removed_pool_txids.size()) * COST_PER_POOL_HASH);
|
||||
}
|
||||
for (const auto &added_pool_tx: added_pool_txs)
|
||||
{
|
||||
COMMAND_RPC_GET_BLOCKS_FAST::pool_tx_info info;
|
||||
info.tx_hash = added_pool_tx.first;
|
||||
std::stringstream oss;
|
||||
binary_archive<true> ar(oss);
|
||||
bool r = req.prune
|
||||
? const_cast<cryptonote::transaction&>(added_pool_tx.second.tx).serialize_base(ar)
|
||||
: ::serialization::serialize(ar, const_cast<cryptonote::transaction&>(added_pool_tx.second.tx));
|
||||
if (!r)
|
||||
{
|
||||
res.status = "Failed to serialize transaction";
|
||||
return true;
|
||||
}
|
||||
info.tx_blob = oss.str();
|
||||
info.double_spend_seen = added_pool_tx.second.double_spend_seen;
|
||||
res.added_pool_txs.push_back(std::move(info));
|
||||
}
|
||||
}
|
||||
if (success)
|
||||
{
|
||||
res.pool_info_extent = incremental ? COMMAND_RPC_GET_BLOCKS_FAST::INCREMENTAL : COMMAND_RPC_GET_BLOCKS_FAST::FULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
res.status = "Failed to get pool info";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT;
|
||||
if (m_rpc_payment)
|
||||
if (get_blocks)
|
||||
{
|
||||
max_blocks = res.credits / COST_PER_BLOCK;
|
||||
if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT)
|
||||
max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT;
|
||||
if (max_blocks == 0)
|
||||
// quick check for noop
|
||||
if (!req.block_ids.empty())
|
||||
{
|
||||
res.status = CORE_RPC_STATUS_PAYMENT_REQUIRED;
|
||||
uint64_t last_block_height;
|
||||
crypto::hash last_block_hash;
|
||||
m_core.get_blockchain_top(last_block_height, last_block_hash);
|
||||
if (last_block_hash == req.block_ids.front())
|
||||
{
|
||||
res.start_height = 0;
|
||||
res.current_height = last_block_height + 1;
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT;
|
||||
if (m_rpc_payment)
|
||||
{
|
||||
max_blocks = res.credits / COST_PER_BLOCK;
|
||||
if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT)
|
||||
max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT;
|
||||
if (max_blocks == 0)
|
||||
{
|
||||
res.status = CORE_RPC_STATUS_PAYMENT_REQUIRED;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > > bs;
|
||||
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
|
||||
{
|
||||
res.status = "Failed";
|
||||
add_host_fail(ctx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > > bs;
|
||||
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
|
||||
{
|
||||
res.status = "Failed";
|
||||
add_host_fail(ctx);
|
||||
return true;
|
||||
}
|
||||
CHECK_PAYMENT_SAME_TS(req, res, bs.size() * COST_PER_BLOCK);
|
||||
|
||||
CHECK_PAYMENT_SAME_TS(req, res, bs.size() * COST_PER_BLOCK);
|
||||
|
||||
size_t size = 0, ntxes = 0;
|
||||
res.blocks.reserve(bs.size());
|
||||
res.output_indices.reserve(bs.size());
|
||||
for(auto& bd: bs)
|
||||
{
|
||||
res.blocks.resize(res.blocks.size()+1);
|
||||
res.blocks.back().pruned = req.prune;
|
||||
res.blocks.back().block = bd.first.first;
|
||||
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());
|
||||
if (req.no_miner_tx)
|
||||
res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
|
||||
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)
|
||||
size_t size = 0, ntxes = 0;
|
||||
res.blocks.reserve(bs.size());
|
||||
res.output_indices.reserve(bs.size());
|
||||
for(auto& bd: bs)
|
||||
{
|
||||
res.blocks.back().txs.push_back({std::move(i->second), crypto::null_hash});
|
||||
i->second.clear();
|
||||
i->second.shrink_to_fit();
|
||||
size += res.blocks.back().txs.back().blob.size();
|
||||
}
|
||||
res.blocks.resize(res.blocks.size()+1);
|
||||
res.blocks.back().pruned = req.prune;
|
||||
res.blocks.back().block = bd.first.first;
|
||||
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());
|
||||
if (req.no_miner_tx)
|
||||
res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
|
||||
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)
|
||||
{
|
||||
res.blocks.back().txs.push_back({std::move(i->second), crypto::null_hash});
|
||||
i->second.clear();
|
||||
i->second.shrink_to_fit();
|
||||
size += res.blocks.back().txs.back().blob.size();
|
||||
}
|
||||
|
||||
const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1);
|
||||
if (n_txes_to_lookup > 0)
|
||||
{
|
||||
std::vector<std::vector<uint64_t>> indices;
|
||||
bool r = m_core.get_tx_outputs_gindexs(req.no_miner_tx ? bd.second.front().first : bd.first.second, n_txes_to_lookup, indices);
|
||||
if (!r)
|
||||
const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1);
|
||||
if (n_txes_to_lookup > 0)
|
||||
{
|
||||
res.status = "Failed";
|
||||
return true;
|
||||
std::vector<std::vector<uint64_t>> indices;
|
||||
bool r = m_core.get_tx_outputs_gindexs(req.no_miner_tx ? bd.second.front().first : bd.first.second, n_txes_to_lookup, indices);
|
||||
if (!r)
|
||||
{
|
||||
res.status = "Failed";
|
||||
return true;
|
||||
}
|
||||
if (indices.size() != n_txes_to_lookup || res.output_indices.back().indices.size() != (req.no_miner_tx ? 1 : 0))
|
||||
{
|
||||
res.status = "Failed";
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < indices.size(); ++i)
|
||||
res.output_indices.back().indices.push_back({std::move(indices[i])});
|
||||
}
|
||||
if (indices.size() != n_txes_to_lookup || res.output_indices.back().indices.size() != (req.no_miner_tx ? 1 : 0))
|
||||
{
|
||||
res.status = "Failed";
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < indices.size(); ++i)
|
||||
res.output_indices.back().indices.push_back({std::move(indices[i])});
|
||||
}
|
||||
MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, size " << size);
|
||||
}
|
||||
|
||||
MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, size " << size);
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
@@ -919,17 +996,16 @@ namespace cryptonote
|
||||
// try the pool for any missing txes
|
||||
size_t found_in_pool = 0;
|
||||
std::unordered_set<crypto::hash> pool_tx_hashes;
|
||||
std::unordered_map<crypto::hash, tx_info> per_tx_pool_tx_info;
|
||||
std::unordered_map<crypto::hash, tx_memory_pool::tx_details> per_tx_pool_tx_details;
|
||||
if (!missed_txs.empty())
|
||||
{
|
||||
std::vector<tx_info> pool_tx_info;
|
||||
std::vector<spent_key_image_info> pool_key_image_info;
|
||||
bool r = m_core.get_pool_transactions_and_spent_keys_info(pool_tx_info, pool_key_image_info, !request_has_rpc_origin || !restricted);
|
||||
std::vector<std::pair<crypto::hash, tx_memory_pool::tx_details>> pool_txs;
|
||||
bool r = m_core.get_pool_transactions_info(missed_txs, pool_txs, !request_has_rpc_origin || !restricted);
|
||||
if(r)
|
||||
{
|
||||
// sort to match original request
|
||||
std::vector<std::tuple<crypto::hash, cryptonote::blobdata, crypto::hash, cryptonote::blobdata>> sorted_txs;
|
||||
std::vector<tx_info>::const_iterator i;
|
||||
std::vector<std::pair<crypto::hash, tx_memory_pool::tx_details>>::const_iterator i;
|
||||
unsigned txs_processed = 0;
|
||||
for (const crypto::hash &h: vh)
|
||||
{
|
||||
@@ -949,36 +1025,23 @@ namespace cryptonote
|
||||
sorted_txs.push_back(std::move(txs[txs_processed]));
|
||||
++txs_processed;
|
||||
}
|
||||
else if ((i = std::find_if(pool_tx_info.begin(), pool_tx_info.end(), [h](const tx_info &txi) { return epee::string_tools::pod_to_hex(h) == txi.id_hash; })) != pool_tx_info.end())
|
||||
else if ((i = std::find_if(pool_txs.begin(), pool_txs.end(), [h](const std::pair<crypto::hash, tx_memory_pool::tx_details> &pt) { return h == pt.first; })) != pool_txs.end())
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
if (!cryptonote::parse_and_validate_tx_from_blob(i->tx_blob, tx))
|
||||
{
|
||||
res.status = "Failed to parse and validate tx from blob";
|
||||
return true;
|
||||
}
|
||||
const tx_memory_pool::tx_details &td = i->second;
|
||||
std::stringstream ss;
|
||||
binary_archive<true> ba(ss);
|
||||
bool r = const_cast<cryptonote::transaction&>(tx).serialize_base(ba);
|
||||
bool r = const_cast<cryptonote::transaction&>(td.tx).serialize_base(ba);
|
||||
if (!r)
|
||||
{
|
||||
res.status = "Failed to serialize transaction base";
|
||||
return true;
|
||||
}
|
||||
const cryptonote::blobdata pruned = ss.str();
|
||||
const crypto::hash prunable_hash = tx.version == 1 ? crypto::null_hash : get_transaction_prunable_hash(tx);
|
||||
sorted_txs.push_back(std::make_tuple(h, pruned, prunable_hash, std::string(i->tx_blob, pruned.size())));
|
||||
const crypto::hash prunable_hash = td.tx.version == 1 ? crypto::null_hash : get_transaction_prunable_hash(td.tx);
|
||||
sorted_txs.push_back(std::make_tuple(h, pruned, prunable_hash, std::string(td.tx_blob, pruned.size())));
|
||||
missed_txs.erase(std::find(missed_txs.begin(), missed_txs.end(), h));
|
||||
pool_tx_hashes.insert(h);
|
||||
const std::string hash_string = epee::string_tools::pod_to_hex(h);
|
||||
for (const auto &ti: pool_tx_info)
|
||||
{
|
||||
if (ti.id_hash == hash_string)
|
||||
{
|
||||
per_tx_pool_tx_info.insert(std::make_pair(h, ti));
|
||||
break;
|
||||
}
|
||||
}
|
||||
per_tx_pool_tx_details.insert(std::make_pair(h, td));
|
||||
++found_in_pool;
|
||||
}
|
||||
}
|
||||
@@ -1074,8 +1137,8 @@ namespace cryptonote
|
||||
{
|
||||
e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max();
|
||||
e.confirmations = 0;
|
||||
auto it = per_tx_pool_tx_info.find(tx_hash);
|
||||
if (it != per_tx_pool_tx_info.end())
|
||||
auto it = per_tx_pool_tx_details.find(tx_hash);
|
||||
if (it != per_tx_pool_tx_details.end())
|
||||
{
|
||||
e.double_spend_seen = it->second.double_spend_seen;
|
||||
e.relayed = it->second.relayed;
|
||||
@@ -1277,6 +1340,8 @@ namespace cryptonote
|
||||
add_reason(reason, "too few outputs");
|
||||
if ((res.tx_extra_too_big = tvc.m_tx_extra_too_big))
|
||||
add_reason(reason, "tx-extra too big");
|
||||
if ((res.nonzero_unlock_time = tvc.m_nonzero_unlock_time))
|
||||
add_reason(reason, "tx unlock time is not zero");
|
||||
const std::string punctuation = reason.empty() ? "" : ": ";
|
||||
if (tvc.m_verifivation_failed)
|
||||
{
|
||||
@@ -2115,7 +2180,8 @@ namespace cryptonote
|
||||
// Fixing of high orphan issue for most pools
|
||||
// Thanks Boolberry!
|
||||
block b;
|
||||
if(!parse_and_validate_block_from_blob(blockblob, b))
|
||||
crypto::hash blk_id;
|
||||
if(!parse_and_validate_block_from_blob(blockblob, b, blk_id))
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
|
||||
error_resp.message = "Wrong block blob";
|
||||
@@ -2138,6 +2204,7 @@ namespace cryptonote
|
||||
error_resp.message = "Block not accepted";
|
||||
return false;
|
||||
}
|
||||
res.block_id = epee::string_tools::pod_to_hex(blk_id);
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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 12
|
||||
#define CORE_RPC_VERSION_MINOR 14
|
||||
#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)
|
||||
|
||||
@@ -162,18 +162,29 @@ namespace cryptonote
|
||||
struct COMMAND_RPC_GET_BLOCKS_FAST
|
||||
{
|
||||
|
||||
enum REQUESTED_INFO
|
||||
{
|
||||
BLOCKS_ONLY = 0,
|
||||
BLOCKS_AND_POOL = 1,
|
||||
POOL_ONLY = 2
|
||||
};
|
||||
|
||||
struct request_t: public rpc_access_request_base
|
||||
{
|
||||
uint8_t requested_info;
|
||||
std::list<crypto::hash> block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */
|
||||
uint64_t start_height;
|
||||
bool prune;
|
||||
bool no_miner_tx;
|
||||
uint64_t pool_info_since;
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_PARENT(rpc_access_request_base)
|
||||
KV_SERIALIZE_OPT(requested_info, (uint8_t)0)
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids)
|
||||
KV_SERIALIZE(start_height)
|
||||
KV_SERIALIZE(prune)
|
||||
KV_SERIALIZE_OPT(no_miner_tx, false)
|
||||
KV_SERIALIZE_OPT(pool_info_since, (uint64_t)0)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
@@ -196,12 +207,37 @@ namespace cryptonote
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct pool_tx_info
|
||||
{
|
||||
crypto::hash tx_hash;
|
||||
blobdata tx_blob;
|
||||
bool double_spend_seen;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB(tx_hash)
|
||||
KV_SERIALIZE(tx_blob)
|
||||
KV_SERIALIZE(double_spend_seen)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
enum POOL_INFO_EXTENT
|
||||
{
|
||||
NONE = 0,
|
||||
INCREMENTAL = 1,
|
||||
FULL = 2
|
||||
};
|
||||
|
||||
struct response_t: public rpc_access_response_base
|
||||
{
|
||||
std::vector<block_complete_entry> blocks;
|
||||
uint64_t start_height;
|
||||
uint64_t current_height;
|
||||
std::vector<block_output_indices> output_indices;
|
||||
uint64_t daemon_time;
|
||||
uint8_t pool_info_extent;
|
||||
std::vector<pool_tx_info> added_pool_txs;
|
||||
std::vector<crypto::hash> remaining_added_pool_txids;
|
||||
std::vector<crypto::hash> removed_pool_txids;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_PARENT(rpc_access_response_base)
|
||||
@@ -209,6 +245,17 @@ namespace cryptonote
|
||||
KV_SERIALIZE(start_height)
|
||||
KV_SERIALIZE(current_height)
|
||||
KV_SERIALIZE(output_indices)
|
||||
KV_SERIALIZE_OPT(daemon_time, (uint64_t) 0)
|
||||
KV_SERIALIZE_OPT(pool_info_extent, (uint8_t) 0)
|
||||
if (pool_info_extent != POOL_INFO_EXTENT::NONE)
|
||||
{
|
||||
KV_SERIALIZE(added_pool_txs)
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(remaining_added_pool_txids)
|
||||
}
|
||||
if (pool_info_extent == POOL_INFO_EXTENT::INCREMENTAL)
|
||||
{
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(removed_pool_txids)
|
||||
}
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
@@ -593,6 +640,7 @@ namespace cryptonote
|
||||
bool too_few_outputs;
|
||||
bool sanity_check_failed;
|
||||
bool tx_extra_too_big;
|
||||
bool nonzero_unlock_time;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_PARENT(rpc_access_response_base)
|
||||
@@ -608,6 +656,7 @@ namespace cryptonote
|
||||
KV_SERIALIZE(too_few_outputs)
|
||||
KV_SERIALIZE(sanity_check_failed)
|
||||
KV_SERIALIZE(tx_extra_too_big)
|
||||
KV_SERIALIZE(nonzero_unlock_time)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
@@ -1068,8 +1117,11 @@ namespace cryptonote
|
||||
|
||||
struct response_t: public rpc_response_base
|
||||
{
|
||||
std::string block_id;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_PARENT(rpc_response_base)
|
||||
KV_SERIALIZE(block_id)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
|
||||
@@ -422,6 +422,16 @@ namespace rpc
|
||||
if (!res.error_details.empty()) res.error_details += " and ";
|
||||
res.error_details += "too few outputs";
|
||||
}
|
||||
if (tvc.m_tx_extra_too_big)
|
||||
{
|
||||
if (!res.error_details.empty()) res.error_details += " and ";
|
||||
res.error_details += "tx_extra too long";
|
||||
}
|
||||
if (tvc.m_nonzero_unlock_time)
|
||||
{
|
||||
if (!res.error_details.empty()) res.error_details += " and ";
|
||||
res.error_details += "non-zero unlock time";
|
||||
}
|
||||
if (res.error_details.empty())
|
||||
{
|
||||
res.error_details = "an unknown issue was found with the transaction";
|
||||
@@ -510,6 +520,8 @@ namespace rpc
|
||||
res.info.target_height = res.info.height;
|
||||
}
|
||||
|
||||
m_core.get_blockchain_top(res.info.top_block_height, res.info.top_block_hash);
|
||||
|
||||
auto& chain = m_core.get_blockchain_storage();
|
||||
|
||||
res.info.wide_difficulty = chain.get_difficulty_for_next_block();
|
||||
|
||||
@@ -48,6 +48,11 @@ void GetHeight::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) c
|
||||
|
||||
void GetHeight::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, height, height);
|
||||
}
|
||||
|
||||
@@ -61,6 +66,11 @@ void GetBlocksFast::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest
|
||||
|
||||
void GetBlocksFast::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -76,6 +86,11 @@ void GetBlocksFast::Response::doToJson(rapidjson::Writer<epee::byte_stream>& des
|
||||
|
||||
void GetBlocksFast::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, blocks, blocks);
|
||||
GET_FROM_JSON_OBJECT(val, start_height, start_height);
|
||||
GET_FROM_JSON_OBJECT(val, current_height, current_height);
|
||||
@@ -91,6 +106,11 @@ void GetHashesFast::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest
|
||||
|
||||
void GetHashesFast::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, known_hashes, known_hashes);
|
||||
GET_FROM_JSON_OBJECT(val, start_height, start_height);
|
||||
}
|
||||
@@ -100,10 +120,16 @@ void GetHashesFast::Response::doToJson(rapidjson::Writer<epee::byte_stream>& des
|
||||
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(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, hashes, hashes);
|
||||
GET_FROM_JSON_OBJECT(val, start_height, start_height);
|
||||
GET_FROM_JSON_OBJECT(val, current_height, current_height);
|
||||
@@ -117,6 +143,11 @@ void GetTransactions::Request::doToJson(rapidjson::Writer<epee::byte_stream>& de
|
||||
|
||||
void GetTransactions::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, tx_hashes, tx_hashes);
|
||||
}
|
||||
|
||||
@@ -128,6 +159,11 @@ void GetTransactions::Response::doToJson(rapidjson::Writer<epee::byte_stream>& d
|
||||
|
||||
void GetTransactions::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, txs, txs);
|
||||
GET_FROM_JSON_OBJECT(val, missed_hashes, missed_hashes);
|
||||
}
|
||||
@@ -140,6 +176,11 @@ void KeyImagesSpent::Request::doToJson(rapidjson::Writer<epee::byte_stream>& des
|
||||
|
||||
void KeyImagesSpent::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, key_images, key_images);
|
||||
}
|
||||
|
||||
@@ -150,6 +191,11 @@ void KeyImagesSpent::Response::doToJson(rapidjson::Writer<epee::byte_stream>& de
|
||||
|
||||
void KeyImagesSpent::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, spent_status, spent_status);
|
||||
}
|
||||
|
||||
@@ -161,6 +207,11 @@ void GetTxGlobalOutputIndices::Request::doToJson(rapidjson::Writer<epee::byte_st
|
||||
|
||||
void GetTxGlobalOutputIndices::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, tx_hash, tx_hash);
|
||||
}
|
||||
|
||||
@@ -171,6 +222,11 @@ void GetTxGlobalOutputIndices::Response::doToJson(rapidjson::Writer<epee::byte_s
|
||||
|
||||
void GetTxGlobalOutputIndices::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, output_indices, output_indices);
|
||||
}
|
||||
|
||||
@@ -182,6 +238,11 @@ void SendRawTx::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest) co
|
||||
|
||||
void SendRawTx::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, tx, tx);
|
||||
GET_FROM_JSON_OBJECT(val, relay, relay);
|
||||
}
|
||||
@@ -194,6 +255,11 @@ void SendRawTx::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) c
|
||||
|
||||
void SendRawTx::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, relayed, relayed);
|
||||
}
|
||||
|
||||
@@ -205,6 +271,11 @@ void SendRawTxHex::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest)
|
||||
|
||||
void SendRawTxHex::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, tx_as_hex, tx_as_hex);
|
||||
GET_FROM_JSON_OBJECT(val, relay, relay);
|
||||
}
|
||||
@@ -219,6 +290,11 @@ void StartMining::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest)
|
||||
|
||||
void StartMining::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, miner_address, miner_address);
|
||||
GET_FROM_JSON_OBJECT(val, threads_count, threads_count);
|
||||
GET_FROM_JSON_OBJECT(val, do_background_mining, do_background_mining);
|
||||
@@ -266,6 +342,11 @@ void MiningStatus::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest
|
||||
|
||||
void MiningStatus::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, active, active);
|
||||
GET_FROM_JSON_OBJECT(val, speed, speed);
|
||||
GET_FROM_JSON_OBJECT(val, threads_count, threads_count);
|
||||
@@ -288,6 +369,11 @@ void GetInfo::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest) con
|
||||
|
||||
void GetInfo::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, info, info);
|
||||
}
|
||||
|
||||
@@ -314,6 +400,11 @@ void GetBlockHash::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest)
|
||||
|
||||
void GetBlockHash::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, height, height);
|
||||
}
|
||||
|
||||
@@ -324,6 +415,11 @@ void GetBlockHash::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest
|
||||
|
||||
void GetBlockHash::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, hash, hash);
|
||||
}
|
||||
|
||||
@@ -342,6 +438,11 @@ void GetLastBlockHeader::Response::doToJson(rapidjson::Writer<epee::byte_stream>
|
||||
|
||||
void GetLastBlockHeader::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, header, header);
|
||||
}
|
||||
|
||||
@@ -353,6 +454,11 @@ void GetBlockHeaderByHash::Request::doToJson(rapidjson::Writer<epee::byte_stream
|
||||
|
||||
void GetBlockHeaderByHash::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, hash, hash);
|
||||
}
|
||||
|
||||
@@ -363,6 +469,11 @@ void GetBlockHeaderByHash::Response::doToJson(rapidjson::Writer<epee::byte_strea
|
||||
|
||||
void GetBlockHeaderByHash::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, header, header);
|
||||
}
|
||||
|
||||
@@ -374,6 +485,11 @@ void GetBlockHeaderByHeight::Request::doToJson(rapidjson::Writer<epee::byte_stre
|
||||
|
||||
void GetBlockHeaderByHeight::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, height, height);
|
||||
}
|
||||
|
||||
@@ -384,6 +500,11 @@ void GetBlockHeaderByHeight::Response::doToJson(rapidjson::Writer<epee::byte_str
|
||||
|
||||
void GetBlockHeaderByHeight::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, header, header);
|
||||
}
|
||||
|
||||
@@ -395,6 +516,11 @@ void GetBlockHeadersByHeight::Request::doToJson(rapidjson::Writer<epee::byte_str
|
||||
|
||||
void GetBlockHeadersByHeight::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, heights, heights);
|
||||
}
|
||||
|
||||
@@ -405,6 +531,11 @@ void GetBlockHeadersByHeight::Response::doToJson(rapidjson::Writer<epee::byte_st
|
||||
|
||||
void GetBlockHeadersByHeight::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, headers, headers);
|
||||
}
|
||||
|
||||
@@ -424,6 +555,11 @@ void GetPeerList::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest)
|
||||
|
||||
void GetPeerList::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, white_list, white_list);
|
||||
GET_FROM_JSON_OBJECT(val, gray_list, gray_list);
|
||||
}
|
||||
@@ -436,6 +572,11 @@ void SetLogLevel::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest)
|
||||
|
||||
void SetLogLevel::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, level, level);
|
||||
}
|
||||
|
||||
@@ -462,6 +603,11 @@ void GetTransactionPool::Response::doToJson(rapidjson::Writer<epee::byte_stream>
|
||||
|
||||
void GetTransactionPool::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, transactions, transactions);
|
||||
GET_FROM_JSON_OBJECT(val, key_images, key_images);
|
||||
}
|
||||
@@ -474,6 +620,11 @@ void HardForkInfo::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest)
|
||||
|
||||
void HardForkInfo::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, version, version);
|
||||
}
|
||||
|
||||
@@ -484,6 +635,11 @@ void HardForkInfo::Response::doToJson(rapidjson::Writer<epee::byte_stream>& dest
|
||||
|
||||
void HardForkInfo::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, info, info);
|
||||
}
|
||||
|
||||
@@ -499,6 +655,11 @@ void GetOutputHistogram::Request::doToJson(rapidjson::Writer<epee::byte_stream>&
|
||||
|
||||
void GetOutputHistogram::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, amounts, amounts);
|
||||
GET_FROM_JSON_OBJECT(val, min_count, min_count);
|
||||
GET_FROM_JSON_OBJECT(val, max_count, max_count);
|
||||
@@ -513,6 +674,11 @@ void GetOutputHistogram::Response::doToJson(rapidjson::Writer<epee::byte_stream>
|
||||
|
||||
void GetOutputHistogram::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, histogram, histogram);
|
||||
}
|
||||
|
||||
@@ -524,6 +690,11 @@ void GetOutputKeys::Request::doToJson(rapidjson::Writer<epee::byte_stream>& dest
|
||||
|
||||
void GetOutputKeys::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, outputs, outputs);
|
||||
}
|
||||
|
||||
@@ -534,6 +705,11 @@ void GetOutputKeys::Response::doToJson(rapidjson::Writer<epee::byte_stream>& des
|
||||
|
||||
void GetOutputKeys::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, keys, keys);
|
||||
}
|
||||
|
||||
@@ -552,6 +728,11 @@ void GetRPCVersion::Response::doToJson(rapidjson::Writer<epee::byte_stream>& des
|
||||
|
||||
void GetRPCVersion::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, version, version);
|
||||
}
|
||||
|
||||
@@ -562,6 +743,11 @@ void GetFeeEstimate::Request::doToJson(rapidjson::Writer<epee::byte_stream>& des
|
||||
|
||||
void GetFeeEstimate::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, num_grace_blocks, num_grace_blocks);
|
||||
}
|
||||
|
||||
@@ -575,6 +761,11 @@ void GetFeeEstimate::Response::doToJson(rapidjson::Writer<epee::byte_stream>& de
|
||||
|
||||
void GetFeeEstimate::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, estimated_base_fee, estimated_base_fee);
|
||||
GET_FROM_JSON_OBJECT(val, fee_mask, fee_mask);
|
||||
GET_FROM_JSON_OBJECT(val, size_scale, size_scale);
|
||||
@@ -591,6 +782,11 @@ void GetOutputDistribution::Request::doToJson(rapidjson::Writer<epee::byte_strea
|
||||
|
||||
void GetOutputDistribution::Request::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, amounts, amounts);
|
||||
GET_FROM_JSON_OBJECT(val, from_height, from_height);
|
||||
GET_FROM_JSON_OBJECT(val, to_height, to_height);
|
||||
@@ -605,6 +801,11 @@ void GetOutputDistribution::Response::doToJson(rapidjson::Writer<epee::byte_stre
|
||||
|
||||
void GetOutputDistribution::Response::fromJson(const rapidjson::Value& val)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw json::WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, status, status);
|
||||
GET_FROM_JSON_OBJECT(val, distributions, distributions);
|
||||
}
|
||||
|
||||
@@ -176,6 +176,7 @@ namespace rpc
|
||||
{
|
||||
uint64_t height;
|
||||
uint64_t target_height;
|
||||
uint64_t top_block_height;
|
||||
cryptonote::difficulty_type wide_difficulty;
|
||||
uint64_t difficulty;
|
||||
uint64_t target;
|
||||
|
||||
+15
-6
@@ -158,13 +158,22 @@ void ZmqServer::serve()
|
||||
|
||||
if (!pub || sockets[2].revents)
|
||||
{
|
||||
std::string message = MONERO_UNWRAP(net::zmq::receive(rep.get(), read_flags));
|
||||
MDEBUG("Received RPC request: \"" << message << "\"");
|
||||
epee::byte_slice response = handler.handle(std::move(message));
|
||||
expect<std::string> message = net::zmq::receive(rep.get(), read_flags);
|
||||
if (!message)
|
||||
{
|
||||
// EAGAIN can occur when using `zmq_poll`, which doesn't inspect for message validity
|
||||
if (message != net::zmq::make_error_code(EAGAIN))
|
||||
MONERO_THROW(message.error(), "Read failure on ZMQ-RPC");
|
||||
}
|
||||
else // no errors
|
||||
{
|
||||
MDEBUG("Received RPC request: \"" << *message << "\"");
|
||||
epee::byte_slice response = handler.handle(std::move(*message));
|
||||
|
||||
const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()};
|
||||
MDEBUG("Sending RPC reply: \"" << response_view << "\"");
|
||||
MONERO_UNWRAP(net::zmq::send(std::move(response), rep.get()));
|
||||
const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()};
|
||||
MDEBUG("Sending RPC reply: \"" << response_view << "\"");
|
||||
MONERO_UNWRAP(net::zmq::send(std::move(response), rep.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace serialization
|
||||
typename std::enable_if<!use_container_varint<T>(), bool>::type
|
||||
serialize_container_element(Archive& ar, T& e)
|
||||
{
|
||||
return ::do_serialize(ar, e);
|
||||
return do_serialize(ar, e);
|
||||
}
|
||||
|
||||
template<typename Archive, typename T>
|
||||
@@ -52,7 +52,7 @@ namespace serialization
|
||||
static constexpr const bool previously_varint = std::is_same<uint64_t, T>() || std::is_same<uint32_t, T>();
|
||||
|
||||
if (!previously_varint && ar.varint_bug_backward_compatibility_enabled() && !typename Archive::is_saving())
|
||||
return ::do_serialize(ar, e);
|
||||
return do_serialize(ar, e);
|
||||
ar.serialize_varint(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,14 +42,12 @@ struct debug_archive : public json_archive<W> {
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct serializer<debug_archive<true>, T>
|
||||
static inline bool do_serialize(debug_archive<true> &ar, T &v)
|
||||
{
|
||||
static void serialize(debug_archive<true> &ar, T &v)
|
||||
{
|
||||
ar.begin_object();
|
||||
ar.tag(variant_serialization_traits<debug_archive<true>, T>::get_tag());
|
||||
serializer<json_archive<true>, T>::serialize(ar, v);
|
||||
do_serialize(static_cast<json_archive<true>&>(ar), v);
|
||||
ar.end_object();
|
||||
ar.stream() << std::endl;
|
||||
}
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
#include "cryptonote_basic/difficulty.h"
|
||||
#include "serialization.h"
|
||||
|
||||
template<> struct is_basic_type<cryptonote::difficulty_type> { typedef boost::true_type type; };
|
||||
|
||||
template <template <bool> class Archive>
|
||||
inline bool do_serialize(Archive<false>& ar, cryptonote::difficulty_type &diff)
|
||||
{
|
||||
|
||||
@@ -273,7 +273,10 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::t
|
||||
{
|
||||
INSERT_INTO_JSON_OBJECT(dest, signatures, tx.signatures);
|
||||
}
|
||||
INSERT_INTO_JSON_OBJECT(dest, ringct, tx.rct_signatures);
|
||||
{
|
||||
dest.Key("ringct");
|
||||
toJsonValue(dest, tx.rct_signatures, tx.pruned);
|
||||
}
|
||||
|
||||
dest.EndObject();
|
||||
}
|
||||
@@ -1111,7 +1114,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResp
|
||||
GET_FROM_JSON_OBJECT(val, response.reward, reward);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& sig)
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& sig, const bool prune)
|
||||
{
|
||||
using boost::adaptors::transform;
|
||||
|
||||
@@ -1131,7 +1134,7 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig&
|
||||
}
|
||||
|
||||
// prunable
|
||||
if (!sig.p.bulletproofs.empty() || !sig.p.bulletproofs_plus.empty() || !sig.p.rangeSigs.empty() || !sig.p.MGs.empty() || !sig.get_pseudo_outs().empty())
|
||||
if (!prune && (!sig.p.bulletproofs.empty() || !sig.p.bulletproofs_plus.empty() || !sig.p.rangeSigs.empty() || !sig.p.MGs.empty() || !sig.get_pseudo_outs().empty()))
|
||||
{
|
||||
dest.Key("prunable");
|
||||
dest.StartObject();
|
||||
@@ -1140,6 +1143,7 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig&
|
||||
INSERT_INTO_JSON_OBJECT(dest, bulletproofs, sig.p.bulletproofs);
|
||||
INSERT_INTO_JSON_OBJECT(dest, bulletproofs_plus, sig.p.bulletproofs_plus);
|
||||
INSERT_INTO_JSON_OBJECT(dest, mlsags, sig.p.MGs);
|
||||
INSERT_INTO_JSON_OBJECT(dest, clsags, sig.p.CLSAGs);
|
||||
INSERT_INTO_JSON_OBJECT(dest, pseudo_outs, sig.get_pseudo_outs());
|
||||
|
||||
dest.EndObject();
|
||||
@@ -1175,6 +1179,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig)
|
||||
GET_FROM_JSON_OBJECT(prunable->value, sig.p.bulletproofs, bulletproofs);
|
||||
GET_FROM_JSON_OBJECT(prunable->value, sig.p.bulletproofs_plus, bulletproofs_plus);
|
||||
GET_FROM_JSON_OBJECT(prunable->value, sig.p.MGs, mlsags);
|
||||
GET_FROM_JSON_OBJECT(prunable->value, sig.p.CLSAGs, clsags);
|
||||
GET_FROM_JSON_OBJECT(prunable->value, pseudo_outs, pseudo_outs);
|
||||
|
||||
sig.get_pseudo_outs() = std::move(pseudo_outs);
|
||||
@@ -1185,6 +1190,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig)
|
||||
sig.p.bulletproofs.clear();
|
||||
sig.p.bulletproofs_plus.clear();
|
||||
sig.p.MGs.clear();
|
||||
sig.p.CLSAGs.clear();
|
||||
sig.get_pseudo_outs().clear();
|
||||
}
|
||||
}
|
||||
@@ -1393,13 +1399,41 @@ void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig)
|
||||
GET_FROM_JSON_OBJECT(val, sig.cc, cc);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::clsag& sig)
|
||||
{
|
||||
dest.StartObject();
|
||||
|
||||
INSERT_INTO_JSON_OBJECT(dest, s, sig.s);
|
||||
INSERT_INTO_JSON_OBJECT(dest, c1, sig.c1);
|
||||
INSERT_INTO_JSON_OBJECT(dest, D, sig.D);
|
||||
|
||||
dest.EndObject();
|
||||
}
|
||||
|
||||
void fromJsonValue(const rapidjson::Value& val, rct::clsag& sig)
|
||||
{
|
||||
if (!val.IsObject())
|
||||
{
|
||||
throw WRONG_TYPE("key64 (rct::key[64])");
|
||||
}
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, sig.s, s);
|
||||
GET_FROM_JSON_OBJECT(val, sig.c1, c1);
|
||||
GET_FROM_JSON_OBJECT(val, sig.D, D);
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::DaemonInfo& info)
|
||||
{
|
||||
dest.StartObject();
|
||||
|
||||
const uint64_t difficulty_top64 = (info.wide_difficulty >> 64).convert_to<std::uint64_t>();
|
||||
const uint64_t cumulative_difficulty_top64 = (info.wide_cumulative_difficulty >> 64).convert_to<std::uint64_t>();
|
||||
|
||||
INSERT_INTO_JSON_OBJECT(dest, height, info.height);
|
||||
INSERT_INTO_JSON_OBJECT(dest, target_height, info.target_height);
|
||||
INSERT_INTO_JSON_OBJECT(dest, top_block_height, info.top_block_height);
|
||||
INSERT_INTO_JSON_OBJECT(dest, difficulty, info.difficulty);
|
||||
INSERT_INTO_JSON_OBJECT(dest, difficulty_top64, difficulty_top64);
|
||||
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);
|
||||
@@ -1414,12 +1448,14 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::r
|
||||
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, cumulative_difficulty_top64, cumulative_difficulty_top64);
|
||||
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, adjusted_time, info.adjusted_time);
|
||||
INSERT_INTO_JSON_OBJECT(dest, start_time, info.start_time);
|
||||
INSERT_INTO_JSON_OBJECT(dest, version, info.version);
|
||||
|
||||
dest.EndObject();
|
||||
}
|
||||
@@ -1431,9 +1467,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
|
||||
throw WRONG_TYPE("json object");
|
||||
}
|
||||
|
||||
uint64_t difficulty_top64 = 0;
|
||||
uint64_t cumulative_difficulty_top64 = 0;
|
||||
|
||||
GET_FROM_JSON_OBJECT(val, info.height, height);
|
||||
GET_FROM_JSON_OBJECT(val, info.target_height, target_height);
|
||||
GET_FROM_JSON_OBJECT(val, info.top_block_height, top_block_height);
|
||||
GET_FROM_JSON_OBJECT(val, info.difficulty, difficulty);
|
||||
GET_FROM_JSON_OBJECT(val, difficulty_top64, difficulty_top64);
|
||||
GET_FROM_JSON_OBJECT(val, info.target, target);
|
||||
GET_FROM_JSON_OBJECT(val, info.tx_count, tx_count);
|
||||
GET_FROM_JSON_OBJECT(val, info.tx_pool_size, tx_pool_size);
|
||||
@@ -1448,12 +1489,22 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
|
||||
GET_FROM_JSON_OBJECT(val, info.nettype, nettype);
|
||||
GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash);
|
||||
GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty);
|
||||
GET_FROM_JSON_OBJECT(val, cumulative_difficulty_top64, cumulative_difficulty_top64);
|
||||
GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit);
|
||||
GET_FROM_JSON_OBJECT(val, info.block_weight_limit, block_weight_limit);
|
||||
GET_FROM_JSON_OBJECT(val, info.block_size_median, block_size_median);
|
||||
GET_FROM_JSON_OBJECT(val, info.block_weight_median, block_weight_median);
|
||||
GET_FROM_JSON_OBJECT(val, info.adjusted_time, adjusted_time);
|
||||
GET_FROM_JSON_OBJECT(val, info.start_time, start_time);
|
||||
GET_FROM_JSON_OBJECT(val, info.version, version);
|
||||
|
||||
info.wide_difficulty = difficulty_top64;
|
||||
info.wide_difficulty <<= 64;
|
||||
info.wide_difficulty += info.difficulty;
|
||||
|
||||
info.wide_cumulative_difficulty = cumulative_difficulty_top64;
|
||||
info.wide_cumulative_difficulty <<= 64;
|
||||
info.wide_cumulative_difficulty += info.cumulative_difficulty;
|
||||
}
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_distribution& dist)
|
||||
|
||||
@@ -281,7 +281,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error);
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::BlockHeaderResponse& response);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& i);
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& sig, bool prune);
|
||||
void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig);
|
||||
|
||||
void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key);
|
||||
@@ -304,6 +304,9 @@ void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig);
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::mgSig& sig);
|
||||
void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::clsag& sig);
|
||||
void fromJsonValue(const rapidjson::Value& val, rct::clsag& sig);
|
||||
|
||||
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::DaemonInfo& info);
|
||||
void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info);
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace serialization
|
||||
typename std::enable_if<!use_pair_varint<T>(), bool>::type
|
||||
serialize_pair_element(Archive& ar, T& e)
|
||||
{
|
||||
return ::do_serialize(ar, e);
|
||||
return do_serialize(ar, e);
|
||||
}
|
||||
|
||||
template<typename Archive, typename T>
|
||||
@@ -57,7 +57,7 @@ namespace serialization
|
||||
static constexpr const bool previously_varint = std::is_same<uint64_t, T>();
|
||||
|
||||
if (!previously_varint && ar.varint_bug_backward_compatibility_enabled() && !typename Archive::is_saving())
|
||||
return ::do_serialize(ar, e);
|
||||
return do_serialize(ar, e);
|
||||
ar.serialize_varint(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -57,73 +57,30 @@
|
||||
template <class T>
|
||||
struct is_blob_type { typedef boost::false_type type; };
|
||||
|
||||
/*! \struct has_free_serializer
|
||||
*
|
||||
* \brief a descriptor for dispatching serialize
|
||||
*/
|
||||
template <class T>
|
||||
struct has_free_serializer { typedef boost::true_type type; };
|
||||
|
||||
/*! \struct is_basic_type
|
||||
*
|
||||
* \brief a descriptor for dispatching serialize
|
||||
*/
|
||||
template <class T>
|
||||
struct is_basic_type { typedef boost::false_type type; };
|
||||
|
||||
template<typename F, typename S>
|
||||
struct is_basic_type<std::pair<F,S>> { typedef boost::true_type type; };
|
||||
template<>
|
||||
struct is_basic_type<std::string> { typedef boost::true_type type; };
|
||||
|
||||
/*! \struct serializer
|
||||
*
|
||||
* \brief ... wouldn't a class be better?
|
||||
*
|
||||
* \detailed The logic behind serializing data. Places the archive
|
||||
* data into the supplied parameter. This dispatches based on the
|
||||
* supplied \a T template parameter's traits of is_blob_type or it is
|
||||
* an integral (as defined by the is_integral trait). Depends on the
|
||||
* \a Archive parameter to have overloaded the serialize_blob(T v,
|
||||
* size_t size) and serialize_int(T v) base on which trait it
|
||||
* applied. When the class has neither types, it falls to the
|
||||
* overloaded method do_serialize(Archive ar) in T to do the work.
|
||||
*/
|
||||
template <class Archive, class T>
|
||||
struct serializer{
|
||||
static bool serialize(Archive &ar, T &v) {
|
||||
return serialize(ar, v, typename boost::is_integral<T>::type(), typename is_blob_type<T>::type(), typename is_basic_type<T>::type());
|
||||
}
|
||||
template<typename A>
|
||||
static bool serialize(Archive &ar, T &v, boost::false_type, boost::true_type, A a) {
|
||||
ar.serialize_blob(&v, sizeof(v));
|
||||
return true;
|
||||
}
|
||||
template<typename A>
|
||||
static bool serialize(Archive &ar, T &v, boost::true_type, boost::false_type, A a) {
|
||||
ar.serialize_int(v);
|
||||
return true;
|
||||
}
|
||||
static bool serialize(Archive &ar, T &v, boost::false_type, boost::false_type, boost::false_type) {
|
||||
//serialize_custom(ar, v, typename has_free_serializer<T>::type());
|
||||
return v.do_serialize(ar);
|
||||
}
|
||||
static bool serialize(Archive &ar, T &v, boost::false_type, boost::false_type, boost::true_type) {
|
||||
//serialize_custom(ar, v, typename has_free_serializer<T>::type());
|
||||
return do_serialize(ar, v);
|
||||
}
|
||||
static void serialize_custom(Archive &ar, T &v, boost::true_type) {
|
||||
}
|
||||
};
|
||||
|
||||
/*! \fn do_serialize(Archive &ar, T &v)
|
||||
*
|
||||
* \brief just calls the serialize function defined for ar and v...
|
||||
* \brief main function for dispatching serialization for a given pair of archive and value types
|
||||
*
|
||||
* Types marked true with is_blob_type<T> will be serialized as a blob, integral types will be
|
||||
* serialized as integers, and types who have a `member_do_serialize` method will be serialized
|
||||
* using that method. Booleans are serialized like blobs.
|
||||
*/
|
||||
template <class Archive, class T>
|
||||
inline bool do_serialize(Archive &ar, T &v)
|
||||
inline std::enable_if_t<is_blob_type<T>::type::value, bool> do_serialize(Archive &ar, T &v)
|
||||
{
|
||||
return ::serializer<Archive, T>::serialize(ar, v);
|
||||
ar.serialize_blob(&v, sizeof(v));
|
||||
return true;
|
||||
}
|
||||
template <class Archive, class T>
|
||||
inline std::enable_if_t<boost::is_integral<T>::value, bool> do_serialize(Archive &ar, T &v)
|
||||
{
|
||||
ar.serialize_int(v);
|
||||
return true;
|
||||
}
|
||||
template <class Archive, class T>
|
||||
inline auto do_serialize(Archive &ar, T &v) -> decltype(v.member_do_serialize(ar), true)
|
||||
{
|
||||
return v.member_do_serialize(ar);
|
||||
}
|
||||
template <class Archive>
|
||||
inline bool do_serialize(Archive &ar, bool &v)
|
||||
@@ -144,16 +101,6 @@ inline bool do_serialize(Archive &ar, bool &v)
|
||||
typedef boost::true_type type; \
|
||||
}
|
||||
|
||||
/*! \macro FREE_SERIALIZER
|
||||
*
|
||||
* \brief adds the has_free_serializer to the type
|
||||
*/
|
||||
#define FREE_SERIALIZER(T) \
|
||||
template<> \
|
||||
struct has_free_serializer<T> { \
|
||||
typedef boost::true_type type; \
|
||||
}
|
||||
|
||||
/*! \macro VARIANT_TAG
|
||||
*
|
||||
* \brief Adds the tag \tag to the \a Archive of \a Type
|
||||
@@ -174,7 +121,7 @@ inline bool do_serialize(Archive &ar, bool &v)
|
||||
*/
|
||||
#define BEGIN_SERIALIZE() \
|
||||
template <bool W, template <bool> class Archive> \
|
||||
bool do_serialize(Archive<W> &ar) {
|
||||
bool member_do_serialize(Archive<W> &ar) {
|
||||
|
||||
/*! \macro BEGIN_SERIALIZE_OBJECT
|
||||
*
|
||||
@@ -183,7 +130,7 @@ inline bool do_serialize(Archive &ar, bool &v)
|
||||
*/
|
||||
#define BEGIN_SERIALIZE_OBJECT() \
|
||||
template <bool W, template <bool> class Archive> \
|
||||
bool do_serialize(Archive<W> &ar) { \
|
||||
bool member_do_serialize(Archive<W> &ar) { \
|
||||
ar.begin_object(); \
|
||||
bool r = do_serialize_object(ar); \
|
||||
ar.end_object(); \
|
||||
@@ -197,11 +144,6 @@ inline bool do_serialize(Archive &ar, bool &v)
|
||||
#define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) \
|
||||
::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive<W>::is_saving())
|
||||
|
||||
/*! \macro PREPARE_CUSTOM_DEQUE_SERIALIZATION
|
||||
*/
|
||||
#define PREPARE_CUSTOM_DEQUE_SERIALIZATION(size, vec) \
|
||||
::serialization::detail::prepare_custom_deque_serialization(size, vec, typename Archive<W>::is_saving())
|
||||
|
||||
/*! \macro END_SERIALIZE
|
||||
* \brief self-explanatory
|
||||
*/
|
||||
@@ -209,16 +151,6 @@ inline bool do_serialize(Archive &ar, bool &v)
|
||||
return ar.good(); \
|
||||
}
|
||||
|
||||
/*! \macro VALUE(f)
|
||||
* \brief the same as FIELD(f)
|
||||
*/
|
||||
#define VALUE(f) \
|
||||
do { \
|
||||
ar.tag(#f); \
|
||||
bool r = ::do_serialize(ar, f); \
|
||||
if (!r || !ar.good()) return false; \
|
||||
} while(0);
|
||||
|
||||
/*! \macro FIELD_N(t,f)
|
||||
*
|
||||
* \brief serializes a field \a f tagged \a t
|
||||
@@ -226,7 +158,7 @@ inline bool do_serialize(Archive &ar, bool &v)
|
||||
#define FIELD_N(t, f) \
|
||||
do { \
|
||||
ar.tag(t); \
|
||||
bool r = ::do_serialize(ar, f); \
|
||||
bool r = do_serialize(ar, f); \
|
||||
if (!r || !ar.good()) return false; \
|
||||
} while(0);
|
||||
|
||||
@@ -237,7 +169,7 @@ inline bool do_serialize(Archive &ar, bool &v)
|
||||
#define FIELD(f) \
|
||||
do { \
|
||||
ar.tag(#f); \
|
||||
bool r = ::do_serialize(ar, f); \
|
||||
bool r = do_serialize(ar, f); \
|
||||
if (!r || !ar.good()) return false; \
|
||||
} while(0);
|
||||
|
||||
@@ -247,7 +179,7 @@ inline bool do_serialize(Archive &ar, bool &v)
|
||||
*/
|
||||
#define FIELDS(f) \
|
||||
do { \
|
||||
bool r = ::do_serialize(ar, f); \
|
||||
bool r = do_serialize(ar, f); \
|
||||
if (!r || !ar.good()) return false; \
|
||||
} while(0);
|
||||
|
||||
@@ -317,17 +249,6 @@ namespace serialization {
|
||||
vec.resize(size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void prepare_custom_deque_serialization(size_t size, std::deque<T>& vec, const boost::mpl::bool_<true>& /*is_saving*/)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void prepare_custom_deque_serialization(size_t size, std::deque<T>& vec, const boost::mpl::bool_<false>& /*is_saving*/)
|
||||
{
|
||||
vec.resize(size);
|
||||
}
|
||||
|
||||
/*! \fn do_check_stream_state
|
||||
*
|
||||
* \brief self explanatory
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace serialization
|
||||
template <typename Archive, class T>
|
||||
bool serialize_tuple_element(Archive& ar, T& e)
|
||||
{
|
||||
return ::do_serialize(ar, e);
|
||||
return do_serialize(ar, e);
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
|
||||
+17
-28
@@ -72,7 +72,7 @@ struct variant_reader
|
||||
{
|
||||
if(variant_serialization_traits<Archive, current_type>::get_tag() == t) {
|
||||
current_type x;
|
||||
if(!::do_serialize(ar, x))
|
||||
if(!do_serialize(ar, x))
|
||||
{
|
||||
ar.set_fail();
|
||||
return false;
|
||||
@@ -100,19 +100,13 @@ struct variant_reader<Archive, Variant, TBegin, TBegin>
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <template <bool> class Archive, BOOST_VARIANT_ENUM_PARAMS(typename T)>
|
||||
struct serializer<Archive<false>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>>
|
||||
{
|
||||
typedef boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> variant_type;
|
||||
typedef typename Archive<false>::variant_tag_type variant_tag_type;
|
||||
typedef typename variant_type::types types;
|
||||
|
||||
static bool serialize(Archive<false> &ar, variant_type &v) {
|
||||
variant_tag_type t;
|
||||
template <template <bool> class Archive, typename... T>
|
||||
static bool do_serialize(Archive<false> &ar, boost::variant<T...> &v) {
|
||||
using types = typename boost::variant<T...>::types;
|
||||
typename Archive<false>::variant_tag_type t;
|
||||
ar.begin_variant();
|
||||
ar.read_variant_tag(t);
|
||||
if(!variant_reader<Archive<false>, variant_type,
|
||||
if(!variant_reader<Archive<false>, boost::variant<T...>,
|
||||
typename boost::mpl::begin<types>::type,
|
||||
typename boost::mpl::end<types>::type>::read(ar, v, t))
|
||||
{
|
||||
@@ -121,27 +115,21 @@ struct serializer<Archive<false>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>>
|
||||
}
|
||||
ar.end_variant();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <template <bool> class Archive, BOOST_VARIANT_ENUM_PARAMS(typename T)>
|
||||
struct serializer<Archive<true>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>>
|
||||
template <template <bool> class Archive>
|
||||
struct variant_write_visitor : public boost::static_visitor<bool>
|
||||
{
|
||||
typedef boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> variant_type;
|
||||
//typedef typename Archive<true>::variant_tag_type variant_tag_type;
|
||||
|
||||
struct visitor : public boost::static_visitor<bool>
|
||||
{
|
||||
Archive<true> &ar;
|
||||
|
||||
visitor(Archive<true> &a) : ar(a) { }
|
||||
variant_write_visitor(Archive<true> &a) : ar(a) { }
|
||||
|
||||
template <class T>
|
||||
bool operator ()(T &rv) const
|
||||
{
|
||||
ar.begin_variant();
|
||||
ar.write_variant_tag(variant_serialization_traits<Archive<true>, T>::get_tag());
|
||||
if(!::do_serialize(ar, rv))
|
||||
if(!do_serialize(ar, rv))
|
||||
{
|
||||
ar.set_fail();
|
||||
return false;
|
||||
@@ -149,9 +137,10 @@ struct serializer<Archive<true>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>>
|
||||
ar.end_variant();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static bool serialize(Archive<true> &ar, variant_type &v) {
|
||||
return boost::apply_visitor(visitor(ar), v);
|
||||
}
|
||||
};
|
||||
|
||||
template <template <bool> class Archive, typename... T>
|
||||
static bool do_serialize(Archive<true> &ar, boost::variant<T...> &v)
|
||||
{
|
||||
return boost::apply_visitor(variant_write_visitor<Archive>(ar), v);
|
||||
}
|
||||
|
||||
@@ -155,11 +155,6 @@ typedef cryptonote::simple_wallet sw;
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
enum TransferType {
|
||||
Transfer,
|
||||
TransferLocked,
|
||||
};
|
||||
|
||||
static std::string get_human_readable_timespan(std::chrono::seconds seconds);
|
||||
static std::string get_human_readable_timespan(uint64_t seconds);
|
||||
|
||||
@@ -196,9 +191,7 @@ namespace
|
||||
const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [uses] [index=<N1>[,<N2>[,...]]]");
|
||||
const char* USAGE_PAYMENTS("payments <PID_1> [<PID_2> ... <PID_N>]");
|
||||
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>,...] | index=all] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]");
|
||||
const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [subtractfeefrom=<D0>[,<D1>,all,...]] [<payment_id>]");
|
||||
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_ACCOUNT("sweep_account <account> [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)>]");
|
||||
@@ -531,7 +524,52 @@ namespace
|
||||
fail_msg_writer() << sw::tr("invalid format for subaddress lookahead; must be <major>:<minor>");
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr const char* SFFD_ARG_NAME{"subtractfeefrom="};
|
||||
|
||||
bool parse_subtract_fee_from_outputs
|
||||
(
|
||||
const std::string& arg,
|
||||
tools::wallet2::unique_index_container& subtract_fee_from_outputs,
|
||||
bool& subtract_fee_from_all,
|
||||
bool& matches
|
||||
)
|
||||
{
|
||||
matches = false;
|
||||
if (!boost::string_ref{arg}.starts_with(SFFD_ARG_NAME)) // if arg doesn't match
|
||||
return true;
|
||||
matches = true;
|
||||
|
||||
const char* arg_end = arg.c_str() + arg.size();
|
||||
for (const char* p = arg.c_str() + strlen(SFFD_ARG_NAME); p < arg_end;)
|
||||
{
|
||||
const char* new_p = nullptr;
|
||||
const unsigned long dest_index = strtoul(p, const_cast<char**>(&new_p), 10);
|
||||
if (dest_index == 0 && new_p == p) // numerical conversion failed
|
||||
{
|
||||
if (0 != strncmp(p, "all", 3))
|
||||
{
|
||||
fail_msg_writer() << tr("Failed to parse subtractfeefrom list");
|
||||
return false;
|
||||
}
|
||||
subtract_fee_from_all = true;
|
||||
break;
|
||||
}
|
||||
else if (dest_index > std::numeric_limits<uint32_t>::max())
|
||||
{
|
||||
fail_msg_writer() << tr("Destination index is too large") << ": " << dest_index;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
subtract_fee_from_outputs.insert(dest_index);
|
||||
p = new_p + 1; // skip the comma
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
void simple_wallet::handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon)
|
||||
{
|
||||
@@ -3215,7 +3253,6 @@ bool simple_wallet::scan_tx(const std::vector<std::string> &args)
|
||||
}
|
||||
txids.insert(txid);
|
||||
}
|
||||
std::vector<crypto::hash> txids_v(txids.begin(), txids.end());
|
||||
|
||||
if (!m_wallet->is_trusted_daemon()) {
|
||||
message_writer(console_color_red, true) << tr("WARNING: this operation may reveal the txids to the remote node and affect your privacy");
|
||||
@@ -3228,7 +3265,9 @@ bool simple_wallet::scan_tx(const std::vector<std::string> &args)
|
||||
LOCK_IDLE_SCOPE();
|
||||
m_in_manual_refresh.store(true);
|
||||
try {
|
||||
m_wallet->scan_tx(txids_v);
|
||||
m_wallet->scan_tx(txids);
|
||||
} catch (const tools::error::wont_reprocess_recent_txs_via_untrusted_daemon &e) {
|
||||
fail_msg_writer() << e.what() << ". Either connect to a trusted daemon by passing --trusted-daemon when starting the wallet, or use rescan_bc to rescan the chain.";
|
||||
} catch (const std::exception &e) {
|
||||
fail_msg_writer() << e.what();
|
||||
}
|
||||
@@ -3289,15 +3328,7 @@ simple_wallet::simple_wallet()
|
||||
tr("Show the blockchain height."));
|
||||
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::on_command, this, &simple_wallet::transfer, _1),
|
||||
tr(USAGE_TRANSFER),
|
||||
tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. 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. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
|
||||
m_cmd_binder.set_handler("locked_transfer",
|
||||
boost::bind(&simple_wallet::on_command, this, &simple_wallet::locked_transfer,_1),
|
||||
tr(USAGE_LOCKED_TRANSFER),
|
||||
tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. 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. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
|
||||
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>,...]\" 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."));
|
||||
tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. 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. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included). The \"subtractfeefrom=\" list allows you to choose which destinations to fund the tx fee from instead of the change output. The fee will be split across the chosen destinations proportionally equally. For example, to make 3 transfers where the fee is taken from the first and third destinations, one could do: \"transfer <addr1> 3 <addr2> 0.5 <addr3> 1 subtractfeefrom=0,2\". Let's say the tx fee is 0.1. The balance would drop by exactly 4.5 XMR including fees, and addr1 & addr3 would receive 2.925 & 0.975 XMR, respectively. Use \"subtractfeefrom=all\" to spread the fee across all destinations."));
|
||||
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"));
|
||||
@@ -5894,7 +5925,10 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
|
||||
{
|
||||
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);});
|
||||
m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks, received_money);
|
||||
// For manual refresh don't allow incremental checking of the pool: Because we did not process the txs
|
||||
// for us in the pool during automatic refresh we could miss some of them if we checked the pool
|
||||
// incrementally here
|
||||
m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks, received_money, true, false);
|
||||
|
||||
if (reset == ResetSoftKeepKI)
|
||||
{
|
||||
@@ -6539,7 +6573,7 @@ bool simple_wallet::on_command(bool (simple_wallet::*cmd)(const std::vector<std:
|
||||
return (this->*cmd)(args);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms)
|
||||
bool simple_wallet::transfer_main(const std::vector<std::string> &args_, bool called_by_mms)
|
||||
{
|
||||
// "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"
|
||||
if (!try_connect_to_daemon())
|
||||
@@ -6555,7 +6589,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
|
||||
local_args.erase(local_args.begin());
|
||||
}
|
||||
|
||||
uint32_t priority = 0;
|
||||
uint32_t priority = m_wallet->get_default_priority();
|
||||
if (local_args.size() > 0 && parse_priority(local_args[0], priority))
|
||||
local_args.erase(local_args.begin());
|
||||
|
||||
@@ -6591,7 +6625,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t min_args = (transfer_type == TransferLocked) ? 2 : 1;
|
||||
const size_t min_args = 1;
|
||||
if(local_args.size() < min_args)
|
||||
{
|
||||
fail_msg_writer() << tr("wrong number of arguments");
|
||||
@@ -6616,24 +6650,25 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t locked_blocks = 0;
|
||||
if (transfer_type == TransferLocked)
|
||||
// Parse subtractfeefrom destination list
|
||||
tools::wallet2::unique_index_container subtract_fee_from_outputs;
|
||||
bool subtract_fee_from_all = false;
|
||||
for (auto it = local_args.begin(); it < local_args.end();)
|
||||
{
|
||||
try
|
||||
bool matches = false;
|
||||
if (!parse_subtract_fee_from_outputs(*it, subtract_fee_from_outputs, subtract_fee_from_all, matches))
|
||||
{
|
||||
locked_blocks = boost::lexical_cast<uint64_t>(local_args.back());
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
fail_msg_writer() << tr("bad locked_blocks parameter:") << " " << local_args.back();
|
||||
return false;
|
||||
}
|
||||
if (locked_blocks > 1000000)
|
||||
else if (matches)
|
||||
{
|
||||
fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)");
|
||||
return false;
|
||||
it = local_args.erase(it);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
local_args.pop_back();
|
||||
}
|
||||
|
||||
vector<cryptonote::address_parse_info> dsts_info;
|
||||
@@ -6732,33 +6767,20 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
|
||||
dsts.push_back(de);
|
||||
}
|
||||
|
||||
if (subtract_fee_from_all)
|
||||
{
|
||||
subtract_fee_from_outputs.clear();
|
||||
for (decltype(subtract_fee_from_outputs)::value_type i = 0; i < dsts.size(); ++i)
|
||||
subtract_fee_from_outputs.insert(i);
|
||||
}
|
||||
|
||||
SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;);
|
||||
|
||||
try
|
||||
{
|
||||
// figure out what tx will be necessary
|
||||
std::vector<tools::wallet2::pending_tx> ptx_vector;
|
||||
uint64_t bc_height, unlock_block = 0;
|
||||
std::string err;
|
||||
switch (transfer_type)
|
||||
{
|
||||
case TransferLocked:
|
||||
bc_height = get_daemon_blockchain_height(err);
|
||||
if (!err.empty())
|
||||
{
|
||||
fail_msg_writer() << tr("failed to get blockchain height: ") << err;
|
||||
return false;
|
||||
}
|
||||
unlock_block = bc_height + locked_blocks;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown transfer method, using default");
|
||||
/* FALLTHRU */
|
||||
case Transfer:
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
}
|
||||
auto ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, priority, extra,
|
||||
m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs);
|
||||
|
||||
if (ptx_vector.empty())
|
||||
{
|
||||
@@ -6868,11 +6890,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
|
||||
if (dust_in_fee != 0) prompt << boost::format(tr(", of which %s is dust from change")) % print_money(dust_in_fee);
|
||||
if (dust_not_in_fee != 0) prompt << tr(".") << ENDL << boost::format(tr("A total of %s from dust change will be sent to dust address"))
|
||||
% print_money(dust_not_in_fee);
|
||||
if (transfer_type == TransferLocked)
|
||||
{
|
||||
float days = locked_blocks / 720.0f;
|
||||
prompt << boost::format(tr(".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_money(change) % ((unsigned long long)unlock_block) % days;
|
||||
}
|
||||
if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
|
||||
return false;
|
||||
bool default_ring_size = true;
|
||||
@@ -6992,29 +7009,7 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_)
|
||||
PRINT_USAGE(USAGE_TRANSFER);
|
||||
return true;
|
||||
}
|
||||
transfer_main(Transfer, args_, false);
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::locked_transfer(const std::vector<std::string> &args_)
|
||||
{
|
||||
if (args_.size() < 1)
|
||||
{
|
||||
PRINT_USAGE(USAGE_LOCKED_TRANSFER);
|
||||
return true;
|
||||
}
|
||||
transfer_main(TransferLocked, args_, false);
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_)
|
||||
{
|
||||
if (args_.size() < 1)
|
||||
{
|
||||
PRINT_USAGE(USAGE_LOCKED_SWEEP_ALL);
|
||||
return true;
|
||||
}
|
||||
sweep_main(m_current_subaddress_account, 0, true, args_);
|
||||
transfer_main(args_, false);
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -7126,7 +7121,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, const std::vector<std::string> &args_)
|
||||
bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vector<std::string> &args_)
|
||||
{
|
||||
auto print_usage = [this, account, below]()
|
||||
{
|
||||
@@ -7206,41 +7201,6 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t unlock_block = 0;
|
||||
if (locked) {
|
||||
uint64_t locked_blocks = 0;
|
||||
|
||||
if (local_args.size() < 2) {
|
||||
fail_msg_writer() << tr("missing lockedblocks parameter");
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
locked_blocks = boost::lexical_cast<uint64_t>(local_args[1]);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
fail_msg_writer() << tr("bad locked_blocks parameter");
|
||||
return true;
|
||||
}
|
||||
if (locked_blocks > 1000000)
|
||||
{
|
||||
fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)");
|
||||
return true;
|
||||
}
|
||||
std::string err;
|
||||
uint64_t bc_height = get_daemon_blockchain_height(err);
|
||||
if (!err.empty())
|
||||
{
|
||||
fail_msg_writer() << tr("failed to get blockchain height: ") << err;
|
||||
return true;
|
||||
}
|
||||
unlock_block = bc_height + locked_blocks;
|
||||
|
||||
local_args.erase(local_args.begin() + 1);
|
||||
}
|
||||
|
||||
size_t outputs = 1;
|
||||
if (local_args.size() > 0 && local_args[0].substr(0, 8) == "outputs=")
|
||||
{
|
||||
@@ -7315,7 +7275,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co
|
||||
try
|
||||
{
|
||||
// figure out what tx will be necessary
|
||||
auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, fake_outs_count, unlock_block /* unlock_time */, priority, extra, account, subaddr_indices);
|
||||
auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, fake_outs_count, priority, extra, account, subaddr_indices);
|
||||
|
||||
if (ptx_vector.empty())
|
||||
{
|
||||
@@ -7572,7 +7532,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
|
||||
try
|
||||
{
|
||||
// figure out what tx will be necessary
|
||||
auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, 0 /* unlock_time */, priority, extra);
|
||||
auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, priority, extra);
|
||||
|
||||
if (ptx_vector.empty())
|
||||
{
|
||||
@@ -7682,7 +7642,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
|
||||
{
|
||||
sweep_main(m_current_subaddress_account, 0, false, args_);
|
||||
sweep_main(m_current_subaddress_account, 0, args_);
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -7702,7 +7662,7 @@ bool simple_wallet::sweep_account(const std::vector<std::string> &args_)
|
||||
}
|
||||
local_args.erase(local_args.begin());
|
||||
|
||||
sweep_main(account, 0, false, local_args);
|
||||
sweep_main(account, 0, local_args);
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -7720,7 +7680,7 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
|
||||
fail_msg_writer() << tr("invalid amount threshold");
|
||||
return true;
|
||||
}
|
||||
sweep_main(m_current_subaddress_account, below, false, std::vector<std::string>(++args_.begin(), args_.end()));
|
||||
sweep_main(m_current_subaddress_account, below, std::vector<std::string>(++args_.begin(), args_.end()));
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -11322,7 +11282,7 @@ void simple_wallet::mms_sync(const std::vector<std::string> &args)
|
||||
void simple_wallet::mms_transfer(const std::vector<std::string> &args)
|
||||
{
|
||||
// It's too complicated to check any arguments here, just let 'transfer_main' do the whole job
|
||||
transfer_main(Transfer, args, true);
|
||||
transfer_main(args, true);
|
||||
}
|
||||
|
||||
void simple_wallet::mms_delete(const std::vector<std::string> &args)
|
||||
|
||||
@@ -170,11 +170,9 @@ namespace cryptonote
|
||||
bool show_incoming_transfers(const std::vector<std::string> &args);
|
||||
bool show_payments(const std::vector<std::string> &args);
|
||||
bool show_blockchain_height(const std::vector<std::string> &args);
|
||||
bool transfer_main(int transfer_type, const std::vector<std::string> &args, bool called_by_mms);
|
||||
bool transfer_main(const std::vector<std::string> &args, bool called_by_mms);
|
||||
bool transfer(const std::vector<std::string> &args);
|
||||
bool locked_transfer(const std::vector<std::string> &args);
|
||||
bool locked_sweep_all(const std::vector<std::string> &args);
|
||||
bool sweep_main(uint32_t account, uint64_t below, bool locked, const std::vector<std::string> &args);
|
||||
bool sweep_main(uint32_t account, uint64_t below, const std::vector<std::string> &args);
|
||||
bool sweep_all(const std::vector<std::string> &args);
|
||||
bool sweep_account(const std::vector<std::string> &args);
|
||||
bool sweep_below(const std::vector<std::string> &args);
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_MONERO_VERSION "0.18.2.1"
|
||||
#define DEF_MONERO_VERSION "0.18.3.4"
|
||||
#define DEF_MONERO_RELEASE_NAME "Fluorine Fermi"
|
||||
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
|
||||
#define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@
|
||||
|
||||
@@ -1302,11 +1302,15 @@ bool WalletImpl::scanTransactions(const std::vector<std::string> &txids)
|
||||
}
|
||||
txids_u.insert(txid);
|
||||
}
|
||||
std::vector<crypto::hash> txids_v(txids_u.begin(), txids_u.end());
|
||||
|
||||
try
|
||||
{
|
||||
m_wallet->scan_tx(txids_v);
|
||||
m_wallet->scan_tx(txids_u);
|
||||
}
|
||||
catch (const tools::error::wont_reprocess_recent_txs_via_untrusted_daemon &e)
|
||||
{
|
||||
setStatusError(e.what());
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
@@ -1584,11 +1588,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
|
||||
fake_outs_count = m_wallet->adjust_mixin(mixin_count);
|
||||
|
||||
if (amount) {
|
||||
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */,
|
||||
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count,
|
||||
adjusted_priority,
|
||||
extra, subaddr_account, subaddr_indices);
|
||||
} else {
|
||||
transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count, 0 /* unlock_time */,
|
||||
transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count,
|
||||
adjusted_priority,
|
||||
extra, subaddr_account, subaddr_indices);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
|
||||
// Public interface for libwallet library
|
||||
namespace Monero {
|
||||
|
||||
@@ -392,4 +392,56 @@ boost::optional<std::string> NodeRPCProxy::get_rpc_payment_info(bool mining, boo
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
boost::optional<std::string> NodeRPCProxy::get_transactions(const std::vector<crypto::hash> &txids, const std::function<void(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request&, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response&, bool)> &f)
|
||||
{
|
||||
const size_t SLICE_SIZE = 100; // RESTRICTED_TRANSACTIONS_COUNT as defined in rpc/core_rpc_server.cpp
|
||||
for (size_t offset = 0; offset < txids.size(); offset += SLICE_SIZE)
|
||||
{
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req_t = AUTO_VAL_INIT(req_t);
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response resp_t = AUTO_VAL_INIT(resp_t);
|
||||
|
||||
const size_t n_txids = std::min<size_t>(SLICE_SIZE, txids.size() - offset);
|
||||
for (size_t n = offset; n < (offset + n_txids); ++n)
|
||||
req_t.txs_hashes.push_back(epee::string_tools::pod_to_hex(txids[n]));
|
||||
MDEBUG("asking for " << req_t.txs_hashes.size() << " transactions");
|
||||
req_t.decode_as_json = false;
|
||||
req_t.prune = true;
|
||||
|
||||
bool r = false;
|
||||
{
|
||||
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
|
||||
uint64_t pre_call_credits = m_rpc_payment_state.credits;
|
||||
req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
|
||||
r = net_utils::invoke_http_json("/gettransactions", req_t, resp_t, m_http_client, rpc_timeout);
|
||||
if (r && resp_t.status == CORE_RPC_STATUS_OK)
|
||||
check_rpc_cost(m_rpc_payment_state, "/gettransactions", resp_t.credits, pre_call_credits, resp_t.txs.size() * COST_PER_TX);
|
||||
}
|
||||
|
||||
f(req_t, resp_t, r);
|
||||
}
|
||||
return boost::optional<std::string>();
|
||||
}
|
||||
|
||||
boost::optional<std::string> NodeRPCProxy::get_block_header_by_height(uint64_t height, cryptonote::block_header_response &block_header)
|
||||
{
|
||||
if (m_offline)
|
||||
return boost::optional<std::string>("offline");
|
||||
|
||||
cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request req_t = AUTO_VAL_INIT(req_t);
|
||||
cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response resp_t = AUTO_VAL_INIT(resp_t);
|
||||
req_t.height = height;
|
||||
|
||||
{
|
||||
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
|
||||
uint64_t pre_call_credits = m_rpc_payment_state.credits;
|
||||
req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
|
||||
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req_t, resp_t, m_http_client, rpc_timeout);
|
||||
RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "getblockheaderbyheight");
|
||||
check_rpc_cost(m_rpc_payment_state, "getblockheaderbyheight", resp_t.credits, pre_call_credits, COST_PER_BLOCK_HEADER);
|
||||
}
|
||||
|
||||
block_header = std::move(resp_t.block_header);
|
||||
return boost::optional<std::string>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,6 +59,8 @@ public:
|
||||
boost::optional<std::string> get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, std::vector<uint64_t> &fees);
|
||||
boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask);
|
||||
boost::optional<std::string> get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie);
|
||||
boost::optional<std::string> get_transactions(const std::vector<crypto::hash> &txids, const std::function<void(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request&, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response&, bool)> &f);
|
||||
boost::optional<std::string> get_block_header_by_height(uint64_t height, cryptonote::block_header_response &block_header);
|
||||
|
||||
private:
|
||||
template<typename T> void handle_payment_changes(const T &res, std::true_type) {
|
||||
|
||||
+1094
-363
File diff suppressed because it is too large
Load Diff
+75
-24
@@ -139,6 +139,7 @@ private:
|
||||
public:
|
||||
// Full wallet callbacks
|
||||
virtual void on_new_block(uint64_t height, const cryptonote::block& block) {}
|
||||
virtual void on_reorg(uint64_t height, uint64_t blocks_detached, size_t transfers_detached) {}
|
||||
virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, uint64_t burnt, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) {}
|
||||
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {}
|
||||
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {}
|
||||
@@ -476,7 +477,7 @@ private:
|
||||
time_t m_sent_time;
|
||||
std::vector<cryptonote::tx_destination_entry> m_dests;
|
||||
crypto::hash m_payment_id;
|
||||
enum { pending, pending_not_in_pool, failed } m_state;
|
||||
enum { pending, pending_in_pool, failed } m_state;
|
||||
uint64_t m_timestamp;
|
||||
uint32_t m_subaddr_account; // subaddress account of your wallet to be used in this transfer
|
||||
std::set<uint32_t> m_subaddr_indices; // set of address indices used as inputs in this transfer
|
||||
@@ -598,6 +599,7 @@ private:
|
||||
|
||||
typedef std::vector<transfer_details> transfer_container;
|
||||
typedef serializable_unordered_multimap<crypto::hash, payment_details> payment_container;
|
||||
typedef std::set<uint32_t> unique_index_container;
|
||||
|
||||
struct multisig_sig
|
||||
{
|
||||
@@ -817,6 +819,30 @@ private:
|
||||
bool empty() const { return tx_extra_fields.empty() && primary.empty() && additional.empty(); }
|
||||
};
|
||||
|
||||
struct detached_blockchain_data
|
||||
{
|
||||
hashchain detached_blockchain;
|
||||
size_t original_chain_size;
|
||||
std::unordered_set<crypto::hash> detached_tx_hashes;
|
||||
std::unordered_map<crypto::hash, std::vector<cryptonote::tx_destination_entry>> detached_confirmed_txs_dests;
|
||||
};
|
||||
|
||||
struct process_tx_entry_t
|
||||
{
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry tx_entry;
|
||||
cryptonote::transaction tx;
|
||||
crypto::hash tx_hash;
|
||||
};
|
||||
|
||||
struct tx_entry_data
|
||||
{
|
||||
std::vector<process_tx_entry_t> tx_entries;
|
||||
uint64_t lowest_height;
|
||||
uint64_t highest_height;
|
||||
|
||||
tx_entry_data(): lowest_height((uint64_t)-1), highest_height(0) {}
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet or restores one. Assumes the multisig setup
|
||||
* has already completed for the provided multisig info.
|
||||
@@ -916,22 +942,32 @@ private:
|
||||
/*!
|
||||
* \brief store_to Stores wallet to another file(s), deleting old ones
|
||||
* \param path Path to the wallet file (keys and address filenames will be generated based on this filename)
|
||||
* \param password Password to protect new wallet (TODO: probably better save the password in the wallet object?)
|
||||
* \param password Password that currently locks the wallet
|
||||
* \param force_rewrite_keys if true, always rewrite keys file
|
||||
*
|
||||
* Leave both "path" and "password" blank to restore the cache file to the current position in the disk
|
||||
* (which is the same as calling `store()`). If you want to store the wallet with a new password,
|
||||
* use the method `change_password()`.
|
||||
*
|
||||
* Normally the keys file is not overwritten when storing, except when force_rewrite_keys is true
|
||||
* or when `path` is a new wallet file.
|
||||
*
|
||||
* \throw error::invalid_password If storing keys file and old password is incorrect
|
||||
*/
|
||||
void store_to(const std::string &path, const epee::wipeable_string &password);
|
||||
void store_to(const std::string &path, const epee::wipeable_string &password, bool force_rewrite_keys = false);
|
||||
/*!
|
||||
* \brief get_keys_file_data Get wallet keys data which can be stored to a wallet file.
|
||||
* \param password Password of the encrypted wallet buffer (TODO: probably better save the password in the wallet object?)
|
||||
* \param password Password that currently locks the wallet
|
||||
* \param watch_only true to include only view key, false to include both spend and view keys
|
||||
* \return Encrypted wallet keys data which can be stored to a wallet file
|
||||
* \throw error::invalid_password if password does not match current wallet
|
||||
*/
|
||||
boost::optional<wallet2::keys_file_data> get_keys_file_data(const epee::wipeable_string& password, bool watch_only);
|
||||
/*!
|
||||
* \brief get_cache_file_data Get wallet cache data which can be stored to a wallet file.
|
||||
* \param password Password to protect the wallet cache data (TODO: probably better save the password in the wallet object?)
|
||||
* \return Encrypted wallet cache data which can be stored to a wallet file
|
||||
* \return Encrypted wallet cache data which can be stored to a wallet file (using current password)
|
||||
*/
|
||||
boost::optional<wallet2::cache_file_data> get_cache_file_data(const epee::wipeable_string& password);
|
||||
boost::optional<wallet2::cache_file_data> get_cache_file_data();
|
||||
|
||||
std::string path() const;
|
||||
|
||||
@@ -1024,7 +1060,7 @@ private:
|
||||
bool is_deprecated() const;
|
||||
void refresh(bool trusted_daemon);
|
||||
void refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched);
|
||||
void refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money, bool check_pool = true);
|
||||
void refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money, bool check_pool = true, bool try_incremental = true, uint64_t max_blocks = std::numeric_limits<uint64_t>::max());
|
||||
bool refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_money, bool& ok);
|
||||
|
||||
void set_refresh_type(RefreshType refresh_type) { m_refresh_type = refresh_type; }
|
||||
@@ -1035,7 +1071,7 @@ private:
|
||||
bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const;
|
||||
bool has_multisig_partial_key_images() const;
|
||||
bool has_unknown_key_images() const;
|
||||
bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
|
||||
bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string()) const;
|
||||
bool key_on_device() const { return get_device_type() != hw::device::device_type::SOFTWARE; }
|
||||
hw::device::device_type get_device_type() const { return m_key_device_type; }
|
||||
bool reconnect_device();
|
||||
@@ -1052,10 +1088,10 @@ private:
|
||||
template<typename T>
|
||||
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, const bool use_view_tags);
|
||||
uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, const bool use_view_tags);
|
||||
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, const bool use_view_tags);
|
||||
uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, const bool use_view_tags);
|
||||
|
||||
void commit_tx(pending_tx& ptx_vector);
|
||||
void commit_tx(std::vector<pending_tx>& ptx_vector);
|
||||
@@ -1077,11 +1113,11 @@ private:
|
||||
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
|
||||
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
|
||||
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
|
||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); // pass subaddr_indices by value on purpose
|
||||
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
|
||||
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
bool sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, std::vector<cryptonote::tx_destination_entry> dsts) const;
|
||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
|
||||
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
|
||||
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
bool sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, const std::vector<cryptonote::tx_destination_entry>& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const;
|
||||
void cold_tx_aux_import(const std::vector<pending_tx>& ptx, const std::vector<std::string>& tx_device_aux);
|
||||
void cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux);
|
||||
uint64_t cold_key_image_sync(uint64_t &spent, uint64_t &unspent);
|
||||
@@ -1381,7 +1417,7 @@ private:
|
||||
std::string get_spend_proof(const crypto::hash &txid, const std::string &message);
|
||||
bool check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str);
|
||||
|
||||
void scan_tx(const std::vector<crypto::hash> &txids);
|
||||
void scan_tx(const std::unordered_set<crypto::hash> &txids);
|
||||
|
||||
/*!
|
||||
* \brief Generates a proof that proves the reserve of unspent funds
|
||||
@@ -1507,9 +1543,9 @@ 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::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed = false);
|
||||
void update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed = false, bool try_incremental = 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);
|
||||
void remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes, bool remove_if_found);
|
||||
|
||||
std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const;
|
||||
std::string encrypt(const epee::span<char> &span, const crypto::secret_key &skey, bool authenticated = true) const;
|
||||
@@ -1529,7 +1565,7 @@ private:
|
||||
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
|
||||
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
|
||||
|
||||
uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask) const;
|
||||
static uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask);
|
||||
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
|
||||
uint64_t get_base_fee(uint32_t priority);
|
||||
uint64_t get_base_fee();
|
||||
@@ -1645,6 +1681,7 @@ private:
|
||||
void thaw(const crypto::key_image &ki);
|
||||
bool frozen(const crypto::key_image &ki) const;
|
||||
bool frozen(const transfer_details &td) const;
|
||||
bool frozen(const multisig_tx_set& txs) const; // does partially signed txset contain frozen enotes?
|
||||
|
||||
bool save_to_file(const std::string& path_to_file, const std::string& binary, bool is_printable = false) const;
|
||||
static bool load_from_file(const std::string& path_to_file, std::string& target_str, size_t max_size = 1000000000);
|
||||
@@ -1700,18 +1737,24 @@ private:
|
||||
*/
|
||||
bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password);
|
||||
bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password, boost::optional<crypto::chacha_key>& keys_to_encrypt);
|
||||
void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
|
||||
void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL, bool ignore_callbacks = false);
|
||||
bool should_skip_block(const cryptonote::block &b, uint64_t height) const;
|
||||
void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
|
||||
void detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
|
||||
detached_blockchain_data detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
|
||||
void handle_reorg(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
|
||||
void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const;
|
||||
bool clear();
|
||||
void clear_soft(bool keep_key_images=false);
|
||||
void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t ¤t_height);
|
||||
void pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t ¤t_height);
|
||||
void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes);
|
||||
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force = false);
|
||||
void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &last, bool &error, std::exception_ptr &exception);
|
||||
void pull_and_parse_next_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &last, bool &error, std::exception_ptr &exception);
|
||||
void process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
|
||||
bool accept_pool_tx_for_processing(const crypto::hash &txid);
|
||||
void process_unconfirmed_transfer(bool incremental, const crypto::hash &txid, wallet2::unconfirmed_transfer_details &tx_details, bool seen_in_pool, std::chrono::system_clock::time_point now, bool refreshed);
|
||||
void process_pool_info_extent(const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response &res, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed);
|
||||
void update_pool_state_by_pool_query(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed = false);
|
||||
void update_pool_state_from_pool_data(bool incremental, const std::vector<crypto::hash> &removed_pool_txids, const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &added_pool_txs, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed);
|
||||
uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers) const;
|
||||
bool prepare_file_names(const std::string& file_path);
|
||||
void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height);
|
||||
@@ -1754,6 +1797,9 @@ private:
|
||||
crypto::chacha_key get_ringdb_key();
|
||||
void setup_keys(const epee::wipeable_string &password);
|
||||
size_t get_transfer_details(const crypto::key_image &ki) const;
|
||||
tx_entry_data get_tx_entries(const std::unordered_set<crypto::hash> &txids);
|
||||
void sort_scan_tx_entries(std::vector<process_tx_entry_t> &unsorted_tx_entries);
|
||||
void process_scan_txs(const tx_entry_data &txs_to_scan, const tx_entry_data &txs_to_reprocess, const std::unordered_set<crypto::hash> &tx_hashes_to_reprocess, detached_blockchain_data &dbd);
|
||||
|
||||
void register_devices();
|
||||
hw::device& lookup_device(const std::string & device_descriptor);
|
||||
@@ -1846,6 +1892,11 @@ private:
|
||||
// If m_refresh_from_block_height is explicitly set to zero we need this to differentiate it from the case that
|
||||
// m_refresh_from_block_height was defaulted to zero.*/
|
||||
bool m_explicit_refresh_from_block_height;
|
||||
uint64_t m_pool_info_query_time;
|
||||
std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> m_process_pool_txs;
|
||||
uint64_t m_skip_to_height;
|
||||
// m_skip_to_height is useful when we don't want to modify the wallet's restore height.
|
||||
// m_refresh_from_block_height is also a wallet's restore height which should remain constant unless explicitly modified by the user.
|
||||
bool m_confirm_non_default_ring_size;
|
||||
AskPasswordType m_ask_password;
|
||||
uint64_t m_max_reorg_depth;
|
||||
|
||||
@@ -85,6 +85,8 @@ namespace tools
|
||||
// tx_too_big
|
||||
// zero_amount
|
||||
// zero_destination
|
||||
// subtract_fee_from_bad_index
|
||||
// nonzero_unlock_time
|
||||
// wallet_rpc_error *
|
||||
// daemon_busy
|
||||
// no_connection_to_daemon
|
||||
@@ -93,6 +95,8 @@ namespace tools
|
||||
// get_output_distribution
|
||||
// payment_required
|
||||
// wallet_files_doesnt_correspond
|
||||
// scan_tx_error *
|
||||
// wont_reprocess_recent_txs_via_untrusted_daemon
|
||||
//
|
||||
// * - class with protected ctor
|
||||
|
||||
@@ -589,20 +593,17 @@ namespace tools
|
||||
std::string && loc
|
||||
, sources_t const & sources
|
||||
, destinations_t const & destinations
|
||||
, uint64_t unlock_time
|
||||
, cryptonote::network_type nettype
|
||||
)
|
||||
: transfer_error(std::move(loc), "transaction was not constructed")
|
||||
, m_sources(sources)
|
||||
, m_destinations(destinations)
|
||||
, m_unlock_time(unlock_time)
|
||||
, m_nettype(nettype)
|
||||
{
|
||||
}
|
||||
|
||||
const sources_t& sources() const { return m_sources; }
|
||||
const destinations_t& destinations() const { return m_destinations; }
|
||||
uint64_t unlock_time() const { return m_unlock_time; }
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
@@ -634,15 +635,12 @@ namespace tools
|
||||
cryptonote::print_money(dst.amount);
|
||||
}
|
||||
|
||||
ss << "\nunlock_time: " << m_unlock_time;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
sources_t m_sources;
|
||||
destinations_t m_destinations;
|
||||
uint64_t m_unlock_time;
|
||||
cryptonote::network_type m_nettype;
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -777,6 +775,23 @@ namespace tools
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct subtract_fee_from_bad_index : public transfer_error
|
||||
{
|
||||
explicit subtract_fee_from_bad_index(std::string&& loc, long bad_index)
|
||||
: transfer_error(std::move(loc),
|
||||
"subtractfeefrom: bad index: " + std::to_string(bad_index) + " (indexes are 0-based)")
|
||||
{
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct nonzero_unlock_time : public transfer_error
|
||||
{
|
||||
explicit nonzero_unlock_time(std::string&& loc)
|
||||
: transfer_error(std::move(loc), "transaction cannot have non-zero unlock time")
|
||||
{
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct wallet_rpc_error : public wallet_logic_error
|
||||
{
|
||||
const std::string& request() const { return m_request; }
|
||||
@@ -915,6 +930,23 @@ namespace tools
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct scan_tx_error : public wallet_logic_error
|
||||
{
|
||||
protected:
|
||||
explicit scan_tx_error(std::string&& loc, const std::string& message)
|
||||
: wallet_logic_error(std::move(loc), message)
|
||||
{
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct wont_reprocess_recent_txs_via_untrusted_daemon : public scan_tx_error
|
||||
{
|
||||
explicit wont_reprocess_recent_txs_via_untrusted_daemon(std::string&& loc)
|
||||
: scan_tx_error(std::move(loc), "The wallet has already seen 1 or more recent transactions than the scanned tx")
|
||||
{
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ using namespace epee;
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
|
||||
|
||||
#define DEFAULT_AUTO_REFRESH_PERIOD 20 // seconds
|
||||
#define REFRESH_INFICATIVE_BLOCK_CHUNK_SIZE 256 // just to split refresh in separate calls to play nicer with other threads
|
||||
|
||||
#define CHECK_MULTISIG_ENABLED() \
|
||||
do \
|
||||
@@ -79,6 +80,7 @@ namespace
|
||||
const command_line::arg_descriptor<bool> arg_restricted = {"restricted-rpc", "Restricts to view-only commands", false};
|
||||
const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
|
||||
const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false};
|
||||
const command_line::arg_descriptor<bool> arg_no_initial_sync = {"no-initial-sync", "Skips the initial sync before listening for connections", false};
|
||||
|
||||
constexpr const char default_rpc_username[] = "monero";
|
||||
|
||||
@@ -149,12 +151,17 @@ namespace tools
|
||||
return true;
|
||||
if (boost::posix_time::microsec_clock::universal_time() < m_last_auto_refresh_time + boost::posix_time::seconds(m_auto_refresh_period))
|
||||
return true;
|
||||
uint64_t blocks_fetched = 0;
|
||||
try {
|
||||
if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon());
|
||||
bool received_money = false;
|
||||
if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon(), 0, blocks_fetched, received_money, true, true, REFRESH_INFICATIVE_BLOCK_CHUNK_SIZE);
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_ERROR("Exception at while refreshing, what=" << ex.what());
|
||||
}
|
||||
m_last_auto_refresh_time = boost::posix_time::microsec_clock::universal_time();
|
||||
// if we got the max amount of blocks, do not set the last refresh time, we did only part of the refresh and will
|
||||
// continue asap, and only set the last refresh time once the refresh is actually finished
|
||||
if (blocks_fetched < REFRESH_INFICATIVE_BLOCK_CHUNK_SIZE)
|
||||
m_last_auto_refresh_time = boost::posix_time::microsec_clock::universal_time();
|
||||
return true;
|
||||
}, 1000);
|
||||
m_net_server.add_idle_handler([this](){
|
||||
@@ -238,7 +245,7 @@ namespace tools
|
||||
);
|
||||
|
||||
std::string temp = "monero-wallet-rpc." + bind_port + ".login";
|
||||
rpc_login_file = tools::private_file::create(temp);
|
||||
rpc_login_file = tools::private_file::drop_and_recreate(temp);
|
||||
if (!rpc_login_file.handle())
|
||||
{
|
||||
LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file"));
|
||||
@@ -981,9 +988,9 @@ namespace tools
|
||||
return amount;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
template<typename Ts, typename Tu, typename Tk>
|
||||
template<typename Ts, typename Tu, typename Tk, typename Ta>
|
||||
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, Tu &weight, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay,
|
||||
bool get_tx_key, Ts& tx_key, Tu &amount, Ta &amounts_by_dest, 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, Tk &spent_key_images, epee::json_rpc::error &er)
|
||||
{
|
||||
for (const auto & ptx : ptx_vector)
|
||||
@@ -1000,6 +1007,12 @@ namespace tools
|
||||
fill(fee, ptx.fee);
|
||||
fill(weight, cryptonote::get_transaction_weight(ptx.tx));
|
||||
|
||||
// add amounts by destination
|
||||
tools::wallet_rpc::amounts_list abd;
|
||||
for (const auto& dst : ptx.dests)
|
||||
abd.amounts.push_back(dst.amount);
|
||||
fill(amounts_by_dest, abd);
|
||||
|
||||
// add spent key images
|
||||
tools::wallet_rpc::key_image_list key_image_list;
|
||||
bool all_are_txin_to_key = std::all_of(ptx.tx.vin.begin(), ptx.tx.vin.end(), [&](const cryptonote::txin_v& s_e) -> bool
|
||||
@@ -1067,6 +1080,12 @@ namespace tools
|
||||
er.message = "Command unavailable in restricted mode.";
|
||||
return false;
|
||||
}
|
||||
else if (req.unlock_time)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME;
|
||||
er.message = "Transaction cannot have non-zero unlock time";
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_MULTISIG_ENABLED();
|
||||
|
||||
@@ -1080,7 +1099,7 @@ namespace tools
|
||||
{
|
||||
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_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices, req.subtract_fee_from_outputs);
|
||||
|
||||
if (ptx_vector.empty())
|
||||
{
|
||||
@@ -1097,7 +1116,7 @@ namespace tools
|
||||
return false;
|
||||
}
|
||||
|
||||
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,
|
||||
return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.amounts_by_dest, 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, res.spent_key_images, er);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
@@ -1121,6 +1140,12 @@ namespace tools
|
||||
er.message = "Command unavailable in restricted mode.";
|
||||
return false;
|
||||
}
|
||||
else if (req.unlock_time)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME;
|
||||
er.message = "Transaction cannot have non-zero unlock time";
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_MULTISIG_ENABLED();
|
||||
|
||||
@@ -1135,7 +1160,7 @@ namespace tools
|
||||
uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
|
||||
uint32_t priority = m_wallet->adjust_priority(req.priority);
|
||||
LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices);
|
||||
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
|
||||
|
||||
if (ptx_vector.empty())
|
||||
@@ -1145,7 +1170,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.weight_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.amounts_by_dest_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, res.spent_key_images_list, er);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
@@ -1534,7 +1559,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.weight_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.amounts_by_dest_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, res.spent_key_images_list, er);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
@@ -1557,6 +1582,12 @@ namespace tools
|
||||
er.message = "Command unavailable in restricted mode.";
|
||||
return false;
|
||||
}
|
||||
else if (req.unlock_time)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME;
|
||||
er.message = "Transaction cannot have non-zero unlock time";
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_MULTISIG_ENABLED();
|
||||
|
||||
@@ -1592,9 +1623,9 @@ namespace tools
|
||||
{
|
||||
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, 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, 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.weight_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.amounts_by_dest_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, res.spent_key_images_list, er);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
@@ -1617,6 +1648,12 @@ namespace tools
|
||||
er.message = "Command unavailable in restricted mode.";
|
||||
return false;
|
||||
}
|
||||
else if (req.unlock_time)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME;
|
||||
er.message = "Transaction cannot have non-zero unlock time";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req.outputs < 1)
|
||||
{
|
||||
@@ -1649,7 +1686,7 @@ namespace tools
|
||||
{
|
||||
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_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra);
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, priority, extra);
|
||||
|
||||
if (ptx_vector.empty())
|
||||
{
|
||||
@@ -1671,7 +1708,7 @@ namespace tools
|
||||
return false;
|
||||
}
|
||||
|
||||
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,
|
||||
return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.amounts_by_dest, 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, res.spent_key_images, er);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
@@ -3167,7 +3204,7 @@ namespace tools
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<crypto::hash> txids;
|
||||
std::unordered_set<crypto::hash> txids;
|
||||
std::list<std::string>::const_iterator i = req.txids.begin();
|
||||
while (i != req.txids.end())
|
||||
{
|
||||
@@ -3180,11 +3217,15 @@ namespace tools
|
||||
}
|
||||
|
||||
crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
|
||||
txids.push_back(txid);
|
||||
txids.insert(txid);
|
||||
}
|
||||
|
||||
try {
|
||||
m_wallet->scan_tx(txids);
|
||||
} catch (const tools::error::wont_reprocess_recent_txs_via_untrusted_daemon &e) {
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = e.what() + std::string(". Either connect to a trusted daemon or rescan the chain.");
|
||||
return false;
|
||||
} catch (const std::exception &e) {
|
||||
handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
|
||||
return false;
|
||||
@@ -3802,7 +3843,7 @@ namespace tools
|
||||
std::string old_language;
|
||||
|
||||
// check the given seed
|
||||
{
|
||||
if (!req.enable_multisig_experimental) {
|
||||
if (!crypto::ElectrumWords::words_to_bytes(req.seed, recovery_key, old_language))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
@@ -3825,6 +3866,13 @@ namespace tools
|
||||
|
||||
// process seed_offset if given
|
||||
{
|
||||
if (req.enable_multisig_experimental && !req.seed_offset.empty())
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Multisig seeds are not compatible with seed offsets";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!req.seed_offset.empty())
|
||||
{
|
||||
recovery_key = cryptonote::decrypt_key(recovery_key, req.seed_offset);
|
||||
@@ -3888,7 +3936,27 @@ namespace tools
|
||||
crypto::secret_key recovery_val;
|
||||
try
|
||||
{
|
||||
recovery_val = wal->generate(wallet_file, std::move(rc.second).password(), recovery_key, true, false, false);
|
||||
if (req.enable_multisig_experimental)
|
||||
{
|
||||
// Parse multisig seed into raw multisig data
|
||||
epee::wipeable_string multisig_data;
|
||||
multisig_data.resize(req.seed.size() / 2);
|
||||
if (!epee::from_hex::to_buffer(epee::to_mut_byte_span(multisig_data), req.seed))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Multisig seed not represented as hexadecimal string";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate multisig wallet
|
||||
wal->generate(wallet_file, std::move(rc.second).password(), multisig_data, false);
|
||||
wal->enable_multisig(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate normal wallet
|
||||
recovery_val = wal->generate(wallet_file, std::move(rc.second).password(), recovery_key, true, false, false);
|
||||
}
|
||||
MINFO("Wallet has been restored.\n");
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
@@ -3899,7 +3967,7 @@ namespace tools
|
||||
|
||||
// // Convert the secret key back to seed
|
||||
epee::wipeable_string electrum_words;
|
||||
if (!crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language))
|
||||
if (!req.enable_multisig_experimental && !crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = "Failed to encode seed";
|
||||
@@ -4521,6 +4589,7 @@ public:
|
||||
const auto password_file = command_line::get_arg(vm, arg_password_file);
|
||||
const auto prompt_for_password = command_line::get_arg(vm, arg_prompt_for_password);
|
||||
const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
|
||||
const auto no_initial_sync = command_line::get_arg(vm, arg_no_initial_sync);
|
||||
|
||||
if(!wallet_file.empty() && !from_json.empty())
|
||||
{
|
||||
@@ -4589,7 +4658,8 @@ public:
|
||||
|
||||
try
|
||||
{
|
||||
wal->refresh(wal->is_trusted_daemon());
|
||||
if (!no_initial_sync)
|
||||
wal->refresh(wal->is_trusted_daemon());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@@ -4700,6 +4770,7 @@ int main(int argc, char** argv) {
|
||||
command_line::add_arg(desc_params, arg_wallet_dir);
|
||||
command_line::add_arg(desc_params, arg_prompt_for_password);
|
||||
command_line::add_arg(desc_params, arg_rpc_client_secret_key);
|
||||
command_line::add_arg(desc_params, arg_no_initial_sync);
|
||||
|
||||
daemonizer::init_options(hidden_options, desc_params);
|
||||
desc_params.add(hidden_options);
|
||||
|
||||
@@ -263,9 +263,9 @@ namespace tools
|
||||
bool not_open(epee::json_rpc::error& er);
|
||||
void handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code);
|
||||
|
||||
template<typename Ts, typename Tu, typename Tk>
|
||||
template<typename Ts, typename Tu, typename Tk, typename Ta>
|
||||
bool fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
|
||||
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,
|
||||
bool get_tx_key, Ts& tx_key, Tu &amount, Ta &amounts_by_dest, 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, Tk &spent_key_images, 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);
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
// advance which version they will stop working with
|
||||
// Don't go over 32767 for any of these
|
||||
#define WALLET_RPC_VERSION_MAJOR 1
|
||||
#define WALLET_RPC_VERSION_MINOR 26
|
||||
#define WALLET_RPC_VERSION_MINOR 27
|
||||
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
|
||||
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
|
||||
namespace tools
|
||||
@@ -530,6 +530,46 @@ namespace wallet_rpc
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct amounts_list
|
||||
{
|
||||
std::list<uint64_t> amounts;
|
||||
|
||||
bool operator==(const amounts_list& other) const { return amounts == other.amounts; }
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(amounts)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct single_transfer_response
|
||||
{
|
||||
std::string tx_hash;
|
||||
std::string tx_key;
|
||||
uint64_t amount;
|
||||
amounts_list amounts_by_dest;
|
||||
uint64_t fee;
|
||||
uint64_t weight;
|
||||
std::string tx_blob;
|
||||
std::string tx_metadata;
|
||||
std::string multisig_txset;
|
||||
std::string unsigned_txset;
|
||||
key_image_list spent_key_images;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash)
|
||||
KV_SERIALIZE(tx_key)
|
||||
KV_SERIALIZE(amount)
|
||||
KV_SERIALIZE_OPT(amounts_by_dest, decltype(amounts_by_dest)())
|
||||
KV_SERIALIZE(fee)
|
||||
KV_SERIALIZE(weight)
|
||||
KV_SERIALIZE(tx_blob)
|
||||
KV_SERIALIZE(tx_metadata)
|
||||
KV_SERIALIZE(multisig_txset)
|
||||
KV_SERIALIZE(unsigned_txset)
|
||||
KV_SERIALIZE(spent_key_images)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_TRANSFER
|
||||
{
|
||||
struct request_t
|
||||
@@ -537,6 +577,7 @@ namespace wallet_rpc
|
||||
std::list<transfer_destination> destinations;
|
||||
uint32_t account_index;
|
||||
std::set<uint32_t> subaddr_indices;
|
||||
std::set<uint32_t> subtract_fee_from_outputs;
|
||||
uint32_t priority;
|
||||
uint64_t ring_size;
|
||||
uint64_t unlock_time;
|
||||
@@ -550,6 +591,7 @@ namespace wallet_rpc
|
||||
KV_SERIALIZE(destinations)
|
||||
KV_SERIALIZE(account_index)
|
||||
KV_SERIALIZE(subaddr_indices)
|
||||
KV_SERIALIZE_OPT(subtract_fee_from_outputs, decltype(subtract_fee_from_outputs)())
|
||||
KV_SERIALIZE(priority)
|
||||
KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
|
||||
KV_SERIALIZE(unlock_time)
|
||||
@@ -562,35 +604,39 @@ namespace wallet_rpc
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct response_t
|
||||
{
|
||||
std::string tx_hash;
|
||||
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;
|
||||
std::string unsigned_txset;
|
||||
key_image_list spent_key_images;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash)
|
||||
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)
|
||||
KV_SERIALIZE(unsigned_txset)
|
||||
KV_SERIALIZE(spent_key_images)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef single_transfer_response response_t;
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct split_transfer_response
|
||||
{
|
||||
std::list<std::string> tx_hash_list;
|
||||
std::list<std::string> tx_key_list;
|
||||
std::list<uint64_t> amount_list;
|
||||
std::list<amounts_list> amounts_by_dest_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;
|
||||
std::string unsigned_txset;
|
||||
std::list<key_image_list> spent_key_images_list;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash_list)
|
||||
KV_SERIALIZE(tx_key_list)
|
||||
KV_SERIALIZE(amount_list)
|
||||
KV_SERIALIZE_OPT(amounts_by_dest_list, decltype(amounts_by_dest_list)())
|
||||
KV_SERIALIZE(fee_list)
|
||||
KV_SERIALIZE(weight_list)
|
||||
KV_SERIALIZE(tx_blob_list)
|
||||
KV_SERIALIZE(tx_metadata_list)
|
||||
KV_SERIALIZE(multisig_txset)
|
||||
KV_SERIALIZE(unsigned_txset)
|
||||
KV_SERIALIZE(spent_key_images_list)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_TRANSFER_SPLIT
|
||||
{
|
||||
struct request_t
|
||||
@@ -623,41 +669,7 @@ namespace wallet_rpc
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct key_list
|
||||
{
|
||||
std::list<std::string> keys;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(keys)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response_t
|
||||
{
|
||||
std::list<std::string> tx_hash_list;
|
||||
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;
|
||||
std::string unsigned_txset;
|
||||
std::list<key_image_list> spent_key_images_list;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash_list)
|
||||
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)
|
||||
KV_SERIALIZE(unsigned_txset)
|
||||
KV_SERIALIZE(spent_key_images_list)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef split_transfer_response response_t;
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
@@ -821,41 +833,7 @@ namespace wallet_rpc
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct key_list
|
||||
{
|
||||
std::list<std::string> keys;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(keys)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response_t
|
||||
{
|
||||
std::list<std::string> tx_hash_list;
|
||||
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;
|
||||
std::string unsigned_txset;
|
||||
std::list<key_image_list> spent_key_images_list;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash_list)
|
||||
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)
|
||||
KV_SERIALIZE(unsigned_txset)
|
||||
KV_SERIALIZE(spent_key_images_list)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef split_transfer_response response_t;
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
@@ -897,41 +875,7 @@ namespace wallet_rpc
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct key_list
|
||||
{
|
||||
std::list<std::string> keys;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(keys)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response_t
|
||||
{
|
||||
std::list<std::string> tx_hash_list;
|
||||
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;
|
||||
std::string unsigned_txset;
|
||||
std::list<key_image_list> spent_key_images_list;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash_list)
|
||||
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)
|
||||
KV_SERIALIZE(unsigned_txset)
|
||||
KV_SERIALIZE(spent_key_images_list)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef split_transfer_response response_t;
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
@@ -967,32 +911,7 @@ namespace wallet_rpc
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct response_t
|
||||
{
|
||||
std::string tx_hash;
|
||||
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;
|
||||
std::string unsigned_txset;
|
||||
key_image_list spent_key_images;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(tx_hash)
|
||||
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)
|
||||
KV_SERIALIZE(unsigned_txset)
|
||||
KV_SERIALIZE(spent_key_images)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef single_transfer_response response_t;
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
@@ -2360,6 +2279,7 @@ namespace wallet_rpc
|
||||
std::string password;
|
||||
std::string language;
|
||||
bool autosave_current;
|
||||
bool enable_multisig_experimental;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_OPT(restore_height, (uint64_t)0)
|
||||
@@ -2369,6 +2289,7 @@ namespace wallet_rpc
|
||||
KV_SERIALIZE(password)
|
||||
KV_SERIALIZE(language)
|
||||
KV_SERIALIZE_OPT(autosave_current, true)
|
||||
KV_SERIALIZE_OPT(enable_multisig_experimental, false)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
@@ -79,3 +79,5 @@
|
||||
#define WALLET_RPC_ERROR_CODE_ZERO_AMOUNT -46
|
||||
#define WALLET_RPC_ERROR_CODE_INVALID_SIGNATURE_TYPE -47
|
||||
#define WALLET_RPC_ERROR_CODE_DISABLED -48
|
||||
#define WALLET_RPC_ERROR_CODE_PROXY_ALREADY_DEFINED -49
|
||||
#define WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME -50
|
||||
|
||||
@@ -72,14 +72,8 @@ else ()
|
||||
include_directories(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/gtest/include")
|
||||
endif (GTest_FOUND)
|
||||
|
||||
file(COPY
|
||||
data/wallet_9svHk1.keys
|
||||
data/wallet_9svHk1
|
||||
data/outputs
|
||||
data/unsigned_monero_tx
|
||||
data/signed_monero_tx
|
||||
data/sha256sum
|
||||
DESTINATION data)
|
||||
message(STATUS "Copying test data directory...")
|
||||
file(COPY data DESTINATION .) # Copy data directory from source root to build root
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "fuzz" OR OSSFUZZ)
|
||||
add_subdirectory(fuzz)
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ Functional tests are located under the `tests/functional_tests` directory.
|
||||
|
||||
Building all the tests requires installing the following dependencies:
|
||||
```bash
|
||||
pip install requests psutil monotonic zmq
|
||||
pip install requests psutil monotonic zmq deepdiff
|
||||
```
|
||||
|
||||
First, run a regtest daemon in the offline mode and with a fixed difficulty:
|
||||
|
||||
@@ -350,7 +350,7 @@ bool gen_block_miner_tx_has_2_in::generate(std::vector<test_event_entry>& events
|
||||
destinations.push_back(de);
|
||||
|
||||
transaction tmp_tx;
|
||||
if (!construct_tx(miner_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tmp_tx, 0))
|
||||
if (!construct_tx(miner_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tmp_tx))
|
||||
return false;
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
|
||||
@@ -393,7 +393,7 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector<test_event_entry>
|
||||
destinations.push_back(de);
|
||||
|
||||
transaction tmp_tx;
|
||||
if (!construct_tx(miner_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tmp_tx, 0))
|
||||
if (!construct_tx(miner_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tmp_tx))
|
||||
return false;
|
||||
|
||||
MAKE_MINER_TX_MANUALLY(miner_tx, blk_1);
|
||||
|
||||
@@ -136,7 +136,7 @@ bool gen_bpp_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
|
||||
rct_txes.resize(rct_txes.size() + 1);
|
||||
bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), 0, tx_key, additional_tx_keys, true, rct_config[n]);
|
||||
bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
|
||||
|
||||
if (post_tx && !post_tx(rct_txes.back(), n))
|
||||
|
||||
@@ -136,7 +136,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
|
||||
rct_txes.resize(rct_txes.size() + 1);
|
||||
bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), 0, tx_key, additional_tx_keys, true, rct_config[n]);
|
||||
bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), tx_key, additional_tx_keys, true, rct_config[n]);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
|
||||
|
||||
if (post_tx && !post_tx(rct_txes.back(), n))
|
||||
|
||||
@@ -1028,7 +1028,7 @@ bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote
|
||||
vector<tx_destination_entry> destinations;
|
||||
fill_tx_sources_and_destinations(events, blk_head, from, get_address(to), amount, fee, nmix, sources, destinations);
|
||||
|
||||
return construct_tx_rct(from.get_keys(), sources, destinations, from.get_keys().m_account_address, std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version);
|
||||
return construct_tx_rct(from.get_keys(), sources, destinations, from.get_keys().m_account_address, std::vector<uint8_t>(), tx, rct, range_proof_type, bp_version);
|
||||
}
|
||||
|
||||
bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx, const cryptonote::block& blk_head,
|
||||
@@ -1044,7 +1044,7 @@ bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote
|
||||
|
||||
fill_tx_destinations(from, destinations, fee, sources, destinations_all, false);
|
||||
|
||||
return construct_tx_rct(from.get_keys(), sources, destinations_all, get_address(from), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version);
|
||||
return construct_tx_rct(from.get_keys(), sources, destinations_all, get_address(from), std::vector<uint8_t>(), tx, rct, range_proof_type, bp_version);
|
||||
}
|
||||
|
||||
bool construct_tx_to_key(cryptonote::transaction& tx,
|
||||
@@ -1054,7 +1054,7 @@ bool construct_tx_to_key(cryptonote::transaction& tx,
|
||||
{
|
||||
vector<tx_destination_entry> destinations;
|
||||
fill_tx_destinations(from, get_address(to), amount, fee, sources, destinations, rct);
|
||||
return construct_tx_rct(from.get_keys(), sources, destinations, get_address(from), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version);
|
||||
return construct_tx_rct(from.get_keys(), sources, destinations, get_address(from), std::vector<uint8_t>(), tx, rct, range_proof_type, bp_version);
|
||||
}
|
||||
|
||||
bool construct_tx_to_key(cryptonote::transaction& tx,
|
||||
@@ -1065,10 +1065,10 @@ bool construct_tx_to_key(cryptonote::transaction& tx,
|
||||
{
|
||||
vector<tx_destination_entry> all_destinations;
|
||||
fill_tx_destinations(from, destinations, fee, sources, all_destinations, rct);
|
||||
return construct_tx_rct(from.get_keys(), sources, all_destinations, get_address(from), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version);
|
||||
return construct_tx_rct(from.get_keys(), sources, all_destinations, get_address(from), std::vector<uint8_t>(), tx, rct, range_proof_type, bp_version);
|
||||
}
|
||||
|
||||
bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys, std::vector<cryptonote::tx_source_entry>& sources, const std::vector<cryptonote::tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time, bool rct, rct::RangeProofType range_proof_type, int bp_version)
|
||||
bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys, std::vector<cryptonote::tx_source_entry>& sources, const std::vector<cryptonote::tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, cryptonote::transaction& tx, bool rct, rct::RangeProofType range_proof_type, int bp_version)
|
||||
{
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||
subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0, 0};
|
||||
@@ -1076,7 +1076,7 @@ bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys, std::
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
std::vector<tx_destination_entry> destinations_copy = destinations;
|
||||
rct::RCTConfig rct_config = {range_proof_type, bp_version};
|
||||
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config);
|
||||
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, tx_key, additional_tx_keys, rct, rct_config);
|
||||
}
|
||||
|
||||
transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const block& blk_head,
|
||||
|
||||
@@ -450,7 +450,7 @@ bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys,
|
||||
std::vector<cryptonote::tx_source_entry>& sources,
|
||||
const std::vector<cryptonote::tx_destination_entry>& destinations,
|
||||
const boost::optional<cryptonote::account_public_address>& change_addr,
|
||||
std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time,
|
||||
std::vector<uint8_t> extra, cryptonote::transaction& tx,
|
||||
bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0);
|
||||
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ bool gen_double_spend_in_tx<txs_keeped_by_block>::generate(std::vector<test_even
|
||||
destinations.push_back(de);
|
||||
|
||||
cryptonote::transaction tx_1;
|
||||
if (!construct_tx(bob_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tx_1, 0))
|
||||
if (!construct_tx(bob_account.get_keys(), sources, destinations, boost::none, std::vector<uint8_t>(), tx_1))
|
||||
return false;
|
||||
|
||||
SET_EVENT_VISITOR_SETT(events, txs_keeped_by_block ? event_visitor_settings::set_txs_keeped_by_block : 0);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user