Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99be9a044f | |||
| 75bec6336a | |||
| 68a73a2b4d | |||
| dc18efa3d7 | |||
| d8e39bd381 | |||
| 0f15707077 | |||
| f38b07335a | |||
| 057a77a09f | |||
| 573a369a8a | |||
| 988056dc09 | |||
| ce4ef6da96 | |||
| 15384cc135 | |||
| f456b3f023 | |||
| 480b050cc3 | |||
| c48f572e46 | |||
| f73a2b1587 | |||
| 1d20b8171d | |||
| b0ea7cb528 | |||
| 153819fc4c | |||
| cbc5cf43f2 | |||
| 1976c9afa0 | |||
| 29208a33cb | |||
| cbc297acfb | |||
| f698f2b708 | |||
| 8cb10345d6 | |||
| 47b41eabfe | |||
| abd37fcf5b | |||
| cce309512c | |||
| f5d701c550 | |||
| d05298358c | |||
| 5973985148 | |||
| 2c2432245f | |||
| 38d4811c89 | |||
| cbf636c7a9 | |||
| 8c53995a88 | |||
| 146cac02ca | |||
| 02b24cb353 | |||
| e6f9c0013b | |||
| 5d4ace8cae | |||
| 1bb5d25e31 | |||
| 85c9fe515d | |||
| 401dda5f10 | |||
| 66184f3085 | |||
| 853171bbf0 | |||
| 1f27fdf6a5 | |||
| 0bef4265ac | |||
| a7b0c93c7d | |||
| 9885b8b0f2 | |||
| fdb31856dd | |||
| d7445b576f | |||
| 802c4bb0e4 | |||
| 5ffa31c48e | |||
| 1cd21bfba5 | |||
| 7d358cdb1f | |||
| 09402d0edb | |||
| 07f8e9e891 | |||
| 6adf03cdc5 | |||
| 7cbae6ca98 | |||
| 0be63cffa8 | |||
| 864a78ee5f | |||
| af4f97bf66 | |||
| 89f3d46120 | |||
| 3178bbe083 | |||
| 4b6a3fbaa0 | |||
| 6b9bbadd6b | |||
| 12e7c4188e | |||
| e29b2e9997 | |||
| 1d3657afb5 | |||
| 0cbf5571d3 | |||
| 848a0c05b0 | |||
| 625147e577 | |||
| b03d7091f7 | |||
| 93db74a91e | |||
| 4b7eb573b2 | |||
| 5b98bebad1 | |||
| 0de1571abd | |||
| 09ee78197c | |||
| 401f5d967b | |||
| 3679925331 | |||
| fc8a5d68f1 | |||
| 57e57c239b | |||
| 876c08db53 |
@@ -36,7 +36,7 @@ jobs:
|
||||
key: ccache-${{ runner.os }}-build-${{ github.sha }}
|
||||
restore-keys: ccache-${{ runner.os }}-build-
|
||||
- name: install dependencies
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi openssl zmq libpgm miniupnpc ldns expat libunwind-headers protobuf ccache
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi openssl zmq libpgm miniupnpc expat libunwind-headers protobuf ccache
|
||||
- name: build
|
||||
run: |
|
||||
${{env.CCACHE_SETTINGS}}
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
CCACHE_TEMPDIR: /tmp/.ccache-temp
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, ubuntu-18.04]
|
||||
os: [ubuntu-22.04, ubuntu-20.04]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
@@ -101,7 +101,7 @@ jobs:
|
||||
${{env.BUILD_DEFAULT_LINUX}}
|
||||
|
||||
libwallet-ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
CCACHE_TEMPDIR: /tmp/.ccache-temp
|
||||
steps:
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
|
||||
test-ubuntu:
|
||||
needs: build-ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
CCACHE_TEMPDIR: /tmp/.ccache-temp
|
||||
steps:
|
||||
@@ -155,6 +155,7 @@ jobs:
|
||||
- name: tests
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: ON
|
||||
DNS_PUBLIC: tcp://9.9.9.9
|
||||
run: |
|
||||
${{env.CCACHE_SETTINGS}}
|
||||
${{env.BUILD_DEFAULT_LINUX}}
|
||||
|
||||
@@ -18,7 +18,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build-cross:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
CCACHE_TEMPDIR: /tmp/.ccache-temp
|
||||
strategy:
|
||||
@@ -36,13 +36,13 @@ jobs:
|
||||
packages: "python3 gperf g++-aarch64-linux-gnu"
|
||||
- name: "i686 Win"
|
||||
host: "i686-w64-mingw32"
|
||||
packages: "python3 g++-mingw-w64-i686 qttools5-dev-tools"
|
||||
packages: "python3 g++-mingw-w64-i686"
|
||||
- name: "i686 Linux"
|
||||
host: "i686-pc-linux-gnu"
|
||||
packages: "gperf cmake g++-multilib python3-zmq"
|
||||
- name: "Win64"
|
||||
host: "x86_64-w64-mingw32"
|
||||
packages: "cmake python3 g++-mingw-w64-x86-64 qttools5-dev-tools"
|
||||
packages: "cmake python3 g++-mingw-w64-x86-64"
|
||||
- name: "x86_64 Linux"
|
||||
host: "x86_64-unknown-linux-gnu"
|
||||
packages: "gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
name: ci/gh-actions/gitian
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build-gitian:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
operating-system:
|
||||
- name: "Linux"
|
||||
option: "l"
|
||||
- name: "Windows"
|
||||
option: "w"
|
||||
- name: "Android"
|
||||
option: "a"
|
||||
- name: "FreeBSD"
|
||||
option: "f"
|
||||
- name: "macOS"
|
||||
option: "m"
|
||||
name: ${{ matrix.operating-system.name }}
|
||||
steps:
|
||||
- name: prepare
|
||||
run: |
|
||||
sudo apt update
|
||||
curl -O https://raw.githubusercontent.com/monero-project/monero/${{ github.ref_name }}/contrib/gitian/gitian-build.py
|
||||
chmod +x gitian-build.py
|
||||
- name: setup
|
||||
run: |
|
||||
./gitian-build.py --setup --docker github-actions ${{ github.ref_name }}
|
||||
- name: build
|
||||
run: |
|
||||
./gitian-build.py --docker --detach-sign --no-commit --build -j 3 -o ${{ matrix.operating-system.option }} github-actions ${{ github.ref_name }}
|
||||
- name: post build
|
||||
run: |
|
||||
cd out/${{ github.ref_name }}
|
||||
shasum -a256 *
|
||||
echo \`\`\` >> $GITHUB_STEP_SUMMARY
|
||||
shasum -a256 * >> $GITHUB_STEP_SUMMARY
|
||||
echo \`\`\` >> $GITHUB_STEP_SUMMARY
|
||||
- uses: actions/upload-artifact@v3.1.0
|
||||
with:
|
||||
name: ${{ matrix.operating-system.name }}
|
||||
path: |
|
||||
out/${{ github.ref_name }}/*
|
||||
+13
-1
@@ -794,7 +794,7 @@ else()
|
||||
set(USE_LTO_DEFAULT false)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,10485760")
|
||||
if(NOT BUILD_64)
|
||||
add_definitions(-DWINVER=0x0501 -D_WIN32_WINNT=0x0501)
|
||||
add_definitions(-DWINVER=0x0600 -D_WIN32_WINNT=0x0600)
|
||||
endif()
|
||||
endif()
|
||||
set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wold-style-definition -Wstrict-prototypes")
|
||||
@@ -1178,6 +1178,9 @@ find_library(NORM_LIBRARY norm)
|
||||
find_library(GSSAPI_LIBRARY gssapi_krb5)
|
||||
find_library(PROTOLIB_LIBRARY protolib)
|
||||
find_library(SODIUM_LIBRARY sodium)
|
||||
find_library(BSD_LIBRARY bsd)
|
||||
find_library(MD_LIBRARY md)
|
||||
find_library(PROTOKIT_LIBRARY protokit)
|
||||
|
||||
if(NOT ZMQ_INCLUDE_PATH)
|
||||
message(FATAL_ERROR "Could not find required header zmq.h")
|
||||
@@ -1200,6 +1203,15 @@ endif()
|
||||
if(SODIUM_LIBRARY)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${SODIUM_LIBRARY}")
|
||||
endif()
|
||||
if(BSD_LIBRARY)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${BSD_LIBRARY}")
|
||||
endif()
|
||||
if(MD_LIBRARY)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${MD_LIBRARY}")
|
||||
endif()
|
||||
if(PROTOKIT_LIBRARY)
|
||||
set(ZMQ_LIB "${ZMQ_LIB};${PROTOKIT_LIBRARY}")
|
||||
endif()
|
||||
|
||||
include(external/supercop/functions.cmake) # place after setting flags and before src directory inclusion
|
||||
add_subdirectory(contrib)
|
||||
|
||||
@@ -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.1.0 | ringsize = 16, bulletproofs+, view tags, adjusted dynamic block weight algorithm
|
||||
| 2689608 | 2022-08-14 | v16 | v0.18.0.0 | v0.18.1.0 | forbid old v14 transaction format
|
||||
| 2688888 | 2022-08-13 | v15 | v0.18.0.0 | v0.18.2.0 | ringsize = 16, bulletproofs+, view tags, adjusted dynamic block weight algorithm
|
||||
| 2689608 | 2022-08-14 | v16 | v0.18.0.0 | v0.18.2.0 | 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.
|
||||
@@ -178,7 +178,6 @@ library archives (`.a`).
|
||||
| libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | `libunwind-devel` | YES | Stack traces |
|
||||
| liblzma | any | NO | `liblzma-dev` | `xz` | `liblzma-devel` | `xz-devel` | YES | For libunwind |
|
||||
| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | `readline-devel` | YES | Input editing |
|
||||
| ldns | 1.6.17 | NO | `libldns-dev` | `ldns` | `libldns-devel` | `ldns-devel` | YES | SSL toolkit |
|
||||
| expat | 1.1 | NO | `libexpat1-dev` | `expat` | `expat-devel` | `expat-devel` | YES | XML parsing |
|
||||
| GTest | 1.5 | YES | `libgtest-dev`[1] | `gtest` | `gtest-devel` | `gtest-devel` | YES | Test suite |
|
||||
| ccache | any | NO | `ccache` | `ccache` | `ccache` | `ccache` | YES | Compil. cache |
|
||||
@@ -205,23 +204,23 @@ then:
|
||||
Install all dependencies at once on Debian/Ubuntu:
|
||||
|
||||
```
|
||||
sudo apt update && sudo apt install build-essential cmake pkg-config libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-locale-dev libboost-program-options-dev libboost-regex-dev libboost-serialization-dev libboost-system-dev libboost-thread-dev python3 ccache doxygen graphviz
|
||||
sudo apt update && sudo apt install build-essential cmake pkg-config libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-locale-dev libboost-program-options-dev libboost-regex-dev libboost-serialization-dev libboost-system-dev libboost-thread-dev python3 ccache doxygen graphviz
|
||||
```
|
||||
|
||||
Install all dependencies at once on Arch:
|
||||
```
|
||||
sudo pacman -Syu --needed base-devel cmake boost openssl zeromq libpgm unbound libsodium libunwind xz readline ldns expat gtest python3 ccache doxygen graphviz qt5-tools hidapi libusb protobuf systemd
|
||||
sudo pacman -Syu --needed base-devel cmake boost openssl zeromq libpgm unbound libsodium libunwind xz readline expat gtest python3 ccache doxygen graphviz qt5-tools hidapi libusb protobuf systemd
|
||||
```
|
||||
|
||||
Install all dependencies at once on Fedora:
|
||||
```
|
||||
sudo dnf install gcc gcc-c++ cmake pkgconf boost-devel openssl-devel zeromq-devel openpgm-devel unbound-devel libsodium-devel libunwind-devel xz-devel readline-devel ldns-devel expat-devel gtest-devel ccache doxygen graphviz qt5-linguist hidapi-devel libusbx-devel protobuf-devel protobuf-compiler systemd-devel
|
||||
sudo dnf install gcc gcc-c++ cmake pkgconf boost-devel openssl-devel zeromq-devel openpgm-devel unbound-devel libsodium-devel libunwind-devel xz-devel readline-devel expat-devel gtest-devel ccache doxygen graphviz qt5-linguist hidapi-devel libusbx-devel protobuf-devel protobuf-compiler systemd-devel
|
||||
```
|
||||
|
||||
Install all dependencies at once on openSUSE:
|
||||
|
||||
```
|
||||
sudo zypper ref && sudo zypper in cppzmq-devel ldns-devel libboost_chrono-devel libboost_date_time-devel libboost_filesystem-devel libboost_locale-devel libboost_program_options-devel libboost_regex-devel libboost_serialization-devel libboost_system-devel libboost_thread-devel libexpat-devel libminiupnpc-devel libsodium-devel libunwind-devel unbound-devel cmake doxygen ccache fdupes gcc-c++ libevent-devel libopenssl-devel pkgconf-pkg-config readline-devel xz-devel libqt5-qttools-devel patterns-devel-C-C++-devel_C_C++
|
||||
sudo zypper ref && sudo zypper in cppzmq-devel libboost_chrono-devel libboost_date_time-devel libboost_filesystem-devel libboost_locale-devel libboost_program_options-devel libboost_regex-devel libboost_serialization-devel libboost_system-devel libboost_thread-devel libexpat-devel libminiupnpc-devel libsodium-devel libunwind-devel unbound-devel cmake doxygen ccache fdupes gcc-c++ libevent-devel libopenssl-devel pkgconf-pkg-config readline-devel xz-devel libqt5-qttools-devel patterns-devel-C-C++-devel_C_C++
|
||||
```
|
||||
|
||||
Install all dependencies at once on macOS with the provided Brewfile:
|
||||
@@ -345,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.1.0
|
||||
git checkout v0.18.2.0
|
||||
```
|
||||
|
||||
* Build:
|
||||
@@ -464,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.1.0'. 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.2.0'. If you don't care about the version and just want binaries from master, skip this step:
|
||||
|
||||
```bash
|
||||
git checkout v0.18.1.0
|
||||
git checkout v0.18.2.0
|
||||
```
|
||||
|
||||
* If you are on a 64-bit system, run:
|
||||
|
||||
@@ -113,7 +113,7 @@ if ( LibUSB_FOUND )
|
||||
if (APPLE OR LibUSB_VERSION_1.0.16 OR STATIC)
|
||||
if (APPLE)
|
||||
if(DEPENDS)
|
||||
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES "-framework Foundation -framework IOKit")
|
||||
list(APPEND TEST_COMPILE_EXTRA_LIBRARIES "-framework Foundation -framework IOKit -framework Security")
|
||||
else()
|
||||
find_library(COREFOUNDATION CoreFoundation)
|
||||
find_library(IOKIT IOKit)
|
||||
|
||||
@@ -25,7 +25,6 @@ brew "unbound"
|
||||
brew "libsodium"
|
||||
brew "miniupnpc"
|
||||
brew "readline"
|
||||
brew "ldns"
|
||||
brew "expat"
|
||||
brew "ccache"
|
||||
brew "doxygen"
|
||||
|
||||
@@ -110,8 +110,7 @@ $(host_arch)_$(host_os)_id_string+=$(shell $(host_CXX) --version 2>/dev/null)
|
||||
$(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null)
|
||||
$(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null)
|
||||
|
||||
qt_packages_$(NO_QT) = $(qt_packages)
|
||||
packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_)
|
||||
packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages)
|
||||
native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages)
|
||||
|
||||
all_packages = $(packages) $(native_packages)
|
||||
|
||||
@@ -7,27 +7,12 @@ ac_tool_prefix=${host_alias}-
|
||||
if test -z $with_boost; then
|
||||
with_boost=$depends_prefix
|
||||
fi
|
||||
if test -z $with_qt_plugindir; then
|
||||
with_qt_plugindir=$depends_prefix/plugins
|
||||
fi
|
||||
if test -z $with_qt_translationdir; then
|
||||
with_qt_translationdir=$depends_prefix/translations
|
||||
fi
|
||||
|
||||
if test x@host_os@ = xdarwin; then
|
||||
BREW=no
|
||||
PORT=no
|
||||
fi
|
||||
|
||||
if test x@host_os@ = xmingw32; then
|
||||
if test -z $with_qt_incdir; then
|
||||
with_qt_incdir=$depends_prefix/include
|
||||
fi
|
||||
if test -z $with_qt_libdir; then
|
||||
with_qt_libdir=$depends_prefix/lib
|
||||
fi
|
||||
fi
|
||||
|
||||
PATH=$depends_prefix/native/bin:$PATH
|
||||
PKG_CONFIG="`which pkg-config` --static"
|
||||
|
||||
|
||||
@@ -143,8 +143,11 @@ $(1)_config_env+=PKG_CONFIG_PATH=$($($(1)_type)_prefix)/share/pkgconfig
|
||||
$(1)_config_env+=PATH="$(build_prefix)/bin:$(PATH)"
|
||||
$(1)_build_env+=PATH="$(build_prefix)/bin:$(PATH)"
|
||||
$(1)_stage_env+=PATH="$(build_prefix)/bin:$(PATH)"
|
||||
$(1)_autoconf=./configure --host=$($($(1)_type)_host) --disable-dependency-tracking --prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)"
|
||||
$(1)_autoconf=./configure --host=$($($(1)_type)_host) --prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)"
|
||||
|
||||
ifneq ($(1),libusb)
|
||||
$(1)_autoconf += --disable-dependency-tracking
|
||||
endif
|
||||
ifneq ($($(1)_nm),)
|
||||
$(1)_autoconf += NM="$$($(1)_nm)"
|
||||
endif
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package=hidapi
|
||||
$(package)_version=0.11.0
|
||||
$(package)_download_path=https://github.com/libusb/hidapi/archive
|
||||
$(package)_version=0.13.1
|
||||
$(package)_download_path=https://github.com/libusb/hidapi/archive/refs/tags
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=391d8e52f2d6a5cf76e2b0c079cfefe25497ba1d4659131297081fc0cd744632
|
||||
$(package)_sha256_hash=476a2c9a4dc7d1fc97dd223b84338dbea3809a84caea2dcd887d9778725490e3
|
||||
$(package)_linux_dependencies=libusb eudev
|
||||
$(package)_patches=missing_win_include.patch
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package=ldns
|
||||
$(package)_version=1.7.1
|
||||
$(package)_download_path=https://www.nlnetlabs.nl/downloads/$(package)/
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=8ac84c16bdca60e710eea75782356f3ac3b55680d40e1530d7cea474ac208229
|
||||
$(package)_dependencies=openssl
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--disable-shared --enable-static --with-drill
|
||||
$(package)_config_opts+=--with-ssl=$(host_prefix)
|
||||
$(package)_config_opts_release=--disable-debug-mode
|
||||
$(package)_config_opts_linux=--with-pic
|
||||
endef
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub .
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
$($(package)_autoconf)
|
||||
endef
|
||||
|
||||
define $(package)_build_cmds
|
||||
$(MAKE)
|
||||
endef
|
||||
|
||||
define $(package)_stage_cmds
|
||||
$(MAKE) DESTDIR=$($(package)_staging_dir) install-h install-lib
|
||||
endef
|
||||
|
||||
define $(package)_postprocess_cmds
|
||||
rm lib/*.la
|
||||
endef
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package=libusb
|
||||
$(package)_version=1.0.22
|
||||
$(package)_download_path=https://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-$($(package)_version)/
|
||||
$(package)_version=1.0.26
|
||||
$(package)_download_path=https://github.com/libusb/libusb/releases/download/v$($(package)_version)
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.bz2
|
||||
$(package)_sha256_hash=75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157
|
||||
$(package)_sha256_hash=12ce7a61fc9854d1d2a1ffe095f7b5fac19ddba095c259e6067a46500381b5a5
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
autoreconf -i
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package=openssl
|
||||
$(package)_version=1.1.1l
|
||||
$(package)_version=1.1.1t
|
||||
$(package)_download_path=https://www.openssl.org/source
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=0b7a3e5e59c34827fe0c3a74b7ec8baef302b98fa80088d7f9153aa16fa76bd1
|
||||
$(package)_patches=fix_darwin.patch
|
||||
$(package)_sha256_hash=8dee9b24bdb1dcbf0c3d1e9b02fb8f6bf22165e807f45adeb7c9677536859d3b
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_env=AR="$($(package)_ar)" ARFLAGS=$($(package)_arflags) RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)"
|
||||
@@ -50,8 +49,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 && \
|
||||
patch -p1 < $($(package)_patch_dir)/fix_darwin.patch
|
||||
sed -i.old 's|"engines", "apps", "test", "util", "tools", "fuzz"|"engines", "tools"|' Configure
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
packages:=boost openssl zeromq libiconv expat ldns unbound
|
||||
packages:=boost openssl zeromq libiconv expat unbound
|
||||
|
||||
# ccache is useless in gitian builds
|
||||
ifneq ($(GITIAN),1)
|
||||
@@ -20,7 +20,6 @@ freebsd_packages = ncurses readline sodium
|
||||
|
||||
linux_packages = eudev ncurses readline sodium $(hardware_packages)
|
||||
linux_native_packages = $(hardware_native_packages)
|
||||
qt_packages = qt
|
||||
|
||||
ifeq ($(build_tests),ON)
|
||||
packages += gtest
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
PACKAGE=qt
|
||||
$(package)_version=5.15.1
|
||||
$(package)_download_path=https://download.qt.io/official_releases/qt/5.15/$($(package)_version)/submodules
|
||||
$(package)_suffix=everywhere-src-$($(package)_version).tar.xz
|
||||
$(package)_file_name=qtbase-$($(package)_suffix)
|
||||
$(package)_sha256_hash=33960404d579675b7210de103ed06a72613bfc4305443e278e2d32a3eb1f3d8c
|
||||
$(package)_build_subdir=qtbase
|
||||
$(package)_qt_libs=corelib
|
||||
$(package)_patches=fix_qt_pkgconfig.patch fix_no_printer.patch fix_rcc_determinism.patch no-xlib.patch
|
||||
|
||||
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
|
||||
$(package)_qttranslations_sha256_hash=46e0c0e3a511fbcc803a4146204062e47f6ed43b34d98a3c27372a03b8746bd8
|
||||
|
||||
$(package)_qttools_file_name=qttools-$($(package)_suffix)
|
||||
$(package)_qttools_sha256_hash=c98ee5f0f980bf68cbf0c94d62434816a92441733de50bd9adbe9b9055f03498
|
||||
|
||||
$(package)_extra_sources = $($(package)_qttranslations_file_name)
|
||||
$(package)_extra_sources += $($(package)_qttools_file_name)
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts_release = -release
|
||||
$(package)_config_opts_debug = -debug
|
||||
$(package)_config_opts += -bindir $(build_prefix)/bin
|
||||
$(package)_config_opts += -c++std c++11
|
||||
$(package)_config_opts += -confirm-license
|
||||
$(package)_config_opts += -dbus-runtime
|
||||
$(package)_config_opts += -hostprefix $(build_prefix)
|
||||
$(package)_config_opts += -no-compile-examples
|
||||
$(package)_config_opts += -no-cups
|
||||
$(package)_config_opts += -no-egl
|
||||
$(package)_config_opts += -no-eglfs
|
||||
$(package)_config_opts += -no-evdev
|
||||
$(package)_config_opts += -no-gui
|
||||
$(package)_config_opts += -no-freetype
|
||||
$(package)_config_opts += -no-gif
|
||||
$(package)_config_opts += -no-glib
|
||||
$(package)_config_opts += -no-icu
|
||||
$(package)_config_opts += -no-ico
|
||||
$(package)_config_opts += -no-iconv
|
||||
$(package)_config_opts += -no-kms
|
||||
$(package)_config_opts += -no-linuxfb
|
||||
$(package)_config_opts += -no-libjpeg
|
||||
$(package)_config_opts += -no-libudev
|
||||
$(package)_config_opts += -no-mtdev
|
||||
$(package)_config_opts += -no-openvg
|
||||
$(package)_config_opts += -no-reduce-relocations
|
||||
$(package)_config_opts += -no-sql-db2
|
||||
$(package)_config_opts += -no-sql-ibase
|
||||
$(package)_config_opts += -no-sql-oci
|
||||
$(package)_config_opts += -no-sql-tds
|
||||
$(package)_config_opts += -no-sql-mysql
|
||||
$(package)_config_opts += -no-sql-odbc
|
||||
$(package)_config_opts += -no-sql-psql
|
||||
$(package)_config_opts += -no-sql-sqlite
|
||||
$(package)_config_opts += -no-sql-sqlite2
|
||||
$(package)_config_opts += -no-use-gold-linker
|
||||
$(package)_config_opts += -nomake examples
|
||||
$(package)_config_opts += -nomake tests
|
||||
$(package)_config_opts += -opensource
|
||||
$(package)_config_opts += -no-openssl
|
||||
$(package)_config_opts += -optimized-qmake
|
||||
$(package)_config_opts += -pch
|
||||
$(package)_config_opts += -pkg-config
|
||||
$(package)_config_opts += -prefix $(host_prefix)
|
||||
$(package)_config_opts += -no-libpng
|
||||
$(package)_config_opts += -qt-pcre
|
||||
$(package)_config_opts += -qt-harfbuzz
|
||||
$(package)_config_opts += -no-zlib
|
||||
$(package)_config_opts += -static
|
||||
$(package)_config_opts += -silent
|
||||
$(package)_config_opts += -v
|
||||
$(package)_config_opts += -no-feature-bearermanagement
|
||||
$(package)_config_opts += -no-feature-colordialog
|
||||
$(package)_config_opts += -no-feature-dial
|
||||
$(package)_config_opts += -no-feature-filesystemwatcher
|
||||
$(package)_config_opts += -no-feature-fontcombobox
|
||||
$(package)_config_opts += -no-feature-ftp
|
||||
$(package)_config_opts += -no-feature-image_heuristic_mask
|
||||
$(package)_config_opts += -no-feature-keysequenceedit
|
||||
$(package)_config_opts += -no-feature-lcdnumber
|
||||
$(package)_config_opts += -no-feature-pdf
|
||||
$(package)_config_opts += -no-feature-printdialog
|
||||
$(package)_config_opts += -no-feature-printer
|
||||
$(package)_config_opts += -no-feature-printpreviewdialog
|
||||
$(package)_config_opts += -no-feature-printpreviewwidget
|
||||
$(package)_config_opts += -no-feature-sessionmanager
|
||||
$(package)_config_opts += -no-feature-sql
|
||||
$(package)_config_opts += -no-feature-statemachine
|
||||
$(package)_config_opts += -no-feature-syntaxhighlighter
|
||||
$(package)_config_opts += -no-feature-textbrowser
|
||||
$(package)_config_opts += -no-feature-textodfwriter
|
||||
$(package)_config_opts += -no-feature-topleveldomain
|
||||
$(package)_config_opts += -no-feature-udpsocket
|
||||
$(package)_config_opts += -no-feature-undocommand
|
||||
$(package)_config_opts += -no-feature-undogroup
|
||||
$(package)_config_opts += -no-feature-undostack
|
||||
$(package)_config_opts += -no-feature-undoview
|
||||
$(package)_config_opts += -no-feature-vnc
|
||||
$(package)_config_opts += -no-feature-wizard
|
||||
$(package)_config_opts_linux = -no-fontconfig
|
||||
$(package)_config_opts_linux += -no-opengl
|
||||
$(package)_config_opts_linux += -no-xcb
|
||||
$(package)_config_opts_linux += -no-feature-xlib
|
||||
endef
|
||||
|
||||
define $(package)_fetch_cmds
|
||||
$(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \
|
||||
$(call fetch_file,$(package),$($(package)_download_path),$($(package)_qttranslations_file_name),$($(package)_qttranslations_file_name),$($(package)_qttranslations_sha256_hash)) && \
|
||||
$(call fetch_file,$(package),$($(package)_download_path),$($(package)_qttools_file_name),$($(package)_qttools_file_name),$($(package)_qttools_sha256_hash))
|
||||
endef
|
||||
|
||||
define $(package)_extract_cmds
|
||||
mkdir -p $($(package)_extract_dir) && \
|
||||
echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \
|
||||
echo "$($(package)_qttranslations_sha256_hash) $($(package)_source_dir)/$($(package)_qttranslations_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \
|
||||
echo "$($(package)_qttools_sha256_hash) $($(package)_source_dir)/$($(package)_qttools_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \
|
||||
$(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \
|
||||
mkdir qtbase && \
|
||||
tar --strip-components=1 -xf $($(package)_source) -C qtbase && \
|
||||
mkdir qttranslations && \
|
||||
tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttranslations_file_name) -C qttranslations && \
|
||||
mkdir qttools && \
|
||||
tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttools_file_name) -C qttools
|
||||
endef
|
||||
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
sed -i.old "s|FT_Get_Font_Format|FT_Get_X11_Font_Format|" qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp && \
|
||||
sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \
|
||||
sed -i.old "/updateqm.depends =/d" qttranslations/translations/translations.pro && \
|
||||
sed -i.old "s/src_plugins.depends = src_sql src_network/src_plugins.depends = src_network/" qtbase/src/src.pro && \
|
||||
cp -r qtbase/mkspecs/linux-arm-gnueabi-g++ qtbase/mkspecs/bitcoin-linux-g++ && \
|
||||
sed -i.old "s/arm-linux-gnueabi-/$(host)-/g" qtbase/mkspecs/bitcoin-linux-g++/qmake.conf && \
|
||||
patch -p1 -i $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \
|
||||
patch -p1 -i $($(package)_patch_dir)/fix_no_printer.patch && \
|
||||
echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \
|
||||
echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \
|
||||
echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \
|
||||
patch -p1 -i $($(package)_patch_dir)/no-xlib.patch && \
|
||||
echo "QMAKE_LINK_OBJECT_MAX = 10" >> qtbase/mkspecs/win32-g++/qmake.conf && \
|
||||
echo "QMAKE_LINK_OBJECT_SCRIPT = object_script" >> qtbase/mkspecs/win32-g++/qmake.conf && \
|
||||
sed -i.old "s|QMAKE_CFLAGS += |!host_build: QMAKE_CFLAGS = $($(package)_cflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \
|
||||
sed -i.old "s|QMAKE_CXXFLAGS += |!host_build: QMAKE_CXXFLAGS = $($(package)_cxxflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \
|
||||
sed -i.old "0,/^QMAKE_LFLAGS_/s|^QMAKE_LFLAGS_|!host_build: QMAKE_LFLAGS = $($(package)_ldflags)\n&|" qtbase/mkspecs/win32-g++/qmake.conf && \
|
||||
sed -i.old "s/LIBRARY_PATH/(CROSS_)?\0/g" qtbase/mkspecs/features/toolchain.prf
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
export PKG_CONFIG_SYSROOT_DIR=/ && \
|
||||
export PKG_CONFIG_LIBDIR=$(host_prefix)/lib/pkgconfig && \
|
||||
export PKG_CONFIG_PATH=$(host_prefix)/share/pkgconfig && \
|
||||
./configure $($(package)_config_opts) && \
|
||||
echo "CONFIG += force_bootstrap" >> mkspecs/qconfig.pri && \
|
||||
$(MAKE) sub-src-clean && \
|
||||
cd ../qttranslations && ../qtbase/bin/qmake qttranslations.pro -o Makefile && \
|
||||
cd translations && ../../qtbase/bin/qmake translations.pro -o Makefile && cd ../.. &&\
|
||||
cd qttools/src/linguist/lrelease/ && ../../../../qtbase/bin/qmake lrelease.pro -o Makefile
|
||||
endef
|
||||
|
||||
define $(package)_build_cmds
|
||||
$(MAKE) -C src $(addprefix sub-,$($(package)_qt_libs)) && \
|
||||
$(MAKE) -C ../qttools/src/linguist/lrelease && \
|
||||
$(MAKE) -C ../qttranslations
|
||||
endef
|
||||
|
||||
define $(package)_stage_cmds
|
||||
$(MAKE) -C src INSTALL_ROOT=$($(package)_staging_dir) $(addsuffix -install_subtargets,$(addprefix sub-,$($(package)_qt_libs))) && cd .. &&\
|
||||
$(MAKE) -C qttools/src/linguist/lrelease INSTALL_ROOT=$($(package)_staging_dir) install_target && \
|
||||
$(MAKE) -C qttranslations INSTALL_ROOT=$($(package)_staging_dir) install_subtargets
|
||||
endef
|
||||
|
||||
define $(package)_postprocess_cmds
|
||||
rm -rf native/mkspecs/ native/lib/ lib/cmake/ && \
|
||||
rm -f lib/lib*.la lib/*.prl plugins/*/*.prl
|
||||
endef
|
||||
@@ -3,7 +3,7 @@ $(package)_version=1.15.0
|
||||
$(package)_download_path=https://www.nlnetlabs.nl/downloads/$(package)/
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=a480dc6c8937447b98d161fe911ffc76cfaffa2da18788781314e81339f1126f
|
||||
$(package)_dependencies=openssl expat ldns
|
||||
$(package)_dependencies=openssl expat
|
||||
$(package)_patches=disable-glibc-reallocarray.patch
|
||||
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
From 96ac8f13f4d0ee96baf5724d9f96c44c34b8606c Mon Sep 17 00:00:00 2001
|
||||
From: David Carlier <devnexen@gmail.com>
|
||||
Date: Tue, 24 Aug 2021 22:40:14 +0100
|
||||
Subject: [PATCH] Darwin platform allows to build on releases before
|
||||
Yosemite/ios 8.
|
||||
|
||||
issue #16407 #16408
|
||||
|
||||
Reviewed-by: Paul Dale <pauli@openssl.org>
|
||||
Reviewed-by: Tomas Mraz <tomas@openssl.org>
|
||||
(Merged from https://github.com/openssl/openssl/pull/16409)
|
||||
---
|
||||
crypto/rand/rand_unix.c | 5 +----
|
||||
include/crypto/rand.h | 10 ++++++++++
|
||||
2 files changed, 11 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/crypto/rand/rand_unix.c b/crypto/rand/rand_unix.c
|
||||
index 43f1069d151d..0f4525106af7 100644
|
||||
--- a/crypto/rand/rand_unix.c
|
||||
+++ b/crypto/rand/rand_unix.c
|
||||
@@ -34,9 +34,6 @@
|
||||
#if defined(__OpenBSD__)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
-#if defined(__APPLE__)
|
||||
-# include <CommonCrypto/CommonRandom.h>
|
||||
-#endif
|
||||
|
||||
#if defined(OPENSSL_SYS_UNIX) || defined(__DJGPP__)
|
||||
# include <sys/types.h>
|
||||
@@ -381,7 +378,7 @@ static ssize_t syscall_random(void *buf, size_t buflen)
|
||||
if (errno != ENOSYS)
|
||||
return -1;
|
||||
}
|
||||
-# elif defined(__APPLE__)
|
||||
+# elif defined(OPENSSL_APPLE_CRYPTO_RANDOM)
|
||||
if (CCRandomGenerateBytes(buf, buflen) == kCCSuccess)
|
||||
return (ssize_t)buflen;
|
||||
|
||||
diff --git a/include/crypto/rand.h b/include/crypto/rand.h
|
||||
index 5350d3a93119..674f840fd13c 100644
|
||||
--- a/include/crypto/rand.h
|
||||
+++ b/include/crypto/rand.h
|
||||
@@ -20,6 +20,16 @@
|
||||
|
||||
# include <openssl/rand.h>
|
||||
|
||||
+# if defined(__APPLE__) && !defined(OPENSSL_NO_APPLE_CRYPTO_RANDOM)
|
||||
+# include <Availability.h>
|
||||
+# if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000) || \
|
||||
+ (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000)
|
||||
+# define OPENSSL_APPLE_CRYPTO_RANDOM 1
|
||||
+# include <CommonCrypto/CommonCryptoError.h>
|
||||
+# include <CommonCrypto/CommonRandom.h>
|
||||
+# endif
|
||||
+# endif
|
||||
+
|
||||
/* forward declaration */
|
||||
typedef struct rand_pool_st RAND_POOL;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
--- x/qtbase/src/plugins/platforms/cocoa/qprintengine_mac_p.h
|
||||
+++ y/qtbase/src/plugins/platforms/cocoa/qprintengine_mac_p.h
|
||||
@@ -52,6 +52,7 @@
|
||||
//
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
+#include <qpa/qplatformprintdevice.h>
|
||||
|
||||
#ifndef QT_NO_PRINTER
|
||||
|
||||
--- x/qtbase/src/plugins/plugins.pro
|
||||
+++ y/qtbase/src/plugins/plugins.pro
|
||||
@@ -9,6 +9,3 @@ qtHaveModule(gui) {
|
||||
!android:qtConfig(library): SUBDIRS *= generic
|
||||
}
|
||||
qtHaveModule(widgets): SUBDIRS += styles
|
||||
-
|
||||
-!winrt:qtHaveModule(printsupport): \
|
||||
- SUBDIRS += printsupport
|
||||
@@ -1,11 +0,0 @@
|
||||
--- old/qtbase/mkspecs/features/qt_module.prf
|
||||
+++ new/qtbase/mkspecs/features/qt_module.prf
|
||||
@@ -269,7 +269,7 @@ load(qt_installs)
|
||||
load(qt_targets)
|
||||
|
||||
# this builds on top of qt_common
|
||||
-!internal_module:if(unix|mingw):!if(darwin:debug_and_release:CONFIG(debug, debug|release)) {
|
||||
+if(unix|mingw):!if(darwin:debug_and_release:CONFIG(debug, debug|release)) {
|
||||
CONFIG += create_pc
|
||||
QMAKE_PKGCONFIG_DESTDIR = pkgconfig
|
||||
host_build: \
|
||||
@@ -1,15 +0,0 @@
|
||||
--- old/qtbase/src/tools/rcc/rcc.cpp
|
||||
+++ new/qtbase/src/tools/rcc/rcc.cpp
|
||||
@@ -207,7 +207,11 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib)
|
||||
if (lib.formatVersion() >= 2) {
|
||||
// last modified time stamp
|
||||
const QDateTime lastModified = m_fileInfo.lastModified();
|
||||
- lib.writeNumber8(quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0));
|
||||
+ quint64 lastmod = quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0);
|
||||
+ static const quint64 sourceDate = 1000 * qgetenv("QT_RCC_SOURCE_DATE_OVERRIDE").toULongLong();
|
||||
+ if (sourceDate != 0)
|
||||
+ lastmod = sourceDate;
|
||||
+ lib.writeNumber8(lastmod);
|
||||
if (text || pass1)
|
||||
lib.writeChar('\n');
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
From 9563cef873ae82e06f60708d706d054717e801ce Mon Sep 17 00:00:00 2001
|
||||
From: Carl Dong <contact@carldong.me>
|
||||
Date: Thu, 18 Jul 2019 17:22:05 -0400
|
||||
Subject: [PATCH] Wrap xlib related code blocks in #if's
|
||||
|
||||
They are not necessary to compile QT.
|
||||
---
|
||||
qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp | 8 ++++++++
|
||||
1 file changed, 8 insertions(+)
|
||||
|
||||
diff --git a/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp b/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp
|
||||
index 7c62c2e2b3..c05c6c0a07 100644
|
||||
--- a/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp
|
||||
+++ b/qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp
|
||||
@@ -49,7 +49,9 @@
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QBitmap>
|
||||
#include <QtGui/private/qguiapplication_p.h>
|
||||
+#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
|
||||
#include <X11/cursorfont.h>
|
||||
+#endif
|
||||
#include <xcb/xfixes.h>
|
||||
#include <xcb/xcb_image.h>
|
||||
|
||||
@@ -391,6 +393,7 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *window)
|
||||
xcb_flush(xcb_connection());
|
||||
}
|
||||
|
||||
+#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
|
||||
static int cursorIdForShape(int cshape)
|
||||
{
|
||||
int cursorId = 0;
|
||||
@@ -444,6 +447,7 @@ static int cursorIdForShape(int cshape)
|
||||
}
|
||||
return cursorId;
|
||||
}
|
||||
+#endif
|
||||
|
||||
xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape)
|
||||
{
|
||||
@@ -556,7 +560,9 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape)
|
||||
xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
|
||||
{
|
||||
xcb_connection_t *conn = xcb_connection();
|
||||
+#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
|
||||
int cursorId = cursorIdForShape(cshape);
|
||||
+#endif
|
||||
xcb_cursor_t cursor = XCB_NONE;
|
||||
|
||||
// Try Xcursor first
|
||||
@@ -586,6 +592,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
|
||||
// Non-standard X11 cursors are created from bitmaps
|
||||
cursor = createNonStandardCursor(cshape);
|
||||
|
||||
+#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
|
||||
// Create a glpyh cursor if everything else failed
|
||||
if (!cursor && cursorId) {
|
||||
cursor = xcb_generate_id(conn);
|
||||
@@ -593,6 +600,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
|
||||
cursorId, cursorId + 1,
|
||||
0xFFFF, 0xFFFF, 0xFFFF, 0, 0, 0);
|
||||
}
|
||||
+#endif
|
||||
|
||||
if (cursor && cshape >= 0 && cshape < Qt::LastCursor && connection()->hasXFixes()) {
|
||||
const char *name = cursorNames[cshape].front();
|
||||
---
|
||||
2.22.0
|
||||
|
||||
@@ -27,8 +27,6 @@ SET(Terminfo_LIBRARY @prefix@/lib/libtinfo.a)
|
||||
SET(UNBOUND_INCLUDE_DIR @prefix@/include)
|
||||
SET(UNBOUND_LIBRARIES @prefix@/lib/libunbound.a)
|
||||
|
||||
SET(LRELEASE_PATH @prefix@/native/bin CACHE FILEPATH "path to lrelease" FORCE)
|
||||
|
||||
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android")
|
||||
SET(LIBUNWIND_INCLUDE_DIR @prefix@/include)
|
||||
SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a)
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <fstream>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include "string_tools.h"
|
||||
#endif
|
||||
@@ -70,7 +70,7 @@ namespace file_io_utils
|
||||
|
||||
bool save_string_to_file(const std::string& path_to_file, const std::string& str)
|
||||
{
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
std::wstring wide_path;
|
||||
try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; }
|
||||
HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
@@ -104,7 +104,7 @@ namespace file_io_utils
|
||||
|
||||
bool load_file_to_string(const std::string& path_to_file, std::string& target_str, size_t max_size)
|
||||
{
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
std::wstring wide_path;
|
||||
try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; }
|
||||
HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
@@ -153,7 +153,7 @@ namespace file_io_utils
|
||||
|
||||
bool get_file_size(const std::string& path_to_file, uint64_t &size)
|
||||
{
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
std::wstring wide_path;
|
||||
try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; }
|
||||
HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace serialization
|
||||
byte_stream ss;
|
||||
ss.reserve(initial_buffer_size);
|
||||
store_to_binary(ss);
|
||||
target = epee::byte_slice{std::move(ss)};
|
||||
target = epee::byte_slice{std::move(ss), false};
|
||||
return true;
|
||||
CATCH_ENTRY("portable_storage::store_to_binary", false);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ The dockrun.sh script will do everything to build the binaries. Just specify the
|
||||
version to build as its only argument, e.g.
|
||||
|
||||
```bash
|
||||
./dockrun.sh v0.18.1.0
|
||||
VERSION=v0.18.2.0
|
||||
./dockrun.sh $VERSION
|
||||
```
|
||||
|
||||
The build should run to completion with no errors, and will display the SHA256 checksums
|
||||
@@ -78,7 +79,7 @@ e.g.
|
||||
|
||||
```bash
|
||||
# Run build processes with 8 threads
|
||||
OPT="-j 8" ./dockrun.sh v0.18.1.0
|
||||
OPT="-j 8" ./dockrun.sh $VERSION
|
||||
```
|
||||
|
||||
Post-build
|
||||
@@ -98,16 +99,16 @@ more builder/var/install-linux.log
|
||||
more builder/var/build-linux.log
|
||||
```
|
||||
|
||||
You can find the compiled archives inside of the container at the following directory (be sure to replace `v0.18.1.0` with the version being built):
|
||||
You can find the compiled archives inside of the container at the following directory:
|
||||
|
||||
```bash
|
||||
docker exec -it gitrun /bin/bash
|
||||
ls -la out/v0.18.1.0/
|
||||
ls -la out/$VERSION/
|
||||
```
|
||||
|
||||
To copy the compiled archives to the local host out of the Docker container, you can run the following (be sure to replace `v0.18.1.0` with the version being built):
|
||||
To copy the compiled archives to the local host out of the Docker container, you can run the following:
|
||||
|
||||
```bash
|
||||
mkdir out
|
||||
docker cp gitrun:/home/ubuntu/out/v0.18.1.0 out
|
||||
docker cp gitrun:/home/ubuntu/out/$VERSION out
|
||||
```
|
||||
|
||||
@@ -133,7 +133,7 @@ Common setup part:
|
||||
su - gitianuser
|
||||
|
||||
GH_USER=YOUR_GITHUB_USER_NAME
|
||||
VERSION=v0.18.1.0
|
||||
VERSION=v0.18.2.0
|
||||
```
|
||||
|
||||
Where `GH_USER` is your GitHub user name and `VERSION` is the version tag you want to build.
|
||||
|
||||
@@ -46,6 +46,77 @@ using namespace cryptonote;
|
||||
|
||||
static bool stop_requested = false;
|
||||
|
||||
static bool do_inputs, do_outputs, do_ringsize, do_hours, do_emission, do_fees, do_diff;
|
||||
|
||||
static struct tm prevtm, currtm;
|
||||
static uint64_t prevsz, currsz;
|
||||
static uint64_t prevtxs, currtxs;
|
||||
static uint64_t currblks;
|
||||
static uint64_t h;
|
||||
static uint64_t totins, totouts, totrings;
|
||||
static boost::multiprecision::uint128_t prevemission, prevfees;
|
||||
static boost::multiprecision::uint128_t emission, fees;
|
||||
static boost::multiprecision::uint128_t totdiff, mindiff, maxdiff;
|
||||
|
||||
#define MAX_INOUT 0xffffffff
|
||||
#define MAX_RINGS 0xffffffff
|
||||
|
||||
static uint32_t minins = MAX_INOUT, maxins;
|
||||
static uint32_t minouts = MAX_INOUT, maxouts;
|
||||
static uint32_t minrings = MAX_RINGS, maxrings;
|
||||
static uint32_t io, tottxs;
|
||||
static uint32_t txhr[24];
|
||||
|
||||
static void doprint()
|
||||
{
|
||||
char timebuf[64];
|
||||
|
||||
strftime(timebuf, sizeof(timebuf), "%Y-%m-%d", &prevtm);
|
||||
prevtm = currtm;
|
||||
std::cout << timebuf << "\t" << currblks << "\t" << h << "\t" << currtxs << "\t" << prevtxs + currtxs << "\t" << currsz << "\t" << prevsz + currsz;
|
||||
prevsz += currsz;
|
||||
currsz = 0;
|
||||
prevtxs += currtxs;
|
||||
currtxs = 0;
|
||||
if (!tottxs)
|
||||
tottxs = 1;
|
||||
if (do_emission) {
|
||||
std::cout << "\t" << print_money(emission) << "\t" << print_money(prevemission + emission);
|
||||
prevemission += emission;
|
||||
emission = 0;
|
||||
}
|
||||
if (do_fees) {
|
||||
std::cout << "\t" << print_money(fees) << "\t" << print_money(prevfees + fees);
|
||||
prevfees += fees;
|
||||
fees = 0;
|
||||
}
|
||||
if (do_diff) {
|
||||
std::cout << "\t" << (maxdiff ? mindiff : 0) << "\t" << maxdiff << "\t" << totdiff / currblks;
|
||||
mindiff = 0; maxdiff = 0; totdiff = 0;
|
||||
}
|
||||
if (do_inputs) {
|
||||
std::cout << "\t" << (maxins ? minins : 0) << "\t" << maxins << "\t" << totins * 1.0 / tottxs;
|
||||
minins = MAX_INOUT; maxins = 0; totins = 0;
|
||||
}
|
||||
if (do_outputs) {
|
||||
std::cout << "\t" << (maxouts ? minouts : 0) << "\t" << maxouts << "\t" << totouts * 1.0 / tottxs;
|
||||
minouts = MAX_INOUT; maxouts = 0; totouts = 0;
|
||||
}
|
||||
if (do_ringsize) {
|
||||
std::cout << "\t" << (maxrings ? minrings : 0) << "\t" << maxrings << "\t" << totrings * 1.0 / tottxs;
|
||||
minrings = MAX_RINGS; maxrings = 0; totrings = 0;
|
||||
}
|
||||
if (do_hours) {
|
||||
for (int i=0; i<24; i++) {
|
||||
std::cout << "\t" << txhr[i];
|
||||
txhr[i] = 0;
|
||||
}
|
||||
}
|
||||
currblks = 0;
|
||||
tottxs = 0;
|
||||
std::cout << ENDL;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
TRY_ENTRY();
|
||||
@@ -123,13 +194,13 @@ int main(int argc, char* argv[])
|
||||
network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
|
||||
block_start = command_line::get_arg(vm, arg_block_start);
|
||||
block_stop = command_line::get_arg(vm, arg_block_stop);
|
||||
bool do_inputs = command_line::get_arg(vm, arg_inputs);
|
||||
bool do_outputs = command_line::get_arg(vm, arg_outputs);
|
||||
bool do_ringsize = command_line::get_arg(vm, arg_ringsize);
|
||||
bool do_hours = command_line::get_arg(vm, arg_hours);
|
||||
bool do_emission = command_line::get_arg(vm, arg_emission);
|
||||
bool do_fees = command_line::get_arg(vm, arg_fees);
|
||||
bool do_diff = command_line::get_arg(vm, arg_diff);
|
||||
do_inputs = command_line::get_arg(vm, arg_inputs);
|
||||
do_outputs = command_line::get_arg(vm, arg_outputs);
|
||||
do_ringsize = command_line::get_arg(vm, arg_ringsize);
|
||||
do_hours = command_line::get_arg(vm, arg_hours);
|
||||
do_emission = command_line::get_arg(vm, arg_emission);
|
||||
do_fees = command_line::get_arg(vm, arg_fees);
|
||||
do_diff = command_line::get_arg(vm, arg_diff);
|
||||
|
||||
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
|
||||
std::unique_ptr<Blockchain> core_storage;
|
||||
@@ -211,25 +282,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
||||
}
|
||||
std::cout << ENDL;
|
||||
|
||||
#define MAX_INOUT 0xffffffff
|
||||
#define MAX_RINGS 0xffffffff
|
||||
|
||||
struct tm prevtm = {0}, currtm;
|
||||
uint64_t prevsz = 0, currsz = 0;
|
||||
uint64_t prevtxs = 0, currtxs = 0;
|
||||
uint64_t currblks = 0;
|
||||
uint64_t totins = 0, totouts = 0, totrings = 0;
|
||||
boost::multiprecision::uint128_t prevemission = 0, prevfees = 0;
|
||||
boost::multiprecision::uint128_t emission = 0, fees = 0;
|
||||
boost::multiprecision::uint128_t totdiff = 0, mindiff = 0, maxdiff = 0;
|
||||
uint32_t minins = MAX_INOUT, maxins = 0;
|
||||
uint32_t minouts = MAX_INOUT, maxouts = 0;
|
||||
uint32_t minrings = MAX_RINGS, maxrings = 0;
|
||||
uint32_t io, tottxs = 0;
|
||||
uint32_t txhr[24] = {0};
|
||||
unsigned int i;
|
||||
|
||||
for (uint64_t h = block_start; h < block_stop; ++h)
|
||||
for (h = block_start; h < block_stop; ++h)
|
||||
{
|
||||
cryptonote::blobdata bd = db->get_block_blob_from_height(h);
|
||||
cryptonote::block blk;
|
||||
@@ -239,7 +292,6 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
||||
return 1;
|
||||
}
|
||||
time_t tt = blk.timestamp;
|
||||
char timebuf[64];
|
||||
epee::misc_utils::get_gmt_time(tt, currtm);
|
||||
if (!prevtm.tm_year)
|
||||
prevtm = currtm;
|
||||
@@ -247,54 +299,9 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
||||
if (currtm.tm_mday > prevtm.tm_mday || (currtm.tm_mday == 1 && prevtm.tm_mday > 27))
|
||||
{
|
||||
// check for timestamp fudging around month ends
|
||||
if (prevtm.tm_mday == 1 && currtm.tm_mday > 27)
|
||||
goto skip;
|
||||
strftime(timebuf, sizeof(timebuf), "%Y-%m-%d", &prevtm);
|
||||
prevtm = currtm;
|
||||
std::cout << timebuf << "\t" << currblks << "\t" << h << "\t" << currtxs << "\t" << prevtxs + currtxs << "\t" << currsz << "\t" << prevsz + currsz;
|
||||
prevsz += currsz;
|
||||
currsz = 0;
|
||||
prevtxs += currtxs;
|
||||
currtxs = 0;
|
||||
if (!tottxs)
|
||||
tottxs = 1;
|
||||
if (do_emission) {
|
||||
std::cout << "\t" << print_money(emission) << "\t" << print_money(prevemission + emission);
|
||||
prevemission += emission;
|
||||
emission = 0;
|
||||
}
|
||||
if (do_fees) {
|
||||
std::cout << "\t" << print_money(fees) << "\t" << print_money(prevfees + fees);
|
||||
prevfees += fees;
|
||||
fees = 0;
|
||||
}
|
||||
if (do_diff) {
|
||||
std::cout << "\t" << (maxdiff ? mindiff : 0) << "\t" << maxdiff << "\t" << totdiff / currblks;
|
||||
mindiff = 0; maxdiff = 0; totdiff = 0;
|
||||
}
|
||||
if (do_inputs) {
|
||||
std::cout << "\t" << (maxins ? minins : 0) << "\t" << maxins << "\t" << totins * 1.0 / tottxs;
|
||||
minins = MAX_INOUT; maxins = 0; totins = 0;
|
||||
}
|
||||
if (do_outputs) {
|
||||
std::cout << "\t" << (maxouts ? minouts : 0) << "\t" << maxouts << "\t" << totouts * 1.0 / tottxs;
|
||||
minouts = MAX_INOUT; maxouts = 0; totouts = 0;
|
||||
}
|
||||
if (do_ringsize) {
|
||||
std::cout << "\t" << (maxrings ? minrings : 0) << "\t" << maxrings << "\t" << totrings * 1.0 / tottxs;
|
||||
minrings = MAX_RINGS; maxrings = 0; totrings = 0;
|
||||
}
|
||||
if (do_hours) {
|
||||
for (i=0; i<24; i++) {
|
||||
std::cout << "\t" << txhr[i];
|
||||
txhr[i] = 0;
|
||||
}
|
||||
}
|
||||
currblks = 0;
|
||||
tottxs = 0;
|
||||
std::cout << ENDL;
|
||||
if (!(prevtm.tm_mday == 1 && currtm.tm_mday > 27))
|
||||
doprint();
|
||||
}
|
||||
skip:
|
||||
currsz += bd.size();
|
||||
uint64_t coinbase_amount;
|
||||
uint64_t tx_fee_amount = 0;
|
||||
@@ -371,6 +378,8 @@ skip:
|
||||
if (stop_requested)
|
||||
break;
|
||||
}
|
||||
if (currblks)
|
||||
doprint();
|
||||
|
||||
core_storage->deinit();
|
||||
return 0;
|
||||
|
||||
Binary file not shown.
@@ -241,6 +241,9 @@ namespace cryptonote
|
||||
ADD_CHECKPOINT2(2182500, "0d22b5f81982eff21d094af9e821dc2007e6342069e3b1a37b15d97646353124", "0xead4a874083492");
|
||||
ADD_CHECKPOINT2(2661600, "41c9060e8426012238e8a26da26fcb90797436896cc70886a894c2c560bcccf2", "0x2e0d87526ff161f");
|
||||
ADD_CHECKPOINT2(2677000, "1b9fee6246eeb176bd17d637bf252e9af54a4218675f01b4449cc0901867f9eb", "0x2f165bc1a5163ba");
|
||||
ADD_CHECKPOINT2(2706000, "d8eb144c5e1fe6b329ecc900ec95e7792fccff84175fb23a25ed59d7299a511c", "0x310f7d89372f705");
|
||||
ADD_CHECKPOINT2(2720000, "b19fb41dff15bd1016afbee9f8469f05aab715c9e5d1b974466a11fd58ecbb86", "0x3216b5851ddbb61");
|
||||
ADD_CHECKPOINT2(2817000, "39726d19ccaac01d150bec827b877ffae710b516bd633503662036ef4422e577", "0x3900669561954c1");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2014-2022, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
#include <mutex>
|
||||
|
||||
namespace tools
|
||||
{
|
||||
template<typename T, size_t MAX_SIZE>
|
||||
class data_cache
|
||||
{
|
||||
public:
|
||||
void add(const T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
if (data.insert(value).second)
|
||||
{
|
||||
T& old_value = buf[counter++ % MAX_SIZE];
|
||||
data.erase(old_value);
|
||||
old_value = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool has(const T& value) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
return (data.find(value) != data.end());
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mutex m;
|
||||
std::unordered_set<T> data;
|
||||
T buf[MAX_SIZE] = {};
|
||||
size_t counter = 0;
|
||||
};
|
||||
}
|
||||
@@ -30,6 +30,8 @@
|
||||
// check local first (in the event of static or in-source compilation of libunbound)
|
||||
#include "unbound.h"
|
||||
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <stdlib.h>
|
||||
#include "include_base_utils.h"
|
||||
#include "common/threadpool.h"
|
||||
@@ -327,11 +329,6 @@ std::vector<std::string> DNSResolver::get_record(const std::string& url, int rec
|
||||
dnssec_available = false;
|
||||
dnssec_valid = false;
|
||||
|
||||
if (!check_address_syntax(url.c_str()))
|
||||
{
|
||||
return addresses;
|
||||
}
|
||||
|
||||
// destructor takes care of cleanup
|
||||
ub_result_ptr result;
|
||||
|
||||
@@ -414,16 +411,6 @@ DNSResolver DNSResolver::create()
|
||||
return DNSResolver();
|
||||
}
|
||||
|
||||
bool DNSResolver::check_address_syntax(const char *addr) const
|
||||
{
|
||||
// if string doesn't contain a dot, we won't consider it a url for now.
|
||||
if (strchr(addr,'.') == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace dns_utils
|
||||
{
|
||||
|
||||
@@ -521,7 +508,7 @@ bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std
|
||||
|
||||
// send all requests in parallel
|
||||
std::deque<bool> avail(dns_urls.size(), false), valid(dns_urls.size(), false);
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool& tpool = tools::threadpool::getInstanceForIO();
|
||||
tools::threadpool::waiter waiter(tpool);
|
||||
for (size_t n = 0; n < dns_urls.size(); ++n)
|
||||
{
|
||||
|
||||
@@ -159,15 +159,6 @@ private:
|
||||
// TODO: modify this to accommodate DNSSEC
|
||||
std::vector<std::string> get_record(const std::string& url, int record_type, boost::optional<std::string> (*reader)(const char *,size_t), bool& dnssec_available, bool& dnssec_valid);
|
||||
|
||||
/**
|
||||
* @brief Checks a string to see if it looks like a URL
|
||||
*
|
||||
* @param addr the string to be checked
|
||||
*
|
||||
* @return true if it looks enough like a URL, false if not
|
||||
*/
|
||||
bool check_address_syntax(const char *addr) const;
|
||||
|
||||
DNSResolverData *m_data;
|
||||
}; // class DNSResolver
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <cstddef>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -42,10 +43,14 @@ namespace tools
|
||||
class threadpool
|
||||
{
|
||||
public:
|
||||
static threadpool& getInstance() {
|
||||
static threadpool& getInstanceForCompute() {
|
||||
static threadpool instance;
|
||||
return instance;
|
||||
}
|
||||
static threadpool& getInstanceForIO() {
|
||||
static threadpool instance(8);
|
||||
return instance;
|
||||
}
|
||||
static threadpool *getNewForUnitTests(unsigned max_threads = 0) {
|
||||
return new threadpool(max_threads);
|
||||
}
|
||||
|
||||
+25
-15
@@ -30,29 +30,39 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#define CTHR_MUTEX_TYPE HANDLE
|
||||
#define CTHR_MUTEX_INIT NULL
|
||||
#define CTHR_MUTEX_LOCK(x) do { if (x == NULL) { \
|
||||
HANDLE p = CreateMutex(NULL, FALSE, NULL); \
|
||||
if (InterlockedCompareExchangePointer((PVOID*)&x, (PVOID)p, NULL) != NULL) \
|
||||
CloseHandle(p); \
|
||||
} WaitForSingleObject(x, INFINITE); } while(0)
|
||||
#define CTHR_MUTEX_UNLOCK(x) ReleaseMutex(x)
|
||||
|
||||
#define CTHR_RWLOCK_TYPE SRWLOCK
|
||||
#define CTHR_RWLOCK_INIT SRWLOCK_INIT
|
||||
#define CTHR_RWLOCK_LOCK_WRITE(x) AcquireSRWLockExclusive(&x)
|
||||
#define CTHR_RWLOCK_UNLOCK_WRITE(x) ReleaseSRWLockExclusive(&x)
|
||||
#define CTHR_RWLOCK_LOCK_READ(x) AcquireSRWLockShared(&x)
|
||||
#define CTHR_RWLOCK_UNLOCK_READ(x) ReleaseSRWLockShared(&x)
|
||||
#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)
|
||||
#define CTHR_THREAD_JOIN(thr) WaitForSingleObject(thr, INFINITE)
|
||||
#define CTHR_THREAD_CREATE(thr, func, arg) ((thr = (HANDLE)_beginthread(func, 0, arg)) != -1L)
|
||||
#define CTHR_THREAD_JOIN(thr) WaitForSingleObject((HANDLE)thr, INFINITE)
|
||||
|
||||
#else
|
||||
|
||||
#include <pthread.h>
|
||||
#define CTHR_MUTEX_TYPE pthread_mutex_t
|
||||
#define CTHR_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
|
||||
#define CTHR_MUTEX_LOCK(x) pthread_mutex_lock(&x)
|
||||
#define CTHR_MUTEX_UNLOCK(x) pthread_mutex_unlock(&x)
|
||||
|
||||
#define CTHR_RWLOCK_TYPE pthread_rwlock_t
|
||||
#define CTHR_RWLOCK_INIT PTHREAD_RWLOCK_INITIALIZER
|
||||
#define CTHR_RWLOCK_LOCK_WRITE(x) pthread_rwlock_wrlock(&x)
|
||||
#define CTHR_RWLOCK_UNLOCK_WRITE(x) pthread_rwlock_unlock(&x)
|
||||
#define CTHR_RWLOCK_LOCK_READ(x) pthread_rwlock_rdlock(&x)
|
||||
#define CTHR_RWLOCK_UNLOCK_READ(x) pthread_rwlock_unlock(&x)
|
||||
#define CTHR_RWLOCK_TRYLOCK_READ(x) (pthread_rwlock_tryrdlock(&x) == 0)
|
||||
|
||||
#define CTHR_THREAD_TYPE pthread_t
|
||||
#define CTHR_THREAD_RTYPE void *
|
||||
#define CTHR_THREAD_RETURN return NULL
|
||||
#define CTHR_THREAD_CREATE(thr, func, arg) pthread_create(&thr, NULL, func, arg)
|
||||
#define CTHR_THREAD_CREATE(thr, func, arg) (pthread_create(&thr, NULL, func, arg) == 0)
|
||||
#define CTHR_THREAD_JOIN(thr) pthread_join(thr, NULL)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -97,5 +97,9 @@ void rx_slow_hash_allocate_state(void);
|
||||
void rx_slow_hash_free_state(void);
|
||||
uint64_t rx_seedheight(const uint64_t height);
|
||||
void rx_seedheights(const uint64_t height, uint64_t *seed_height, uint64_t *next_height);
|
||||
void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const char *seedhash, const void *data, size_t length, char *hash, int miners, int is_alt);
|
||||
void rx_reorg(const uint64_t split_height);
|
||||
|
||||
void rx_set_main_seedhash(const char *seedhash, size_t max_dataset_init_threads);
|
||||
void rx_slow_hash(const char *seedhash, const void *data, size_t length, char *result_hash);
|
||||
|
||||
void rx_set_miner_thread(uint32_t value, size_t max_dataset_init_threads);
|
||||
uint32_t rx_get_miner_thread(void);
|
||||
|
||||
+331
-185
@@ -43,32 +43,41 @@
|
||||
|
||||
#define RX_LOGCAT "randomx"
|
||||
|
||||
// Report large page allocation failures as debug messages
|
||||
#define alloc_err_msg(x) mdebug(RX_LOGCAT, x);
|
||||
|
||||
static CTHR_RWLOCK_TYPE main_dataset_lock = CTHR_RWLOCK_INIT;
|
||||
static CTHR_RWLOCK_TYPE main_cache_lock = CTHR_RWLOCK_INIT;
|
||||
|
||||
static randomx_dataset *main_dataset = NULL;
|
||||
static randomx_cache *main_cache = NULL;
|
||||
static char main_seedhash[HASH_SIZE];
|
||||
static int main_seedhash_set = 0;
|
||||
|
||||
static CTHR_RWLOCK_TYPE secondary_cache_lock = CTHR_RWLOCK_INIT;
|
||||
|
||||
static randomx_cache *secondary_cache = NULL;
|
||||
static char secondary_seedhash[HASH_SIZE];
|
||||
static int secondary_seedhash_set = 0;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define THREADV __declspec(thread)
|
||||
#else
|
||||
#define THREADV __thread
|
||||
#endif
|
||||
|
||||
typedef struct rx_state {
|
||||
CTHR_MUTEX_TYPE rs_mutex;
|
||||
char rs_hash[HASH_SIZE];
|
||||
uint64_t rs_height;
|
||||
randomx_cache *rs_cache;
|
||||
} rx_state;
|
||||
static THREADV randomx_vm *main_vm_full = NULL;
|
||||
static THREADV randomx_vm *main_vm_light = NULL;
|
||||
static THREADV randomx_vm *secondary_vm_light = NULL;
|
||||
|
||||
static CTHR_MUTEX_TYPE rx_mutex = CTHR_MUTEX_INIT;
|
||||
static CTHR_MUTEX_TYPE rx_dataset_mutex = CTHR_MUTEX_INIT;
|
||||
static THREADV uint32_t miner_thread = 0;
|
||||
|
||||
static rx_state rx_s[2] = {{CTHR_MUTEX_INIT,{0},0,0},{CTHR_MUTEX_INIT,{0},0,0}};
|
||||
|
||||
static randomx_dataset *rx_dataset;
|
||||
static int rx_dataset_nomem;
|
||||
static int rx_dataset_nolp;
|
||||
static uint64_t rx_dataset_height;
|
||||
static THREADV randomx_vm *rx_vm = NULL;
|
||||
static bool is_main(const char* seedhash) { return main_seedhash_set && (memcmp(seedhash, main_seedhash, HASH_SIZE) == 0); }
|
||||
static bool is_secondary(const char* seedhash) { return secondary_seedhash_set && (memcmp(seedhash, secondary_seedhash, HASH_SIZE) == 0); }
|
||||
|
||||
static void local_abort(const char *msg)
|
||||
{
|
||||
merror(RX_LOGCAT, "%s", msg);
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
#ifdef NDEBUG
|
||||
_exit(1);
|
||||
@@ -77,6 +86,16 @@ static void local_abort(const char *msg)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void hash2hex(const char* hash, char* hex) {
|
||||
const char* d = "0123456789abcdef";
|
||||
for (int i = 0; i < HASH_SIZE; ++i) {
|
||||
const uint8_t b = hash[i];
|
||||
hex[i * 2 + 0] = d[b >> 4];
|
||||
hex[i * 2 + 1] = d[b & 15];
|
||||
}
|
||||
hex[HASH_SIZE * 2] = '\0';
|
||||
}
|
||||
|
||||
static inline int disabled_flags(void) {
|
||||
static int flags = -1;
|
||||
|
||||
@@ -157,19 +176,6 @@ static unsigned int get_seedhash_epoch_blocks(void)
|
||||
return blocks;
|
||||
}
|
||||
|
||||
void rx_reorg(const uint64_t split_height) {
|
||||
int i;
|
||||
CTHR_MUTEX_LOCK(rx_mutex);
|
||||
for (i=0; i<2; i++) {
|
||||
if (split_height <= rx_s[i].rs_height) {
|
||||
if (rx_s[i].rs_height == rx_dataset_height)
|
||||
rx_dataset_height = 1;
|
||||
rx_s[i].rs_height = 1; /* set to an invalid seed height */
|
||||
}
|
||||
}
|
||||
CTHR_MUTEX_UNLOCK(rx_mutex);
|
||||
}
|
||||
|
||||
uint64_t rx_seedheight(const uint64_t height) {
|
||||
const uint64_t seedhash_epoch_lag = get_seedhash_epoch_lag();
|
||||
const uint64_t seedhash_epoch_blocks = get_seedhash_epoch_blocks();
|
||||
@@ -183,6 +189,103 @@ void rx_seedheights(const uint64_t height, uint64_t *seedheight, uint64_t *nexth
|
||||
*nextheight = rx_seedheight(height + get_seedhash_epoch_lag());
|
||||
}
|
||||
|
||||
static void rx_alloc_dataset(randomx_flags flags, randomx_dataset** dataset, int ignore_env)
|
||||
{
|
||||
if (*dataset) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (disabled_flags() & RANDOMX_FLAG_FULL_MEM) {
|
||||
static int shown = 0;
|
||||
if (!shown) {
|
||||
shown = 1;
|
||||
minfo(RX_LOGCAT, "RandomX dataset is disabled by MONERO_RANDOMX_UMASK environment variable.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ignore_env && !getenv("MONERO_RANDOMX_FULL_MEM")) {
|
||||
static int shown = 0;
|
||||
if (!shown) {
|
||||
shown = 1;
|
||||
minfo(RX_LOGCAT, "RandomX dataset is not enabled by default. Use MONERO_RANDOMX_FULL_MEM environment variable to enable it.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
*dataset = randomx_alloc_dataset((flags | RANDOMX_FLAG_LARGE_PAGES) & ~disabled_flags());
|
||||
if (!*dataset) {
|
||||
alloc_err_msg("Couldn't allocate RandomX dataset using large pages");
|
||||
*dataset = randomx_alloc_dataset(flags & ~disabled_flags());
|
||||
if (!*dataset) {
|
||||
merror(RX_LOGCAT, "Couldn't allocate RandomX dataset");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_alloc_cache(randomx_flags flags, randomx_cache** cache)
|
||||
{
|
||||
if (*cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
*cache = randomx_alloc_cache((flags | RANDOMX_FLAG_LARGE_PAGES) & ~disabled_flags());
|
||||
if (!*cache) {
|
||||
alloc_err_msg("Couldn't allocate RandomX cache using large pages");
|
||||
*cache = randomx_alloc_cache(flags & ~disabled_flags());
|
||||
if (!*cache) local_abort("Couldn't allocate RandomX cache");
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_init_full_vm(randomx_flags flags, randomx_vm** vm)
|
||||
{
|
||||
if (*vm || !main_dataset || (disabled_flags() & RANDOMX_FLAG_FULL_MEM)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((flags & RANDOMX_FLAG_JIT) && !miner_thread) {
|
||||
flags |= RANDOMX_FLAG_SECURE;
|
||||
}
|
||||
|
||||
*vm = randomx_create_vm((flags | RANDOMX_FLAG_LARGE_PAGES | RANDOMX_FLAG_FULL_MEM) & ~disabled_flags(), NULL, main_dataset);
|
||||
if (!*vm) {
|
||||
static int shown = 0;
|
||||
if (!shown) {
|
||||
shown = 1;
|
||||
alloc_err_msg("Couldn't allocate RandomX full VM using large pages (will print only once)");
|
||||
}
|
||||
*vm = randomx_create_vm((flags | RANDOMX_FLAG_FULL_MEM) & ~disabled_flags(), NULL, main_dataset);
|
||||
if (!*vm) {
|
||||
merror(RX_LOGCAT, "Couldn't allocate RandomX full VM");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_init_light_vm(randomx_flags flags, randomx_vm** vm, randomx_cache* cache)
|
||||
{
|
||||
if (*vm) {
|
||||
randomx_vm_set_cache(*vm, cache);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((flags & RANDOMX_FLAG_JIT) && !miner_thread) {
|
||||
flags |= RANDOMX_FLAG_SECURE;
|
||||
}
|
||||
|
||||
flags &= ~RANDOMX_FLAG_FULL_MEM;
|
||||
|
||||
*vm = randomx_create_vm((flags | RANDOMX_FLAG_LARGE_PAGES) & ~disabled_flags(), cache, NULL);
|
||||
if (!*vm) {
|
||||
static int shown = 0;
|
||||
if (!shown) {
|
||||
shown = 1;
|
||||
alloc_err_msg("Couldn't allocate RandomX light VM using large pages (will print only once)");
|
||||
}
|
||||
*vm = randomx_create_vm(flags & ~disabled_flags(), cache, NULL);
|
||||
if (!*vm) local_abort("Couldn't allocate RandomX light VM");
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct seedinfo {
|
||||
randomx_cache *si_cache;
|
||||
unsigned long si_start;
|
||||
@@ -191,187 +294,230 @@ typedef struct seedinfo {
|
||||
|
||||
static CTHR_THREAD_RTYPE rx_seedthread(void *arg) {
|
||||
seedinfo *si = arg;
|
||||
randomx_init_dataset(rx_dataset, si->si_cache, si->si_start, si->si_count);
|
||||
randomx_init_dataset(main_dataset, si->si_cache, si->si_start, si->si_count);
|
||||
CTHR_THREAD_RETURN;
|
||||
}
|
||||
|
||||
static void rx_initdata(randomx_cache *rs_cache, const int miners, const uint64_t seedheight) {
|
||||
if (miners > 1) {
|
||||
unsigned long delta = randomx_dataset_item_count() / miners;
|
||||
unsigned long start = 0;
|
||||
int i;
|
||||
seedinfo *si;
|
||||
CTHR_THREAD_TYPE *st;
|
||||
si = malloc(miners * sizeof(seedinfo));
|
||||
if (si == NULL)
|
||||
local_abort("Couldn't allocate RandomX mining threadinfo");
|
||||
st = malloc(miners * sizeof(CTHR_THREAD_TYPE));
|
||||
if (st == NULL) {
|
||||
free(si);
|
||||
local_abort("Couldn't allocate RandomX mining threadlist");
|
||||
}
|
||||
for (i=0; i<miners-1; i++) {
|
||||
si[i].si_cache = rs_cache;
|
||||
si[i].si_start = start;
|
||||
si[i].si_count = delta;
|
||||
start += delta;
|
||||
}
|
||||
si[i].si_cache = rs_cache;
|
||||
static void rx_init_dataset(size_t max_threads) {
|
||||
if (!main_dataset) {
|
||||
return;
|
||||
}
|
||||
|
||||
// leave 2 CPU cores for other tasks
|
||||
const size_t num_threads = (max_threads < 4) ? 1 : (max_threads - 2);
|
||||
seedinfo* si = malloc(num_threads * sizeof(seedinfo));
|
||||
if (!si) local_abort("Couldn't allocate RandomX mining threadinfo");
|
||||
|
||||
const uint32_t delta = randomx_dataset_item_count() / num_threads;
|
||||
uint32_t start = 0;
|
||||
|
||||
const size_t n1 = num_threads - 1;
|
||||
for (size_t i = 0; i < n1; ++i) {
|
||||
si[i].si_cache = main_cache;
|
||||
si[i].si_start = start;
|
||||
si[i].si_count = randomx_dataset_item_count() - start;
|
||||
for (i=1; i<miners; i++) {
|
||||
CTHR_THREAD_CREATE(st[i], rx_seedthread, &si[i]);
|
||||
}
|
||||
randomx_init_dataset(rx_dataset, rs_cache, 0, si[0].si_count);
|
||||
for (i=1; i<miners; i++) {
|
||||
CTHR_THREAD_JOIN(st[i]);
|
||||
}
|
||||
free(st);
|
||||
free(si);
|
||||
} else {
|
||||
randomx_init_dataset(rx_dataset, rs_cache, 0, randomx_dataset_item_count());
|
||||
si[i].si_count = delta;
|
||||
start += delta;
|
||||
}
|
||||
rx_dataset_height = seedheight;
|
||||
|
||||
si[n1].si_cache = main_cache;
|
||||
si[n1].si_start = start;
|
||||
si[n1].si_count = randomx_dataset_item_count() - start;
|
||||
|
||||
CTHR_THREAD_TYPE *st = malloc(num_threads * sizeof(CTHR_THREAD_TYPE));
|
||||
if (!st) local_abort("Couldn't allocate RandomX mining threadlist");
|
||||
|
||||
CTHR_RWLOCK_LOCK_READ(main_cache_lock);
|
||||
for (size_t i = 0; i < n1; ++i) {
|
||||
if (!CTHR_THREAD_CREATE(st[i], rx_seedthread, &si[i])) {
|
||||
local_abort("Couldn't start RandomX seed thread");
|
||||
}
|
||||
}
|
||||
rx_seedthread(&si[n1]);
|
||||
for (size_t i = 0; i < n1; ++i) CTHR_THREAD_JOIN(st[i]);
|
||||
CTHR_RWLOCK_UNLOCK_READ(main_cache_lock);
|
||||
|
||||
free(st);
|
||||
free(si);
|
||||
|
||||
minfo(RX_LOGCAT, "RandomX dataset initialized");
|
||||
}
|
||||
|
||||
void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const char *seedhash, const void *data, size_t length,
|
||||
char *hash, int miners, int is_alt) {
|
||||
uint64_t s_height = rx_seedheight(mainheight);
|
||||
int toggle = (s_height & get_seedhash_epoch_blocks()) != 0;
|
||||
randomx_flags flags = enabled_flags() & ~disabled_flags();
|
||||
rx_state *rx_sp;
|
||||
randomx_cache *cache;
|
||||
typedef struct thread_info {
|
||||
char seedhash[HASH_SIZE];
|
||||
size_t max_threads;
|
||||
} thread_info;
|
||||
|
||||
CTHR_MUTEX_LOCK(rx_mutex);
|
||||
static CTHR_THREAD_RTYPE rx_set_main_seedhash_thread(void *arg) {
|
||||
thread_info* info = arg;
|
||||
|
||||
/* if alt block but with same seed as mainchain, no need for alt cache */
|
||||
if (is_alt) {
|
||||
if (s_height == seedheight && !memcmp(rx_s[toggle].rs_hash, seedhash, HASH_SIZE))
|
||||
is_alt = 0;
|
||||
} else {
|
||||
/* RPC could request an earlier block on mainchain */
|
||||
if (s_height > seedheight)
|
||||
is_alt = 1;
|
||||
/* miner can be ahead of mainchain */
|
||||
else if (s_height < seedheight)
|
||||
toggle ^= 1;
|
||||
CTHR_RWLOCK_LOCK_WRITE(main_dataset_lock);
|
||||
CTHR_RWLOCK_LOCK_WRITE(main_cache_lock);
|
||||
|
||||
// Double check that seedhash wasn't already updated
|
||||
if (is_main(info->seedhash)) {
|
||||
CTHR_RWLOCK_UNLOCK_WRITE(main_cache_lock);
|
||||
CTHR_RWLOCK_UNLOCK_WRITE(main_dataset_lock);
|
||||
free(info);
|
||||
CTHR_THREAD_RETURN;
|
||||
}
|
||||
memcpy(main_seedhash, info->seedhash, HASH_SIZE);
|
||||
main_seedhash_set = 1;
|
||||
|
||||
char buf[HASH_SIZE * 2 + 1];
|
||||
hash2hex(main_seedhash, buf);
|
||||
minfo(RX_LOGCAT, "RandomX new main seed hash is %s", buf);
|
||||
|
||||
const randomx_flags flags = enabled_flags() & ~disabled_flags();
|
||||
rx_alloc_dataset(flags, &main_dataset, 0);
|
||||
rx_alloc_cache(flags, &main_cache);
|
||||
|
||||
randomx_init_cache(main_cache, info->seedhash, HASH_SIZE);
|
||||
minfo(RX_LOGCAT, "RandomX main cache initialized");
|
||||
|
||||
CTHR_RWLOCK_UNLOCK_WRITE(main_cache_lock);
|
||||
|
||||
// From this point, rx_slow_hash can calculate hashes in light mode, but dataset is not initialized yet
|
||||
rx_init_dataset(info->max_threads);
|
||||
|
||||
CTHR_RWLOCK_UNLOCK_WRITE(main_dataset_lock);
|
||||
|
||||
free(info);
|
||||
CTHR_THREAD_RETURN;
|
||||
}
|
||||
|
||||
void rx_set_main_seedhash(const char *seedhash, size_t max_dataset_init_threads) {
|
||||
// Early out if seedhash didn't change
|
||||
if (is_main(seedhash)) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggle ^= (is_alt != 0);
|
||||
// Update main cache and dataset in the background
|
||||
thread_info* info = malloc(sizeof(thread_info));
|
||||
if (!info) local_abort("Couldn't allocate RandomX mining threadinfo");
|
||||
|
||||
rx_sp = &rx_s[toggle];
|
||||
CTHR_MUTEX_LOCK(rx_sp->rs_mutex);
|
||||
CTHR_MUTEX_UNLOCK(rx_mutex);
|
||||
memcpy(info->seedhash, seedhash, HASH_SIZE);
|
||||
info->max_threads = max_dataset_init_threads;
|
||||
|
||||
cache = rx_sp->rs_cache;
|
||||
if (cache == NULL) {
|
||||
if (!(disabled_flags() & RANDOMX_FLAG_LARGE_PAGES)) {
|
||||
cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
|
||||
if (cache == NULL) {
|
||||
mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX cache");
|
||||
}
|
||||
}
|
||||
if (cache == NULL) {
|
||||
cache = randomx_alloc_cache(flags);
|
||||
if (cache == NULL)
|
||||
local_abort("Couldn't allocate RandomX cache");
|
||||
}
|
||||
CTHR_THREAD_TYPE t;
|
||||
if (!CTHR_THREAD_CREATE(t, rx_set_main_seedhash_thread, info)) {
|
||||
local_abort("Couldn't start RandomX seed thread");
|
||||
}
|
||||
if (rx_sp->rs_height != seedheight || rx_sp->rs_cache == NULL || memcmp(seedhash, rx_sp->rs_hash, HASH_SIZE)) {
|
||||
randomx_init_cache(cache, seedhash, HASH_SIZE);
|
||||
rx_sp->rs_cache = cache;
|
||||
rx_sp->rs_height = seedheight;
|
||||
memcpy(rx_sp->rs_hash, seedhash, HASH_SIZE);
|
||||
}
|
||||
if (rx_vm == NULL) {
|
||||
if ((flags & RANDOMX_FLAG_JIT) && !miners) {
|
||||
flags |= RANDOMX_FLAG_SECURE & ~disabled_flags();
|
||||
}
|
||||
if (miners && (disabled_flags() & RANDOMX_FLAG_FULL_MEM)) {
|
||||
miners = 0;
|
||||
}
|
||||
if (miners) {
|
||||
CTHR_MUTEX_LOCK(rx_dataset_mutex);
|
||||
if (!rx_dataset_nomem) {
|
||||
if (rx_dataset == NULL) {
|
||||
if (!(disabled_flags() & RANDOMX_FLAG_LARGE_PAGES)) {
|
||||
rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES);
|
||||
if (rx_dataset == NULL) {
|
||||
mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset");
|
||||
}
|
||||
}
|
||||
if (rx_dataset == NULL)
|
||||
rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT);
|
||||
if (rx_dataset != NULL)
|
||||
rx_initdata(rx_sp->rs_cache, miners, seedheight);
|
||||
}
|
||||
|
||||
void rx_slow_hash(const char *seedhash, const void *data, size_t length, char *result_hash) {
|
||||
const randomx_flags flags = enabled_flags() & ~disabled_flags();
|
||||
int success = 0;
|
||||
|
||||
// Fast path (seedhash == main_seedhash)
|
||||
// Multiple threads can run in parallel in fast or light mode, 1-2 ms or 10-15 ms per hash per thread
|
||||
if (is_main(seedhash)) {
|
||||
// If CTHR_RWLOCK_TRYLOCK_READ fails it means dataset is being initialized now, so use the light mode
|
||||
if (main_dataset && CTHR_RWLOCK_TRYLOCK_READ(main_dataset_lock)) {
|
||||
// Double check that main_seedhash didn't change
|
||||
if (is_main(seedhash)) {
|
||||
rx_init_full_vm(flags, &main_vm_full);
|
||||
if (main_vm_full) {
|
||||
randomx_calculate_hash(main_vm_full, data, length, result_hash);
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
if (rx_dataset != NULL)
|
||||
flags |= RANDOMX_FLAG_FULL_MEM;
|
||||
else {
|
||||
miners = 0;
|
||||
if (!rx_dataset_nomem) {
|
||||
rx_dataset_nomem = 1;
|
||||
mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset for miner");
|
||||
}
|
||||
CTHR_RWLOCK_UNLOCK_READ(main_dataset_lock);
|
||||
} else {
|
||||
CTHR_RWLOCK_LOCK_READ(main_cache_lock);
|
||||
// Double check that main_seedhash didn't change
|
||||
if (is_main(seedhash)) {
|
||||
rx_init_light_vm(flags, &main_vm_light, main_cache);
|
||||
randomx_calculate_hash(main_vm_light, data, length, result_hash);
|
||||
success = 1;
|
||||
}
|
||||
CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
|
||||
CTHR_RWLOCK_UNLOCK_READ(main_cache_lock);
|
||||
}
|
||||
if (!(disabled_flags() & RANDOMX_FLAG_LARGE_PAGES) && !rx_dataset_nolp) {
|
||||
rx_vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, rx_sp->rs_cache, rx_dataset);
|
||||
if(rx_vm == NULL) { //large pages failed
|
||||
mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX VM");
|
||||
rx_dataset_nolp = 1;
|
||||
}
|
||||
}
|
||||
if (rx_vm == NULL)
|
||||
rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset);
|
||||
if(rx_vm == NULL) {//fallback if everything fails
|
||||
flags = RANDOMX_FLAG_DEFAULT | (miners ? RANDOMX_FLAG_FULL_MEM : 0);
|
||||
rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset);
|
||||
}
|
||||
if (rx_vm == NULL)
|
||||
local_abort("Couldn't allocate RandomX VM");
|
||||
} else if (miners) {
|
||||
CTHR_MUTEX_LOCK(rx_dataset_mutex);
|
||||
if (rx_dataset != NULL && rx_dataset_height != seedheight)
|
||||
rx_initdata(cache, miners, seedheight);
|
||||
else if (rx_dataset == NULL) {
|
||||
/* this is a no-op if the cache hasn't changed */
|
||||
randomx_vm_set_cache(rx_vm, rx_sp->rs_cache);
|
||||
}
|
||||
CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
|
||||
} else {
|
||||
/* this is a no-op if the cache hasn't changed */
|
||||
randomx_vm_set_cache(rx_vm, rx_sp->rs_cache);
|
||||
}
|
||||
/* mainchain users can run in parallel */
|
||||
if (!is_alt)
|
||||
CTHR_MUTEX_UNLOCK(rx_sp->rs_mutex);
|
||||
randomx_calculate_hash(rx_vm, data, length, hash);
|
||||
/* altchain slot users always get fully serialized */
|
||||
if (is_alt)
|
||||
CTHR_MUTEX_UNLOCK(rx_sp->rs_mutex);
|
||||
|
||||
if (success) {
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[HASH_SIZE * 2 + 1];
|
||||
|
||||
// Slow path (seedhash != main_seedhash, but seedhash == secondary_seedhash)
|
||||
// Multiple threads can run in parallel in light mode, 10-15 ms per hash per thread
|
||||
if (!secondary_cache) {
|
||||
CTHR_RWLOCK_LOCK_WRITE(secondary_cache_lock);
|
||||
if (!secondary_cache) {
|
||||
hash2hex(seedhash, buf);
|
||||
minfo(RX_LOGCAT, "RandomX new secondary seed hash is %s", buf);
|
||||
|
||||
rx_alloc_cache(flags, &secondary_cache);
|
||||
randomx_init_cache(secondary_cache, seedhash, HASH_SIZE);
|
||||
minfo(RX_LOGCAT, "RandomX secondary cache updated");
|
||||
memcpy(secondary_seedhash, seedhash, HASH_SIZE);
|
||||
secondary_seedhash_set = 1;
|
||||
}
|
||||
CTHR_RWLOCK_UNLOCK_WRITE(secondary_cache_lock);
|
||||
}
|
||||
|
||||
CTHR_RWLOCK_LOCK_READ(secondary_cache_lock);
|
||||
if (is_secondary(seedhash)) {
|
||||
rx_init_light_vm(flags, &secondary_vm_light, secondary_cache);
|
||||
randomx_calculate_hash(secondary_vm_light, data, length, result_hash);
|
||||
success = 1;
|
||||
}
|
||||
CTHR_RWLOCK_UNLOCK_READ(secondary_cache_lock);
|
||||
|
||||
if (success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Slowest path (seedhash != main_seedhash, seedhash != secondary_seedhash)
|
||||
// Only one thread runs at a time and updates secondary_seedhash if needed, up to 200-500 ms per hash
|
||||
CTHR_RWLOCK_LOCK_WRITE(secondary_cache_lock);
|
||||
if (!is_secondary(seedhash)) {
|
||||
hash2hex(seedhash, buf);
|
||||
minfo(RX_LOGCAT, "RandomX new secondary seed hash is %s", buf);
|
||||
|
||||
randomx_init_cache(secondary_cache, seedhash, HASH_SIZE);
|
||||
minfo(RX_LOGCAT, "RandomX secondary cache updated");
|
||||
memcpy(secondary_seedhash, seedhash, HASH_SIZE);
|
||||
secondary_seedhash_set = 1;
|
||||
}
|
||||
rx_init_light_vm(flags, &secondary_vm_light, secondary_cache);
|
||||
randomx_calculate_hash(secondary_vm_light, data, length, result_hash);
|
||||
CTHR_RWLOCK_UNLOCK_WRITE(secondary_cache_lock);
|
||||
}
|
||||
|
||||
void rx_slow_hash_allocate_state(void) {
|
||||
void rx_set_miner_thread(uint32_t value, size_t max_dataset_init_threads) {
|
||||
miner_thread = value;
|
||||
|
||||
// If dataset is not allocated yet, try to allocate and initialize it
|
||||
CTHR_RWLOCK_LOCK_WRITE(main_dataset_lock);
|
||||
if (main_dataset) {
|
||||
CTHR_RWLOCK_UNLOCK_WRITE(main_dataset_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
const randomx_flags flags = enabled_flags() & ~disabled_flags();
|
||||
rx_alloc_dataset(flags, &main_dataset, 1);
|
||||
rx_init_dataset(max_dataset_init_threads);
|
||||
|
||||
CTHR_RWLOCK_UNLOCK_WRITE(main_dataset_lock);
|
||||
}
|
||||
|
||||
void rx_slow_hash_free_state(void) {
|
||||
if (rx_vm != NULL) {
|
||||
randomx_destroy_vm(rx_vm);
|
||||
rx_vm = NULL;
|
||||
uint32_t rx_get_miner_thread() {
|
||||
return miner_thread;
|
||||
}
|
||||
|
||||
void rx_slow_hash_allocate_state() {}
|
||||
|
||||
static void rx_destroy_vm(randomx_vm** vm) {
|
||||
if (*vm) {
|
||||
randomx_destroy_vm(*vm);
|
||||
*vm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void rx_stop_mining(void) {
|
||||
CTHR_MUTEX_LOCK(rx_dataset_mutex);
|
||||
if (rx_dataset != NULL) {
|
||||
randomx_dataset *rd = rx_dataset;
|
||||
rx_dataset = NULL;
|
||||
randomx_release_dataset(rd);
|
||||
}
|
||||
rx_dataset_nomem = 0;
|
||||
rx_dataset_nolp = 0;
|
||||
CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
|
||||
void rx_slow_hash_free_state() {
|
||||
rx_destroy_vm(&main_vm_full);
|
||||
rx_destroy_vm(&main_vm_light);
|
||||
rx_destroy_vm(&secondary_vm_light);
|
||||
}
|
||||
|
||||
@@ -231,6 +231,11 @@ namespace cryptonote
|
||||
*/
|
||||
uint64_t get_window_size() const { return window_size; }
|
||||
|
||||
/**
|
||||
* @brief returns info for all known hard forks
|
||||
*/
|
||||
const std::vector<hardfork_t>& get_hardforks() const { return heights; }
|
||||
|
||||
private:
|
||||
|
||||
uint8_t get_block_version(uint64_t height) const;
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
using namespace epee;
|
||||
|
||||
#include "miner.h"
|
||||
#include "crypto/hash.h"
|
||||
|
||||
|
||||
extern "C" void slow_hash_allocate_state();
|
||||
@@ -436,7 +437,6 @@ namespace cryptonote
|
||||
{
|
||||
m_stop = true;
|
||||
}
|
||||
extern "C" void rx_stop_mining(void);
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool miner::stop()
|
||||
{
|
||||
@@ -469,7 +469,6 @@ namespace cryptonote
|
||||
MINFO("Mining has been stopped, " << m_threads.size() << " finished" );
|
||||
m_threads.clear();
|
||||
m_threads_autodetect.clear();
|
||||
rx_stop_mining();
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
@@ -524,6 +523,8 @@ 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());
|
||||
|
||||
MLOG_SET_THREAD_NAME(std::string("[miner ") + std::to_string(th_local_index) + "]");
|
||||
MGINFO("Miner thread was started ["<< th_local_index << "]");
|
||||
uint32_t nonce = m_starter_nonce + th_local_index;
|
||||
|
||||
@@ -456,6 +456,14 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
|
||||
if (!update_next_cumulative_weight_limit())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_hardfork->get_current_version() >= RX_BLOCK_VERSION)
|
||||
{
|
||||
const crypto::hash seedhash = get_block_id_by_height(crypto::rx_seedheight(m_db->height()));
|
||||
if (seedhash != crypto::null_hash)
|
||||
rx_set_main_seedhash(seedhash.data, tools::get_max_concurrency());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
@@ -570,6 +578,12 @@ void Blockchain::pop_blocks(uint64_t nblocks)
|
||||
|
||||
if (stop_batch)
|
||||
m_db->batch_stop();
|
||||
|
||||
if (m_hardfork->get_current_version() >= RX_BLOCK_VERSION)
|
||||
{
|
||||
const crypto::hash seedhash = get_block_id_by_height(crypto::rx_seedheight(m_db->height()));
|
||||
rx_set_main_seedhash(seedhash.data, tools::get_max_concurrency());
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// This function tells BlockchainDB to remove the top block from the
|
||||
@@ -1239,18 +1253,20 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>
|
||||
}
|
||||
|
||||
m_hardfork->reorganize_from_chain_height(split_height);
|
||||
get_block_longhash_reorg(split_height);
|
||||
|
||||
std::shared_ptr<tools::Notify> reorg_notify = m_reorg_notify;
|
||||
if (reorg_notify)
|
||||
reorg_notify->notify("%s", std::to_string(split_height).c_str(), "%h", std::to_string(m_db->height()).c_str(),
|
||||
"%n", std::to_string(m_db->height() - split_height).c_str(), "%d", std::to_string(discarded_blocks).c_str(), NULL);
|
||||
|
||||
const uint64_t new_height = m_db->height();
|
||||
const crypto::hash seedhash = get_block_id_by_height(crypto::rx_seedheight(new_height));
|
||||
|
||||
crypto::hash prev_id;
|
||||
if (!get_block_hash(alt_chain.back().bl, prev_id))
|
||||
MERROR("Failed to get block hash of an alternative chain's tip");
|
||||
else
|
||||
send_miner_notifications(prev_id, alt_chain.back().already_generated_coins);
|
||||
send_miner_notifications(new_height, seedhash, prev_id, alt_chain.back().already_generated_coins);
|
||||
|
||||
for (const auto& notifier : m_block_notifiers)
|
||||
{
|
||||
@@ -1262,6 +1278,9 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>
|
||||
}
|
||||
}
|
||||
|
||||
if (m_hardfork->get_current_version() >= RX_BLOCK_VERSION)
|
||||
rx_set_main_seedhash(seedhash.data, tools::get_max_concurrency());
|
||||
|
||||
MGINFO_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_db->height());
|
||||
return true;
|
||||
}
|
||||
@@ -2001,7 +2020,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
||||
{
|
||||
seedhash = get_block_id_by_height(seedheight);
|
||||
}
|
||||
get_altblock_longhash(bei.bl, proof_of_work, get_current_blockchain_height(), bei.height, seedheight, seedhash);
|
||||
get_altblock_longhash(bei.bl, proof_of_work, seedhash);
|
||||
} else
|
||||
{
|
||||
get_block_longhash(this, bei.bl, proof_of_work, bei.height, 0);
|
||||
@@ -3434,7 +3453,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
||||
std::vector < uint64_t > results;
|
||||
results.resize(tx.vin.size(), 0);
|
||||
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
|
||||
tools::threadpool::waiter waiter(tpool);
|
||||
int threads = tpool.get_max_concurrency();
|
||||
|
||||
@@ -3613,7 +3632,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
||||
}
|
||||
}
|
||||
|
||||
if (!rct::verRctNonSemanticsSimple(rv))
|
||||
if (!rct::verRctNonSemanticsSimpleCached(rv))
|
||||
{
|
||||
MERROR_VER("Failed to check ringct signatures!");
|
||||
return false;
|
||||
@@ -4552,11 +4571,15 @@ leave:
|
||||
}
|
||||
}
|
||||
|
||||
send_miner_notifications(id, already_generated_coins);
|
||||
const crypto::hash seedhash = get_block_id_by_height(crypto::rx_seedheight(new_height));
|
||||
send_miner_notifications(new_height, seedhash, id, already_generated_coins);
|
||||
|
||||
for (const auto& notifier: m_block_notifiers)
|
||||
notifier(new_height - 1, {std::addressof(bl), 1});
|
||||
|
||||
if (m_hardfork->get_current_version() >= RX_BLOCK_VERSION)
|
||||
rx_set_main_seedhash(seedhash.data, tools::get_max_concurrency());
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
@@ -5137,7 +5160,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
||||
return true;
|
||||
|
||||
bool blocks_exist = false;
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
|
||||
unsigned threads = tpool.get_max_concurrency();
|
||||
blocks.resize(blocks_entry.size());
|
||||
|
||||
@@ -5604,7 +5627,7 @@ void Blockchain::cancel()
|
||||
}
|
||||
|
||||
#if defined(PER_BLOCK_CHECKPOINT)
|
||||
static const char expected_block_hashes_hash[] = "a8b24ef4eeea7241b374d4526a3f7c351b53abe7006a3d7eee02ce0af2cc6d66";
|
||||
static const char expected_block_hashes_hash[] = "b2da201879dc7a14bcb283875a9608d465b247fdea96fc80a2972b5063259591";
|
||||
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
|
||||
{
|
||||
if (get_checkpoints == nullptr || !m_fast_sync)
|
||||
@@ -5761,24 +5784,15 @@ void Blockchain::cache_block_template(const block &b, const cryptonote::account_
|
||||
m_btc_valid = true;
|
||||
}
|
||||
|
||||
void Blockchain::send_miner_notifications(const crypto::hash &prev_id, uint64_t already_generated_coins)
|
||||
void Blockchain::send_miner_notifications(uint64_t height, const crypto::hash &seed_hash, const crypto::hash &prev_id, uint64_t already_generated_coins)
|
||||
{
|
||||
if (m_miner_notifiers.empty())
|
||||
return;
|
||||
|
||||
const uint64_t height = m_db->height();
|
||||
const uint8_t major_version = m_hardfork->get_ideal_version(height);
|
||||
const difficulty_type diff = get_difficulty_for_next_block();
|
||||
const uint64_t median_weight = m_current_block_cumul_weight_median;
|
||||
|
||||
crypto::hash seed_hash = crypto::null_hash;
|
||||
if (m_hardfork->get_current_version() >= RX_BLOCK_VERSION)
|
||||
{
|
||||
uint64_t seed_height, next_height;
|
||||
crypto::rx_seedheights(height, &seed_height, &next_height);
|
||||
seed_hash = get_block_id_by_height(seed_height);
|
||||
}
|
||||
|
||||
std::vector<tx_block_template_backlog_entry> tx_backlog;
|
||||
m_tx_pool.get_block_template_backlog(tx_backlog);
|
||||
|
||||
|
||||
@@ -158,6 +158,13 @@ namespace cryptonote
|
||||
*/
|
||||
bool deinit();
|
||||
|
||||
/**
|
||||
* @brief get a set of blockchain checkpoint hashes
|
||||
*
|
||||
* @return set of blockchain checkpoint hashes
|
||||
*/
|
||||
const checkpoints& get_checkpoints() const { return m_checkpoints; }
|
||||
|
||||
/**
|
||||
* @brief assign a set of blockchain checkpoint hashes
|
||||
*
|
||||
@@ -892,6 +899,13 @@ namespace cryptonote
|
||||
*/
|
||||
uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return m_hardfork->get_earliest_ideal_height_for_version(version); }
|
||||
|
||||
/**
|
||||
* @brief returns info for all known hard forks
|
||||
*
|
||||
* @return the hardforks
|
||||
*/
|
||||
const std::vector<hardfork_t>& get_hardforks() const { return m_hardfork->get_hardforks(); }
|
||||
|
||||
/**
|
||||
* @brief get information about hardfork voting for a version
|
||||
*
|
||||
@@ -1584,9 +1598,11 @@ namespace cryptonote
|
||||
/**
|
||||
* @brief sends new block notifications to ZMQ `miner_data` subscribers
|
||||
*
|
||||
* @param height current blockchain height
|
||||
* @param seed_hash seed hash to use for mining
|
||||
* @param prev_id hash of new blockchain tip
|
||||
* @param already_generated_coins total coins mined by the network so far
|
||||
*/
|
||||
void send_miner_notifications(const crypto::hash &prev_id, uint64_t already_generated_coins);
|
||||
void send_miner_notifications(uint64_t height, const crypto::hash &seed_hash, const crypto::hash &prev_id, uint64_t already_generated_coins);
|
||||
};
|
||||
} // namespace cryptonote
|
||||
|
||||
@@ -252,6 +252,10 @@ namespace cryptonote
|
||||
m_pprotocol = &m_protocol_stub;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
const checkpoints& core::get_checkpoints() const
|
||||
{
|
||||
return m_blockchain_storage.get_checkpoints();
|
||||
}
|
||||
void core::set_checkpoints(checkpoints&& chk_pts)
|
||||
{
|
||||
m_blockchain_storage.set_checkpoints(std::move(chk_pts));
|
||||
@@ -1009,7 +1013,7 @@ namespace cryptonote
|
||||
|
||||
CRITICAL_REGION_LOCAL(m_incoming_tx_lock);
|
||||
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
|
||||
tools::threadpool::waiter waiter(tpool);
|
||||
epee::span<tx_blob_entry>::const_iterator it = tx_blobs.begin();
|
||||
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
|
||||
@@ -1652,10 +1656,6 @@ namespace cryptonote
|
||||
if (((size_t)-1) <= 0xffffffff && block_blob.size() >= 0x3fffffff)
|
||||
MWARNING("This block's size is " << block_blob.size() << ", closing on the 32 bit limit");
|
||||
|
||||
// load json & DNS checkpoints every 10min/hour respectively,
|
||||
// and verify them with respect to what blocks we already have
|
||||
CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
|
||||
|
||||
block lb;
|
||||
if (!b)
|
||||
{
|
||||
|
||||
@@ -436,6 +436,13 @@ namespace cryptonote
|
||||
*/
|
||||
void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol);
|
||||
|
||||
/**
|
||||
* @copydoc Blockchain::get_checkpoints
|
||||
*
|
||||
* @note see Blockchain::get_checkpoints()
|
||||
*/
|
||||
const checkpoints& get_checkpoints() const;
|
||||
|
||||
/**
|
||||
* @copydoc Blockchain::set_checkpoints
|
||||
*
|
||||
|
||||
@@ -669,10 +669,10 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
void get_altblock_longhash(const block& b, crypto::hash& res, const uint64_t main_height, const uint64_t height, const uint64_t seed_height, const crypto::hash& seed_hash)
|
||||
void get_altblock_longhash(const block& b, crypto::hash& res, const crypto::hash& seed_hash)
|
||||
{
|
||||
blobdata bd = get_block_hashing_blob(b);
|
||||
rx_slow_hash(main_height, seed_height, seed_hash.data, bd.data(), bd.size(), res.data, 0, 1);
|
||||
rx_slow_hash(seed_hash.data, bd.data(), bd.size(), res.data);
|
||||
}
|
||||
|
||||
bool get_block_longhash(const Blockchain *pbc, const blobdata& bd, crypto::hash& res, const uint64_t height, const int major_version, const crypto::hash *seed_hash, const int miners)
|
||||
@@ -686,20 +686,16 @@ namespace cryptonote
|
||||
}
|
||||
if (major_version >= RX_BLOCK_VERSION)
|
||||
{
|
||||
uint64_t seed_height, main_height;
|
||||
crypto::hash hash;
|
||||
if (pbc != NULL)
|
||||
{
|
||||
seed_height = rx_seedheight(height);
|
||||
const uint64_t seed_height = rx_seedheight(height);
|
||||
hash = seed_hash ? *seed_hash : pbc->get_pending_block_id_by_height(seed_height);
|
||||
main_height = pbc->get_current_blockchain_height();
|
||||
} else
|
||||
{
|
||||
memset(&hash, 0, sizeof(hash)); // only happens when generating genesis block
|
||||
seed_height = 0;
|
||||
main_height = 0;
|
||||
}
|
||||
rx_slow_hash(main_height, seed_height, hash.data, bd.data(), bd.size(), res.data, seed_hash ? 0 : miners, !!seed_hash);
|
||||
rx_slow_hash(hash.data, bd.data(), bd.size(), res.data);
|
||||
} else {
|
||||
const int pow_variant = major_version >= 7 ? major_version - 6 : 0;
|
||||
crypto::cn_slow_hash(bd.data(), bd.size(), res, pow_variant, height);
|
||||
@@ -713,20 +709,10 @@ namespace cryptonote
|
||||
return get_block_longhash(pbc, bd, res, height, b.major_version, seed_hash, miners);
|
||||
}
|
||||
|
||||
bool get_block_longhash(const Blockchain *pbc, const block& b, crypto::hash& res, const uint64_t height, const int miners)
|
||||
{
|
||||
return get_block_longhash(pbc, b, res, height, NULL, miners);
|
||||
}
|
||||
|
||||
crypto::hash get_block_longhash(const Blockchain *pbc, const block& b, const uint64_t height, const int miners)
|
||||
crypto::hash get_block_longhash(const Blockchain *pbc, const block& b, const uint64_t height, const crypto::hash *seed_hash, const int miners)
|
||||
{
|
||||
crypto::hash p = crypto::null_hash;
|
||||
get_block_longhash(pbc, b, p, height, miners);
|
||||
get_block_longhash(pbc, b, p, height, seed_hash, miners);
|
||||
return p;
|
||||
}
|
||||
|
||||
void get_block_longhash_reorg(const uint64_t split_height)
|
||||
{
|
||||
rx_reorg(split_height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,14 +144,10 @@ namespace cryptonote
|
||||
);
|
||||
|
||||
class Blockchain;
|
||||
bool get_block_longhash(const Blockchain *pb, const blobdata& bd, crypto::hash& res, const uint64_t height,
|
||||
const int major_version, const crypto::hash *seed_hash, const int miners);
|
||||
bool get_block_longhash(const Blockchain *pb, const block& b, crypto::hash& res, const uint64_t height, const int miners);
|
||||
bool get_block_longhash(const Blockchain *pb, const block& b, crypto::hash& res, const uint64_t height, const crypto::hash *seed_hash, const int miners);
|
||||
void get_altblock_longhash(const block& b, crypto::hash& res, const uint64_t main_height, const uint64_t height,
|
||||
const uint64_t seed_height, const crypto::hash& seed_hash);
|
||||
crypto::hash get_block_longhash(const Blockchain *pb, const block& b, const uint64_t height, const int miners);
|
||||
void get_block_longhash_reorg(const uint64_t split_height);
|
||||
bool get_block_longhash(const Blockchain *pb, const blobdata& bd, crypto::hash& res, const uint64_t height, const int major_version, const crypto::hash *seed_hash, const int miners = 0);
|
||||
bool get_block_longhash(const Blockchain *pb, const block& b, crypto::hash& res, const uint64_t height, const crypto::hash *seed_hash = nullptr, const int miners = 0);
|
||||
crypto::hash get_block_longhash(const Blockchain *pb, const block& b, const uint64_t height, const crypto::hash *seed_hash = nullptr, const int miners = 0);
|
||||
void get_altblock_longhash(const block& b, crypto::hash& res, const crypto::hash& seed_hash);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -537,6 +537,10 @@ namespace cryptonote
|
||||
MLOG_PEER_STATE("requesting chain");
|
||||
}
|
||||
|
||||
// load json & DNS checkpoints every 10min/hour respectively,
|
||||
// and verify them with respect to what blocks we already have
|
||||
CHECK_AND_ASSERT_MES(m_core.update_checkpoints(), 1, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -819,6 +823,10 @@ namespace cryptonote
|
||||
post_notify<NOTIFY_REQUEST_CHAIN>(r, context);
|
||||
MLOG_PEER_STATE("requesting chain");
|
||||
}
|
||||
|
||||
// load json & DNS checkpoints every 10min/hour respectively,
|
||||
// and verify them with respect to what blocks we already have
|
||||
CHECK_AND_ASSERT_MES(m_core.update_checkpoints(), 1, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -542,6 +542,7 @@ namespace levin
|
||||
i_core_events* core_;
|
||||
std::vector<blobdata> txs_;
|
||||
boost::uuids::uuid source_;
|
||||
relay_method tx_relay;
|
||||
|
||||
//! \pre Called in `zone_->strand`
|
||||
void operator()()
|
||||
@@ -549,7 +550,7 @@ namespace levin
|
||||
if (!zone_ || !core_ || txs_.empty())
|
||||
return;
|
||||
|
||||
if (!zone_->fluffing)
|
||||
if (!zone_->fluffing || tx_relay == relay_method::local)
|
||||
{
|
||||
core_->on_transactions_relayed(epee::to_span(txs_), relay_method::stem);
|
||||
for (int tries = 2; 0 < tries; tries--)
|
||||
@@ -589,7 +590,7 @@ namespace levin
|
||||
|
||||
change_channels(change_channels&&) = default;
|
||||
change_channels(const change_channels& source)
|
||||
: zone_(source.zone_), map_(source.map_.clone())
|
||||
: zone_(source.zone_), map_(source.map_.clone()), fluffing_(source.fluffing_)
|
||||
{}
|
||||
|
||||
//! \pre Called within `zone_->strand`.
|
||||
@@ -871,7 +872,7 @@ namespace levin
|
||||
{
|
||||
// this will change a local/forward tx to stem or fluff ...
|
||||
zone_->strand.dispatch(
|
||||
dandelionpp_notify{zone_, core_, std::move(txs), source}
|
||||
dandelionpp_notify{zone_, core_, std::move(txs), source, tx_relay}
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -511,7 +511,7 @@ namespace trezor {
|
||||
tools::wallet2::signed_tx_set & signed_tx,
|
||||
hw::tx_aux_data & aux_data)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(unsigned_tx.transfers.first == 0, "Unsuported non zero offset");
|
||||
CHECK_AND_ASSERT_THROW_MES(std::get<0>(unsigned_tx.transfers) == 0, "Unsuported non zero offset");
|
||||
|
||||
TREZOR_AUTO_LOCK_CMD();
|
||||
require_connected();
|
||||
@@ -522,7 +522,7 @@ namespace trezor {
|
||||
const size_t num_tx = unsigned_tx.txes.size();
|
||||
m_num_transations_to_sign = num_tx;
|
||||
signed_tx.key_images.clear();
|
||||
signed_tx.key_images.resize(unsigned_tx.transfers.second.size());
|
||||
signed_tx.key_images.resize(std::get<2>(unsigned_tx.transfers).size());
|
||||
|
||||
for(size_t tx_idx = 0; tx_idx < num_tx; ++tx_idx) {
|
||||
std::shared_ptr<protocol::tx::Signer> signer;
|
||||
@@ -566,8 +566,8 @@ namespace trezor {
|
||||
cpend.key_images = key_images;
|
||||
|
||||
// KI sync
|
||||
for(size_t cidx=0, trans_max=unsigned_tx.transfers.second.size(); cidx < trans_max; ++cidx){
|
||||
signed_tx.key_images[cidx] = unsigned_tx.transfers.second[cidx].m_key_image;
|
||||
for(size_t cidx=0, trans_max=std::get<2>(unsigned_tx.transfers).size(); cidx < trans_max; ++cidx){
|
||||
signed_tx.key_images[cidx] = std::get<2>(unsigned_tx.transfers)[cidx].m_key_image;
|
||||
}
|
||||
|
||||
size_t num_sources = cdata.tx_data.sources.size();
|
||||
@@ -579,9 +579,9 @@ namespace trezor {
|
||||
CHECK_AND_ASSERT_THROW_MES(src_idx < cdata.tx.vin.size(), "Invalid idx_mapped");
|
||||
|
||||
size_t idx_map_src = cdata.tx_data.selected_transfers[idx_mapped];
|
||||
CHECK_AND_ASSERT_THROW_MES(idx_map_src >= unsigned_tx.transfers.first, "Invalid offset");
|
||||
CHECK_AND_ASSERT_THROW_MES(idx_map_src >= std::get<0>(unsigned_tx.transfers), "Invalid offset");
|
||||
|
||||
idx_map_src -= unsigned_tx.transfers.first;
|
||||
idx_map_src -= std::get<0>(unsigned_tx.transfers);
|
||||
CHECK_AND_ASSERT_THROW_MES(idx_map_src < signed_tx.key_images.size(), "Invalid key image index");
|
||||
|
||||
const auto vini = boost::get<cryptonote::txin_to_key>(cdata.tx.vin[src_idx]);
|
||||
|
||||
@@ -230,8 +230,8 @@ namespace tx {
|
||||
}
|
||||
|
||||
const tools::wallet2::transfer_details & get_transfer(size_t idx) const {
|
||||
CHECK_AND_ASSERT_THROW_MES(idx < m_unsigned_tx->transfers.second.size() + m_unsigned_tx->transfers.first && idx >= m_unsigned_tx->transfers.first, "Invalid transfer index");
|
||||
return m_unsigned_tx->transfers.second[idx - m_unsigned_tx->transfers.first];
|
||||
CHECK_AND_ASSERT_THROW_MES(idx < std::get<2>(m_unsigned_tx->transfers).size() + std::get<0>(m_unsigned_tx->transfers) && idx >= std::get<0>(m_unsigned_tx->transfers), "Invalid transfer index");
|
||||
return std::get<2>(m_unsigned_tx->transfers)[idx - std::get<0>(m_unsigned_tx->transfers)];
|
||||
}
|
||||
|
||||
const tools::wallet2::transfer_details & get_source_transfer(size_t idx) const {
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
using namespace std;
|
||||
using namespace epee;
|
||||
using namespace cryptonote;
|
||||
using boost::lexical_cast;
|
||||
namespace po = boost::program_options;
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
@@ -84,6 +83,9 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
|
||||
|
||||
try
|
||||
{
|
||||
if (total == 0)
|
||||
throw std::runtime_error("Signer group of size 0 is not allowed.");
|
||||
|
||||
// create M wallets first
|
||||
std::vector<boost::shared_ptr<tools::wallet2>> wallets(total);
|
||||
for (size_t n = 0; n < total; ++n)
|
||||
@@ -118,13 +120,17 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
|
||||
ss << " " << name << std::endl;
|
||||
}
|
||||
|
||||
//exchange keys unless exchange_multisig_keys returns no extra info
|
||||
while (!kex_msgs_intermediate[0].empty())
|
||||
// exchange keys until the wallets are done
|
||||
bool ready{false};
|
||||
wallets[0]->multisig(&ready);
|
||||
while (!ready)
|
||||
{
|
||||
for (size_t n = 0; n < total; ++n)
|
||||
{
|
||||
kex_msgs_intermediate[n] = wallets[n]->exchange_multisig_keys(pwd_container->password(), kex_msgs_intermediate);
|
||||
}
|
||||
|
||||
wallets[0]->multisig(&ready);
|
||||
}
|
||||
|
||||
std::string address = wallets[0]->get_account().get_public_address_str(wallets[0]->nettype());
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace multisig
|
||||
bool multisig_account::multisig_is_ready() const
|
||||
{
|
||||
if (main_kex_rounds_done())
|
||||
return m_kex_rounds_complete >= multisig_kex_rounds_required(m_signers.size(), m_threshold) + 1;
|
||||
return m_kex_rounds_complete >= multisig_setup_rounds_required(m_signers.size(), m_threshold);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -175,19 +175,20 @@ namespace multisig
|
||||
// only mutate account if update succeeds
|
||||
multisig_account temp_account{*this};
|
||||
temp_account.set_multisig_config(threshold, std::move(signers));
|
||||
temp_account.kex_update_impl(expanded_msgs_rnd1);
|
||||
temp_account.kex_update_impl(expanded_msgs_rnd1, false);
|
||||
*this = std::move(temp_account);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// multisig_account: EXTERNAL
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void multisig_account::kex_update(const std::vector<multisig_kex_msg> &expanded_msgs)
|
||||
void multisig_account::kex_update(const std::vector<multisig_kex_msg> &expanded_msgs,
|
||||
const bool force_update_use_with_caution /*= false*/)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(account_is_active(), "multisig account: tried to update kex, but kex isn't initialized yet.");
|
||||
CHECK_AND_ASSERT_THROW_MES(!multisig_is_ready(), "multisig account: tried to update kex, but kex is already complete.");
|
||||
|
||||
multisig_account temp_account{*this};
|
||||
temp_account.kex_update_impl(expanded_msgs);
|
||||
temp_account.kex_update_impl(expanded_msgs, force_update_use_with_caution);
|
||||
*this = std::move(temp_account);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@@ -200,4 +201,11 @@ namespace multisig
|
||||
return num_signers - threshold + 1;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// EXTERNAL
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
std::uint32_t multisig_setup_rounds_required(const std::uint32_t num_signers, const std::uint32_t threshold)
|
||||
{
|
||||
return multisig_kex_rounds_required(num_signers, threshold) + 1;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
} //namespace multisig
|
||||
|
||||
@@ -169,12 +169,20 @@ namespace multisig
|
||||
* - The main interface for multisig key exchange, this handles all the work of processing input messages,
|
||||
* creating new messages for new rounds, and finalizing the multisig shared public key when kex is complete.
|
||||
* param: expanded_msgs - kex messages corresponding to the account's 'in progress' round
|
||||
* param: force_update_use_with_caution - try to force the account to update with messages from an incomplete signer set.
|
||||
* - If this is the post-kex verification round, only require one input message.
|
||||
* - Force updating here should only be done if we can safely assume an honest signer subgroup of size 'threshold'
|
||||
* will complete the account.
|
||||
* - If this is an intermediate round, only require messages from 'num signers - 1 - (round - 1)' other signers.
|
||||
* - If force updating with maliciously-crafted messages, the resulting account will be invalid (either unable
|
||||
* to complete signatures, or a 'hostage' to the malicious signer [i.e. can't sign without his participation]).
|
||||
*/
|
||||
void kex_update(const std::vector<multisig_kex_msg> &expanded_msgs);
|
||||
void kex_update(const std::vector<multisig_kex_msg> &expanded_msgs,
|
||||
const bool force_update_use_with_caution = false);
|
||||
|
||||
private:
|
||||
// implementation of kex_update() (non-transactional)
|
||||
void kex_update_impl(const std::vector<multisig_kex_msg> &expanded_msgs);
|
||||
void kex_update_impl(const std::vector<multisig_kex_msg> &expanded_msgs, const bool incomplete_signer_set);
|
||||
/**
|
||||
* brief: initialize_kex_update - Helper for kex_update_impl()
|
||||
* - Collect the local signer's shared keys to ignore in incoming messages, build the aggregate ancillary key
|
||||
@@ -245,4 +253,13 @@ namespace multisig
|
||||
* return: number of kex rounds required
|
||||
*/
|
||||
std::uint32_t multisig_kex_rounds_required(const std::uint32_t num_signers, const std::uint32_t threshold);
|
||||
|
||||
/**
|
||||
* brief: multisig_setup_rounds_required - The number of setup rounds required to produce an M-of-N shared key.
|
||||
* - A participant must complete all kex rounds and 1 initialization round.
|
||||
* param: num_signers - number of participants in multisig (N)
|
||||
* param: threshold - threshold of multisig (M)
|
||||
* return: number of setup rounds required
|
||||
*/
|
||||
std::uint32_t multisig_setup_rounds_required(const std::uint32_t num_signers, const std::uint32_t threshold);
|
||||
} //namespace multisig
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace multisig
|
||||
"Multisig threshold may not be larger than number of signers.");
|
||||
CHECK_AND_ASSERT_THROW_MES(threshold > 0, "Multisig threshold must be > 0.");
|
||||
CHECK_AND_ASSERT_THROW_MES(round > 0, "Multisig kex round must be > 0.");
|
||||
CHECK_AND_ASSERT_THROW_MES(round <= multisig_kex_rounds_required(num_signers, threshold) + 1,
|
||||
CHECK_AND_ASSERT_THROW_MES(round <= multisig_setup_rounds_required(num_signers, threshold),
|
||||
"Trying to process multisig kex for an invalid round.");
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@@ -181,7 +181,8 @@ namespace multisig
|
||||
* Key aggregation via aggregation coefficients prevents key cancellation attacks.
|
||||
* See: https://www.getmonero.org/resources/research-lab/pubs/MRL-0009.pdf
|
||||
* param: final_keys - address components (public keys) obtained from other participants (not shared with local)
|
||||
* param: privkeys_inout - private keys of address components known by local; each key will be multiplied by an aggregation coefficient (return by reference)
|
||||
* param: privkeys_inout - private keys of address components known by local; each key will be multiplied by an aggregation
|
||||
* coefficient (return by reference)
|
||||
* return: final multisig public spend key for the account
|
||||
*/
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@@ -199,7 +200,8 @@ namespace multisig
|
||||
for (std::size_t multisig_keys_index{0}; multisig_keys_index < privkeys_inout.size(); ++multisig_keys_index)
|
||||
{
|
||||
crypto::public_key pubkey;
|
||||
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(privkeys_inout[multisig_keys_index], pubkey), "Failed to derive public key");
|
||||
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(privkeys_inout[multisig_keys_index], pubkey),
|
||||
"Failed to derive public key");
|
||||
|
||||
own_keys_mapping[pubkey] = multisig_keys_index;
|
||||
|
||||
@@ -307,8 +309,7 @@ namespace multisig
|
||||
* INTERNAL
|
||||
*
|
||||
* brief: multisig_kex_msgs_sanitize_pubkeys - Sanitize multisig kex messages.
|
||||
* - Removes duplicates from msg pubkeys, ignores pubkeys equal to the local account's signing key,
|
||||
* ignores messages signed by the local account, ignores keys found in input 'exclusion set',
|
||||
* - Removes duplicates from msg pubkeys, ignores keys found in input 'exclusion set',
|
||||
* constructs map of pubkey:origins.
|
||||
* - Requires that all input msgs have the same round number.
|
||||
*
|
||||
@@ -316,15 +317,13 @@ namespace multisig
|
||||
*
|
||||
* - If the messages' round numbers are all '1', then only the message signing pubkey is considered
|
||||
* 'recommended'. Furthermore, the 'exclusion set' is ignored.
|
||||
* param: own_pubkey - local account's signing key (key used to sign multisig messages)
|
||||
* param: expanded_msgs - set of multisig kex messages to process
|
||||
* param: exclude_pubkeys - pubkeys to exclude from output set
|
||||
* outparam: sanitized_pubkeys_out - processed pubkeys obtained from msgs, mapped to their origins
|
||||
* return: round number shared by all input msgs
|
||||
*/
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static std::uint32_t multisig_kex_msgs_sanitize_pubkeys(const crypto::public_key &own_pubkey,
|
||||
const std::vector<multisig_kex_msg> &expanded_msgs,
|
||||
static std::uint32_t multisig_kex_msgs_sanitize_pubkeys(const std::vector<multisig_kex_msg> &expanded_msgs,
|
||||
const std::vector<crypto::public_key> &exclude_pubkeys,
|
||||
multisig_keyset_map_memsafe_t &sanitized_pubkeys_out)
|
||||
{
|
||||
@@ -339,10 +338,6 @@ namespace multisig
|
||||
// - origins = all the signing pubkeys that recommended a given msg pubkey
|
||||
for (const auto &expanded_msg : expanded_msgs)
|
||||
{
|
||||
// ignore messages from self
|
||||
if (expanded_msg.get_signing_pubkey() == own_pubkey)
|
||||
continue;
|
||||
|
||||
// in round 1, only the signing pubkey is treated as a msg pubkey
|
||||
if (round == 1)
|
||||
{
|
||||
@@ -355,10 +350,6 @@ namespace multisig
|
||||
// copy all pubkeys from message into list
|
||||
for (const auto &pubkey : expanded_msg.get_msg_pubkeys())
|
||||
{
|
||||
// ignore own pubkey
|
||||
if (pubkey == own_pubkey)
|
||||
continue;
|
||||
|
||||
// ignore pubkeys in 'ignore' set
|
||||
if (std::find(exclude_pubkeys.begin(), exclude_pubkeys.end(), pubkey) != exclude_pubkeys.end())
|
||||
continue;
|
||||
@@ -375,6 +366,31 @@ namespace multisig
|
||||
/**
|
||||
* INTERNAL
|
||||
*
|
||||
* brief: remove_key_from_mapped_sets - Remove a specified key from the mapped sets in a multisig keyset map.
|
||||
* param: key_to_remove - specified key to remove
|
||||
* inoutparam: keyset_inout - keyset to update
|
||||
*/
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
static void remove_key_from_mapped_sets(const crypto::public_key &key_to_remove,
|
||||
multisig_keyset_map_memsafe_t &keyset_inout)
|
||||
{
|
||||
// remove specified key from each mapped set
|
||||
for (auto keyset_it = keyset_inout.begin(); keyset_it != keyset_inout.end();)
|
||||
{
|
||||
// remove specified key from this set
|
||||
keyset_it->second.erase(key_to_remove);
|
||||
|
||||
// remove empty keyset positions or increment iterator
|
||||
if (keyset_it->second.size() == 0)
|
||||
keyset_it = keyset_inout.erase(keyset_it);
|
||||
else
|
||||
++keyset_it;
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* INTERNAL
|
||||
*
|
||||
* brief: evaluate_multisig_kex_round_msgs - Evaluate pubkeys from a kex round in order to prepare for the next round.
|
||||
* - Sanitizes input msgs.
|
||||
* - Require uniqueness in: 'exclude_pubkeys'.
|
||||
@@ -392,6 +408,8 @@ namespace multisig
|
||||
* param: signers - expected participants in multisig kex
|
||||
* param: expanded_msgs - set of multisig kex messages to process
|
||||
* param: exclude_pubkeys - derivations held by the local account corresponding to round 'expected_round'
|
||||
* param: incomplete_signer_set - only require the minimum number of signers to complete this round
|
||||
* minimum = num_signers - (round num - 1) (including local signer)
|
||||
* return: fully sanitized and validated pubkey:origins map for building the account's next kex round message
|
||||
*/
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@@ -400,7 +418,8 @@ namespace multisig
|
||||
const std::uint32_t expected_round,
|
||||
const std::vector<crypto::public_key> &signers,
|
||||
const std::vector<multisig_kex_msg> &expanded_msgs,
|
||||
const std::vector<crypto::public_key> &exclude_pubkeys)
|
||||
const std::vector<crypto::public_key> &exclude_pubkeys,
|
||||
const bool incomplete_signer_set)
|
||||
{
|
||||
// exclude_pubkeys should all be unique
|
||||
for (auto it = exclude_pubkeys.begin(); it != exclude_pubkeys.end(); ++it)
|
||||
@@ -410,21 +429,31 @@ namespace multisig
|
||||
}
|
||||
|
||||
// sanitize input messages
|
||||
multisig_keyset_map_memsafe_t pubkey_origins_map;
|
||||
const std::uint32_t round = multisig_kex_msgs_sanitize_pubkeys(base_pubkey, expanded_msgs, exclude_pubkeys, pubkey_origins_map);
|
||||
multisig_keyset_map_memsafe_t pubkey_origins_map; //map: [pubkey : [origins]]
|
||||
const std::uint32_t round = multisig_kex_msgs_sanitize_pubkeys(expanded_msgs, exclude_pubkeys, pubkey_origins_map);
|
||||
CHECK_AND_ASSERT_THROW_MES(round == expected_round,
|
||||
"Kex messages were for round [" << round << "], but expected round is [" << expected_round << "]");
|
||||
|
||||
// remove the local signer from each origins set in the sanitized pubkey map
|
||||
// note: intermediate kex rounds only need keys from other signers to make progress (keys from self are useless)
|
||||
remove_key_from_mapped_sets(base_pubkey, pubkey_origins_map);
|
||||
|
||||
// evaluate pubkeys collected
|
||||
std::unordered_map<crypto::public_key, std::unordered_set<crypto::public_key>> origin_pubkeys_map;
|
||||
std::unordered_map<crypto::public_key, std::unordered_set<crypto::public_key>> origin_pubkeys_map; //map: [origin: [pubkeys]]
|
||||
|
||||
// 1. each pubkey should be recommended by a precise number of signers
|
||||
const std::size_t num_recommendations_per_pubkey_required{
|
||||
incomplete_signer_set
|
||||
? 1
|
||||
: round
|
||||
};
|
||||
|
||||
for (const auto &pubkey_and_origins : pubkey_origins_map)
|
||||
{
|
||||
// expected amount = round_num
|
||||
// With each successive round, pubkeys are shared by incrementally larger groups,
|
||||
// starting at 1 in round 1 (i.e. the local multisig key to start kex with).
|
||||
CHECK_AND_ASSERT_THROW_MES(pubkey_and_origins.second.size() == round,
|
||||
CHECK_AND_ASSERT_THROW_MES(pubkey_and_origins.second.size() >= num_recommendations_per_pubkey_required,
|
||||
"A pubkey recommended by multisig kex messages had an unexpected number of recommendations.");
|
||||
|
||||
// map (sanitized) pubkeys back to origins
|
||||
@@ -433,8 +462,18 @@ namespace multisig
|
||||
}
|
||||
|
||||
// 2. the number of unique signers recommending pubkeys should equal the number of signers passed in (minus the local signer)
|
||||
CHECK_AND_ASSERT_THROW_MES(origin_pubkeys_map.size() == signers.size() - 1,
|
||||
"Number of unique other signers does not equal number of other signers that recommended pubkeys.");
|
||||
// - if an incomplete set is allowed, then we need at least one signer to represent each subgroup in this round that
|
||||
// doesn't include the local signer
|
||||
const std::size_t num_signers_required{
|
||||
incomplete_signer_set
|
||||
? signers.size() - 1 - (round - 1)
|
||||
: signers.size() - 1
|
||||
};
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(origin_pubkeys_map.size() >= num_signers_required,
|
||||
"Number of unique other signers recommending pubkeys does not equal number of required other signers "
|
||||
"(kex round: " << round << ", num signers found: " << origin_pubkeys_map.size() << ", num signers required: " <<
|
||||
num_signers_required << ").");
|
||||
|
||||
// 3. each origin should recommend a precise number of pubkeys
|
||||
|
||||
@@ -461,19 +500,20 @@ namespace multisig
|
||||
|
||||
// other signers: (N - 2) choose (msg_round_num - 1)
|
||||
// - Each signer recommends keys they share with other signers.
|
||||
// - In each round, a signer shares a key with 'round num - 1' other signers.
|
||||
// - Since 'origins pubkey map' excludes keys shared with the local account,
|
||||
// only keys shared with participants 'other than local and self' will be in the map (e.g. N - 2 signers).
|
||||
// - So other signers will recommend (N - 2) choose (msg_round_num - 1) pubkeys (after removing keys shared with local).
|
||||
// - Each origin should have a shared key with each group of size 'round - 1'.
|
||||
// Note: Keys shared with local are ignored to facilitate kex round boosting, where one or more signers may
|
||||
// - In each round, every group of size 'round num' will have a key. From a single signer's perspective,
|
||||
// they will share a key with every group of size 'round num - 1' of other signers.
|
||||
// - Since 'origins pubkey map' excludes keys shared with the local account, only keys shared with participants
|
||||
// 'other than local and self' will be in the map (e.g. N - 2 signers).
|
||||
// - Other signers will recommend (N - 2) choose (msg_round_num - 1) pubkeys (after removing keys shared with local).
|
||||
// Note: Keys shared with local are filtered out to facilitate kex round boosting, where one or more signers may
|
||||
// have boosted the local signer (implying they didn't have access to the local signer's previous round msg).
|
||||
const std::uint32_t expected_recommendations_others = n_choose_k_f(signers.size() - 2, round - 1);
|
||||
|
||||
// local: (N - 1) choose (msg_round_num - 1)
|
||||
const std::uint32_t expected_recommendations_self = n_choose_k_f(signers.size() - 1, round - 1);
|
||||
|
||||
// note: expected_recommendations_others would be 0 in the last round of 1-of-N, but we return early for that case
|
||||
// note: expected_recommendations_others would be 0 in the last round of 1-of-N, but we don't call this function for
|
||||
// that case
|
||||
CHECK_AND_ASSERT_THROW_MES(expected_recommendations_self > 0 && expected_recommendations_others > 0,
|
||||
"Bad num signers or round num (possibly numerical limits exceeded).");
|
||||
|
||||
@@ -485,7 +525,7 @@ namespace multisig
|
||||
for (const auto &origin_and_pubkeys : origin_pubkeys_map)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(origin_and_pubkeys.second.size() == expected_recommendations_others,
|
||||
"A pubkey recommended by multisig kex messages had an unexpected number of recommendations.");
|
||||
"A multisig signer recommended an unexpected number of pubkeys.");
|
||||
|
||||
// 2 (continued). only expected signers should be recommending keys
|
||||
CHECK_AND_ASSERT_THROW_MES(std::find(signers.begin(), signers.end(), origin_and_pubkeys.first) != signers.end(),
|
||||
@@ -507,6 +547,7 @@ namespace multisig
|
||||
* param: expected_round - expected kex round of input messages
|
||||
* param: signers - expected participants in multisig kex
|
||||
* param: expanded_msgs - set of multisig kex messages to process
|
||||
* param: incomplete_signer_set - only require the minimum amount of messages to complete this round (1 message)
|
||||
* return: sanitized and validated pubkey:origins map
|
||||
*/
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@@ -514,15 +555,20 @@ namespace multisig
|
||||
const crypto::public_key &base_pubkey,
|
||||
const std::uint32_t expected_round,
|
||||
const std::vector<crypto::public_key> &signers,
|
||||
const std::vector<multisig_kex_msg> &expanded_msgs)
|
||||
const std::vector<multisig_kex_msg> &expanded_msgs,
|
||||
const bool incomplete_signer_set)
|
||||
{
|
||||
// sanitize input messages
|
||||
const std::vector<crypto::public_key> dummy;
|
||||
multisig_keyset_map_memsafe_t pubkey_origins_map;
|
||||
const std::uint32_t round = multisig_kex_msgs_sanitize_pubkeys(base_pubkey, expanded_msgs, dummy, pubkey_origins_map);
|
||||
multisig_keyset_map_memsafe_t pubkey_origins_map; //map: [pubkey : [origins]]
|
||||
const std::uint32_t round = multisig_kex_msgs_sanitize_pubkeys(expanded_msgs, dummy, pubkey_origins_map);
|
||||
CHECK_AND_ASSERT_THROW_MES(round == expected_round,
|
||||
"Kex messages were for round [" << round << "], but expected round is [" << expected_round << "]");
|
||||
|
||||
// note: do NOT remove the local signer from the pubkey origins map, since the post-kex round can be force-updated with
|
||||
// just the local signer's post-kex message (if the local signer were removed, then the post-kex message's pubkeys
|
||||
// would be completely lost)
|
||||
|
||||
// evaluate pubkeys collected
|
||||
|
||||
// 1) there should only be two pubkeys
|
||||
@@ -533,17 +579,26 @@ namespace multisig
|
||||
CHECK_AND_ASSERT_THROW_MES(pubkey_origins_map.begin()->second == (++(pubkey_origins_map.begin()))->second,
|
||||
"Multisig post-kex round messages from other signers did not all recommend the same pubkey pair.");
|
||||
|
||||
// 3) all signers should be present in the recommendation list
|
||||
// 3) all signers should be present in the recommendation list (unless an incomplete list is permitted)
|
||||
auto origins = pubkey_origins_map.begin()->second;
|
||||
origins.insert(base_pubkey); //add self
|
||||
origins.insert(base_pubkey); //add self if missing
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(origins.size() == signers.size(),
|
||||
"Multisig post-kex round message origins don't line up with multisig signer set.");
|
||||
const std::size_t num_signers_required{
|
||||
incomplete_signer_set
|
||||
? 1
|
||||
: signers.size()
|
||||
};
|
||||
|
||||
for (const crypto::public_key &signer : signers)
|
||||
CHECK_AND_ASSERT_THROW_MES(origins.size() >= num_signers_required,
|
||||
"Multisig post-kex round message origins don't line up with multisig signer set "
|
||||
"(num signers found: " << origins.size() << ", num signers required: " << num_signers_required << ").");
|
||||
|
||||
for (const crypto::public_key &origin : origins)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(origins.find(signer) != origins.end(),
|
||||
"Could not find an expected signer in multisig post-kex round messages (all signers expected).");
|
||||
// note: if num_signers_required == signers.size(), then this test will ensure all signers are present in 'origins',
|
||||
// which contains only unique pubkeys
|
||||
CHECK_AND_ASSERT_THROW_MES(std::find(signers.begin(), signers.end(), origin) != signers.end(),
|
||||
"An unknown origin recommended a multisig post-kex verification messsage.");
|
||||
}
|
||||
|
||||
return pubkey_origins_map;
|
||||
@@ -564,6 +619,7 @@ namespace multisig
|
||||
* param: expanded_msgs - set of multisig kex messages to process
|
||||
* param: exclude_pubkeys - keys held by the local account corresponding to round 'current_round'
|
||||
* - If 'current_round' is the final round, these are the local account's shares of the final aggregate key.
|
||||
* param: incomplete_signer_set - allow messages from an incomplete signer set
|
||||
* outparam: keys_to_origins_map_out - map between round keys and identity keys
|
||||
* - If in the final round, these are key shares recommended by other signers for the final aggregate key.
|
||||
* - Otherwise, these are the local account's DH derivations for the next round.
|
||||
@@ -578,6 +634,7 @@ namespace multisig
|
||||
const std::vector<crypto::public_key> &signers,
|
||||
const std::vector<multisig_kex_msg> &expanded_msgs,
|
||||
const std::vector<crypto::public_key> &exclude_pubkeys,
|
||||
const bool incomplete_signer_set,
|
||||
multisig_keyset_map_memsafe_t &keys_to_origins_map_out)
|
||||
{
|
||||
check_multisig_config(current_round, threshold, signers.size());
|
||||
@@ -598,7 +655,8 @@ namespace multisig
|
||||
current_round,
|
||||
signers,
|
||||
expanded_msgs,
|
||||
exclude_pubkeys);
|
||||
exclude_pubkeys,
|
||||
incomplete_signer_set);
|
||||
}
|
||||
else //(current_round == kex_rounds_required + 1)
|
||||
{
|
||||
@@ -606,7 +664,8 @@ namespace multisig
|
||||
evaluated_pubkeys = evaluate_multisig_post_kex_round_msgs(base_pubkey,
|
||||
current_round,
|
||||
signers,
|
||||
expanded_msgs);
|
||||
expanded_msgs,
|
||||
incomplete_signer_set);
|
||||
}
|
||||
|
||||
// prepare keys-to-origins map for updating the multisig account
|
||||
@@ -693,9 +752,9 @@ namespace multisig
|
||||
{
|
||||
// post-kex verification round: check that the multisig pubkey and common pubkey were recommended by other signers
|
||||
CHECK_AND_ASSERT_THROW_MES(result_keys_to_origins_map.count(m_multisig_pubkey) > 0,
|
||||
"Multisig post-kex round: expected multisig pubkey wasn't found in other signers' messages.");
|
||||
"Multisig post-kex round: expected multisig pubkey wasn't found in input messages.");
|
||||
CHECK_AND_ASSERT_THROW_MES(result_keys_to_origins_map.count(m_common_pubkey) > 0,
|
||||
"Multisig post-kex round: expected common pubkey wasn't found in other signers' messages.");
|
||||
"Multisig post-kex round: expected common pubkey wasn't found in input messages.");
|
||||
|
||||
// save keys that should be recommended to other signers
|
||||
// - for convenience, re-recommend the post-kex verification message once an account is complete
|
||||
@@ -790,7 +849,8 @@ namespace multisig
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// multisig_account: INTERNAL
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void multisig_account::kex_update_impl(const std::vector<multisig_kex_msg> &expanded_msgs)
|
||||
void multisig_account::kex_update_impl(const std::vector<multisig_kex_msg> &expanded_msgs,
|
||||
const bool incomplete_signer_set)
|
||||
{
|
||||
// check messages are for the expected kex round
|
||||
check_messages_round(expanded_msgs, m_kex_rounds_complete + 1);
|
||||
@@ -816,6 +876,7 @@ namespace multisig
|
||||
m_signers,
|
||||
expanded_msgs,
|
||||
exclude_pubkeys,
|
||||
incomplete_signer_set,
|
||||
result_keys_to_origins_map);
|
||||
|
||||
// finish account update
|
||||
|
||||
+7
-1
@@ -38,7 +38,7 @@ namespace net
|
||||
{
|
||||
void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port)
|
||||
{
|
||||
// require ipv6 address format "[addr:addr:addr:...:addr]:port"
|
||||
// If IPv6 address format with port "[addr:addr:addr:...:addr]:port"
|
||||
if (address.find(']') != std::string::npos)
|
||||
{
|
||||
host = address.substr(1, address.rfind(']') - 1);
|
||||
@@ -47,6 +47,12 @@ namespace net
|
||||
port = address.substr(address.rfind(':') + 1);
|
||||
}
|
||||
}
|
||||
// Else if IPv6 address format without port e.g. "addr:addr:addr:...:addr"
|
||||
else if (std::count(address.begin(), address.end(), ':') >= 2)
|
||||
{
|
||||
host = address;
|
||||
}
|
||||
// Else IPv4, Tor, I2P address or hostname
|
||||
else
|
||||
{
|
||||
host = address.substr(0, address.rfind(':'));
|
||||
|
||||
@@ -38,6 +38,16 @@
|
||||
|
||||
namespace net
|
||||
{
|
||||
/*!
|
||||
* \brief Takes a valid address string (IP, Tor, I2P, or DNS name) and splits it into host and port
|
||||
*
|
||||
* The host of an IPv6 addresses in the format "[x:x:..:x]:port" will have the braces stripped.
|
||||
* For example, when the address is "[ffff::2023]", host will be set to "ffff::2023".
|
||||
*
|
||||
* \param address The address string one wants to split
|
||||
* \param[out] host The host part of the address string. Is always set.
|
||||
* \param[out] port The port part of the address string. Is only set when address string contains a port.
|
||||
*/
|
||||
void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port);
|
||||
|
||||
/*!
|
||||
|
||||
+15
-23
@@ -645,20 +645,10 @@ namespace nodetool
|
||||
{
|
||||
using namespace boost::asio;
|
||||
|
||||
std::string host = addr;
|
||||
// Split addr string into host string and port string
|
||||
std::string host;
|
||||
std::string port = std::to_string(default_port);
|
||||
size_t colon_pos = addr.find_last_of(':');
|
||||
size_t dot_pos = addr.find_last_of('.');
|
||||
size_t square_brace_pos = addr.find('[');
|
||||
|
||||
// IPv6 will have colons regardless. IPv6 and IPv4 address:port will have a colon but also either a . or a [
|
||||
// as IPv6 addresses specified as address:port are to be specified as "[addr:addr:...:addr]:port"
|
||||
// One may also specify an IPv6 address as simply "[addr:addr:...:addr]" without the port; in that case
|
||||
// the square braces will be stripped here.
|
||||
if ((std::string::npos != colon_pos && std::string::npos != dot_pos) || std::string::npos != square_brace_pos)
|
||||
{
|
||||
net::get_network_address_host_and_port(addr, host, port);
|
||||
}
|
||||
net::get_network_address_host_and_port(addr, host, port);
|
||||
MINFO("Resolving node address: host=" << host << ", port=" << port);
|
||||
|
||||
io_service io_srv;
|
||||
@@ -695,34 +685,32 @@ namespace nodetool
|
||||
std::set<std::string> full_addrs;
|
||||
if (m_nettype == cryptonote::TESTNET)
|
||||
{
|
||||
full_addrs.insert("212.83.175.67:28080");
|
||||
full_addrs.insert("212.83.172.165:28080");
|
||||
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");
|
||||
}
|
||||
else if (m_nettype == cryptonote::STAGENET)
|
||||
{
|
||||
full_addrs.insert("162.210.173.150:38080");
|
||||
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");
|
||||
}
|
||||
else if (m_nettype == cryptonote::FAKECHAIN)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
full_addrs.insert("212.83.175.67:18080");
|
||||
full_addrs.insert("212.83.172.165:18080");
|
||||
full_addrs.insert("176.9.0.187:18080");
|
||||
full_addrs.insert("88.198.163.90:18080");
|
||||
full_addrs.insert("95.217.25.101:18080");
|
||||
full_addrs.insert("136.244.105.131:18080");
|
||||
full_addrs.insert("104.238.221.81: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");
|
||||
}
|
||||
return full_addrs;
|
||||
}
|
||||
@@ -857,6 +845,8 @@ namespace nodetool
|
||||
"4pixvbejrvihnkxmduo2agsnmc3rrulrqc7s3cbwwrep6h6hrzsibeqd.onion:18083",
|
||||
"zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083",
|
||||
"qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083",
|
||||
"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083",
|
||||
"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083",
|
||||
};
|
||||
}
|
||||
return {};
|
||||
@@ -865,7 +855,9 @@ namespace nodetool
|
||||
{
|
||||
return {
|
||||
"s3l6ke4ed3df466khuebb4poienoingwof7oxtbo6j4n56sghe3a.b32.i2p:18080",
|
||||
"sel36x6fibfzujwvt4hf5gxolz6kd3jpvbjqg6o3ud2xtionyl2q.b32.i2p:18080"
|
||||
"sel36x6fibfzujwvt4hf5gxolz6kd3jpvbjqg6o3ud2xtionyl2q.b32.i2p:18080",
|
||||
"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p:18080",
|
||||
"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p:18080",
|
||||
};
|
||||
}
|
||||
return {};
|
||||
@@ -2413,7 +2405,7 @@ namespace nodetool
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}, "0.0.0.0", m_ssl_support);
|
||||
if(!r)
|
||||
{
|
||||
LOG_WARNING_CC(context, "Failed to call connect_async, network error.");
|
||||
|
||||
+40
-3
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "misc_log_ex.h"
|
||||
#include "misc_language.h"
|
||||
#include "common/data_cache.h"
|
||||
#include "common/perf_timer.h"
|
||||
#include "common/threadpool.h"
|
||||
#include "common/util.h"
|
||||
@@ -1333,7 +1334,7 @@ namespace rct {
|
||||
try
|
||||
{
|
||||
if (semantics) {
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
|
||||
tools::threadpool::waiter waiter(tpool);
|
||||
std::deque<bool> results(rv.outPk.size(), false);
|
||||
DP("range proofs verified?");
|
||||
@@ -1383,7 +1384,7 @@ namespace rct {
|
||||
{
|
||||
PERF_TIMER(verRctSemanticsSimple);
|
||||
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
|
||||
tools::threadpool::waiter waiter(tpool);
|
||||
std::deque<bool> results;
|
||||
std::vector<const Bulletproof*> bp_proofs;
|
||||
@@ -1536,7 +1537,7 @@ namespace rct {
|
||||
const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size());
|
||||
|
||||
std::deque<bool> results(threads);
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
|
||||
tools::threadpool::waiter waiter(tpool);
|
||||
|
||||
const keyV &pseudoOuts = bulletproof || bulletproof_plus ? rv.p.pseudoOuts : rv.pseudoOuts;
|
||||
@@ -1578,6 +1579,42 @@ namespace rct {
|
||||
}
|
||||
}
|
||||
|
||||
bool verRctNonSemanticsSimpleCached(const rctSig & rv)
|
||||
{
|
||||
// Hello future Monero dev! If you got this assert, read the following carefully:
|
||||
//
|
||||
// RCT cache assumes that this function will serialize and hash all rv's fields used for RingCT verification
|
||||
// If you're about to add a new RCTType here, first you must check that binary_archive serialization writes all rv's fields to the binary blob
|
||||
// If it's not the case, rewrite this function to serialize everything, even some "temporary" fields which are not serialized normally
|
||||
CHECK_AND_ASSERT_MES_L1(rv.type <= RCTTypeBulletproofPlus, false, "Unknown RCT type. Make sure RCT cache works correctly with this type and then enable it in the code here.");
|
||||
|
||||
// Don't cache older (or newer) rctSig types
|
||||
// This cache only makes sense when it caches data from mempool first,
|
||||
// so only "current fork version-enabled" RCT types need to be cached
|
||||
if (rv.type != RCTTypeBulletproofPlus)
|
||||
return verRctNonSemanticsSimple(rv);
|
||||
|
||||
// Get the hash of rv
|
||||
std::stringstream ss;
|
||||
binary_archive<true> ar(ss);
|
||||
|
||||
::do_serialize(ar, const_cast<rctSig&>(rv));
|
||||
|
||||
crypto::hash h;
|
||||
cryptonote::get_blob_hash(ss.str(), h);
|
||||
|
||||
static tools::data_cache<crypto::hash, 8192> cache;
|
||||
|
||||
if (cache.has(h))
|
||||
return true;
|
||||
|
||||
const bool res = verRctNonSemanticsSimple(rv);
|
||||
if (res)
|
||||
cache.add(h);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//RingCT protocol
|
||||
//genRct:
|
||||
// creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
|
||||
|
||||
@@ -132,6 +132,7 @@ namespace rct {
|
||||
bool verRctSemanticsSimple(const rctSig & rv);
|
||||
bool verRctSemanticsSimple(const std::vector<const rctSig*> & rv);
|
||||
bool verRctNonSemanticsSimple(const rctSig & rv);
|
||||
bool verRctNonSemanticsSimpleCached(const rctSig & rv);
|
||||
static inline bool verRctSimple(const rctSig & rv) { return verRctSemanticsSimple(rv) && verRctNonSemanticsSimple(rv); }
|
||||
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
|
||||
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);
|
||||
|
||||
@@ -2290,6 +2290,12 @@ namespace cryptonote
|
||||
return m_bootstrap_daemon->handle_result(false, {});
|
||||
}
|
||||
|
||||
if (bootstrap_daemon_height < m_core.get_checkpoints().get_max_height())
|
||||
{
|
||||
MINFO("Bootstrap daemon height is lower than the latest checkpoint");
|
||||
return m_bootstrap_daemon->handle_result(false, {});
|
||||
}
|
||||
|
||||
if (!m_p2p.get_payload_object().no_sync())
|
||||
{
|
||||
uint64_t top_height = m_core.get_current_blockchain_height();
|
||||
@@ -2855,6 +2861,10 @@ namespace cryptonote
|
||||
|
||||
res.version = CORE_RPC_VERSION;
|
||||
res.release = MONERO_VERSION_IS_RELEASE;
|
||||
res.current_height = m_core.get_current_blockchain_height();
|
||||
res.target_height = m_p2p.get_payload_object().is_synchronized() ? 0 : m_core.get_target_blockchain_height();
|
||||
for (const auto &hf : m_core.get_blockchain_storage().get_hardforks())
|
||||
res.hard_forks.push_back({hf.version, hf.height});
|
||||
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 10
|
||||
#define CORE_RPC_VERSION_MINOR 11
|
||||
#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)
|
||||
|
||||
@@ -2123,15 +2123,34 @@ namespace cryptonote
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct hf_entry
|
||||
{
|
||||
uint8_t hf_version;
|
||||
uint64_t height;
|
||||
|
||||
bool operator==(const hf_entry& hfe) const { return hf_version == hfe.hf_version && height == hfe.height; }
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(hf_version)
|
||||
KV_SERIALIZE(height)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response_t: public rpc_response_base
|
||||
{
|
||||
uint32_t version;
|
||||
bool release;
|
||||
uint64_t current_height;
|
||||
uint64_t target_height;
|
||||
std::vector<hf_entry> hard_forks;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE_PARENT(rpc_response_base)
|
||||
KV_SERIALIZE(version)
|
||||
KV_SERIALIZE(release)
|
||||
KV_SERIALIZE_OPT(current_height, (uint64_t)0)
|
||||
KV_SERIALIZE_OPT(target_height, (uint64_t)0)
|
||||
KV_SERIALIZE_OPT(hard_forks, std::vector<hf_entry>())
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
|
||||
@@ -236,10 +236,8 @@ namespace cryptonote
|
||||
*(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(nonce);
|
||||
if (block.major_version >= RX_BLOCK_VERSION)
|
||||
{
|
||||
const uint64_t seed_height = is_current ? info.seed_height : info.previous_seed_height;
|
||||
const crypto::hash &seed_hash = is_current ? info.seed_hash : info.previous_seed_hash;
|
||||
const uint64_t height = cryptonote::get_block_height(block);
|
||||
crypto::rx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data, 0, 0);
|
||||
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
// Copyright (c) 2014-2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "serialization.h"
|
||||
|
||||
namespace serialization
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <typename Archive, class T>
|
||||
bool serialize_tuple_element(Archive& ar, T& e)
|
||||
{
|
||||
return ::do_serialize(ar, e);
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
bool serialize_tuple_element(Archive& ar, uint64_t& e)
|
||||
{
|
||||
ar.serialize_varint(e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <template <bool> class Archive, class E0, class E1, class E2>
|
||||
inline bool do_serialize(Archive<false>& ar, std::tuple<E0,E1,E2>& p)
|
||||
{
|
||||
size_t cnt;
|
||||
ar.begin_array(cnt);
|
||||
if (!ar.good())
|
||||
return false;
|
||||
if (cnt != 3)
|
||||
return false;
|
||||
|
||||
if (!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.delimit_array();
|
||||
if (!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.delimit_array();
|
||||
if (!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
|
||||
ar.end_array();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <template <bool> class Archive, class E0, class E1, class E2>
|
||||
inline bool do_serialize(Archive<true>& ar, std::tuple<E0,E1,E2>& p)
|
||||
{
|
||||
ar.begin_array(3);
|
||||
if (!ar.good())
|
||||
return false;
|
||||
if(!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.delimit_array();
|
||||
if(!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.delimit_array();
|
||||
if(!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.end_array();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <template <bool> class Archive, class E0, class E1, class E2, class E3>
|
||||
inline bool do_serialize(Archive<false>& ar, std::tuple<E0,E1,E2,E3>& p)
|
||||
{
|
||||
size_t cnt;
|
||||
ar.begin_array(cnt);
|
||||
if (!ar.good())
|
||||
return false;
|
||||
if (cnt != 4)
|
||||
return false;
|
||||
|
||||
if (!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.delimit_array();
|
||||
if (!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.delimit_array();
|
||||
if (!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.delimit_array();
|
||||
if (!::serialization::detail::serialize_tuple_element(ar, std::get<3>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
|
||||
ar.end_array();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <template <bool> class Archive, class E0, class E1, class E2, class E3>
|
||||
inline bool do_serialize(Archive<true>& ar, std::tuple<E0,E1,E2,E3>& p)
|
||||
{
|
||||
ar.begin_array(4);
|
||||
if (!ar.good())
|
||||
return false;
|
||||
if(!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.delimit_array();
|
||||
if(!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.delimit_array();
|
||||
if(!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.delimit_array();
|
||||
if(!::serialization::detail::serialize_tuple_element(ar, std::get<3>(p)))
|
||||
return false;
|
||||
if (!ar.good())
|
||||
return false;
|
||||
ar.end_array();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -181,7 +181,6 @@ namespace
|
||||
const command_line::arg_descriptor<bool> arg_restore_from_seed = {"restore-from-seed", sw::tr("alias for --restore-deterministic-wallet"), false};
|
||||
const command_line::arg_descriptor<bool> arg_restore_multisig_wallet = {"restore-multisig-wallet", sw::tr("Recover multisig wallet using Electrum-style mnemonic seed"), false};
|
||||
const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), false};
|
||||
const command_line::arg_descriptor<bool> arg_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false};
|
||||
const command_line::arg_descriptor<uint64_t> arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0};
|
||||
const command_line::arg_descriptor<std::string> arg_restore_date = {"restore-date", sw::tr("Restore from estimated blockchain height on specified date"), ""};
|
||||
const command_line::arg_descriptor<bool> arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the monero network"), false};
|
||||
@@ -244,7 +243,7 @@ namespace
|
||||
const char* USAGE_IMPORT_OUTPUTS("import_outputs <filename>");
|
||||
const char* USAGE_SHOW_TRANSFER("show_transfer <txid>");
|
||||
const char* USAGE_MAKE_MULTISIG("make_multisig <threshold> <string1> [<string>...]");
|
||||
const char* USAGE_EXCHANGE_MULTISIG_KEYS("exchange_multisig_keys <string> [<string>...]");
|
||||
const char* USAGE_EXCHANGE_MULTISIG_KEYS("exchange_multisig_keys [force-update-use-with-caution] <string> [<string>...]");
|
||||
const char* USAGE_EXPORT_MULTISIG_INFO("export_multisig_info <filename>");
|
||||
const char* USAGE_IMPORT_MULTISIG_INFO("import_multisig_info <filename> [<filename>...]");
|
||||
const char* USAGE_SIGN_MULTISIG("sign_multisig <filename>");
|
||||
@@ -1139,11 +1138,22 @@ bool simple_wallet::make_multisig_main(const std::vector<std::string> &args, boo
|
||||
bool simple_wallet::exchange_multisig_keys(const std::vector<std::string> &args)
|
||||
{
|
||||
CHECK_MULTISIG_ENABLED();
|
||||
exchange_multisig_keys_main(args, false);
|
||||
bool force_update_use_with_caution = false;
|
||||
|
||||
auto local_args = args;
|
||||
if (args.size() >= 1 && local_args[0] == "force-update-use-with-caution")
|
||||
{
|
||||
force_update_use_with_caution = true;
|
||||
local_args.erase(local_args.begin());
|
||||
}
|
||||
|
||||
exchange_multisig_keys_main(local_args, force_update_use_with_caution, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> &args, bool called_by_mms) {
|
||||
bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> &args,
|
||||
const bool force_update_use_with_caution,
|
||||
const bool called_by_mms) {
|
||||
CHECK_MULTISIG_ENABLED();
|
||||
bool ready;
|
||||
if (m_wallet->key_on_device())
|
||||
@@ -1169,15 +1179,9 @@ bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> &
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.size() < 1)
|
||||
{
|
||||
PRINT_USAGE(USAGE_EXCHANGE_MULTISIG_KEYS);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::string multisig_extra_info = m_wallet->exchange_multisig_keys(orig_pwd_container->password(), args);
|
||||
std::string multisig_extra_info = m_wallet->exchange_multisig_keys(orig_pwd_container->password(), args, force_update_use_with_caution);
|
||||
bool ready;
|
||||
m_wallet->multisig(&ready);
|
||||
if (!ready)
|
||||
@@ -3233,8 +3237,7 @@ bool simple_wallet::scan_tx(const std::vector<std::string> &args)
|
||||
}
|
||||
|
||||
simple_wallet::simple_wallet()
|
||||
: m_allow_mismatched_daemon_version(false)
|
||||
, m_refresh_progress_reporter(*this)
|
||||
: m_refresh_progress_reporter(*this)
|
||||
, m_idle_run(true)
|
||||
, m_auto_refresh_enabled(false)
|
||||
, m_auto_refresh_refreshing(false)
|
||||
@@ -4118,6 +4121,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
||||
|
||||
epee::wipeable_string multisig_keys;
|
||||
epee::wipeable_string password;
|
||||
epee::wipeable_string seed_pass;
|
||||
|
||||
if (!handle_command_line(vm))
|
||||
return false;
|
||||
@@ -4134,6 +4138,17 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
||||
if(!ask_wallet_create_if_needed()) return false;
|
||||
}
|
||||
|
||||
bool enable_multisig = false;
|
||||
if (m_restore_multisig_wallet) {
|
||||
fail_msg_writer() << tr("Multisig is disabled.");
|
||||
fail_msg_writer() << tr("Multisig is an experimental feature and may have bugs. Things that could go wrong include: funds sent to a multisig wallet can't be spent at all, can only be spent with the participation of a malicious group member, or can be stolen by a malicious group member.");
|
||||
if (!command_line::is_yes(input_line("Do you want to continue restoring a multisig wallet?", true))) {
|
||||
message_writer() << tr("You have canceled restoring a multisig wallet.");
|
||||
return false;
|
||||
}
|
||||
enable_multisig = true;
|
||||
}
|
||||
|
||||
if (!m_generate_new.empty() || m_restoring)
|
||||
{
|
||||
if (!m_subaddress_lookahead.empty() && !parse_subaddress_lookahead(m_subaddress_lookahead))
|
||||
@@ -4213,19 +4228,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
||||
auto pwd_container = password_prompter(tr("Enter seed offset passphrase, empty if none"), false);
|
||||
if (std::cin.eof() || !pwd_container)
|
||||
return false;
|
||||
epee::wipeable_string seed_pass = pwd_container->password();
|
||||
if (!seed_pass.empty())
|
||||
{
|
||||
if (m_restore_multisig_wallet)
|
||||
{
|
||||
crypto::secret_key key;
|
||||
crypto::cn_slow_hash(seed_pass.data(), seed_pass.size(), (crypto::hash&)key);
|
||||
sc_reduce32((unsigned char*)key.data);
|
||||
multisig_keys = m_wallet->decrypt<epee::wipeable_string>(std::string(multisig_keys.data(), multisig_keys.size()), key, true);
|
||||
}
|
||||
else
|
||||
m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
|
||||
}
|
||||
seed_pass = pwd_container->password();
|
||||
if (!seed_pass.empty() && !m_restore_multisig_wallet)
|
||||
m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
|
||||
}
|
||||
if (!m_generate_from_view_key.empty())
|
||||
{
|
||||
@@ -4568,7 +4573,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
||||
m_wallet_file = m_generate_new;
|
||||
boost::optional<epee::wipeable_string> r;
|
||||
if (m_restore_multisig_wallet)
|
||||
r = new_wallet(vm, multisig_keys, old_language);
|
||||
r = new_wallet(vm, multisig_keys, seed_pass, old_language);
|
||||
else
|
||||
r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language);
|
||||
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
|
||||
@@ -4667,6 +4672,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
||||
}
|
||||
m_wallet->set_refresh_from_block_height(m_restore_height);
|
||||
}
|
||||
if (enable_multisig)
|
||||
m_wallet->enable_multisig(true);
|
||||
m_wallet->rewrite(m_wallet_file, password);
|
||||
}
|
||||
else
|
||||
@@ -4755,7 +4762,6 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
|
||||
m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet) || command_line::get_arg(vm, arg_restore_from_seed);
|
||||
m_restore_multisig_wallet = command_line::get_arg(vm, arg_restore_multisig_wallet);
|
||||
m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic);
|
||||
m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version);
|
||||
m_restore_height = command_line::get_arg(vm, arg_restore_height);
|
||||
m_restore_date = command_line::get_arg(vm, arg_restore_date);
|
||||
m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay);
|
||||
@@ -4786,12 +4792,20 @@ bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version)
|
||||
uint32_t version_ = 0;
|
||||
if (!version)
|
||||
version = &version_;
|
||||
if (!m_wallet->check_connection(version))
|
||||
bool wallet_is_outdated = false, daemon_is_outdated = false;
|
||||
if (!m_wallet->check_connection(version, NULL, 200000, &wallet_is_outdated, &daemon_is_outdated))
|
||||
{
|
||||
if (!silent)
|
||||
{
|
||||
if (m_wallet->is_offline())
|
||||
fail_msg_writer() << tr("wallet failed to connect to daemon, because it is set to offline mode");
|
||||
else if (wallet_is_outdated)
|
||||
fail_msg_writer() << tr("wallet failed to connect to daemon, because it is not up to date. ") <<
|
||||
tr("Please make sure you are running the latest wallet.");
|
||||
else if (daemon_is_outdated)
|
||||
fail_msg_writer() << tr("wallet failed to connect to daemon: ") << m_wallet->get_daemon_address() << ". " <<
|
||||
tr("Daemon is not up to date. "
|
||||
"Please make sure the daemon is running the latest version or change the daemon address using the 'set_daemon' command.");
|
||||
else
|
||||
fail_msg_writer() << tr("wallet failed to connect to daemon: ") << m_wallet->get_daemon_address() << ". " <<
|
||||
tr("Daemon either is not started or wrong port was passed. "
|
||||
@@ -4799,7 +4813,7 @@ bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!m_allow_mismatched_daemon_version && ((*version >> 16) != CORE_RPC_VERSION_MAJOR))
|
||||
if (!m_wallet->is_mismatched_daemon_version_allowed() && ((*version >> 16) != CORE_RPC_VERSION_MAJOR))
|
||||
{
|
||||
if (!silent)
|
||||
fail_msg_writer() << boost::format(tr("Daemon uses a different RPC major version (%u) than the wallet (%u): %s. Either update one of them, or use --allow-mismatched-daemon-version.")) % (*version>>16) % CORE_RPC_VERSION_MAJOR % m_wallet->get_daemon_address();
|
||||
@@ -5057,7 +5071,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
|
||||
const epee::wipeable_string &multisig_keys, const std::string &old_language)
|
||||
const epee::wipeable_string &multisig_keys, const epee::wipeable_string &seed_pass, const std::string &old_language)
|
||||
{
|
||||
std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc;
|
||||
try { rc = tools::wallet2::make_new(vm, false, password_prompter); }
|
||||
@@ -5091,7 +5105,16 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
||||
|
||||
try
|
||||
{
|
||||
m_wallet->generate(m_wallet_file, std::move(rc.second).password(), multisig_keys, create_address_file);
|
||||
if (seed_pass.empty())
|
||||
m_wallet->generate(m_wallet_file, std::move(rc.second).password(), multisig_keys, create_address_file);
|
||||
else
|
||||
{
|
||||
crypto::secret_key key;
|
||||
crypto::cn_slow_hash(seed_pass.data(), seed_pass.size(), (crypto::hash&)key);
|
||||
sc_reduce32((unsigned char*)key.data);
|
||||
const epee::wipeable_string &msig_keys = m_wallet->decrypt<epee::wipeable_string>(std::string(multisig_keys.data(), multisig_keys.size()), key, true);
|
||||
m_wallet->generate(m_wallet_file, std::move(rc.second).password(), msig_keys, create_address_file);
|
||||
}
|
||||
bool ready;
|
||||
uint32_t threshold, total;
|
||||
if (!m_wallet->multisig(&ready, &threshold, &total) || !ready)
|
||||
@@ -7912,8 +7935,10 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
|
||||
bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
|
||||
{
|
||||
std::string extra_message;
|
||||
if (!txs.transfers.second.empty())
|
||||
extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.second.size()).str();
|
||||
if (!std::get<2>(txs.new_transfers).empty())
|
||||
extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(txs.new_transfers).size()).str();
|
||||
else if (!std::get<2>(txs.transfers).empty())
|
||||
extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(txs.transfers).size()).str();
|
||||
return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}, extra_message);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -10627,7 +10652,6 @@ int main(int argc, char* argv[])
|
||||
command_line::add_arg(desc_params, arg_restore_multisig_wallet );
|
||||
command_line::add_arg(desc_params, arg_non_deterministic );
|
||||
command_line::add_arg(desc_params, arg_electrum_seed );
|
||||
command_line::add_arg(desc_params, arg_allow_mismatched_daemon_version);
|
||||
command_line::add_arg(desc_params, arg_restore_height);
|
||||
command_line::add_arg(desc_params, arg_restore_date);
|
||||
command_line::add_arg(desc_params, arg_do_not_relay);
|
||||
@@ -11157,7 +11181,8 @@ void simple_wallet::mms_next(const std::vector<std::string> &args)
|
||||
mms::message m = ms.get_message_by_id(data.message_ids[i]);
|
||||
sig_args[i] = m.content;
|
||||
}
|
||||
command_successful = exchange_multisig_keys_main(sig_args, true);
|
||||
// todo: update mms to enable 'key exchange force updating'
|
||||
command_successful = exchange_multisig_keys_main(sig_args, false, true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace cryptonote
|
||||
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
|
||||
const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
|
||||
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm,
|
||||
const epee::wipeable_string &multisig_keys, const std::string &old_language);
|
||||
const epee::wipeable_string &multisig_keys, const epee::wipeable_string &seed_pass, const std::string &old_language);
|
||||
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm);
|
||||
boost::optional<epee::wipeable_string> open_wallet(const boost::program_options::variables_map& vm);
|
||||
bool close_wallet();
|
||||
@@ -235,7 +235,7 @@ namespace cryptonote
|
||||
bool make_multisig(const std::vector<std::string>& args);
|
||||
bool make_multisig_main(const std::vector<std::string>& args, bool called_by_mms);
|
||||
bool exchange_multisig_keys(const std::vector<std::string> &args);
|
||||
bool exchange_multisig_keys_main(const std::vector<std::string> &args, bool called_by_mms);
|
||||
bool exchange_multisig_keys_main(const std::vector<std::string> &args, const bool force_update_use_with_caution, const bool called_by_mms);
|
||||
bool export_multisig(const std::vector<std::string>& args);
|
||||
bool export_multisig_main(const std::vector<std::string>& args, bool called_by_mms);
|
||||
bool import_multisig(const std::vector<std::string>& args);
|
||||
@@ -429,7 +429,6 @@ namespace cryptonote
|
||||
bool m_restore_deterministic_wallet; // recover flag
|
||||
bool m_restore_multisig_wallet; // recover flag
|
||||
bool m_non_deterministic; // old 2-random generation
|
||||
bool m_allow_mismatched_daemon_version;
|
||||
bool m_restoring; // are we restoring, by whatever method?
|
||||
uint64_t m_restore_height; // optional
|
||||
bool m_do_not_relay;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_MONERO_VERSION "0.18.1.0"
|
||||
#define DEF_MONERO_VERSION "0.18.2.0"
|
||||
#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@
|
||||
|
||||
+17
-11
@@ -63,8 +63,8 @@ namespace {
|
||||
static const int MAX_REFRESH_INTERVAL_MILLIS = 1000 * 60 * 1;
|
||||
// Default refresh interval when connected to remote node
|
||||
static const int DEFAULT_REMOTE_NODE_REFRESH_INTERVAL_MILLIS = 1000 * 10;
|
||||
// Connection timeout 30 sec
|
||||
static const int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 1000 * 30;
|
||||
// Connection timeout 20 sec
|
||||
static const int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 1000 * 20;
|
||||
|
||||
std::string get_default_ringdb_path(cryptonote::network_type nettype)
|
||||
{
|
||||
@@ -535,7 +535,7 @@ bool WalletImpl::createWatchOnly(const std::string &path, const std::string &pas
|
||||
view_wallet->generate(path, password, address, viewkey);
|
||||
|
||||
// Export/Import outputs
|
||||
auto outputs = m_wallet->export_outputs();
|
||||
auto outputs = m_wallet->export_outputs(true/*all*/);
|
||||
view_wallet->import_outputs(outputs);
|
||||
|
||||
// Copy scanned blockchain
|
||||
@@ -553,7 +553,7 @@ bool WalletImpl::createWatchOnly(const std::string &path, const std::string &pas
|
||||
|
||||
// Export/Import key images
|
||||
// We already know the spent status from the outputs we exported, thus no need to check them again
|
||||
auto key_images = m_wallet->export_key_images();
|
||||
auto key_images = m_wallet->export_key_images(true/*all*/);
|
||||
uint64_t spent = 0;
|
||||
uint64_t unspent = 0;
|
||||
view_wallet->import_key_images(key_images.second, key_images.first, spent, unspent, false);
|
||||
@@ -1146,8 +1146,8 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file
|
||||
|
||||
// Check tx data and construct confirmation message
|
||||
std::string extra_message;
|
||||
if (!transaction->m_unsigned_tx_set.transfers.second.empty())
|
||||
extra_message = (boost::format("%u outputs to import. ") % (unsigned)transaction->m_unsigned_tx_set.transfers.second.size()).str();
|
||||
if (!std::get<2>(transaction->m_unsigned_tx_set.transfers).empty())
|
||||
extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(transaction->m_unsigned_tx_set.transfers).size()).str();
|
||||
transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message);
|
||||
setStatus(transaction->status(), transaction->errorString());
|
||||
|
||||
@@ -1396,12 +1396,12 @@ string WalletImpl::makeMultisig(const vector<string>& info, const uint32_t thres
|
||||
return string();
|
||||
}
|
||||
|
||||
std::string WalletImpl::exchangeMultisigKeys(const std::vector<std::string> &info) {
|
||||
std::string WalletImpl::exchangeMultisigKeys(const std::vector<std::string> &info, const bool force_update_use_with_caution /*= false*/) {
|
||||
try {
|
||||
clearStatus();
|
||||
checkMultisigWalletNotReady(m_wallet);
|
||||
|
||||
return m_wallet->exchange_multisig_keys(epee::wipeable_string(m_password), info);
|
||||
return m_wallet->exchange_multisig_keys(epee::wipeable_string(m_password), info, force_update_use_with_caution);
|
||||
} catch (const exception& e) {
|
||||
LOG_ERROR("Error on exchanging multisig keys: " << e.what());
|
||||
setStatusError(string(tr("Failed to exchange multisig keys: ")) + e.what());
|
||||
@@ -1782,7 +1782,7 @@ uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::str
|
||||
m_wallet->use_fork_rules(HF_VERSION_CLSAG, 0),
|
||||
m_wallet->use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, 0),
|
||||
m_wallet->use_fork_rules(HF_VERSION_VIEW_TAGS, 0),
|
||||
m_wallet->get_base_fee(),
|
||||
m_wallet->get_base_fee(priority),
|
||||
m_wallet->get_fee_quantization_mask());
|
||||
}
|
||||
|
||||
@@ -2173,9 +2173,15 @@ bool WalletImpl::connectToDaemon()
|
||||
Wallet::ConnectionStatus WalletImpl::connected() const
|
||||
{
|
||||
uint32_t version = 0;
|
||||
m_is_connected = m_wallet->check_connection(&version, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS);
|
||||
bool wallet_is_outdated = false, daemon_is_outdated = false;
|
||||
m_is_connected = m_wallet->check_connection(&version, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS, &wallet_is_outdated, &daemon_is_outdated);
|
||||
if (!m_is_connected)
|
||||
return Wallet::ConnectionStatus_Disconnected;
|
||||
{
|
||||
if (!m_wallet->light_wallet() && (wallet_is_outdated || daemon_is_outdated))
|
||||
return Wallet::ConnectionStatus_WrongVersion;
|
||||
else
|
||||
return Wallet::ConnectionStatus_Disconnected;
|
||||
}
|
||||
// Version check is not implemented in light wallets nodes/wallets
|
||||
if (!m_wallet->light_wallet() && (version >> 16) != CORE_RPC_VERSION_MAJOR)
|
||||
return Wallet::ConnectionStatus_WrongVersion;
|
||||
|
||||
@@ -146,7 +146,7 @@ public:
|
||||
MultisigState multisig() const override;
|
||||
std::string getMultisigInfo() const override;
|
||||
std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) override;
|
||||
std::string exchangeMultisigKeys(const std::vector<std::string> &info) override;
|
||||
std::string exchangeMultisigKeys(const std::vector<std::string> &info, const bool force_update_use_with_caution = false) override;
|
||||
bool exportMultisigImages(std::string& images) override;
|
||||
size_t importMultisigImages(const std::vector<std::string>& images) override;
|
||||
bool hasMultisigPartialKeyImages() const override;
|
||||
|
||||
@@ -796,9 +796,10 @@ struct Wallet
|
||||
/**
|
||||
* @brief exchange_multisig_keys - provides additional key exchange round for arbitrary multisig schemes (like N-1/N, M/N)
|
||||
* @param info - base58 encoded key derivations returned by makeMultisig or exchangeMultisigKeys function call
|
||||
* @param force_update_use_with_caution - force multisig account to update even if not all signers contribute round messages
|
||||
* @return new info string if more rounds required or an empty string if wallet creation is done
|
||||
*/
|
||||
virtual std::string exchangeMultisigKeys(const std::vector<std::string> &info) = 0;
|
||||
virtual std::string exchangeMultisigKeys(const std::vector<std::string> &info, const bool force_update_use_with_caution) = 0;
|
||||
/**
|
||||
* @brief exportMultisigImages - exports transfers' key images
|
||||
* @param images - output paramter for hex encoded array of images
|
||||
|
||||
@@ -82,18 +82,21 @@ void NodeRPCProxy::invalidate()
|
||||
m_rpc_payment_seed_hash = crypto::null_hash;
|
||||
m_rpc_payment_next_seed_hash = crypto::null_hash;
|
||||
m_height_time = 0;
|
||||
m_target_height_time = 0;
|
||||
m_rpc_payment_diff = 0;
|
||||
m_rpc_payment_credits_per_hash_found = 0;
|
||||
m_rpc_payment_height = 0;
|
||||
m_rpc_payment_cookie = 0;
|
||||
m_daemon_hard_forks.clear();
|
||||
}
|
||||
|
||||
boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version)
|
||||
boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version, std::vector<std::pair<uint8_t, uint64_t>> &daemon_hard_forks, uint64_t &height, uint64_t &target_height)
|
||||
{
|
||||
if (m_offline)
|
||||
return boost::optional<std::string>("offline");
|
||||
if (m_rpc_version == 0)
|
||||
{
|
||||
const time_t now = time(NULL);
|
||||
cryptonote::COMMAND_RPC_GET_VERSION::request req_t = AUTO_VAL_INIT(req_t);
|
||||
cryptonote::COMMAND_RPC_GET_VERSION::response resp_t = AUTO_VAL_INIT(resp_t);
|
||||
{
|
||||
@@ -101,9 +104,28 @@ boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version
|
||||
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t, m_http_client, rpc_timeout);
|
||||
RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "get_version");
|
||||
}
|
||||
|
||||
m_rpc_version = resp_t.version;
|
||||
m_daemon_hard_forks.clear();
|
||||
for (const auto &hf : resp_t.hard_forks)
|
||||
m_daemon_hard_forks.push_back(std::make_pair(hf.hf_version, hf.height));
|
||||
if (resp_t.current_height > 0 || resp_t.target_height > 0)
|
||||
{
|
||||
m_height = resp_t.current_height;
|
||||
m_target_height = resp_t.target_height;
|
||||
m_height_time = now;
|
||||
m_target_height_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
rpc_version = m_rpc_version;
|
||||
daemon_hard_forks = m_daemon_hard_forks;
|
||||
boost::optional<std::string> result = get_height(height);
|
||||
if (result)
|
||||
return result;
|
||||
result = get_target_height(target_height);
|
||||
if (result)
|
||||
return result;
|
||||
return boost::optional<std::string>();
|
||||
}
|
||||
|
||||
@@ -138,6 +160,7 @@ boost::optional<std::string> NodeRPCProxy::get_info()
|
||||
m_adjusted_time = resp_t.adjusted_time;
|
||||
m_get_info_time = now;
|
||||
m_height_time = now;
|
||||
m_target_height_time = now;
|
||||
}
|
||||
return boost::optional<std::string>();
|
||||
}
|
||||
@@ -160,6 +183,13 @@ boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height)
|
||||
|
||||
boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height)
|
||||
{
|
||||
const time_t now = time(NULL);
|
||||
if (now < m_target_height_time + 30) // re-cache every 30 seconds
|
||||
{
|
||||
height = m_target_height;
|
||||
return boost::optional<std::string>();
|
||||
}
|
||||
|
||||
auto res = get_info();
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
void invalidate();
|
||||
void set_offline(bool offline) { m_offline = offline; }
|
||||
|
||||
boost::optional<std::string> get_rpc_version(uint32_t &version);
|
||||
boost::optional<std::string> get_rpc_version(uint32_t &rpc_version, std::vector<std::pair<uint8_t, uint64_t>> &daemon_hard_forks, uint64_t &height, uint64_t &target_height);
|
||||
boost::optional<std::string> get_height(uint64_t &height);
|
||||
void set_height(uint64_t h);
|
||||
boost::optional<std::string> get_target_height(uint64_t &height);
|
||||
@@ -103,6 +103,8 @@ private:
|
||||
crypto::hash m_rpc_payment_next_seed_hash;
|
||||
uint32_t m_rpc_payment_cookie;
|
||||
time_t m_height_time;
|
||||
time_t m_target_height_time;
|
||||
std::vector<std::pair<uint8_t, uint64_t>> m_daemon_hard_forks;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+251
-67
@@ -47,6 +47,7 @@
|
||||
using namespace epee;
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
#include "hardforks/hardforks.h"
|
||||
#include "cryptonote_core/tx_sanity_check.h"
|
||||
#include "wallet_rpc_helpers.h"
|
||||
#include "wallet2.h"
|
||||
@@ -275,6 +276,7 @@ struct options {
|
||||
const command_line::arg_descriptor<bool> no_dns = {"no-dns", tools::wallet2::tr("Do not use DNS"), false};
|
||||
const command_line::arg_descriptor<bool> offline = {"offline", tools::wallet2::tr("Do not connect to a daemon, nor use DNS"), false};
|
||||
const command_line::arg_descriptor<std::string> extra_entropy = {"extra-entropy", tools::wallet2::tr("File containing extra entropy to initialize the PRNG (any data, aim for 256 bits of entropy to be useful, which typically means more than 256 bits of data)")};
|
||||
const command_line::arg_descriptor<bool> allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", tools::wallet2::tr("Allow communicating with a daemon that uses a different version"), false};
|
||||
};
|
||||
|
||||
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file)
|
||||
@@ -484,6 +486,9 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||
add_extra_entropy_thread_safe(data.data(), data.size());
|
||||
}
|
||||
|
||||
if (command_line::has_arg(vm, opts.allow_mismatched_daemon_version))
|
||||
wallet->allow_mismatched_daemon_version(true);
|
||||
|
||||
try
|
||||
{
|
||||
if (!command_line::is_arg_defaulted(vm, opts.tx_notify))
|
||||
@@ -1218,7 +1223,9 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
|
||||
m_export_format(ExportFormat::Binary),
|
||||
m_load_deprecated_formats(false),
|
||||
m_credits_target(0),
|
||||
m_enable_multisig(false)
|
||||
m_enable_multisig(false),
|
||||
m_has_ever_refreshed_from_node(false),
|
||||
m_allow_mismatched_daemon_version(false)
|
||||
{
|
||||
set_rpc_client_secret_key(rct::rct2sk(rct::skGen()));
|
||||
}
|
||||
@@ -1278,6 +1285,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
|
||||
command_line::add_arg(desc_params, opts.no_dns);
|
||||
command_line::add_arg(desc_params, opts.offline);
|
||||
command_line::add_arg(desc_params, opts.extra_entropy);
|
||||
command_line::add_arg(desc_params, opts.allow_mismatched_daemon_version);
|
||||
}
|
||||
|
||||
std::pair<std::unique_ptr<wallet2>, tools::password_container> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
@@ -1338,6 +1346,7 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
|
||||
}
|
||||
m_rpc_payment_state.expected_spent = 0;
|
||||
m_rpc_payment_state.discrepancy = 0;
|
||||
m_rpc_version = 0;
|
||||
m_node_rpc_proxy.invalidate();
|
||||
}
|
||||
|
||||
@@ -2716,7 +2725,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
|
||||
THROW_WALLET_EXCEPTION_IF(blocks.size() != parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
|
||||
THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::out_of_hashchain_bounds_error);
|
||||
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
|
||||
tools::threadpool::waiter waiter(tpool);
|
||||
|
||||
size_t num_txes = 0;
|
||||
@@ -2914,6 +2923,26 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
||||
refresh(trusted_daemon, start_height, blocks_fetched, received_money);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void check_block_hard_fork_version(cryptonote::network_type nettype, uint8_t hf_version, uint64_t height, bool &wallet_is_outdated, bool &daemon_is_outdated)
|
||||
{
|
||||
const size_t wallet_num_hard_forks = nettype == TESTNET ? num_testnet_hard_forks
|
||||
: nettype == STAGENET ? num_stagenet_hard_forks : num_mainnet_hard_forks;
|
||||
const hardfork_t *wallet_hard_forks = nettype == TESTNET ? testnet_hard_forks
|
||||
: nettype == STAGENET ? stagenet_hard_forks : mainnet_hard_forks;
|
||||
|
||||
wallet_is_outdated = static_cast<size_t>(hf_version) > wallet_num_hard_forks;
|
||||
if (wallet_is_outdated)
|
||||
return;
|
||||
|
||||
// check block's height falls within wallet's expected range for block's given version
|
||||
uint64_t start_height = hf_version == 1 ? 0 : wallet_hard_forks[hf_version - 1].height;
|
||||
uint64_t end_height = static_cast<size_t>(hf_version) + 1 > wallet_num_hard_forks
|
||||
? std::numeric_limits<uint64_t>::max()
|
||||
: wallet_hard_forks[hf_version].height;
|
||||
|
||||
daemon_is_outdated = height < start_height || height >= end_height;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::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)
|
||||
{
|
||||
error = false;
|
||||
@@ -2939,7 +2968,7 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
|
||||
pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices, current_height);
|
||||
THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices");
|
||||
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
|
||||
tools::threadpool::waiter waiter(tpool);
|
||||
parsed_blocks.resize(blocks.size());
|
||||
for (size_t i = 0; i < blocks.size(); ++i)
|
||||
@@ -2955,6 +2984,23 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_allow_mismatched_daemon_version)
|
||||
{
|
||||
// make sure block's hard fork version is expected at the block's height
|
||||
uint8_t hf_version = parsed_blocks[i].block.major_version;
|
||||
uint64_t height = blocks_start_height + i;
|
||||
bool wallet_is_outdated = false;
|
||||
bool daemon_is_outdated = false;
|
||||
check_block_hard_fork_version(m_nettype, hf_version, height, wallet_is_outdated, daemon_is_outdated);
|
||||
THROW_WALLET_EXCEPTION_IF(wallet_is_outdated || daemon_is_outdated, error::incorrect_fork_version,
|
||||
"Unexpected hard fork version v" + std::to_string(hf_version) + " at height " + std::to_string(height) + ". " +
|
||||
(wallet_is_outdated
|
||||
? "Make sure your wallet is up to date"
|
||||
: "Make sure the node you are connected to is running the latest version")
|
||||
);
|
||||
}
|
||||
|
||||
parsed_blocks[i].o_indices = std::move(o_indices[i]);
|
||||
}
|
||||
|
||||
@@ -3419,7 +3465,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
||||
size_t try_count = 0;
|
||||
crypto::hash last_tx_hash_id = m_transfers.size() ? m_transfers.back().m_txid : null_hash;
|
||||
std::list<crypto::hash> short_chain_history;
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
|
||||
tools::threadpool::waiter waiter(tpool);
|
||||
uint64_t blocks_start_height;
|
||||
std::vector<cryptonote::block_complete_entry> blocks;
|
||||
@@ -3535,6 +3581,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
||||
throw std::runtime_error("proxy exception in refresh thread");
|
||||
}
|
||||
|
||||
m_has_ever_refreshed_from_node = true;
|
||||
|
||||
if(!first && blocks_start_height == next_blocks_start_height)
|
||||
{
|
||||
m_node_rpc_proxy.set_height(m_blockchain.size());
|
||||
@@ -3571,6 +3619,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
||||
THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
|
||||
throw;
|
||||
}
|
||||
catch (const error::incorrect_fork_version&)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool");
|
||||
throw;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
blocks_fetched += added_blocks;
|
||||
@@ -4181,6 +4234,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
|
||||
m_auto_mine_for_rpc_payment_threshold = -1.0f;
|
||||
m_credits_target = 0;
|
||||
m_enable_multisig = false;
|
||||
m_allow_mismatched_daemon_version = false;
|
||||
}
|
||||
else if(json.IsObject())
|
||||
{
|
||||
@@ -4684,7 +4738,8 @@ void wallet2::init_type(hw::device::device_type device_type)
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet or restores one.
|
||||
* \brief Generates a wallet or restores one. Assumes the multisig setup
|
||||
* has already completed for the provided multisig info.
|
||||
* \param wallet_ Name of wallet file
|
||||
* \param password Password of wallet file
|
||||
* \param multisig_data The multisig restore info and keys
|
||||
@@ -4743,11 +4798,6 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
|
||||
crypto::public_key local_signer;
|
||||
THROW_WALLET_EXCEPTION_IF(!crypto::secret_key_to_public_key(spend_secret_key, local_signer), error::invalid_multisig_seed);
|
||||
THROW_WALLET_EXCEPTION_IF(std::find(multisig_signers.begin(), multisig_signers.end(), local_signer) == multisig_signers.end(), error::invalid_multisig_seed);
|
||||
rct::key skey = rct::zero();
|
||||
for (const auto &msk: multisig_keys)
|
||||
sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes);
|
||||
THROW_WALLET_EXCEPTION_IF(!(rct::rct2sk(skey) == spend_secret_key), error::invalid_multisig_seed);
|
||||
memwipe(&skey, sizeof(rct::key));
|
||||
|
||||
m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
|
||||
|
||||
@@ -4758,6 +4808,8 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
|
||||
m_multisig = true;
|
||||
m_multisig_threshold = threshold;
|
||||
m_multisig_signers = multisig_signers;
|
||||
// wallet is assumed already finalized
|
||||
m_multisig_rounds_passed = multisig::multisig_setup_rounds_required(m_multisig_signers.size(), m_multisig_threshold);
|
||||
setup_keys(password);
|
||||
|
||||
create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
|
||||
@@ -5075,12 +5127,11 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password,
|
||||
const std::vector<std::string> &kex_messages)
|
||||
const std::vector<std::string> &kex_messages,
|
||||
const bool force_update_use_with_caution /*= false*/)
|
||||
{
|
||||
bool ready{false};
|
||||
CHECK_AND_ASSERT_THROW_MES(multisig(&ready), "The wallet is not multisig");
|
||||
CHECK_AND_ASSERT_THROW_MES(!ready, "Multisig wallet creation process has already been finished");
|
||||
CHECK_AND_ASSERT_THROW_MES(kex_messages.size() > 0, "No key exchange messages passed in.");
|
||||
|
||||
// decrypt account keys
|
||||
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
|
||||
@@ -5099,13 +5150,6 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
|
||||
);
|
||||
}
|
||||
|
||||
// open kex messages
|
||||
std::vector<multisig::multisig_kex_msg> expanded_msgs;
|
||||
expanded_msgs.reserve(kex_messages.size());
|
||||
|
||||
for (const auto &msg : kex_messages)
|
||||
expanded_msgs.emplace_back(msg);
|
||||
|
||||
// reconstruct multisig account
|
||||
multisig::multisig_keyset_map_memsafe_t kex_origins_map;
|
||||
|
||||
@@ -5126,8 +5170,25 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
|
||||
""
|
||||
};
|
||||
|
||||
// KLUDGE: early return if there are no kex messages and main kex is complete (will return the post-kex verification round
|
||||
// message) (it's a kludge because this behavior would be more appropriate for a standalone wallet method)
|
||||
if (kex_messages.size() == 0)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(multisig_account.main_kex_rounds_done(),
|
||||
"Exchange multisig keys: there are no kex messages but the main kex rounds are not done.");
|
||||
|
||||
return multisig_account.get_next_kex_round_msg();
|
||||
}
|
||||
|
||||
// open kex messages
|
||||
std::vector<multisig::multisig_kex_msg> expanded_msgs;
|
||||
expanded_msgs.reserve(kex_messages.size());
|
||||
|
||||
for (const auto &msg : kex_messages)
|
||||
expanded_msgs.emplace_back(msg);
|
||||
|
||||
// update multisig kex
|
||||
multisig_account.kex_update(expanded_msgs);
|
||||
multisig_account.kex_update(expanded_msgs, force_update_use_with_caution);
|
||||
|
||||
// update wallet state
|
||||
|
||||
@@ -5208,7 +5269,7 @@ bool wallet2::multisig(bool *ready, uint32_t *threshold, uint32_t *total) const
|
||||
if (ready)
|
||||
{
|
||||
*ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity())) &&
|
||||
(m_multisig_rounds_passed == multisig::multisig_kex_rounds_required(m_multisig_signers.size(), m_multisig_threshold) + 1);
|
||||
(m_multisig_rounds_passed == multisig::multisig_setup_rounds_required(m_multisig_signers.size(), m_multisig_threshold));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -5324,7 +5385,7 @@ bool wallet2::prepare_file_names(const std::string& file_path)
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
|
||||
bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout, bool *wallet_is_outdated, bool *daemon_is_outdated)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(!m_is_initialized, error::wallet_not_initialized);
|
||||
|
||||
@@ -5361,24 +5422,103 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_rpc_version)
|
||||
{
|
||||
cryptonote::COMMAND_RPC_GET_VERSION::request req_t = AUTO_VAL_INIT(req_t);
|
||||
cryptonote::COMMAND_RPC_GET_VERSION::response resp_t = AUTO_VAL_INIT(resp_t);
|
||||
bool r = invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t);
|
||||
if(!r || resp_t.status != CORE_RPC_STATUS_OK) {
|
||||
if(version)
|
||||
*version = 0;
|
||||
return false;
|
||||
}
|
||||
m_rpc_version = resp_t.version;
|
||||
}
|
||||
if (!m_rpc_version && !check_version(version, wallet_is_outdated, daemon_is_outdated))
|
||||
return false;
|
||||
if (version)
|
||||
*version = m_rpc_version;
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::check_version(uint32_t *version, bool *wallet_is_outdated, bool *daemon_is_outdated)
|
||||
{
|
||||
uint32_t rpc_version;
|
||||
std::vector<std::pair<uint8_t, uint64_t>> daemon_hard_forks;
|
||||
uint64_t height;
|
||||
uint64_t target_height;
|
||||
if (m_node_rpc_proxy.get_rpc_version(rpc_version, daemon_hard_forks, height, target_height))
|
||||
{
|
||||
if(version)
|
||||
*version = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check wallet compatibility with daemon's hard fork version
|
||||
if (!m_allow_mismatched_daemon_version)
|
||||
if (!check_hard_fork_version(m_nettype, daemon_hard_forks, height, target_height, wallet_is_outdated, daemon_is_outdated))
|
||||
return false;
|
||||
|
||||
m_rpc_version = rpc_version;
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::check_hard_fork_version(cryptonote::network_type nettype, const std::vector<std::pair<uint8_t, uint64_t>> &daemon_hard_forks, const uint64_t height, const uint64_t target_height, bool *wallet_is_outdated, bool *daemon_is_outdated)
|
||||
{
|
||||
const size_t wallet_num_hard_forks = nettype == TESTNET ? num_testnet_hard_forks
|
||||
: nettype == STAGENET ? num_stagenet_hard_forks : num_mainnet_hard_forks;
|
||||
const hardfork_t *wallet_hard_forks = nettype == TESTNET ? testnet_hard_forks
|
||||
: nettype == STAGENET ? stagenet_hard_forks : mainnet_hard_forks;
|
||||
|
||||
// First check if wallet or daemon is outdated (whether either are unaware of
|
||||
// a hard fork). Then check if fork has passed rendering versions incompatible
|
||||
if (daemon_hard_forks.size() > 0)
|
||||
{
|
||||
bool daemon_outdated = daemon_hard_forks.size() < wallet_num_hard_forks;
|
||||
bool wallet_outdated = daemon_hard_forks.size() > wallet_num_hard_forks;
|
||||
|
||||
if (daemon_is_outdated)
|
||||
*daemon_is_outdated = daemon_outdated;
|
||||
if (wallet_is_outdated)
|
||||
*wallet_is_outdated = wallet_outdated;
|
||||
|
||||
if (daemon_outdated)
|
||||
{
|
||||
uint64_t daemon_missed_fork_height = wallet_hard_forks[daemon_hard_forks.size()].height;
|
||||
|
||||
// If the daemon missed the fork, then technically it is no longer part of
|
||||
// the Monero network. Don't connect.
|
||||
bool daemon_missed_fork = height >= daemon_missed_fork_height || target_height >= daemon_missed_fork_height;
|
||||
if (daemon_missed_fork)
|
||||
return false;
|
||||
}
|
||||
else if (wallet_outdated)
|
||||
{
|
||||
uint64_t wallet_missed_fork_height = daemon_hard_forks[wallet_num_hard_forks].second;
|
||||
|
||||
// If the wallet missed the fork, then technically it is no longer able
|
||||
// to communicate with the Monero network. Don't connect.
|
||||
bool wallet_missed_fork = height >= wallet_missed_fork_height || target_height >= wallet_missed_fork_height;
|
||||
if (wallet_missed_fork)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-updated daemons won't return daemon_hard_forks in response to
|
||||
// get_version. Fall back to extra call to get_hard_fork_info by version.
|
||||
uint64_t daemon_fork_height;
|
||||
get_hard_fork_info(wallet_num_hard_forks-1/* wallet expects "double fork" pattern */, daemon_fork_height);
|
||||
bool daemon_outdated = daemon_fork_height == std::numeric_limits<uint64_t>::max();
|
||||
|
||||
if (daemon_is_outdated)
|
||||
*daemon_is_outdated = daemon_outdated;
|
||||
|
||||
if (daemon_outdated)
|
||||
{
|
||||
uint64_t daemon_missed_fork_height = wallet_hard_forks[wallet_num_hard_forks-2].height;
|
||||
bool daemon_missed_fork = height >= daemon_missed_fork_height || target_height >= daemon_missed_fork_height;
|
||||
if (daemon_missed_fork)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't need to check if wallet is outdated here because the daemons updated
|
||||
// for a future hard fork will serve daemon_hard_forks above. The check for
|
||||
// an outdated wallet is done above using daemon_hard_forks.
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::set_offline(bool offline)
|
||||
{
|
||||
m_offline = offline;
|
||||
@@ -6604,9 +6744,9 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pending_tx> &txs, signed_tx_set &signed_txes)
|
||||
{
|
||||
if (!exported_txs.new_transfers.second.empty())
|
||||
if (!std::get<2>(exported_txs.new_transfers).empty())
|
||||
import_outputs(exported_txs.new_transfers);
|
||||
else
|
||||
else if (!std::get<2>(exported_txs.transfers).empty())
|
||||
import_outputs(exported_txs.transfers);
|
||||
|
||||
// sign the transactions
|
||||
@@ -10800,7 +10940,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
|
||||
{
|
||||
txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device()));
|
||||
}
|
||||
txs.transfers = std::make_pair(0, m_transfers);
|
||||
txs.transfers = std::make_tuple(0, m_transfers.size(), m_transfers);
|
||||
|
||||
auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev);
|
||||
CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface");
|
||||
@@ -13103,18 +13243,29 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect
|
||||
m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> wallet2::export_outputs(bool all) const
|
||||
std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> wallet2::export_outputs(bool all, uint32_t start, uint32_t count) const
|
||||
{
|
||||
PERF_TIMER(export_outputs);
|
||||
std::vector<tools::wallet2::exported_transfer_details> outs;
|
||||
|
||||
// invalid cases
|
||||
THROW_WALLET_EXCEPTION_IF(count == 0, error::wallet_internal_error, "Nothing requested");
|
||||
THROW_WALLET_EXCEPTION_IF(!all && start > 0, error::wallet_internal_error, "Incremental mode is incompatible with non-zero start");
|
||||
|
||||
// valid cases:
|
||||
// all: all outputs, subject to start/count
|
||||
// !all: incremental, subject to count
|
||||
// for convenience, start/count are allowed to go past the valid range, then nothing is returned
|
||||
|
||||
size_t offset = 0;
|
||||
if (!all)
|
||||
while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request))
|
||||
++offset;
|
||||
else
|
||||
offset = start;
|
||||
|
||||
outs.reserve(m_transfers.size() - offset);
|
||||
for (size_t n = offset; n < m_transfers.size(); ++n)
|
||||
for (size_t n = offset; n < m_transfers.size() && n - offset < count; ++n)
|
||||
{
|
||||
const transfer_details &td = m_transfers[n];
|
||||
|
||||
@@ -13132,20 +13283,22 @@ std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> wall
|
||||
etd.m_flags.m_key_image_partial = td.m_key_image_partial;
|
||||
etd.m_amount = td.m_amount;
|
||||
etd.m_additional_tx_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
|
||||
etd.m_subaddr_index_major = td.m_subaddr_index.major;
|
||||
etd.m_subaddr_index_minor = td.m_subaddr_index.minor;
|
||||
|
||||
outs.push_back(etd);
|
||||
}
|
||||
|
||||
return std::make_pair(offset, outs);
|
||||
return std::make_tuple(offset, m_transfers.size(), outs);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::string wallet2::export_outputs_to_str(bool all) const
|
||||
std::string wallet2::export_outputs_to_str(bool all, uint32_t start, uint32_t count) const
|
||||
{
|
||||
PERF_TIMER(export_outputs_to_str);
|
||||
|
||||
std::stringstream oss;
|
||||
binary_archive<true> ar(oss);
|
||||
auto outputs = export_outputs(all);
|
||||
auto outputs = export_outputs(all, start, count);
|
||||
THROW_WALLET_EXCEPTION_IF(!::serialization::serialize(ar, outputs), error::wallet_internal_error, "Failed to serialize output data");
|
||||
|
||||
std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC));
|
||||
@@ -13158,21 +13311,33 @@ std::string wallet2::export_outputs_to_str(bool all) const
|
||||
return magic + ciphertext;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs)
|
||||
size_t wallet2::import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs)
|
||||
{
|
||||
PERF_TIMER(import_outputs);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error,
|
||||
THROW_WALLET_EXCEPTION_IF(m_has_ever_refreshed_from_node, error::wallet_internal_error,
|
||||
"Hot wallets cannot import outputs");
|
||||
|
||||
// we can now import piecemeal
|
||||
const size_t offset = std::get<0>(outputs);
|
||||
const size_t num_outputs = std::get<1>(outputs);
|
||||
const std::vector<tools::wallet2::transfer_details> &output_array = std::get<2>(outputs);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error,
|
||||
"Imported outputs omit more outputs that we know of");
|
||||
|
||||
const size_t offset = outputs.first;
|
||||
THROW_WALLET_EXCEPTION_IF(offset + output_array.size() > num_outputs, error::wallet_internal_error,
|
||||
"Offset is larger than total outputs");
|
||||
|
||||
const size_t original_size = m_transfers.size();
|
||||
m_transfers.resize(offset + outputs.second.size());
|
||||
for (size_t i = 0; i < offset; ++i)
|
||||
m_transfers[i].m_key_image_request = false;
|
||||
for (size_t i = 0; i < outputs.second.size(); ++i)
|
||||
if (offset + output_array.size() > m_transfers.size())
|
||||
m_transfers.resize(offset + output_array.size());
|
||||
else if (num_outputs < m_transfers.size())
|
||||
m_transfers.resize(num_outputs);
|
||||
|
||||
for (size_t i = 0; i < output_array.size(); ++i)
|
||||
{
|
||||
transfer_details td = outputs.second[i];
|
||||
transfer_details td = output_array[i];
|
||||
|
||||
// skip those we've already imported, or which have different data
|
||||
if (i + offset < original_size)
|
||||
@@ -13206,6 +13371,8 @@ process:
|
||||
THROW_WALLET_EXCEPTION_IF(td.m_internal_output_index >= td.m_tx.vout.size(),
|
||||
error::wallet_internal_error, "Internal index is out of range");
|
||||
crypto::public_key out_key = td.get_public_key();
|
||||
if (should_expand(td.m_subaddr_index))
|
||||
create_one_off_subaddress(td.m_subaddr_index);
|
||||
bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device());
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
|
||||
if (should_expand(td.m_subaddr_index))
|
||||
@@ -13224,24 +13391,36 @@ process:
|
||||
return m_transfers.size();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs)
|
||||
size_t wallet2::import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs)
|
||||
{
|
||||
PERF_TIMER(import_outputs);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error,
|
||||
THROW_WALLET_EXCEPTION_IF(m_has_ever_refreshed_from_node, error::wallet_internal_error,
|
||||
"Hot wallets cannot import outputs");
|
||||
|
||||
// we can now import piecemeal
|
||||
const size_t offset = std::get<0>(outputs);
|
||||
const size_t num_outputs = std::get<1>(outputs);
|
||||
const std::vector<tools::wallet2::exported_transfer_details> &output_array = std::get<2>(outputs);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error,
|
||||
"Imported outputs omit more outputs that we know of. Try using export_outputs all.");
|
||||
|
||||
const size_t offset = outputs.first;
|
||||
THROW_WALLET_EXCEPTION_IF(offset + output_array.size() > num_outputs, error::wallet_internal_error,
|
||||
"Offset is larger than total outputs");
|
||||
|
||||
const size_t original_size = m_transfers.size();
|
||||
m_transfers.resize(offset + outputs.second.size());
|
||||
for (size_t i = 0; i < offset; ++i)
|
||||
m_transfers[i].m_key_image_request = false;
|
||||
for (size_t i = 0; i < outputs.second.size(); ++i)
|
||||
if (offset + output_array.size() > m_transfers.size())
|
||||
m_transfers.resize(offset + output_array.size());
|
||||
else if (num_outputs < m_transfers.size())
|
||||
m_transfers.resize(num_outputs);
|
||||
|
||||
for (size_t i = 0; i < output_array.size(); ++i)
|
||||
{
|
||||
exported_transfer_details etd = outputs.second[i];
|
||||
exported_transfer_details etd = output_array[i];
|
||||
transfer_details &td = m_transfers[i + offset];
|
||||
|
||||
// setup td with "cheao" loaded data
|
||||
// setup td with "cheap" loaded data
|
||||
td.m_block_height = 0;
|
||||
td.m_txid = crypto::null_hash;
|
||||
td.m_global_output_index = etd.m_global_output_index;
|
||||
@@ -13254,6 +13433,8 @@ size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wall
|
||||
td.m_key_image_known = etd.m_flags.m_key_image_known;
|
||||
td.m_key_image_request = etd.m_flags.m_key_image_request;
|
||||
td.m_key_image_partial = false;
|
||||
td.m_subaddr_index.major = etd.m_subaddr_index_major;
|
||||
td.m_subaddr_index.minor = etd.m_subaddr_index_minor;
|
||||
|
||||
// skip those we've already imported, or which have different data
|
||||
if (i + offset < original_size)
|
||||
@@ -13294,6 +13475,8 @@ size_t wallet2::import_outputs(const std::pair<uint64_t, std::vector<tools::wall
|
||||
const crypto::public_key &tx_pub_key = etd.m_tx_pubkey;
|
||||
const std::vector<crypto::public_key> &additional_tx_pub_keys = etd.m_additional_tx_keys;
|
||||
const crypto::public_key& out_key = etd.m_pubkey;
|
||||
if (should_expand(td.m_subaddr_index))
|
||||
create_one_off_subaddress(td.m_subaddr_index);
|
||||
bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device());
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
|
||||
if (should_expand(td.m_subaddr_index))
|
||||
@@ -13350,7 +13533,7 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
|
||||
{
|
||||
std::string body(data, headerlen);
|
||||
|
||||
std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> new_outputs;
|
||||
std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> new_outputs;
|
||||
try
|
||||
{
|
||||
binary_archive<false> ar{epee::strspan<std::uint8_t>(body)};
|
||||
@@ -13360,9 +13543,9 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
|
||||
}
|
||||
catch (...) {}
|
||||
if (!loaded)
|
||||
new_outputs.second.clear();
|
||||
std::get<2>(new_outputs).clear();
|
||||
|
||||
std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> outputs;
|
||||
std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::transfer_details>> outputs;
|
||||
if (!loaded) try
|
||||
{
|
||||
binary_archive<false> ar{epee::strspan<std::uint8_t>(body)};
|
||||
@@ -13387,15 +13570,16 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st)
|
||||
|
||||
if (!loaded)
|
||||
{
|
||||
outputs.first = 0;
|
||||
outputs.second = {};
|
||||
std::get<0>(outputs) = 0;
|
||||
std::get<1>(outputs) = 0;
|
||||
std::get<2>(outputs) = {};
|
||||
}
|
||||
|
||||
imported_outputs = new_outputs.second.empty() ? import_outputs(outputs) : import_outputs(new_outputs);
|
||||
imported_outputs = !std::get<2>(new_outputs).empty() ? import_outputs(new_outputs) : !std::get<2>(outputs).empty() ? import_outputs(outputs) : 0;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to import outputs") + e.what());
|
||||
THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to import outputs: ") + e.what());
|
||||
}
|
||||
|
||||
return imported_outputs;
|
||||
|
||||
+84
-19
@@ -63,6 +63,7 @@
|
||||
#include "serialization/crypto.h"
|
||||
#include "serialization/string.h"
|
||||
#include "serialization/pair.h"
|
||||
#include "serialization/tuple.h"
|
||||
#include "serialization/containers.h"
|
||||
|
||||
#include "wallet_errors.h"
|
||||
@@ -401,9 +402,13 @@ private:
|
||||
} m_flags;
|
||||
uint64_t m_amount;
|
||||
std::vector<crypto::public_key> m_additional_tx_keys;
|
||||
uint32_t m_subaddr_index_major;
|
||||
uint32_t m_subaddr_index_minor;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VERSION_FIELD(0)
|
||||
VERSION_FIELD(1)
|
||||
if (version < 1)
|
||||
return false;
|
||||
FIELD(m_pubkey)
|
||||
VARINT_FIELD(m_internal_output_index)
|
||||
VARINT_FIELD(m_global_output_index)
|
||||
@@ -411,6 +416,8 @@ private:
|
||||
FIELD(m_flags.flags)
|
||||
VARINT_FIELD(m_amount)
|
||||
FIELD(m_additional_tx_keys)
|
||||
VARINT_FIELD(m_subaddr_index_major)
|
||||
VARINT_FIELD(m_subaddr_index_minor)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
@@ -667,16 +674,32 @@ private:
|
||||
struct unsigned_tx_set
|
||||
{
|
||||
std::vector<tx_construction_data> txes;
|
||||
std::pair<size_t, wallet2::transfer_container> transfers;
|
||||
std::pair<size_t, std::vector<wallet2::exported_transfer_details>> new_transfers;
|
||||
std::tuple<uint64_t, uint64_t, wallet2::transfer_container> transfers;
|
||||
std::tuple<uint64_t, uint64_t, std::vector<wallet2::exported_transfer_details>> new_transfers;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VERSION_FIELD(1)
|
||||
VERSION_FIELD(2)
|
||||
FIELD(txes)
|
||||
if (version >= 1)
|
||||
FIELD(new_transfers)
|
||||
else
|
||||
FIELD(transfers)
|
||||
if (version == 0)
|
||||
{
|
||||
std::pair<size_t, wallet2::transfer_container> v0_transfers;
|
||||
FIELD(v0_transfers);
|
||||
std::get<0>(transfers) = std::get<0>(v0_transfers);
|
||||
std::get<1>(transfers) = std::get<0>(v0_transfers) + std::get<1>(v0_transfers).size();
|
||||
std::get<2>(transfers) = std::get<1>(v0_transfers);
|
||||
return true;
|
||||
}
|
||||
if (version == 1)
|
||||
{
|
||||
std::pair<size_t, std::vector<wallet2::exported_transfer_details>> v1_transfers;
|
||||
FIELD(v1_transfers);
|
||||
std::get<0>(new_transfers) = std::get<0>(v1_transfers);
|
||||
std::get<1>(new_transfers) = std::get<0>(v1_transfers) + std::get<1>(v1_transfers).size();
|
||||
std::get<2>(new_transfers) = std::get<1>(v1_transfers);
|
||||
return true;
|
||||
}
|
||||
|
||||
FIELD(new_transfers)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
@@ -794,7 +817,8 @@ private:
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet or restores one.
|
||||
* \brief Generates a wallet or restores one. Assumes the multisig setup
|
||||
* has already completed for the provided multisig info.
|
||||
* \param wallet_ Name of wallet file
|
||||
* \param password Password of wallet file
|
||||
* \param multisig_data The multisig restore info and keys
|
||||
@@ -862,7 +886,8 @@ private:
|
||||
* to other participants
|
||||
*/
|
||||
std::string exchange_multisig_keys(const epee::wipeable_string &password,
|
||||
const std::vector<std::string> &kex_messages);
|
||||
const std::vector<std::string> &kex_messages,
|
||||
const bool force_update_use_with_caution = false);
|
||||
/*!
|
||||
* \brief Get initial message to start multisig key exchange (before 'make_multisig()' is called)
|
||||
* \return string to send to other participants
|
||||
@@ -1068,7 +1093,9 @@ private:
|
||||
bool sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector<crypto::hash> &txids);
|
||||
std::vector<pending_tx> create_unmixable_sweep_transactions();
|
||||
void discard_unmixable_outputs();
|
||||
bool check_connection(uint32_t *version = NULL, bool *ssl = NULL, uint32_t timeout = 200000);
|
||||
bool check_connection(uint32_t *version = NULL, bool *ssl = NULL, uint32_t timeout = 200000, bool *wallet_is_outdated = NULL, bool *daemon_is_outdated = NULL);
|
||||
bool check_version(uint32_t *version, bool *wallet_is_outdated, bool *daemon_is_outdated);
|
||||
bool check_hard_fork_version(cryptonote::network_type nettype, const std::vector<std::pair<uint8_t, uint64_t>> &daemon_hard_forks, const uint64_t height, const uint64_t target_height, bool *wallet_is_outdated, bool *daemon_is_outdated);
|
||||
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
|
||||
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
|
||||
void get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height, uint64_t max_height = (uint64_t)-1, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
|
||||
@@ -1206,11 +1233,17 @@ private:
|
||||
if(ver < 29)
|
||||
return;
|
||||
a & m_rpc_client_secret_key;
|
||||
if(ver < 30)
|
||||
{
|
||||
m_has_ever_refreshed_from_node = false;
|
||||
return;
|
||||
}
|
||||
a & m_has_ever_refreshed_from_node;
|
||||
}
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
MAGIC_FIELD("monero wallet cache")
|
||||
VERSION_FIELD(0)
|
||||
VERSION_FIELD(1)
|
||||
FIELD(m_blockchain)
|
||||
FIELD(m_transfers)
|
||||
FIELD(m_account_public_address)
|
||||
@@ -1236,6 +1269,12 @@ private:
|
||||
FIELD(m_device_last_key_image_sync)
|
||||
FIELD(m_cold_key_images)
|
||||
FIELD(m_rpc_client_secret_key)
|
||||
if (version < 1)
|
||||
{
|
||||
m_has_ever_refreshed_from_node = false;
|
||||
return true;
|
||||
}
|
||||
FIELD(m_has_ever_refreshed_from_node)
|
||||
END_SERIALIZE()
|
||||
|
||||
/*!
|
||||
@@ -1323,6 +1362,8 @@ private:
|
||||
void credits_target(uint64_t threshold) { m_credits_target = threshold; }
|
||||
bool is_multisig_enabled() const { return m_enable_multisig; }
|
||||
void enable_multisig(bool enable) { m_enable_multisig = enable; }
|
||||
bool is_mismatched_daemon_version_allowed() const { return m_allow_mismatched_daemon_version; }
|
||||
void allow_mismatched_daemon_version(bool allow_mismatch) { m_allow_mismatched_daemon_version = allow_mismatch; }
|
||||
|
||||
bool get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
|
||||
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const boost::optional<cryptonote::account_public_address> &single_destination_subaddress = boost::none);
|
||||
@@ -1447,10 +1488,10 @@ private:
|
||||
bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const;
|
||||
|
||||
// Import/Export wallet data
|
||||
std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> export_outputs(bool all = false) const;
|
||||
std::string export_outputs_to_str(bool all = false) const;
|
||||
size_t import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs);
|
||||
size_t import_outputs(const std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs);
|
||||
std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> export_outputs(bool all = false, uint32_t start = 0, uint32_t count = 0xffffffff) const;
|
||||
std::string export_outputs_to_str(bool all = false, uint32_t start = 0, uint32_t count = 0xffffffff) const;
|
||||
size_t import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::exported_transfer_details>> &outputs);
|
||||
size_t import_outputs(const std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::transfer_details>> &outputs);
|
||||
size_t import_outputs_from_str(const std::string &outputs_st);
|
||||
payment_container export_payments() const;
|
||||
void import_payments(const payment_container &payments);
|
||||
@@ -1840,6 +1881,7 @@ private:
|
||||
rpc_payment_state_t m_rpc_payment_state;
|
||||
uint64_t m_credits_target;
|
||||
bool m_enable_multisig;
|
||||
bool m_allow_mismatched_daemon_version;
|
||||
|
||||
// Aux transaction data from device
|
||||
serializable_unordered_map<crypto::hash, std::string> m_tx_device;
|
||||
@@ -1883,11 +1925,13 @@ private:
|
||||
ExportFormat m_export_format;
|
||||
bool m_load_deprecated_formats;
|
||||
|
||||
bool m_has_ever_refreshed_from_node;
|
||||
|
||||
static boost::mutex default_daemon_address_lock;
|
||||
static std::string default_daemon_address;
|
||||
};
|
||||
}
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 29)
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 30)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
||||
@@ -1898,7 +1942,7 @@ BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 18)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 4)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3)
|
||||
@@ -1908,6 +1952,17 @@ namespace boost
|
||||
{
|
||||
namespace serialization
|
||||
{
|
||||
template<class Archive, class F, class S, class T>
|
||||
inline void serialize(
|
||||
Archive & ar,
|
||||
std::tuple<F, S, T> & t,
|
||||
const unsigned int /* file_version */
|
||||
){
|
||||
ar & boost::serialization::make_nvp("f", std::get<0>(t));
|
||||
ar & boost::serialization::make_nvp("s", std::get<1>(t));
|
||||
ar & boost::serialization::make_nvp("t", std::get<2>(t));
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline typename std::enable_if<!Archive::is_loading::value, void>::type initialize_transfer_details(Archive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
@@ -2268,7 +2323,17 @@ namespace boost
|
||||
inline void serialize(Archive &a, tools::wallet2::unsigned_tx_set &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.txes;
|
||||
a & x.transfers;
|
||||
if (ver == 0)
|
||||
{
|
||||
// load old version
|
||||
std::pair<size_t, tools::wallet2::transfer_container> old_transfers;
|
||||
a & old_transfers;
|
||||
std::get<0>(x.transfers) = std::get<0>(old_transfers);
|
||||
std::get<1>(x.transfers) = std::get<0>(old_transfers) + std::get<1>(old_transfers).size();
|
||||
std::get<2>(x.transfers) = std::get<1>(old_transfers);
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error("Boost serialization not supported for newest unsigned_tx_set");
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
|
||||
@@ -439,6 +439,16 @@ namespace tools
|
||||
std::string to_string() const { return refresh_error::to_string(); }
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct incorrect_fork_version : public refresh_error
|
||||
{
|
||||
explicit incorrect_fork_version(std::string&& loc, const std::string& message)
|
||||
: refresh_error(std::move(loc), message)
|
||||
{
|
||||
}
|
||||
|
||||
std::string to_string() const { return refresh_error::to_string(); }
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct signature_check_failed : public wallet_logic_error
|
||||
{
|
||||
explicit signature_check_failed(std::string&& loc, const std::string& message)
|
||||
|
||||
@@ -144,7 +144,7 @@ bool wallet2::search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads
|
||||
n_threads = boost::thread::hardware_concurrency();
|
||||
|
||||
std::vector<crypto::hash> hash(n_threads);
|
||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
|
||||
tools::threadpool::waiter waiter(tpool);
|
||||
|
||||
const uint32_t local_nonce = nonce += n_threads; // wrapping's OK
|
||||
@@ -155,8 +155,7 @@ bool wallet2::search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads
|
||||
const uint8_t major_version = hashing_blob[0];
|
||||
if (major_version >= RX_BLOCK_VERSION)
|
||||
{
|
||||
const int miners = 1;
|
||||
crypto::rx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash[i].data, miners, 0);
|
||||
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash[i].data);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -2792,7 +2792,7 @@ namespace tools
|
||||
|
||||
try
|
||||
{
|
||||
res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str(req.all));
|
||||
res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str(req.all, req.start, req.count));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
@@ -4146,13 +4146,6 @@ namespace tools
|
||||
er.message = "This wallet is not multisig";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ready)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG;
|
||||
er.message = "This wallet is multisig, and already finalized";
|
||||
return false;
|
||||
}
|
||||
CHECK_MULTISIG_ENABLED();
|
||||
|
||||
if (req.multisig_info.size() + 1 < total)
|
||||
@@ -4164,7 +4157,7 @@ namespace tools
|
||||
|
||||
try
|
||||
{
|
||||
res.multisig_info = m_wallet->exchange_multisig_keys(req.password, req.multisig_info);
|
||||
res.multisig_info = m_wallet->exchange_multisig_keys(req.password, req.multisig_info, req.force_update_use_with_caution);
|
||||
m_wallet->multisig(&ready);
|
||||
if (ready)
|
||||
{
|
||||
|
||||
@@ -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 25
|
||||
#define WALLET_RPC_VERSION_MINOR 26
|
||||
#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
|
||||
@@ -1785,9 +1785,13 @@ namespace wallet_rpc
|
||||
struct request_t
|
||||
{
|
||||
bool all;
|
||||
uint32_t start;
|
||||
uint32_t count;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(all)
|
||||
KV_SERIALIZE_OPT(start, 0u)
|
||||
KV_SERIALIZE_OPT(count, 0xffffffffu)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
@@ -2531,10 +2535,12 @@ namespace wallet_rpc
|
||||
{
|
||||
std::string password;
|
||||
std::vector<std::string> multisig_info;
|
||||
bool force_update_use_with_caution;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(password)
|
||||
KV_SERIALIZE(multisig_info)
|
||||
KV_SERIALIZE_OPT(force_update_use_with_caution, false)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
@@ -90,6 +90,7 @@ namespace tests
|
||||
bool get_test_drop_download_height() {return true;}
|
||||
bool prepare_handle_incoming_blocks(const std::vector<cryptonote::block_complete_entry> &blocks_entry, std::vector<cryptonote::block> &blocks) { return true; }
|
||||
bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; }
|
||||
bool update_checkpoints(const bool skip_dns = false) { return true; }
|
||||
uint64_t get_target_blockchain_height() const { return 1; }
|
||||
size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; }
|
||||
virtual void on_transactions_relayed(epee::span<const cryptonote::blobdata> tx_blobs, cryptonote::relay_method tx_relay) {}
|
||||
|
||||
@@ -778,7 +778,7 @@ inline bool do_replay_events_get_core(std::vector<test_event_entry>& events, cry
|
||||
|
||||
t_test_class validator;
|
||||
bool ret = replay_events_through_core<t_test_class>(c, events, validator);
|
||||
tools::threadpool::getInstance().recycle();
|
||||
tools::threadpool::getInstanceForCompute().recycle();
|
||||
// c.deinit();
|
||||
return ret;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -34,13 +34,22 @@
|
||||
from __future__ import print_function
|
||||
from framework.daemon import Daemon
|
||||
from framework.wallet import Wallet
|
||||
import random
|
||||
|
||||
SEED = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
|
||||
STANDARD_ADDRESS = '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
|
||||
SUBADDRESS = '84QRUYawRNrU3NN1VpFRndSukeyEb3Xpv8qZjjsoJZnTYpDYceuUTpog13D7qPxpviS7J29bSgSkR11hFFoXWk2yNdsR9WF'
|
||||
|
||||
class ColdSigningTest():
|
||||
def run_test(self):
|
||||
self.reset()
|
||||
self.create(0)
|
||||
self.mine()
|
||||
self.transfer()
|
||||
for piecemeal_output_export in [False, True]:
|
||||
self.transfer(piecemeal_output_export)
|
||||
for piecemeal_output_export in [False, True]:
|
||||
self.self_transfer_to_subaddress(piecemeal_output_export)
|
||||
self.transfer_after_empty_export_import()
|
||||
|
||||
def reset(self):
|
||||
print('Resetting blockchain')
|
||||
@@ -57,17 +66,15 @@ class ColdSigningTest():
|
||||
try: self.hot_wallet.close_wallet()
|
||||
except: pass
|
||||
|
||||
self.cold_wallet = Wallet(idx = 1)
|
||||
self.cold_wallet = Wallet(idx = 5)
|
||||
# close the wallet if any, will throw if none is loaded
|
||||
try: self.cold_wallet.close_wallet()
|
||||
except: pass
|
||||
|
||||
seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
|
||||
res = self.cold_wallet.restore_deterministic_wallet(seed = seed)
|
||||
self.cold_wallet.set_daemon('127.0.0.1:11111', ssl_support = "disabled")
|
||||
res = self.cold_wallet.restore_deterministic_wallet(seed = SEED)
|
||||
spend_key = self.cold_wallet.query_key("spend_key").key
|
||||
view_key = self.cold_wallet.query_key("view_key").key
|
||||
res = self.hot_wallet.generate_from_keys(viewkey = view_key, address = '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm')
|
||||
res = self.hot_wallet.generate_from_keys(viewkey = view_key, address = STANDARD_ADDRESS)
|
||||
|
||||
ok = False
|
||||
try: res = self.hot_wallet.query_key("spend_key")
|
||||
@@ -79,28 +86,62 @@ class ColdSigningTest():
|
||||
assert ok
|
||||
assert self.cold_wallet.query_key("view_key").key == view_key
|
||||
assert self.cold_wallet.get_address().address == self.hot_wallet.get_address().address
|
||||
assert self.cold_wallet.get_address().address == STANDARD_ADDRESS
|
||||
|
||||
def mine(self):
|
||||
print("Mining some blocks")
|
||||
daemon = Daemon()
|
||||
wallet = Wallet()
|
||||
|
||||
daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 80)
|
||||
daemon.generateblocks(STANDARD_ADDRESS, 80)
|
||||
wallet.refresh()
|
||||
|
||||
def transfer(self):
|
||||
daemon = Daemon()
|
||||
|
||||
print("Creating transaction in hot wallet")
|
||||
|
||||
dst = {'address': '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 'amount': 1000000000000}
|
||||
|
||||
def export_import(self, piecemeal_output_export):
|
||||
self.hot_wallet.refresh()
|
||||
res = self.hot_wallet.export_outputs()
|
||||
self.cold_wallet.import_outputs(res.outputs_data_hex)
|
||||
|
||||
if piecemeal_output_export:
|
||||
res = self.hot_wallet.incoming_transfers()
|
||||
num_outputs = len(res.transfers)
|
||||
done = [False] * num_outputs
|
||||
while len([x for x in done if not done[x]]) > 0:
|
||||
start = int(random.random() * num_outputs)
|
||||
if start == num_outputs:
|
||||
num_outputs -= 1
|
||||
count = 1 + int(random.random() * 5)
|
||||
res = self.hot_wallet.export_outputs(all = True, start = start, count = count)
|
||||
|
||||
# the hot wallet cannot import outputs
|
||||
ok = False
|
||||
try:
|
||||
self.hot_wallet.import_outputs(res.outputs_data_hex)
|
||||
except:
|
||||
ok = True
|
||||
assert ok
|
||||
|
||||
try:
|
||||
self.cold_wallet.import_outputs(res.outputs_data_hex)
|
||||
except Exception as e:
|
||||
# this just means we selected later outputs first, without filling
|
||||
# new outputs first
|
||||
if 'Imported outputs omit more outputs that we know of' not in str(e):
|
||||
raise
|
||||
for i in range(start, start + count):
|
||||
if i < len(done):
|
||||
done[i] = True
|
||||
else:
|
||||
res = self.hot_wallet.export_outputs()
|
||||
self.cold_wallet.import_outputs(res.outputs_data_hex)
|
||||
|
||||
res = self.cold_wallet.export_key_images(True)
|
||||
self.hot_wallet.import_key_images(res.signed_key_images, offset = res.offset)
|
||||
|
||||
def create_tx(self, destination_addr, piecemeal_output_export):
|
||||
daemon = Daemon()
|
||||
|
||||
dst = {'address': destination_addr, 'amount': 1000000000000}
|
||||
|
||||
self.export_import(piecemeal_output_export)
|
||||
|
||||
res = self.hot_wallet.transfer([dst], ring_size = 16, get_tx_key = False)
|
||||
assert len(res.tx_hash) == 32*2
|
||||
txid = res.tx_hash
|
||||
@@ -125,11 +166,11 @@ class ColdSigningTest():
|
||||
assert desc.unlock_time == 0
|
||||
assert desc.payment_id in ['', '0000000000000000']
|
||||
assert desc.change_amount == desc.amount_in - 1000000000000 - fee
|
||||
assert desc.change_address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
|
||||
assert desc.change_address == STANDARD_ADDRESS
|
||||
assert desc.fee == fee
|
||||
assert len(desc.recipients) == 1
|
||||
rec = desc.recipients[0]
|
||||
assert rec.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
|
||||
assert rec.address == destination_addr
|
||||
assert rec.amount == 1000000000000
|
||||
|
||||
res = self.cold_wallet.sign_transfer(unsigned_txset)
|
||||
@@ -148,7 +189,7 @@ class ColdSigningTest():
|
||||
assert len([x for x in (res['pending'] if 'pending' in res else []) if x.txid == txid]) == 1
|
||||
assert len([x for x in (res['out'] if 'out' in res else []) if x.txid == txid]) == 0
|
||||
|
||||
daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1)
|
||||
daemon.generateblocks(STANDARD_ADDRESS, 1)
|
||||
self.hot_wallet.refresh()
|
||||
|
||||
res = self.hot_wallet.get_transfers()
|
||||
@@ -160,6 +201,47 @@ class ColdSigningTest():
|
||||
res = self.cold_wallet.get_tx_key(txid)
|
||||
assert len(res.tx_key) == 64
|
||||
|
||||
self.export_import(piecemeal_output_export)
|
||||
|
||||
def transfer(self, piecemeal_output_export):
|
||||
print("Creating transaction in hot wallet")
|
||||
self.create_tx(STANDARD_ADDRESS, piecemeal_output_export)
|
||||
|
||||
res = self.cold_wallet.get_address()
|
||||
assert len(res['addresses']) == 1
|
||||
assert res['addresses'][0].address == STANDARD_ADDRESS
|
||||
assert res['addresses'][0].used
|
||||
|
||||
res = self.hot_wallet.get_address()
|
||||
assert len(res['addresses']) == 1
|
||||
assert res['addresses'][0].address == STANDARD_ADDRESS
|
||||
assert res['addresses'][0].used
|
||||
|
||||
def self_transfer_to_subaddress(self, piecemeal_output_export):
|
||||
print("Self-spending to subaddress in hot wallet")
|
||||
self.create_tx(SUBADDRESS, piecemeal_output_export)
|
||||
|
||||
res = self.cold_wallet.get_address()
|
||||
assert len(res['addresses']) == 2
|
||||
assert res['addresses'][0].address == STANDARD_ADDRESS
|
||||
assert res['addresses'][0].used
|
||||
assert res['addresses'][1].address == SUBADDRESS
|
||||
assert res['addresses'][1].used
|
||||
|
||||
res = self.hot_wallet.get_address()
|
||||
assert len(res['addresses']) == 2
|
||||
assert res['addresses'][0].address == STANDARD_ADDRESS
|
||||
assert res['addresses'][0].used
|
||||
assert res['addresses'][1].address == SUBADDRESS
|
||||
assert res['addresses'][1].used
|
||||
|
||||
def transfer_after_empty_export_import(self):
|
||||
print("Creating transaction in hot wallet after empty export & import")
|
||||
start_len = len(self.hot_wallet.get_transfers()['in'])
|
||||
self.export_import(False)
|
||||
assert start_len == len(self.hot_wallet.get_transfers()['in'])
|
||||
self.create_tx(STANDARD_ADDRESS, False)
|
||||
assert start_len == len(self.hot_wallet.get_transfers()['in']) - 1
|
||||
|
||||
class Guard:
|
||||
def __enter__(self):
|
||||
|
||||
@@ -40,8 +40,9 @@ except:
|
||||
N_MONERODS = 4
|
||||
|
||||
# 4 wallets connected to the main offline monerod
|
||||
# a wallet connected to the first local online monerod
|
||||
N_WALLETS = 5
|
||||
# 1 wallet connected to the first local online monerod
|
||||
# 1 offline wallet
|
||||
N_WALLETS = 6
|
||||
|
||||
WALLET_DIRECTORY = builddir + "/functional-tests-directory"
|
||||
FUNCTIONAL_TESTS_DIRECTORY = builddir + "/tests/functional_tests"
|
||||
@@ -54,13 +55,14 @@ monerod_extra = [
|
||||
["--add-exclusive-node", "127.0.0.1:18283"],
|
||||
["--add-exclusive-node", "127.0.0.1:18282"],
|
||||
]
|
||||
wallet_base = [builddir + "/bin/monero-wallet-rpc", "--wallet-dir", WALLET_DIRECTORY, "--rpc-bind-port", "wallet_port", "--disable-rpc-login", "--rpc-ssl", "disabled", "--daemon-ssl", "disabled", "--log-level", "1"]
|
||||
wallet_base = [builddir + "/bin/monero-wallet-rpc", "--wallet-dir", WALLET_DIRECTORY, "--rpc-bind-port", "wallet_port", "--disable-rpc-login", "--rpc-ssl", "disabled", "--daemon-ssl", "disabled", "--log-level", "1", "--allow-mismatched-daemon-version"]
|
||||
wallet_extra = [
|
||||
["--daemon-port", "18180"],
|
||||
["--daemon-port", "18180"],
|
||||
["--daemon-port", "18180"],
|
||||
["--daemon-port", "18180"],
|
||||
["--daemon-port", "18182"],
|
||||
["--offline"],
|
||||
]
|
||||
|
||||
command_lines = []
|
||||
|
||||
@@ -50,7 +50,7 @@ BEGIN_INIT_SIMPLE_FUZZER()
|
||||
END_INIT_SIMPLE_FUZZER()
|
||||
|
||||
BEGIN_SIMPLE_FUZZER()
|
||||
std::pair<uint64_t, std::vector<tools::wallet2::transfer_details>> outputs;
|
||||
std::tuple<uint64_t, uint64_t, std::vector<tools::wallet2::transfer_details>> outputs;
|
||||
binary_archive<false> ar{{buf, len}};
|
||||
::serialization::serialize(ar, outputs);
|
||||
size_t n_outputs = wallet->import_outputs(outputs);
|
||||
|
||||
@@ -158,6 +158,17 @@ TEST(DNSResolver, GetTXTRecord)
|
||||
EXPECT_STREQ("donate.getmonero.org", addr.c_str());
|
||||
}
|
||||
|
||||
TEST(DNSResolver, Localhost)
|
||||
{
|
||||
tools::DNSResolver resolver = tools::DNSResolver::create();
|
||||
|
||||
bool avail, valid;
|
||||
std::vector<std::string> ips = resolver.get_ipv4("localhost", avail, valid);
|
||||
|
||||
ASSERT_EQ(1, ips.size());
|
||||
ASSERT_EQ("127.0.0.1", ips[0]);
|
||||
}
|
||||
|
||||
bool is_equal(const char *s, const std::vector<std::string> &v) { return v.size() == 1 && v[0] == s; }
|
||||
|
||||
TEST(DNS_PUBLIC, empty) { EXPECT_TRUE(tools::dns_utils::parse_dns_public("").empty()); }
|
||||
|
||||
+84
-15
@@ -792,25 +792,33 @@ TEST_F(levin_notify, local_without_padding)
|
||||
notifier.new_out_connection();
|
||||
io_service_.poll();
|
||||
|
||||
std::vector<cryptonote::blobdata> txs(2);
|
||||
txs[0].resize(100, 'f');
|
||||
txs[1].resize(200, 'e');
|
||||
std::vector<cryptonote::blobdata> my_txs(2);
|
||||
my_txs[0].resize(100, 'f');
|
||||
my_txs[1].resize(200, 'e');
|
||||
|
||||
std::vector<cryptonote::blobdata> sorted_txs = txs;
|
||||
std::sort(sorted_txs.begin(), sorted_txs.end());
|
||||
std::vector<cryptonote::blobdata> their_txs{2};
|
||||
their_txs[0].resize(300, 'g');
|
||||
their_txs[1].resize(250, 'h');
|
||||
|
||||
std::vector<cryptonote::blobdata> my_sorted_txs = my_txs;
|
||||
std::sort(my_sorted_txs.begin(), my_sorted_txs.end());
|
||||
|
||||
std::vector<cryptonote::blobdata> their_sorted_txs = their_txs;
|
||||
std::sort(their_sorted_txs.begin(), their_sorted_txs.end());
|
||||
|
||||
ASSERT_EQ(10u, contexts_.size());
|
||||
bool has_stemmed = false;
|
||||
bool has_fluffed = false;
|
||||
while (!has_stemmed || !has_fluffed)
|
||||
{
|
||||
// run their "their" txes first
|
||||
auto context = contexts_.begin();
|
||||
EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), cryptonote::relay_method::local));
|
||||
EXPECT_TRUE(notifier.send_txs(their_txs, context->get_id(), cryptonote::relay_method::stem));
|
||||
|
||||
io_service_.reset();
|
||||
ASSERT_LT(0u, io_service_.poll());
|
||||
const bool is_stem = events_.has_stem_txes();
|
||||
EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff));
|
||||
EXPECT_EQ(their_txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff));
|
||||
|
||||
if (!is_stem)
|
||||
{
|
||||
@@ -836,13 +844,41 @@ TEST_F(levin_notify, local_without_padding)
|
||||
{
|
||||
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
|
||||
if (is_stem)
|
||||
EXPECT_EQ(txs, notification.txs);
|
||||
EXPECT_EQ(their_txs, notification.txs);
|
||||
else
|
||||
EXPECT_EQ(sorted_txs, notification.txs);
|
||||
EXPECT_EQ(their_sorted_txs, notification.txs);
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
EXPECT_EQ(!is_stem, notification.dandelionpp_fluff);
|
||||
}
|
||||
|
||||
// run "my" txes which must always be stem
|
||||
context = contexts_.begin();
|
||||
EXPECT_TRUE(notifier.send_txs(my_txs, context->get_id(), cryptonote::relay_method::local));
|
||||
|
||||
io_service_.reset();
|
||||
ASSERT_LT(0u, io_service_.poll());
|
||||
EXPECT_TRUE(events_.has_stem_txes());
|
||||
EXPECT_EQ(my_txs, events_.take_relayed(cryptonote::relay_method::stem));
|
||||
|
||||
send_count = 0;
|
||||
EXPECT_EQ(0u, context->process_send_queue());
|
||||
for (++context; context != contexts_.end(); ++context)
|
||||
{
|
||||
const std::size_t sent = context->process_send_queue();
|
||||
if (sent)
|
||||
{
|
||||
EXPECT_EQ(1u, (context - contexts_.begin()) % 2);
|
||||
}
|
||||
send_count += sent;
|
||||
}
|
||||
|
||||
EXPECT_EQ(1u, send_count);
|
||||
EXPECT_EQ(1u, receiver_.notified_size());
|
||||
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
|
||||
EXPECT_EQ(my_txs, notification.txs);
|
||||
EXPECT_TRUE(notification._.empty());
|
||||
EXPECT_TRUE(!notification.dandelionpp_fluff);
|
||||
|
||||
has_stemmed |= is_stem;
|
||||
has_fluffed |= !is_stem;
|
||||
notifier.run_epoch();
|
||||
@@ -1170,22 +1206,27 @@ TEST_F(levin_notify, local_with_padding)
|
||||
notifier.new_out_connection();
|
||||
io_service_.poll();
|
||||
|
||||
std::vector<cryptonote::blobdata> txs(2);
|
||||
txs[0].resize(100, 'e');
|
||||
txs[1].resize(200, 'f');
|
||||
std::vector<cryptonote::blobdata> my_txs(2);
|
||||
my_txs[0].resize(100, 'e');
|
||||
my_txs[1].resize(200, 'f');
|
||||
|
||||
std::vector<cryptonote::blobdata> their_txs{2};
|
||||
their_txs[0].resize(300, 'g');
|
||||
their_txs[1].resize(250, 'h');
|
||||
|
||||
ASSERT_EQ(10u, contexts_.size());
|
||||
bool has_stemmed = false;
|
||||
bool has_fluffed = false;
|
||||
while (!has_stemmed || !has_fluffed)
|
||||
{
|
||||
// run their "their" txes first
|
||||
auto context = contexts_.begin();
|
||||
EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), cryptonote::relay_method::local));
|
||||
EXPECT_TRUE(notifier.send_txs(their_txs, context->get_id(), cryptonote::relay_method::stem));
|
||||
|
||||
io_service_.reset();
|
||||
ASSERT_LT(0u, io_service_.poll());
|
||||
const bool is_stem = events_.has_stem_txes();
|
||||
EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff));
|
||||
EXPECT_EQ(their_txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff));
|
||||
|
||||
if (!is_stem)
|
||||
{
|
||||
@@ -1211,11 +1252,39 @@ TEST_F(levin_notify, local_with_padding)
|
||||
for (unsigned count = 0; count < (is_stem ? 1u : 9u); ++count)
|
||||
{
|
||||
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
|
||||
EXPECT_EQ(txs, notification.txs);
|
||||
EXPECT_EQ(their_txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_EQ(!is_stem, notification.dandelionpp_fluff);
|
||||
}
|
||||
|
||||
// run "my" txes which must always be stem
|
||||
context = contexts_.begin();
|
||||
EXPECT_TRUE(notifier.send_txs(my_txs, context->get_id(), cryptonote::relay_method::local));
|
||||
|
||||
io_service_.reset();
|
||||
ASSERT_LT(0u, io_service_.poll());
|
||||
EXPECT_TRUE(events_.has_stem_txes());
|
||||
EXPECT_EQ(my_txs, events_.take_relayed(cryptonote::relay_method::stem));
|
||||
|
||||
send_count = 0;
|
||||
EXPECT_EQ(0u, context->process_send_queue());
|
||||
for (++context; context != contexts_.end(); ++context)
|
||||
{
|
||||
const std::size_t sent = context->process_send_queue();
|
||||
if (sent)
|
||||
{
|
||||
EXPECT_EQ(1u, (context - contexts_.begin()) % 2);
|
||||
}
|
||||
send_count += sent;
|
||||
}
|
||||
|
||||
EXPECT_EQ(1u, send_count);
|
||||
EXPECT_EQ(1u, receiver_.notified_size());
|
||||
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
|
||||
EXPECT_EQ(my_txs, notification.txs);
|
||||
EXPECT_FALSE(notification._.empty());
|
||||
EXPECT_TRUE(!notification.dandelionpp_fluff);
|
||||
|
||||
has_stemmed |= is_stem;
|
||||
has_fluffed |= !is_stem;
|
||||
notifier.run_epoch();
|
||||
|
||||
@@ -95,9 +95,47 @@ static std::vector<std::string> exchange_round(std::vector<tools::wallet2>& wall
|
||||
std::vector<std::string> new_infos;
|
||||
new_infos.reserve(infos.size());
|
||||
|
||||
for (size_t i = 0; i < wallets.size(); ++i)
|
||||
new_infos.push_back(wallets[i].exchange_multisig_keys("", infos));
|
||||
|
||||
return new_infos;
|
||||
}
|
||||
|
||||
static std::vector<std::string> exchange_round_force_update(std::vector<tools::wallet2>& wallets,
|
||||
const std::vector<std::string>& infos,
|
||||
const std::size_t round_in_progress)
|
||||
{
|
||||
EXPECT_TRUE(wallets.size() == infos.size());
|
||||
std::vector<std::string> new_infos;
|
||||
std::vector<std::string> temp_force_update_infos;
|
||||
new_infos.reserve(infos.size());
|
||||
|
||||
// when force-updating, we only need at most 'num_signers - 1 - (round - 1)' messages from other signers
|
||||
size_t num_other_messages_required{wallets.size() - 1 - (round_in_progress - 1)};
|
||||
if (num_other_messages_required > wallets.size())
|
||||
num_other_messages_required = 0; //overflow case for post-kex verification round of 1-of-N
|
||||
|
||||
for (size_t i = 0; i < wallets.size(); ++i)
|
||||
{
|
||||
new_infos.push_back(wallets[i].exchange_multisig_keys("", infos));
|
||||
temp_force_update_infos.clear();
|
||||
temp_force_update_infos.reserve(num_other_messages_required + 1);
|
||||
temp_force_update_infos.push_back(infos[i]); //always include the local signer's message for this round
|
||||
|
||||
size_t infos_collected{0};
|
||||
for (size_t wallet_index = 0; wallet_index < wallets.size(); ++wallet_index)
|
||||
{
|
||||
// skip the local signer's message
|
||||
if (wallet_index == i)
|
||||
continue;
|
||||
|
||||
temp_force_update_infos.push_back(infos[wallet_index]);
|
||||
++infos_collected;
|
||||
|
||||
if (infos_collected == num_other_messages_required)
|
||||
break;
|
||||
}
|
||||
|
||||
new_infos.push_back(wallets[i].exchange_multisig_keys("", temp_force_update_infos, true));
|
||||
}
|
||||
|
||||
return new_infos;
|
||||
@@ -105,7 +143,7 @@ static std::vector<std::string> exchange_round(std::vector<tools::wallet2>& wall
|
||||
|
||||
static void check_results(const std::vector<std::string> &intermediate_infos,
|
||||
std::vector<tools::wallet2>& wallets,
|
||||
std::uint32_t M)
|
||||
const std::uint32_t M)
|
||||
{
|
||||
// check results
|
||||
std::unordered_set<crypto::secret_key> unique_privkeys;
|
||||
@@ -167,11 +205,12 @@ static void check_results(const std::vector<std::string> &intermediate_infos,
|
||||
wallets[0].encrypt_keys("");
|
||||
}
|
||||
|
||||
static void make_wallets(std::vector<tools::wallet2>& wallets, unsigned int M)
|
||||
static void make_wallets(const unsigned int M, const unsigned int N, const bool force_update)
|
||||
{
|
||||
std::vector<tools::wallet2> wallets(N);
|
||||
ASSERT_TRUE(wallets.size() > 1 && wallets.size() <= KEYS_COUNT);
|
||||
ASSERT_TRUE(M <= wallets.size());
|
||||
std::uint32_t total_rounds_required = multisig::multisig_kex_rounds_required(wallets.size(), M) + 1;
|
||||
std::uint32_t total_rounds_required = multisig::multisig_setup_rounds_required(wallets.size(), M);
|
||||
std::uint32_t rounds_complete{0};
|
||||
|
||||
// initialize wallets, get first round multisig kex msgs
|
||||
@@ -207,9 +246,12 @@ static void make_wallets(std::vector<tools::wallet2>& wallets, unsigned int M)
|
||||
wallets[0].multisig(&ready);
|
||||
while (!ready)
|
||||
{
|
||||
intermediate_infos = exchange_round(wallets, intermediate_infos);
|
||||
wallets[0].multisig(&ready);
|
||||
if (force_update)
|
||||
intermediate_infos = exchange_round_force_update(wallets, intermediate_infos, rounds_complete + 1);
|
||||
else
|
||||
intermediate_infos = exchange_round(wallets, intermediate_infos);
|
||||
|
||||
wallets[0].multisig(&ready);
|
||||
++rounds_complete;
|
||||
}
|
||||
|
||||
@@ -220,38 +262,38 @@ static void make_wallets(std::vector<tools::wallet2>& wallets, unsigned int M)
|
||||
|
||||
TEST(multisig, make_1_2)
|
||||
{
|
||||
std::vector<tools::wallet2> wallets(2);
|
||||
make_wallets(wallets, 1);
|
||||
make_wallets(1, 2, false);
|
||||
make_wallets(1, 2, true);
|
||||
}
|
||||
|
||||
TEST(multisig, make_1_3)
|
||||
{
|
||||
std::vector<tools::wallet2> wallets(3);
|
||||
make_wallets(wallets, 1);
|
||||
make_wallets(1, 3, false);
|
||||
make_wallets(1, 3, true);
|
||||
}
|
||||
|
||||
TEST(multisig, make_2_2)
|
||||
{
|
||||
std::vector<tools::wallet2> wallets(2);
|
||||
make_wallets(wallets, 2);
|
||||
make_wallets(2, 2, false);
|
||||
make_wallets(2, 2, true);
|
||||
}
|
||||
|
||||
TEST(multisig, make_3_3)
|
||||
{
|
||||
std::vector<tools::wallet2> wallets(3);
|
||||
make_wallets(wallets, 3);
|
||||
make_wallets(3, 3, false);
|
||||
make_wallets(3, 3, true);
|
||||
}
|
||||
|
||||
TEST(multisig, make_2_3)
|
||||
{
|
||||
std::vector<tools::wallet2> wallets(3);
|
||||
make_wallets(wallets, 2);
|
||||
make_wallets(2, 3, false);
|
||||
make_wallets(2, 3, true);
|
||||
}
|
||||
|
||||
TEST(multisig, make_2_4)
|
||||
{
|
||||
std::vector<tools::wallet2> wallets(4);
|
||||
make_wallets(wallets, 2);
|
||||
make_wallets(2, 4, false);
|
||||
make_wallets(2, 4, true);
|
||||
}
|
||||
|
||||
TEST(multisig, multisig_kex_msg)
|
||||
@@ -272,9 +314,7 @@ TEST(multisig, multisig_kex_msg)
|
||||
signing_skey = rct::rct2sk(rct::skGen());
|
||||
}
|
||||
|
||||
crypto::secret_key ancillary_skey = rct::rct2sk(rct::skGen());
|
||||
while (ancillary_skey == crypto::null_skey)
|
||||
ancillary_skey = rct::rct2sk(rct::skGen());
|
||||
const crypto::secret_key ancillary_skey{rct::rct2sk(rct::skGen())};
|
||||
|
||||
// misc. edge cases
|
||||
EXPECT_NO_THROW((multisig_kex_msg{}));
|
||||
@@ -312,8 +352,8 @@ TEST(multisig, multisig_kex_msg)
|
||||
// test that keys can be recovered if stored in a message and the message's reverse
|
||||
|
||||
// round 1
|
||||
multisig_kex_msg msg_rnd1{1, signing_skey, std::vector<crypto::public_key>{pubkey1}, ancillary_skey};
|
||||
multisig_kex_msg msg_rnd1_reverse{msg_rnd1.get_msg()};
|
||||
const multisig_kex_msg msg_rnd1{1, signing_skey, std::vector<crypto::public_key>{pubkey1}, ancillary_skey};
|
||||
const multisig_kex_msg msg_rnd1_reverse{msg_rnd1.get_msg()};
|
||||
EXPECT_EQ(msg_rnd1.get_round(), 1);
|
||||
EXPECT_EQ(msg_rnd1.get_round(), msg_rnd1_reverse.get_round());
|
||||
EXPECT_EQ(msg_rnd1.get_signing_pubkey(), signing_pubkey);
|
||||
@@ -324,8 +364,8 @@ TEST(multisig, multisig_kex_msg)
|
||||
EXPECT_EQ(msg_rnd1.get_msg_privkey(), msg_rnd1_reverse.get_msg_privkey());
|
||||
|
||||
// round 2
|
||||
multisig_kex_msg msg_rnd2{2, signing_skey, std::vector<crypto::public_key>{pubkey1, pubkey2}, ancillary_skey};
|
||||
multisig_kex_msg msg_rnd2_reverse{msg_rnd2.get_msg()};
|
||||
const multisig_kex_msg msg_rnd2{2, signing_skey, std::vector<crypto::public_key>{pubkey1, pubkey2}, ancillary_skey};
|
||||
const multisig_kex_msg msg_rnd2_reverse{msg_rnd2.get_msg()};
|
||||
EXPECT_EQ(msg_rnd2.get_round(), 2);
|
||||
EXPECT_EQ(msg_rnd2.get_round(), msg_rnd2_reverse.get_round());
|
||||
EXPECT_EQ(msg_rnd2.get_signing_pubkey(), signing_pubkey);
|
||||
|
||||
@@ -936,6 +936,41 @@ TEST(get_network_address, ipv4subnet)
|
||||
EXPECT_STREQ("12.34.0.0/16", address->str().c_str());
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void na_host_and_port_test(std::string addr, std::string exp_host, std::string exp_port)
|
||||
{
|
||||
std::string host{"xxxxx"};
|
||||
std::string port{"xxxxx"};
|
||||
net::get_network_address_host_and_port(addr, host, port);
|
||||
EXPECT_EQ(exp_host, host);
|
||||
EXPECT_EQ(exp_port, port);
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
TEST(get_network_address_host_and_port, ipv4)
|
||||
{
|
||||
na_host_and_port_test("9.9.9.9", "9.9.9.9", "xxxxx");
|
||||
na_host_and_port_test("9.9.9.9:18081", "9.9.9.9", "18081");
|
||||
}
|
||||
|
||||
TEST(get_network_address_host_and_port, ipv6)
|
||||
{
|
||||
na_host_and_port_test("::ffff", "::ffff", "xxxxx");
|
||||
na_host_and_port_test("[::ffff]", "::ffff", "xxxxx");
|
||||
na_host_and_port_test("[::ffff]:00231", "::ffff", "00231");
|
||||
na_host_and_port_test("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "xxxxx");
|
||||
na_host_and_port_test("[7777:7777:7777:7777:7777:7777:7777:7777]", "7777:7777:7777:7777:7777:7777:7777:7777", "xxxxx");
|
||||
na_host_and_port_test("[7777:7777:7777:7777:7777:7777:7777:7777]:48080", "7777:7777:7777:7777:7777:7777:7777:7777", "48080");
|
||||
}
|
||||
|
||||
TEST(get_network_address_host_and_port, hostname)
|
||||
{
|
||||
na_host_and_port_test("localhost", "localhost", "xxxxx");
|
||||
na_host_and_port_test("bar:29080", "bar", "29080"); // Issue https://github.com/monero-project/monero/issues/8633
|
||||
na_host_and_port_test("xmrchain.net:18081", "xmrchain.net", "18081");
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
using stream_type = boost::asio::ip::tcp;
|
||||
|
||||
@@ -72,6 +72,7 @@ public:
|
||||
bool get_test_drop_download_height() const {return true;}
|
||||
bool prepare_handle_incoming_blocks(const std::vector<cryptonote::block_complete_entry> &blocks_entry, std::vector<cryptonote::block> &blocks) { return true; }
|
||||
bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; }
|
||||
bool update_checkpoints(const bool skip_dns = false) { return true; }
|
||||
uint64_t get_target_blockchain_height() const { return 1; }
|
||||
size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; }
|
||||
virtual void on_transactions_relayed(epee::span<const cryptonote::blobdata> tx_blobs, cryptonote::relay_method tx_relay) {}
|
||||
|
||||
@@ -30,6 +30,7 @@ complete -c monero-wallet-cli -l tx-notify -r -d "Run a program for each new inc
|
||||
complete -c monero-wallet-cli -l no-dns -d "Do not use DNS"
|
||||
complete -c monero-wallet-cli -l offline -d "Do not connect to a daemon, nor use DNS"
|
||||
complete -c monero-wallet-cli -l extra-entropy -r -F -d "File containing extra entropy to initialize the PRNG (any data, aim for 256 bits of entropy to be useful, which typically means more than 256 bits of data)"
|
||||
complete -c monero-wallet-cli -l allow-mismatched-daemon-version -d "Allow communicating with a daemon that uses a different version"
|
||||
complete -c monero-wallet-cli -l wallet-file -r -F -d "Use wallet <arg>"
|
||||
complete -c monero-wallet-cli -l generate-new-wallet -r -F -d "Generate new wallet and save it to <arg>"
|
||||
complete -c monero-wallet-cli -l generate-from-device -r -F -d "Generate new wallet from device and save it to <arg>"
|
||||
@@ -45,7 +46,6 @@ complete -c monero-wallet-cli -l restore-from-seed -d "alias for --restore-deter
|
||||
complete -c monero-wallet-cli -l restore-multisig-wallet -d "Recover multisig wallet using Electrum-style mnemonic seed"
|
||||
complete -c monero-wallet-cli -l non-deterministic -d "Generate non-deterministic view and spend keys"
|
||||
complete -c monero-wallet-cli -l electrum-seed -r -d "Specify Electrum seed for wallet recovery/creation"
|
||||
complete -c monero-wallet-cli -l allow-mismatched-daemon-version -d "Allow communicating with a daemon that uses a different RPC version"
|
||||
complete -c monero-wallet-cli -l restore-height -r -d "Restore from specific blockchain height. Default: 0"
|
||||
complete -c monero-wallet-cli -l restore-date -r -d "Restore from estimated blockchain height on specified date"
|
||||
complete -c monero-wallet-cli -l do-not-relay -d "The newly created transaction will not be relayed to the monero network"
|
||||
|
||||
@@ -30,6 +30,7 @@ complete -c monero-wallet-rpc -l tx-notify -r -d "Run a program for each new inc
|
||||
complete -c monero-wallet-rpc -l no-dns -d "Do not use DNS"
|
||||
complete -c monero-wallet-rpc -l offline -d "Do not connect to a daemon, nor use DNS"
|
||||
complete -c monero-wallet-rpc -l extra-entropy -r -F -d "File containing extra entropy to initialize the PRNG (any data, aim for 256 bits of entropy to be useful, which typically means more than 256 bits of data)"
|
||||
complete -c monero-wallet-cli -l allow-mismatched-daemon-version -d "Allow communicating with a daemon that uses a different version"
|
||||
complete -c monero-wallet-rpc -l rpc-bind-port -r -d "Sets bind port for server"
|
||||
complete -c monero-wallet-rpc -l disable-rpc-login -d "Disable HTTP authentication for RPC connections served by this process"
|
||||
complete -c monero-wallet-rpc -l restricted-rpc -d "Restricts to view-only commands"
|
||||
|
||||
@@ -524,12 +524,13 @@ class Wallet(object):
|
||||
}
|
||||
return self.rpc.send_json_rpc_request(finalize_multisig)
|
||||
|
||||
def exchange_multisig_keys(self, multisig_info, password = ''):
|
||||
def exchange_multisig_keys(self, multisig_info, password = '', force_update_use_with_caution = False):
|
||||
exchange_multisig_keys = {
|
||||
'method': 'exchange_multisig_keys',
|
||||
'params' : {
|
||||
'multisig_info': multisig_info,
|
||||
'password': password,
|
||||
'force_update_use_with_caution': force_update_use_with_caution,
|
||||
},
|
||||
'jsonrpc': '2.0',
|
||||
'id': '0'
|
||||
@@ -763,10 +764,13 @@ class Wallet(object):
|
||||
}
|
||||
return self.rpc.send_json_rpc_request(get_languages)
|
||||
|
||||
def export_outputs(self):
|
||||
def export_outputs(self, all = False, start = 0, count = 0xffffffff):
|
||||
export_outputs = {
|
||||
'method': 'export_outputs',
|
||||
'params': {
|
||||
'all': all,
|
||||
'start': start,
|
||||
'count': count,
|
||||
},
|
||||
'jsonrpc': '2.0',
|
||||
'id': '0'
|
||||
|
||||
Reference in New Issue
Block a user