diff options
37 files changed, 276 insertions, 486 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ccc4f56fc..3b327374d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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}} diff --git a/.github/workflows/depends.yml b/.github/workflows/depends.yml index 32df7dc42..57e9e68bb 100644 --- a/.github/workflows/depends.yml +++ b/.github/workflows/depends.yml @@ -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" @@ -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: diff --git a/contrib/brew/Brewfile b/contrib/brew/Brewfile index 36b073b25..c74e7b2a2 100644 --- a/contrib/brew/Brewfile +++ b/contrib/brew/Brewfile @@ -25,7 +25,6 @@ brew "unbound" brew "libsodium" brew "miniupnpc" brew "readline" -brew "ldns" brew "expat" brew "ccache" brew "doxygen" diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile index 3df1d677e..479b2e1c9 100644 --- a/contrib/depends/Makefile +++ b/contrib/depends/Makefile @@ -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) diff --git a/contrib/depends/config.site.in b/contrib/depends/config.site.in index dd91bcb2a..11dab8981 100644 --- a/contrib/depends/config.site.in +++ b/contrib/depends/config.site.in @@ -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" diff --git a/contrib/depends/packages/ldns.mk b/contrib/depends/packages/ldns.mk deleted file mode 100644 index 90c63e821..000000000 --- a/contrib/depends/packages/ldns.mk +++ /dev/null @@ -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 - diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 11e2cb7be..d2d1eca85 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -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 diff --git a/contrib/depends/packages/qt.mk b/contrib/depends/packages/qt.mk deleted file mode 100644 index 76c50f3fd..000000000 --- a/contrib/depends/packages/qt.mk +++ /dev/null @@ -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 diff --git a/contrib/depends/packages/unbound.mk b/contrib/depends/packages/unbound.mk index 9336524f3..421c51f7f 100644 --- a/contrib/depends/packages/unbound.mk +++ b/contrib/depends/packages/unbound.mk @@ -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 diff --git a/contrib/depends/patches/qt/fix_no_printer.patch b/contrib/depends/patches/qt/fix_no_printer.patch deleted file mode 100644 index 137235613..000000000 --- a/contrib/depends/patches/qt/fix_no_printer.patch +++ /dev/null @@ -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 diff --git a/contrib/depends/patches/qt/fix_qt_pkgconfig.patch b/contrib/depends/patches/qt/fix_qt_pkgconfig.patch deleted file mode 100644 index 73f4d89f7..000000000 --- a/contrib/depends/patches/qt/fix_qt_pkgconfig.patch +++ /dev/null @@ -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: \ diff --git a/contrib/depends/patches/qt/fix_rcc_determinism.patch b/contrib/depends/patches/qt/fix_rcc_determinism.patch deleted file mode 100644 index c1b07fe23..000000000 --- a/contrib/depends/patches/qt/fix_rcc_determinism.patch +++ /dev/null @@ -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'); - } diff --git a/contrib/depends/patches/qt/no-xlib.patch b/contrib/depends/patches/qt/no-xlib.patch deleted file mode 100644 index 6800d398c..000000000 --- a/contrib/depends/patches/qt/no-xlib.patch +++ /dev/null @@ -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 - diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index 5f9501329..570065560 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -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) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 6ab6ff4fe..7bb1781d4 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -521,7 +521,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) { diff --git a/src/common/threadpool.h b/src/common/threadpool.h index ce1bc5799..53421e18b 100644 --- a/src/common/threadpool.h +++ b/src/common/threadpool.h @@ -42,10 +42,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); } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index b4abde1ad..eee27987f 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3434,7 +3434,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(); @@ -5137,7 +5137,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()); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 545831199..d8c782f78 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1013,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) { diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index f13e74b0f..eedd1511d 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -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()); diff --git a/src/multisig/multisig_account.cpp b/src/multisig/multisig_account.cpp index f3e78da18..401c6f1fe 100644 --- a/src/multisig/multisig_account.cpp +++ b/src/multisig/multisig_account.cpp @@ -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); } //---------------------------------------------------------------------------------------------------------------------- diff --git a/src/multisig/multisig_account.h b/src/multisig/multisig_account.h index 7beb594b4..9cd0942d4 100644 --- a/src/multisig/multisig_account.h +++ b/src/multisig/multisig_account.h @@ -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 diff --git a/src/multisig/multisig_account_kex_impl.cpp b/src/multisig/multisig_account_kex_impl.cpp index 443e84631..243058b81 100644 --- a/src/multisig/multisig_account_kex_impl.cpp +++ b/src/multisig/multisig_account_kex_impl.cpp @@ -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() + }; + + 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 &signer : signers) + 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 diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 21040317c..477a7907d 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -1333,7 +1333,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 +1383,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 +1536,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; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 860c3f0b0..f59af575e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -243,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>"); @@ -1138,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()) @@ -1168,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) @@ -4787,7 +4792,7 @@ bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version) uint32_t version_ = 0; if (!version) version = &version_; - bool wallet_is_outdated, daemon_is_outdated = false; + 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) @@ -11176,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; } diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 0f2fe7bc6..7c45d45e8 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -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); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 470206bc5..c4d3856d4 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -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) { @@ -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()); @@ -2173,7 +2173,7 @@ bool WalletImpl::connectToDaemon() Wallet::ConnectionStatus WalletImpl::connected() const { uint32_t version = 0; - bool wallet_is_outdated, daemon_is_outdated = false; + 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) { diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 018b2a0ed..ec2d7e9b3 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -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; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index b67bce60c..0ae84adb9 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -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 diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 588ddd572..17ff0e44b 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1346,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(); } @@ -2724,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; @@ -2967,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) @@ -3464,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; @@ -5126,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; @@ -5150,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; @@ -5177,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 diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 115651e3b..3fcefd16f 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -886,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 diff --git a/src/wallet/wallet_rpc_payments.cpp b/src/wallet/wallet_rpc_payments.cpp index 61eaa8070..8474fb568 100644 --- a/src/wallet/wallet_rpc_payments.cpp +++ b/src/wallet/wallet_rpc_payments.cpp @@ -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 diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 1f0a1371f..964945175 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -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) { diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 2cca323ee..60df6296f 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -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 @@ -2535,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; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 5c8c9f79f..d5c9edc55 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -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; } diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index 8f8ad52e1..3b3c4197c 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -96,8 +96,46 @@ static std::vector<std::string> exchange_round(std::vector<tools::wallet2>& wall 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,8 +205,9 @@ 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_setup_rounds_required(wallets.size(), M); @@ -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); diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py index f2618b0a8..0f6db76bd 100644 --- a/utils/python-rpc/framework/wallet.py +++ b/utils/python-rpc/framework/wallet.py @@ -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' |