mirror of
https://github.com/monero-project/monero.git
synced 2026-01-12 17:17:18 +09:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9fde8aa44 | ||
|
|
dae5fcaabc | ||
|
|
5881bec7d6 | ||
|
|
b66c523046 | ||
|
|
5f4e1fcc90 | ||
|
|
5f1e62115c | ||
|
|
6834ce6de2 | ||
|
|
8beb3cd2a1 | ||
|
|
dc24639e64 | ||
|
|
92f42b1e81 | ||
|
|
29d7ef0fe0 | ||
|
|
a9504f7001 | ||
|
|
3c36a6a227 | ||
|
|
e3e1f83694 | ||
|
|
82037f2365 | ||
|
|
26e0cecb95 | ||
|
|
b215ea9f56 | ||
|
|
067e232b19 | ||
|
|
d7f3805d2e | ||
|
|
a04d68f698 | ||
|
|
3b04e2e3d4 | ||
|
|
e25d21a788 | ||
|
|
2509717b11 | ||
|
|
77ef8c1839 | ||
|
|
e0f4606a72 | ||
|
|
d7d6d23867 | ||
|
|
1b9e6861b7 | ||
|
|
cc33d3b2de | ||
|
|
d8f95843c4 | ||
|
|
e16982617b | ||
|
|
eca27122df | ||
|
|
4b609dede3 | ||
|
|
5209bbd0c5 | ||
|
|
161fd13768 | ||
|
|
49c11b2248 | ||
|
|
f5df0e272e | ||
|
|
34da7d852b | ||
|
|
84cc3b916e | ||
|
|
24e519b9b6 | ||
|
|
3512eb5e68 | ||
|
|
42545e9adc | ||
|
|
9d97ffb804 | ||
|
|
71b9dd96ca | ||
|
|
b63f1ea637 | ||
|
|
5f83ec59ca | ||
|
|
b26ab0b580 | ||
|
|
17ab6fdd5a | ||
|
|
a2cd4f2cc7 | ||
|
|
9556ba0d66 | ||
|
|
b8cee6bf9e | ||
|
|
5adbede27f | ||
|
|
735a33e8d8 | ||
|
|
e037534f44 | ||
|
|
9570081ae5 | ||
|
|
e0fa7fa384 | ||
|
|
9c426a30f9 | ||
|
|
ca9b2493ee | ||
|
|
cf436cde1a | ||
|
|
b99e670f84 | ||
|
|
88a0985104 | ||
|
|
4701a78ad1 | ||
|
|
861895e92d | ||
|
|
5fc4d57ee5 | ||
|
|
efbd42f9d9 | ||
|
|
4cbaa6e43d | ||
|
|
f0b3bbf808 | ||
|
|
33aa05678f | ||
|
|
37252f6aec | ||
|
|
67e6c4370b | ||
|
|
7340300460 | ||
|
|
7dd11711b0 | ||
|
|
bcf3f6afdd | ||
|
|
3ebd05d4e5 | ||
|
|
a093092ef7 | ||
|
|
1eebb82bcc | ||
|
|
fb6a363050 | ||
|
|
2e2139ffb7 | ||
|
|
0749a8bd3c | ||
|
|
1b0afeeb1c | ||
|
|
418a993618 | ||
|
|
ea7f954381 | ||
|
|
6e8554221f | ||
|
|
53df2deb36 | ||
|
|
e67016ddb4 | ||
|
|
661439f4e0 | ||
|
|
5fdcda50ee | ||
|
|
921b0fb11b | ||
|
|
7ece1550e1 | ||
|
|
85318e7800 | ||
|
|
a085da3247 | ||
|
|
d8b1ec8b8b | ||
|
|
02563bf4b9 | ||
|
|
c57a65b246 | ||
|
|
06d05c21eb |
@ -26,6 +26,8 @@ env:
|
||||
- HOST=arm-linux-gnueabihf PACKAGES="gperf g++-arm-linux-gnueabihf"
|
||||
# ARM v8
|
||||
- HOST=aarch64-linux-gnu PACKAGES="gperf g++-aarch64-linux-gnu"
|
||||
# i686 Win
|
||||
- HOST=i686-w64-mingw32 PACKAGES="python3 nsis g++-mingw-w64-i686"
|
||||
# i686 Linux
|
||||
- HOST=i686-pc-linux-gnu PACKAGES="gperf cmake g++-multilib bc python3-zmq" RUN_TESTS=true
|
||||
# Win64
|
||||
@ -56,7 +58,7 @@ script:
|
||||
- export TRAVIS_COMMIT_LOG=`git log --format=fuller -1`
|
||||
- OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST
|
||||
- if [ -z "$NO_DEPENDS" ]; then $DOCKER_EXEC ccache --max-size=$CCACHE_SIZE; fi
|
||||
- $DOCKER_EXEC bash -c "mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/share/toolchain.cmake .. && make $MAKEJOBS"
|
||||
- $DOCKER_EXEC bash -c "mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/share/toolchain.cmake -DTRAVIS=true .. && make $MAKEJOBS"
|
||||
- export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/lib
|
||||
after_script:
|
||||
- echo $TRAVIS_COMMIT_RANGE
|
||||
|
||||
@ -40,6 +40,7 @@ if (IOS)
|
||||
endif()
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.7)
|
||||
message(STATUS "CMake version ${CMAKE_VERSION}")
|
||||
|
||||
project(monero)
|
||||
|
||||
@ -139,7 +140,6 @@ if(ARCH_ID STREQUAL "ppc64le")
|
||||
set(PPC64LE 1)
|
||||
set(PPC64 0)
|
||||
set(PPC 0)
|
||||
|
||||
endif()
|
||||
|
||||
if(ARCH_ID STREQUAL "powerpc64" OR ARCH_ID STREQUAL "ppc64")
|
||||
@ -172,6 +172,25 @@ else()
|
||||
message(STATUS "Building without build tag")
|
||||
endif()
|
||||
|
||||
if(NOT MANUAL_SUBMODULES)
|
||||
find_package(Git)
|
||||
if(GIT_FOUND)
|
||||
message(STATUS "Checking submodules")
|
||||
execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/miniupnp && git rev-parse HEAD" OUTPUT_VARIABLE miniupnpLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/unbound && git rev-parse HEAD" OUTPUT_VARIABLE unboundLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/rapidjson && git rev-parse HEAD" OUTPUT_VARIABLE rapidjsonLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/miniupnp | awk '{print $3}'" OUTPUT_VARIABLE miniupnpCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/unbound | awk '{print $3}'" OUTPUT_VARIABLE unboundCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/rapidjson | awk '{print $3}'" OUTPUT_VARIABLE rapidjsonCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
string(COMPARE EQUAL "${miniupnpLocalHead}" "${miniupnpCheckedHead}" miniupnpUpToDate)
|
||||
string(COMPARE EQUAL "${unboundLocalHead}" "${unboundCheckedHead}" unboundUpToDate)
|
||||
string(COMPARE EQUAL "${rapidjsonLocalHead}" "${rapidjsonCheckedHead}" rapidjsonUpToDate)
|
||||
if (NOT miniupnpUpToDate OR NOT unboundUpToDate OR NOT rapidjsonUpToDate)
|
||||
message(FATAL_ERROR "Submodules not up to date. Please update with git submodule init && git submodule update, or run cmake with -DMANUAL_SUBMODULES=1")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG ${OPT_FLAGS_RELEASE}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${OPT_FLAGS_RELEASE}")
|
||||
|
||||
@ -498,10 +517,8 @@ if(MSVC)
|
||||
include_directories(SYSTEM src/platform/msc)
|
||||
else()
|
||||
include(TestCXXAcceptsFlag)
|
||||
if (NOT ARM6)
|
||||
if(NOT DEPENDS OR DEPENDS AND NOT ARM)
|
||||
set(ARCH native CACHE STRING "CPU to build for: -march value or 'default' to not pass -march at all")
|
||||
endif()
|
||||
if (NOT ARCH)
|
||||
set(ARCH native CACHE STRING "CPU to build for: -march value or 'default' to not pass -march at all")
|
||||
endif()
|
||||
message(STATUS "Building on ${CMAKE_SYSTEM_PROCESSOR} for ${ARCH}")
|
||||
if(ARCH STREQUAL "default")
|
||||
@ -529,6 +546,40 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(NO_AES "Explicitly disable AES support" ${NO_AES})
|
||||
|
||||
if(NO_AES)
|
||||
message(STATUS "AES support explicitly disabled")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_AES")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNO_AES")
|
||||
elseif(NOT ARM AND NOT PPC64LE AND NOT PPC64 AND NOT PPC AND NOT S390X)
|
||||
message(STATUS "AES support enabled")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes")
|
||||
elseif(PPC64LE OR PPC64 OR PPC)
|
||||
message(STATUS "AES support not available on POWER")
|
||||
elseif(S390X)
|
||||
message(STATUS "AES support not available on s390x")
|
||||
elseif(ARM6)
|
||||
message(STATUS "AES support not available on ARMv6")
|
||||
elseif(ARM7)
|
||||
message(STATUS "AES support not available on ARMv7")
|
||||
elseif(ARM8)
|
||||
CHECK_CXX_ACCEPTS_FLAG("-march=${ARCH}+crypto" ARCH_PLUS_CRYPTO)
|
||||
if(ARCH_PLUS_CRYPTO)
|
||||
message(STATUS "Crypto extensions enabled for ARMv8")
|
||||
set(ARCH_FLAG "-march=${ARCH}+crypto")
|
||||
else()
|
||||
message(STATUS "Crypto extensions unavailable on your ARMv8 device")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "AES support disabled")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ARCH_FLAG}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARCH_FLAG}")
|
||||
|
||||
set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Wno-error=extra -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-variable -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized")
|
||||
if(NOT MINGW)
|
||||
set(WARNINGS_AS_ERRORS_FLAG "-Werror")
|
||||
@ -639,38 +690,8 @@ else()
|
||||
message(STATUS "Using C++ security hardening flags: ${CXX_SECURITY_FLAGS}")
|
||||
message(STATUS "Using linker security hardening flags: ${LD_SECURITY_FLAGS}")
|
||||
|
||||
option(NO_AES "Explicitly disable AES support" ${NO_AES})
|
||||
|
||||
if(NO_AES)
|
||||
message(STATUS "AES support explicitly disabled")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_AES")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNO_AES")
|
||||
elseif(NOT ARM AND NOT PPC64LE AND NOT PPC64 AND NOT PPC AND NOT S390X)
|
||||
message(STATUS "AES support enabled")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes")
|
||||
elseif(PPC64LE OR PPC64 OR PPC)
|
||||
message(STATUS "AES support not available on POWER")
|
||||
elseif(S390X)
|
||||
message(STATUS "AES support not available on s390x")
|
||||
elseif(ARM6)
|
||||
message(STATUS "AES support not available on ARMv6")
|
||||
elseif(ARM7)
|
||||
message(STATUS "AES support not available on ARMv7")
|
||||
elseif(ARM8)
|
||||
CHECK_CXX_ACCEPTS_FLAG("-march=${ARCH}+crypto" ARCH_PLUS_CRYPTO)
|
||||
if(ARCH_PLUS_CRYPTO)
|
||||
message(STATUS "Crypto extensions enabled for ARMv8")
|
||||
set(ARCH_FLAG "-march=${ARCH}+crypto")
|
||||
else()
|
||||
message(STATUS "Crypto extensions unavailable on your ARMv8 device")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "AES support disabled")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} ${COVERAGE_FLAGS} ${PIC_FLAG} ${C_SECURITY_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_CPP_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG} ${COVERAGE_FLAGS} ${PIC_FLAG} ${CXX_SECURITY_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${COVERAGE_FLAGS} ${PIC_FLAG} ${C_SECURITY_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_CPP_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${COVERAGE_FLAGS} ${PIC_FLAG} ${CXX_SECURITY_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS}")
|
||||
|
||||
# With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that
|
||||
|
||||
@ -115,6 +115,7 @@ COPY . .
|
||||
ENV USE_SINGLE_BUILDDIR=1
|
||||
ARG NPROC
|
||||
RUN set -ex && \
|
||||
git submodule init && git submodule update && \
|
||||
rm -rf build && \
|
||||
if [ -z "$NPROC" ] ; \
|
||||
then make -j$(nproc) release-static ; \
|
||||
|
||||
9
Makefile
9
Makefile
@ -103,10 +103,15 @@ release-static-linux-armv7:
|
||||
mkdir -p $(builddir)/release
|
||||
cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv7" $(topdir) && $(MAKE)
|
||||
|
||||
release-static-android:
|
||||
release-static-android-armv7:
|
||||
mkdir -p $(builddir)/release/translations
|
||||
cd $(builddir)/release/translations && cmake ../../../translations && $(MAKE)
|
||||
cd $(builddir)/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARM_MODE=ON -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" ../.. && $(MAKE)
|
||||
cd $(builddir)/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android-armv7" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARM_MODE=ON -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" ../.. && $(MAKE)
|
||||
|
||||
release-static-android-armv8:
|
||||
mkdir -p $(builddir)/release/translations
|
||||
cd $(builddir)/release/translations && cmake ../../../translations && $(MAKE)
|
||||
cd $(builddir)/release && CC=aarch64-linux-android-clang CXX=aarch64-linux-android-clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android-armv8" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI="arm64-v8a" ../.. && $(MAKE)
|
||||
|
||||
release-static-linux-armv8:
|
||||
mkdir -p $(builddir)/release
|
||||
|
||||
17
README.md
17
README.md
@ -150,14 +150,13 @@ library archives (`.a`).
|
||||
| GTest | 1.5 | YES | `libgtest-dev`^ | `gtest` | `gtest-devel` | YES | Test suite |
|
||||
| Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | YES | Documentation |
|
||||
| Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | YES | Documentation |
|
||||
| pcsclite | ? | NO | `libpcsclite-dev` | ? | `pcsc-lite pcsc-lite-devel` | NO | Ledger |
|
||||
|
||||
|
||||
[^] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must
|
||||
build the library binary manually. This can be done with the following command ```sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libg* /usr/lib/ ```
|
||||
|
||||
Debian / Ubuntu one liner for all dependencies
|
||||
``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpcsclite-dev libpgm-dev```
|
||||
``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpgm-dev```
|
||||
|
||||
### Cloning the repository
|
||||
|
||||
@ -180,7 +179,7 @@ invokes cmake commands as needed.
|
||||
* Change to the root of the source code directory, change to the most recent release branch, and build:
|
||||
|
||||
cd monero
|
||||
git checkout v0.13.0.0
|
||||
git checkout v0.13.0.3
|
||||
make
|
||||
|
||||
*Optional*: If your machine has several cores and enough memory, enable
|
||||
@ -317,11 +316,11 @@ application.
|
||||
|
||||
To build for 64-bit Windows:
|
||||
|
||||
pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium
|
||||
pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi
|
||||
|
||||
To build for 32-bit Windows:
|
||||
|
||||
pacman -S mingw-w64-i686-toolchain make mingw-w64-i686-cmake mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-zeromq mingw-w64-i686-libsodium
|
||||
pacman -S mingw-w64-i686-toolchain make mingw-w64-i686-cmake mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-zeromq mingw-w64-i686-libsodium mingw-w64-i686-hidapi
|
||||
|
||||
* Open the MingW shell via `MinGW-w64-Win64 Shell` shortcut on 64-bit Windows
|
||||
or `MinGW-w64-Win64 Shell` shortcut on 32-bit Windows. Note that if you are
|
||||
@ -341,7 +340,7 @@ application.
|
||||
|
||||
* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.13.0.0'. If you dont care about the version and just want binaries from master, skip this step:
|
||||
|
||||
git checkout v0.13.0.0
|
||||
git checkout v0.13.0.3
|
||||
|
||||
* If you are on a 64-bit system, run:
|
||||
|
||||
@ -472,12 +471,14 @@ Then you can run make as usual.
|
||||
|
||||
### On Linux for Android (using docker):
|
||||
|
||||
# Build image
|
||||
# Build image (for ARM 32-bit)
|
||||
docker build -f utils/build_scripts/android32.Dockerfile -t monero-android .
|
||||
# Build image (for ARM 64-bit)
|
||||
docker build -f utils/build_scripts/android64.Dockerfile -t monero-android .
|
||||
# Create container
|
||||
docker create -it --name monero-android monero-android bash
|
||||
# Get binaries
|
||||
docker cp monero-android:/opt/android/monero/build/release/bin .
|
||||
docker cp monero-android:/src/build/release/bin .
|
||||
|
||||
### Building portable statically linked binaries (Cross Compiling)
|
||||
|
||||
|
||||
@ -39,6 +39,20 @@ find_package_handle_standard_args(HIDAPI
|
||||
|
||||
if(HIDAPI_FOUND)
|
||||
set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARY}")
|
||||
if((STATIC AND UNIX AND NOT APPLE) OR (DEPENDS AND CMAKE_SYSTEM_NAME STREQUAL "Linux"))
|
||||
find_library(LIBUSB-1.0_LIBRARY usb-1.0)
|
||||
find_library(LIBUDEV_LIBRARY udev)
|
||||
if(LIBUSB-1.0_LIBRARY)
|
||||
set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUSB-1.0_LIBRARY}")
|
||||
if(LIBUDEV_LIBRARY)
|
||||
set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUDEV_LIBRARY}")
|
||||
else()
|
||||
message(WARNING "libudev library not found, binaries may fail to link.")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "libusb-1.0 library not found, binaries may fail to link.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(HIDAPI_INCLUDE_DIRS "${HIDAPI_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
@ -5,7 +5,7 @@ $(package)_file_name=$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=a96ecb8637667897b8bd4dee4c22c7c5f08b327be45186e912ce6bc768385852
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--disable-gudev --disable-introspection --disable-hwdb --disable-manpages
|
||||
$(package)_config_opts=--disable-gudev --disable-introspection --disable-hwdb --disable-manpages --disable-shared
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
package=hidapi-darwin
|
||||
$(package)_version=0.8.0-rc1
|
||||
$(package)_download_path=https://github.com/signal11/hidapi/archive
|
||||
$(package)_file_name=hidapi-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=3c147200bf48a04c1e927cd81589c5ddceff61e6dac137a605f6ac9793f4af61
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--enable-static
|
||||
$(package)_config_opts+=--prefix=$(host_prefix)
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
./bootstrap &&\
|
||||
$($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)"
|
||||
endef
|
||||
|
||||
define $(package)_build_cmds
|
||||
$(MAKE)
|
||||
endef
|
||||
|
||||
define $(package)_stage_cmds
|
||||
$(MAKE) DESTDIR=$($(package)_staging_dir) install
|
||||
endef
|
||||
@ -1,28 +0,0 @@
|
||||
package=hidapi-linux
|
||||
$(package)_version=0.8.0-rc1
|
||||
$(package)_download_path=https://github.com/signal11/hidapi/archive
|
||||
$(package)_file_name=hidapi-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=3c147200bf48a04c1e927cd81589c5ddceff61e6dac137a605f6ac9793f4af61
|
||||
$(package)_dependencies=libusb eudev
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--enable-static
|
||||
$(package)_config_opts+=--prefix=$(host_prefix)
|
||||
$(package)_config_opts+=libudev_LIBS="-L$(host_prefix)/lib -ludev"
|
||||
$(package)_config_opts+=libudev_CFLAGS=-I$(host_prefix)/include
|
||||
$(package)_config_opts+=libusb_LIBS="-L$(host_prefix)/lib -lusb-1.0"
|
||||
$(package)_config_opts+=libusb_CFLAGS=-I$(host_prefix)/include/libusb-1.0
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
./bootstrap &&\
|
||||
$($(package)_autoconf) $($(package)_config_opts)
|
||||
endef
|
||||
|
||||
define $(package)_build_cmds
|
||||
$(MAKE)
|
||||
endef
|
||||
|
||||
define $(package)_stage_cmds
|
||||
$(MAKE) DESTDIR=$($(package)_staging_dir) install
|
||||
endef
|
||||
@ -3,10 +3,17 @@ $(package)_version=0.8.0-rc1
|
||||
$(package)_download_path=https://github.com/signal11/hidapi/archive
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=3c147200bf48a04c1e927cd81589c5ddceff61e6dac137a605f6ac9793f4af61
|
||||
$(package)_linux_dependencies=libusb eudev
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--enable-static
|
||||
$(package)_config_opts=--enable-static --disable-shared
|
||||
$(package)_config_opts+=--prefix=$(host_prefix)
|
||||
$(package)_config_opts_darwin+=RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)"
|
||||
$(package)_config_opts_linux+=libudev_LIBS="-L$(host_prefix)/lib -ludev"
|
||||
$(package)_config_opts_linux+=libudev_CFLAGS=-I$(host_prefix)/include
|
||||
$(package)_config_opts_linux+=libusb_LIBS="-L$(host_prefix)/lib -lusb-1.0"
|
||||
$(package)_config_opts_linux+=libusb_CFLAGS=-I$(host_prefix)/include/libusb-1.0
|
||||
$(package)_config_opts_linux+=--with-pic
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt
|
||||
packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt hidapi
|
||||
native_packages := native_ccache
|
||||
|
||||
wallet_packages=bdb
|
||||
|
||||
darwin_native_packages = native_biplist native_ds_store native_mac_alias
|
||||
darwin_packages = sodium-darwin hidapi-darwin
|
||||
darwin_packages = sodium-darwin
|
||||
|
||||
linux_packages = eudev libusb hidapi-linux
|
||||
linux_packages = eudev libusb
|
||||
|
||||
ifeq ($(host_os),linux)
|
||||
packages += unwind
|
||||
@ -15,7 +15,6 @@ endif
|
||||
ifeq ($(host_os),mingw32)
|
||||
packages += icu4c
|
||||
packages += sodium
|
||||
packages += hidapi
|
||||
endif
|
||||
|
||||
ifneq ($(build_os),darwin)
|
||||
|
||||
@ -6,7 +6,7 @@ $(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_build_opts_darwin=OS=Darwin LIBTOOL="$($(package)_libtool)"
|
||||
$(package)_config_opts=--enable-static
|
||||
$(package)_config_opts=--enable-static --disable-shared
|
||||
$(package)_config_opts+=--prefix=$(host_prefix)
|
||||
endef
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ $(package)_file_name=libsodium-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe9e178f4
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts=--enable-static
|
||||
$(package)_config_opts=--enable-static --disable-shared
|
||||
$(package)_config_opts+=--prefix=$(host_prefix)
|
||||
endef
|
||||
|
||||
|
||||
@ -18,6 +18,9 @@ SET(LIBUNWIND_INCLUDE_DIR @prefix@/include)
|
||||
SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a)
|
||||
SET(LIBUNWIND_LIBRARY_DIRS @prefix@/lib)
|
||||
|
||||
SET(LIBUSB-1.0_LIBRARY @prefix@/lib/libusb-1.0.a)
|
||||
SET(LIBUDEV_LIBRARY @prefix@/lib/libudev.a)
|
||||
|
||||
SET(ZMQ_INCLUDE_PATH @prefix@/include)
|
||||
SET(ZMQ_LIB @prefix@/lib/libzmq.a)
|
||||
|
||||
@ -48,7 +51,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
SET(APPLE True)
|
||||
SET(BUILD_TAG "mac-x64")
|
||||
SET(BUILD_64 ON)
|
||||
if(NOT TRAVIS)
|
||||
SET(ARCH "x86_64")
|
||||
endif()
|
||||
SET(BREW OFF)
|
||||
SET(PORT OFF)
|
||||
SET(CMAKE_OSX_SYSROOT "@sdk@/MacOSX10.11.sdk/")
|
||||
@ -78,5 +83,9 @@ elseif(ARCHITECTURE STREQUAL "aarch64")
|
||||
set(BUILD_64 ON)
|
||||
endif()
|
||||
|
||||
if(ARCHITECTURE STREQUAL "i686" AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
SET(LINUX_32 ON)
|
||||
endif()
|
||||
|
||||
#Create a new global cmake flag that indicates building with depends
|
||||
set (DEPENDS true)
|
||||
|
||||
@ -63,7 +63,8 @@ namespace epee
|
||||
|
||||
~async_stdin_reader()
|
||||
{
|
||||
stop();
|
||||
try { stop(); }
|
||||
catch (...) { /* ignore */ }
|
||||
}
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
|
||||
@ -275,6 +275,9 @@ public:
|
||||
}
|
||||
virtual ~async_protocol_handler()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
m_deletion_initiated = true;
|
||||
if(m_connection_initialized)
|
||||
{
|
||||
@ -288,6 +291,9 @@ public:
|
||||
CHECK_AND_ASSERT_MES_NO_RET(0 == boost::interprocess::ipcdetail::atomic_read32(&m_wait_count), "Failed to wait for operation completion. m_wait_count = " << m_wait_count);
|
||||
|
||||
MTRACE(m_connection_context << "~async_protocol_handler()");
|
||||
|
||||
}
|
||||
catch (...) { /* ignore */ }
|
||||
}
|
||||
|
||||
bool start_outer_call()
|
||||
|
||||
@ -106,7 +106,8 @@ namespace net_utils
|
||||
~blocked_mode_client()
|
||||
{
|
||||
//profile_tools::local_coast lc("~blocked_mode_client()", 3);
|
||||
shutdown();
|
||||
try { shutdown(); }
|
||||
catch(...) { /* ignore */ }
|
||||
}
|
||||
|
||||
inline
|
||||
|
||||
@ -137,7 +137,12 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s
|
||||
el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck);
|
||||
el::Helpers::installPreRollOutCallback([filename_base, max_log_files](const char *name, size_t){
|
||||
std::string rname = generate_log_filename(filename_base.c_str());
|
||||
rename(name, rname.c_str());
|
||||
int ret = rename(name, rname.c_str());
|
||||
if (ret < 0)
|
||||
{
|
||||
// can't log a failure, but don't do the file removal below
|
||||
return;
|
||||
}
|
||||
if (max_log_files != 0)
|
||||
{
|
||||
std::vector<boost::filesystem::path> found_files;
|
||||
|
||||
4
external/easylogging++/easylogging++.cc
vendored
4
external/easylogging++/easylogging++.cc
vendored
@ -1975,11 +1975,11 @@ void VRegistry::setCategories(const char* categories, bool clear) {
|
||||
m_cached_allowed_categories.clear();
|
||||
m_categoriesString.clear();
|
||||
}
|
||||
if (!categories)
|
||||
return;
|
||||
if (!m_categoriesString.empty())
|
||||
m_categoriesString += ",";
|
||||
m_categoriesString += categories;
|
||||
if (!categories)
|
||||
return;
|
||||
|
||||
bool isCat = true;
|
||||
bool isLevel = false;
|
||||
|
||||
2
external/unbound
vendored
2
external/unbound
vendored
@ -1 +1 @@
|
||||
Subproject commit d3724dfa553429d368c27aef160f02f5e8b8075f
|
||||
Subproject commit 7f23967954736dcaa366806b9eaba7e2bdfede11
|
||||
@ -649,7 +649,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin
|
||||
{
|
||||
MDB_txn *rtxn;
|
||||
mdb_txn_cursors *rcurs;
|
||||
block_rtxn_start(&rtxn, &rcurs);
|
||||
bool my_rtxn = block_rtxn_start(&rtxn, &rcurs);
|
||||
for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num)
|
||||
{
|
||||
// we have access to block weight, which will be greater or equal to block size,
|
||||
@ -661,7 +661,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin
|
||||
// some blocks were to be skipped for being outliers.
|
||||
++num_blocks_used;
|
||||
}
|
||||
block_rtxn_stop();
|
||||
if (my_rtxn) block_rtxn_stop();
|
||||
avg_block_size = total_block_size / num_blocks_used;
|
||||
MDEBUG("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size);
|
||||
}
|
||||
@ -1145,7 +1145,10 @@ BlockchainLMDB::~BlockchainLMDB()
|
||||
|
||||
// batch transaction shouldn't be active at this point. If it is, consider it aborted.
|
||||
if (m_batch_active)
|
||||
batch_abort();
|
||||
{
|
||||
try { batch_abort(); }
|
||||
catch (...) { /* ignore */ }
|
||||
}
|
||||
if (m_open)
|
||||
close();
|
||||
}
|
||||
@ -3589,7 +3592,9 @@ void BlockchainLMDB::migrate_0_1()
|
||||
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_heights: ", result).c_str()));
|
||||
if (!i) {
|
||||
MDB_stat ms;
|
||||
mdb_stat(txn, m_block_heights, &ms);
|
||||
result = mdb_stat(txn, m_block_heights, &ms);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to query block_heights table: ", result).c_str()));
|
||||
i = ms.ms_entries;
|
||||
}
|
||||
}
|
||||
@ -3692,7 +3697,9 @@ void BlockchainLMDB::migrate_0_1()
|
||||
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_timestamps: ", result).c_str()));
|
||||
if (!i) {
|
||||
MDB_stat ms;
|
||||
mdb_stat(txn, m_block_info, &ms);
|
||||
result = mdb_stat(txn, m_block_info, &ms);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to query block_info table: ", result).c_str()));
|
||||
i = ms.ms_entries;
|
||||
}
|
||||
}
|
||||
@ -3812,7 +3819,9 @@ void BlockchainLMDB::migrate_0_1()
|
||||
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for spent_keys: ", result).c_str()));
|
||||
if (!i) {
|
||||
MDB_stat ms;
|
||||
mdb_stat(txn, m_hf_versions, &ms);
|
||||
result = mdb_stat(txn, m_hf_versions, &ms);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to query hf_versions table: ", result).c_str()));
|
||||
i = ms.ms_entries;
|
||||
}
|
||||
}
|
||||
@ -3967,7 +3976,9 @@ void BlockchainLMDB::migrate_0_1()
|
||||
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs: ", result).c_str()));
|
||||
if (!i) {
|
||||
MDB_stat ms;
|
||||
mdb_stat(txn, m_txs, &ms);
|
||||
result = mdb_stat(txn, m_txs, &ms);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to query txs table: ", result).c_str()));
|
||||
i = ms.ms_entries;
|
||||
if (i) {
|
||||
MDB_val_set(pk, "txblk");
|
||||
|
||||
@ -32,6 +32,8 @@ if(PER_BLOCK_CHECKPOINT)
|
||||
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
|
||||
elseif(APPLE AND NOT DEPENDS)
|
||||
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
|
||||
elseif(LINUX_32)
|
||||
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
|
||||
else()
|
||||
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
|
||||
endif()
|
||||
|
||||
@ -401,7 +401,8 @@ static bool for_all_transactions(const std::string &filename, uint64_t &start_id
|
||||
}
|
||||
|
||||
mdb_cursor_close(cur);
|
||||
mdb_txn_commit(txn);
|
||||
dbr = mdb_txn_commit(txn);
|
||||
if (dbr) throw std::runtime_error("Failed to commit db transaction: " + std::string(mdb_strerror(dbr)));
|
||||
tx_active = false;
|
||||
mdb_dbi_close(env, dbi);
|
||||
mdb_env_close(env);
|
||||
@ -471,7 +472,8 @@ static uint64_t find_first_diverging_transaction(const std::string &first_filena
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
mdb_cursor_close(cur[i]);
|
||||
mdb_txn_commit(txn[i]);
|
||||
dbr = mdb_txn_commit(txn[i]);
|
||||
if (dbr) throw std::runtime_error("Failed to query transaction: " + std::string(mdb_strerror(dbr)));
|
||||
tx_active[i] = false;
|
||||
mdb_dbi_close(env[i], dbi[i]);
|
||||
mdb_env_close(env[i]);
|
||||
@ -534,12 +536,15 @@ static uint64_t get_num_spent_outputs()
|
||||
return count;
|
||||
}
|
||||
|
||||
static void add_spent_output(MDB_cursor *cur, const output_data &od)
|
||||
static bool add_spent_output(MDB_cursor *cur, const output_data &od)
|
||||
{
|
||||
MDB_val k = {sizeof(od.amount), (void*)&od.amount};
|
||||
MDB_val v = {sizeof(od.offset), (void*)&od.offset};
|
||||
int dbr = mdb_cursor_put(cur, &k, &v, 0);
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_KEYEXIST, "Failed to add spent output: " + std::string(mdb_strerror(dbr)));
|
||||
int dbr = mdb_cursor_put(cur, &k, &v, MDB_NODUPDATA);
|
||||
if (dbr == MDB_KEYEXIST)
|
||||
return false;
|
||||
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to add spent output: " + std::string(mdb_strerror(dbr)));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_output_spent(MDB_cursor *cur, const output_data &od)
|
||||
@ -675,7 +680,7 @@ static uint64_t get_ring_subset_instances(MDB_txn *txn, uint64_t amount, const s
|
||||
uint64_t extra = 0;
|
||||
std::vector<uint64_t> subset;
|
||||
subset.reserve(ring.size());
|
||||
for (uint64_t mask = 1; mask < (1u << ring.size()) - 1; ++mask)
|
||||
for (uint64_t mask = 1; mask < (((uint64_t)1) << ring.size()) - 1; ++mask)
|
||||
{
|
||||
subset.resize(0);
|
||||
for (size_t i = 0; i < ring.size(); ++i)
|
||||
@ -1151,8 +1156,8 @@ int main(int argc, char* argv[])
|
||||
if (!is_output_spent(cur, output_data(output.first, output.second)))
|
||||
{
|
||||
blackballs.push_back(output);
|
||||
add_spent_output(cur, output_data(output.first, output.second));
|
||||
inc_stat(txn, output.first ? "pre-rct-extra" : "rct-ring-extra");
|
||||
if (add_spent_output(cur, output_data(output.first, output.second)))
|
||||
inc_stat(txn, output.first ? "pre-rct-extra" : "rct-ring-extra");
|
||||
}
|
||||
}
|
||||
if (!blackballs.empty())
|
||||
@ -1214,8 +1219,8 @@ int main(int argc, char* argv[])
|
||||
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
|
||||
}
|
||||
blackballs.push_back(output);
|
||||
add_spent_output(cur, output_data(txin.amount, absolute[0]));
|
||||
inc_stat(txn, txin.amount ? "pre-rct-ring-size-1" : "rct-ring-size-1");
|
||||
if (add_spent_output(cur, output_data(txin.amount, absolute[0])))
|
||||
inc_stat(txn, txin.amount ? "pre-rct-ring-size-1" : "rct-ring-size-1");
|
||||
}
|
||||
else if (n == 0 && instances == new_ring.size())
|
||||
{
|
||||
@ -1228,8 +1233,8 @@ int main(int argc, char* argv[])
|
||||
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
|
||||
}
|
||||
blackballs.push_back(output);
|
||||
add_spent_output(cur, output_data(txin.amount, absolute[o]));
|
||||
inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings");
|
||||
if (add_spent_output(cur, output_data(txin.amount, absolute[o])))
|
||||
inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings");
|
||||
}
|
||||
}
|
||||
else if (n == 0 && opt_check_subsets && get_ring_subset_instances(txn, txin.amount, new_ring) >= new_ring.size())
|
||||
@ -1243,8 +1248,8 @@ int main(int argc, char* argv[])
|
||||
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
|
||||
}
|
||||
blackballs.push_back(output);
|
||||
add_spent_output(cur, output_data(txin.amount, absolute[o]));
|
||||
inc_stat(txn, txin.amount ? "pre-rct-subset-rings" : "rct-subset-rings");
|
||||
if (add_spent_output(cur, output_data(txin.amount, absolute[o])))
|
||||
inc_stat(txn, txin.amount ? "pre-rct-subset-rings" : "rct-subset-rings");
|
||||
}
|
||||
}
|
||||
else if (n > 0 && get_relative_ring(txn, txin.k_image, relative_ring))
|
||||
@ -1279,8 +1284,8 @@ int main(int argc, char* argv[])
|
||||
std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
|
||||
}
|
||||
blackballs.push_back(output);
|
||||
add_spent_output(cur, output_data(txin.amount, common[0]));
|
||||
inc_stat(txn, txin.amount ? "pre-rct-key-image-attack" : "rct-key-image-attack");
|
||||
if (add_spent_output(cur, output_data(txin.amount, common[0])))
|
||||
inc_stat(txn, txin.amount ? "pre-rct-key-image-attack" : "rct-key-image-attack");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1391,9 +1396,9 @@ int main(int argc, char* argv[])
|
||||
absolute.size() << "-ring where all other outputs are known to be spent");
|
||||
}
|
||||
blackballs.push_back(output);
|
||||
add_spent_output(cur, output_data(od.amount, last_unknown));
|
||||
if (add_spent_output(cur, output_data(od.amount, last_unknown)))
|
||||
inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction");
|
||||
work_spent.push_back(output_data(od.amount, last_unknown));
|
||||
inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -30,9 +30,15 @@ if(APPLE)
|
||||
add_library(blocks STATIC blockexports.c)
|
||||
set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
|
||||
else()
|
||||
add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat)
|
||||
add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
|
||||
add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat)
|
||||
if(LINUX_32)
|
||||
add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat)
|
||||
add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
|
||||
add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat)
|
||||
else()
|
||||
add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat)
|
||||
add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
|
||||
add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat)
|
||||
endif()
|
||||
add_library(blocks STATIC blocks.o testnet_blocks.o stagenet_blocks.o blockexports.c)
|
||||
set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
|
||||
endif()
|
||||
|
||||
@ -37,8 +37,10 @@ set(common_sources
|
||||
expect.cpp
|
||||
util.cpp
|
||||
i18n.cpp
|
||||
notify.cpp
|
||||
password.cpp
|
||||
perf_timer.cpp
|
||||
spawn.cpp
|
||||
threadpool.cpp
|
||||
updates.cpp
|
||||
aligned.c)
|
||||
@ -61,6 +63,7 @@ set(common_private_headers
|
||||
expect.h
|
||||
http_connection.h
|
||||
int-util.h
|
||||
notify.h
|
||||
pod-class.h
|
||||
rpc_client.h
|
||||
scoped_message_writer.h
|
||||
@ -70,6 +73,7 @@ set(common_private_headers
|
||||
i18n.h
|
||||
password.h
|
||||
perf_timer.h
|
||||
spawn.h
|
||||
stack_trace.h
|
||||
threadpool.h
|
||||
updates.h
|
||||
|
||||
@ -46,10 +46,11 @@ namespace bf = boost::filesystem;
|
||||
static const char *DEFAULT_DNS_PUBLIC_ADDR[] =
|
||||
{
|
||||
"194.150.168.168", // CCC (Germany)
|
||||
"81.3.27.54", // Lightning Wire Labs (Germany)
|
||||
"31.3.135.232", // OpenNIC (Switzerland)
|
||||
"80.67.169.40", // FDN (France)
|
||||
"209.58.179.186", // Cyberghost (Singapore)
|
||||
"89.233.43.71", // http://censurfridns.dk (Denmark)
|
||||
"109.69.8.51", // punCAT (Spain)
|
||||
"77.109.148.137", // Xiala.net (Switzerland)
|
||||
"193.58.251.251", // SkyDNS (Russia)
|
||||
};
|
||||
|
||||
static boost::mutex instance_lock;
|
||||
|
||||
62
src/common/notify.cpp
Normal file
62
src/common/notify.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2018, 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.
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "misc_log_ex.h"
|
||||
#include "file_io_utils.h"
|
||||
#include "spawn.h"
|
||||
#include "notify.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- Improve tokenization to handle paths containing whitespaces, quotes, etc.
|
||||
- Windows unicode support (implies implementing unicode command line parsing code)
|
||||
*/
|
||||
Notify::Notify(const char *spec)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(spec, "Null spec");
|
||||
|
||||
boost::split(args, spec, boost::is_any_of(" "));
|
||||
CHECK_AND_ASSERT_THROW_MES(args.size() > 0, "Failed to parse spec");
|
||||
filename = args[0];
|
||||
CHECK_AND_ASSERT_THROW_MES(epee::file_io_utils::is_file_exist(filename), "File not found: " << filename);
|
||||
}
|
||||
|
||||
int Notify::notify(const char *parameter)
|
||||
{
|
||||
std::vector<std::string> margs = args;
|
||||
for (std::string &s: margs)
|
||||
boost::replace_all(s, "%s", parameter);
|
||||
|
||||
return tools::spawn(filename.c_str(), margs, false);
|
||||
}
|
||||
|
||||
}
|
||||
49
src/common/notify.h
Normal file
49
src/common/notify.h
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2018, 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace tools
|
||||
{
|
||||
|
||||
class Notify
|
||||
{
|
||||
public:
|
||||
Notify(const char *spec);
|
||||
|
||||
int notify(const char *parameter);
|
||||
|
||||
private:
|
||||
std::string filename;
|
||||
std::vector<std::string> args;
|
||||
};
|
||||
|
||||
}
|
||||
@ -56,43 +56,59 @@ namespace
|
||||
|
||||
bool read_from_tty(epee::wipeable_string& pass, bool hide_input)
|
||||
{
|
||||
static constexpr const char BACKSPACE = 8;
|
||||
|
||||
HANDLE h_cin = ::GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
||||
DWORD mode_old;
|
||||
::GetConsoleMode(h_cin, &mode_old);
|
||||
DWORD mode_new = mode_old & ~((hide_input ? ENABLE_ECHO_INPUT : 0) | ENABLE_LINE_INPUT);
|
||||
DWORD mode_new = mode_old & ~(hide_input ? ENABLE_ECHO_INPUT : 0);
|
||||
::SetConsoleMode(h_cin, mode_new);
|
||||
|
||||
bool r = true;
|
||||
pass.reserve(tools::password_container::max_password_size);
|
||||
std::vector<int> chlen;
|
||||
chlen.reserve(tools::password_container::max_password_size);
|
||||
while (pass.size() < tools::password_container::max_password_size)
|
||||
{
|
||||
DWORD read;
|
||||
char ch;
|
||||
r = (TRUE == ::ReadConsoleA(h_cin, &ch, 1, &read, NULL));
|
||||
wchar_t ucs2_ch;
|
||||
r = (TRUE == ::ReadConsoleW(h_cin, &ucs2_ch, 1, &read, NULL));
|
||||
r &= (1 == read);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (ch == '\n' || ch == '\r')
|
||||
else if (ucs2_ch == L'\r')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (ucs2_ch == L'\n')
|
||||
{
|
||||
std::cout << std::endl;
|
||||
break;
|
||||
}
|
||||
else if (ch == BACKSPACE)
|
||||
else if (ucs2_ch == L'\b')
|
||||
{
|
||||
if (!pass.empty())
|
||||
{
|
||||
pass.pop_back();
|
||||
int len = chlen.back();
|
||||
chlen.pop_back();
|
||||
while(len-- > 0)
|
||||
pass.pop_back();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
pass.push_back(ch);
|
||||
}
|
||||
|
||||
char utf8_ch[8] = {0};
|
||||
int len;
|
||||
if((len = WideCharToMultiByte(CP_UTF8, 0, &ucs2_ch, 1, utf8_ch, sizeof(utf8_ch), NULL, NULL)) <= 0)
|
||||
break;
|
||||
|
||||
if(pass.size() + len >= tools::password_container::max_password_size)
|
||||
break;
|
||||
|
||||
chlen.push_back(len);
|
||||
pass += utf8_ch;
|
||||
}
|
||||
|
||||
::SetConsoleMode(h_cin, mode_old);
|
||||
@ -146,6 +162,13 @@ namespace
|
||||
if (!aPass.empty())
|
||||
{
|
||||
aPass.pop_back();
|
||||
if (!hide_input)
|
||||
std::cout << "\b\b\b \b\b\b" << std::flush;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hide_input)
|
||||
std::cout << "\b\b \b\b" << std::flush;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -221,6 +244,10 @@ namespace tools
|
||||
: m_password(std::move(password))
|
||||
{
|
||||
}
|
||||
password_container::password_container(const epee::wipeable_string& password) noexcept
|
||||
: m_password(password)
|
||||
{
|
||||
}
|
||||
|
||||
password_container::~password_container() noexcept
|
||||
{
|
||||
|
||||
@ -47,6 +47,7 @@ namespace tools
|
||||
|
||||
//! `password` is used as password
|
||||
password_container(std::string&& password) noexcept;
|
||||
password_container(const epee::wipeable_string& password) noexcept;
|
||||
|
||||
//! \return A password from stdin TTY prompt or `std::cin` pipe.
|
||||
static boost::optional<password_container> prompt(bool verify, const char *mesage = "Password", bool hide_input = true);
|
||||
|
||||
141
src/common/spawn.cpp
Normal file
141
src/common/spawn.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2018, 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.
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef _WIN32
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/scope_exit.hpp>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include "misc_log_ex.h"
|
||||
#include "spawn.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
|
||||
int spawn(const char *filename, const std::vector<std::string>& args, bool wait)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
std::string joined = boost::algorithm::join(args, " ");
|
||||
char *commandLine = !joined.empty() ? &joined[0] : nullptr;
|
||||
STARTUPINFOA si = {};
|
||||
si.cb = sizeof(si);
|
||||
PROCESS_INFORMATION pi;
|
||||
if (!CreateProcessA(filename, commandLine, nullptr, nullptr, false, 0, nullptr, nullptr, &si, &pi))
|
||||
{
|
||||
MERROR("CreateProcess failed. Error code " << GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
BOOST_SCOPE_EXIT(&pi)
|
||||
{
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
}
|
||||
BOOST_SCOPE_EXIT_END
|
||||
|
||||
if (!wait)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD result = WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
if (result != WAIT_OBJECT_0)
|
||||
{
|
||||
MERROR("WaitForSingleObject failed. Result " << result << ", error code " << GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
DWORD exitCode;
|
||||
if (!GetExitCodeProcess(pi.hProcess, &exitCode))
|
||||
{
|
||||
MERROR("GetExitCodeProcess failed. Error code " << GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
MINFO("Child exited with " << exitCode);
|
||||
return static_cast<int>(exitCode);
|
||||
#else
|
||||
char **argv = (char**)alloca(sizeof(char*) * (args.size() + 1));
|
||||
for (size_t n = 0; n < args.size(); ++n)
|
||||
argv[n] = (char*)args[n].c_str();
|
||||
argv[args.size()] = NULL;
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0)
|
||||
{
|
||||
MERROR("Error forking: " << strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// child
|
||||
if (pid == 0)
|
||||
{
|
||||
char *envp[] = {NULL};
|
||||
execve(filename, argv, envp);
|
||||
MERROR("Failed to execve: " << strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// parent
|
||||
if (pid > 0)
|
||||
{
|
||||
if (!wait)
|
||||
return 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int wstatus = 0;
|
||||
pid_t w = waitpid(pid, &wstatus, WUNTRACED | WCONTINUED);
|
||||
if (w < 0) {
|
||||
MERROR("Error waiting for child: " << strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (WIFEXITED(wstatus))
|
||||
{
|
||||
MINFO("Child exited with " << WEXITSTATUS(wstatus));
|
||||
return WEXITSTATUS(wstatus);
|
||||
}
|
||||
if (WIFSIGNALED(wstatus))
|
||||
{
|
||||
MINFO("Child killed by " << WEXITSTATUS(wstatus));
|
||||
return WEXITSTATUS(wstatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
MERROR("Secret passage found");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
36
src/common/spawn.h
Normal file
36
src/common/spawn.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2018, 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tools
|
||||
{
|
||||
|
||||
int spawn(const char *filename, const std::vector<std::string>& args, bool wait);
|
||||
|
||||
}
|
||||
@ -57,7 +57,8 @@ threadpool::~threadpool() {
|
||||
has_work.notify_all();
|
||||
}
|
||||
for (size_t i = 0; i<threads.size(); i++) {
|
||||
threads[i].join();
|
||||
try { threads[i].join(); }
|
||||
catch (...) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -939,4 +939,32 @@ std::string get_nix_version_display_string()
|
||||
}
|
||||
return newval;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string input_line_win()
|
||||
{
|
||||
HANDLE hConIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
DWORD oldMode;
|
||||
|
||||
FlushConsoleInputBuffer(hConIn);
|
||||
GetConsoleMode(hConIn, &oldMode);
|
||||
SetConsoleMode(hConIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT);
|
||||
|
||||
wchar_t buffer[1024];
|
||||
DWORD read;
|
||||
|
||||
ReadConsoleW(hConIn, buffer, sizeof(buffer)/sizeof(wchar_t)-1, &read, nullptr);
|
||||
buffer[read] = 0;
|
||||
|
||||
SetConsoleMode(hConIn, oldMode);
|
||||
CloseHandle(hConIn);
|
||||
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL);
|
||||
std::string buf(size_needed, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, buffer, -1, &buf[0], size_needed, NULL, NULL);
|
||||
buf.pop_back(); //size_needed includes null that we needed to have space for
|
||||
return buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@ -235,4 +235,7 @@ namespace tools
|
||||
boost::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str);
|
||||
|
||||
std::string glob_to_regex(const std::string &val);
|
||||
#ifdef _WIN32
|
||||
std::string input_line_win();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -121,7 +121,8 @@ namespace cryptonote
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
miner::~miner()
|
||||
{
|
||||
stop();
|
||||
try { stop(); }
|
||||
catch (...) { /* ignore */ }
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height)
|
||||
@ -198,8 +199,9 @@ namespace cryptonote
|
||||
{
|
||||
uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0);
|
||||
float hr = static_cast<float>(total_hr)/static_cast<float>(m_last_hash_rates.size());
|
||||
const auto flags = std::cout.flags();
|
||||
const auto precision = std::cout.precision();
|
||||
std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << precision << ENDL;
|
||||
std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << flags << precision << ENDL;
|
||||
}
|
||||
}
|
||||
m_last_hr_merge_time = misc_utils::get_tick_count();
|
||||
|
||||
@ -52,6 +52,7 @@
|
||||
#include "cryptonote_core.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "common/perf_timer.h"
|
||||
#include "common/notify.h"
|
||||
#if defined(PER_BLOCK_CHECKPOINT)
|
||||
#include "blocks/blocks.h"
|
||||
#endif
|
||||
@ -137,8 +138,8 @@ static const struct {
|
||||
|
||||
{ 6, 971400, 0, 1501709789 },
|
||||
{ 7, 1057027, 0, 1512211236 },
|
||||
{ 8, 1057058, 0, 1515967497 },
|
||||
{ 9, 1057778, 0, 1515967498 },
|
||||
{ 8, 1057058, 0, 1533211200 },
|
||||
{ 9, 1057778, 0, 1533297600 },
|
||||
};
|
||||
static const uint64_t testnet_hard_fork_version_1_till = 624633;
|
||||
|
||||
@ -3552,6 +3553,10 @@ leave:
|
||||
get_difficulty_for_next_block(); // just to cache it
|
||||
invalidate_block_template_cache();
|
||||
|
||||
std::shared_ptr<tools::Notify> block_notify = m_block_notify;
|
||||
if (block_notify)
|
||||
block_notify->notify(epee::string_tools::pod_to_hex(id).c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
||||
@ -55,6 +55,8 @@
|
||||
#include "cryptonote_basic/hardfork.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
|
||||
namespace tools { class Notify; }
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
class tx_memory_pool;
|
||||
@ -705,6 +707,13 @@ namespace cryptonote
|
||||
void set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint64_t sync_threshold,
|
||||
blockchain_db_sync_mode sync_mode, bool fast_sync);
|
||||
|
||||
/**
|
||||
* @brief sets a block notify object to call for every new block
|
||||
*
|
||||
* @param notify the notify object to cal at every new block
|
||||
*/
|
||||
void set_block_notify(const std::shared_ptr<tools::Notify> ¬ify) { m_block_notify = notify; }
|
||||
|
||||
/**
|
||||
* @brief Put DB in safe sync mode
|
||||
*/
|
||||
@ -1032,6 +1041,8 @@ namespace cryptonote
|
||||
uint64_t m_btc_expected_reward;
|
||||
bool m_btc_valid;
|
||||
|
||||
std::shared_ptr<tools::Notify> m_block_notify;
|
||||
|
||||
/**
|
||||
* @brief collects the keys for all outputs being "spent" as an input
|
||||
*
|
||||
|
||||
@ -53,6 +53,7 @@ using namespace epee;
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "common/notify.h"
|
||||
#include "version.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
@ -167,6 +168,11 @@ namespace cryptonote
|
||||
, "Set maximum txpool weight in bytes."
|
||||
, DEFAULT_TXPOOL_MAX_WEIGHT
|
||||
};
|
||||
static const command_line::arg_descriptor<std::string> arg_block_notify = {
|
||||
"block-notify"
|
||||
, "Run a program for each new block, '%s' will be replaced by the block hash"
|
||||
, ""
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
core::core(i_cryptonote_protocol* pprotocol):
|
||||
@ -276,6 +282,7 @@ namespace cryptonote
|
||||
command_line::add_arg(desc, arg_offline);
|
||||
command_line::add_arg(desc, arg_disable_dns_checkpoints);
|
||||
command_line::add_arg(desc, arg_max_txpool_weight);
|
||||
command_line::add_arg(desc, arg_block_notify);
|
||||
|
||||
miner::init_options(desc);
|
||||
BlockchainDB::init_options(desc);
|
||||
@ -545,6 +552,16 @@ namespace cryptonote
|
||||
m_blockchain_storage.set_user_options(blocks_threads,
|
||||
sync_on_blocks, sync_threshold, sync_mode, fast_sync);
|
||||
|
||||
try
|
||||
{
|
||||
if (!command_line::is_arg_defaulted(vm, arg_block_notify))
|
||||
m_blockchain_storage.set_block_notify(std::shared_ptr<tools::Notify>(new tools::Notify(command_line::get_arg(vm, arg_block_notify).c_str())));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
MERROR("Failed to parse block notify spec");
|
||||
}
|
||||
|
||||
const std::pair<uint8_t, uint64_t> regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)};
|
||||
const cryptonote::test_options regtest_test_options = {
|
||||
regtest_hard_forks
|
||||
|
||||
@ -127,7 +127,7 @@ namespace cryptonote
|
||||
out_amounts[1] += out_amounts[0];
|
||||
for (size_t n = 1; n < out_amounts.size(); ++n)
|
||||
out_amounts[n - 1] = out_amounts[n];
|
||||
out_amounts.resize(out_amounts.size() - 1);
|
||||
out_amounts.pop_back();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@ -766,7 +766,7 @@ namespace cryptonote
|
||||
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
|
||||
tx_info txi;
|
||||
txi.id_hash = epee::string_tools::pod_to_hex(txid);
|
||||
txi.tx_blob = *bd;
|
||||
txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(*bd);
|
||||
transaction tx;
|
||||
if (!parse_and_validate_tx_from_blob(*bd, tx))
|
||||
{
|
||||
|
||||
@ -32,6 +32,8 @@ if(PER_BLOCK_CHECKPOINT)
|
||||
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
|
||||
elseif(APPLE AND NOT DEPENDS)
|
||||
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
|
||||
elseif(LINUX_32)
|
||||
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
|
||||
else()
|
||||
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
|
||||
endif()
|
||||
|
||||
@ -136,7 +136,14 @@ bool t_daemon::run(bool interactive)
|
||||
{
|
||||
throw std::runtime_error{"Can't run stopped daemon"};
|
||||
}
|
||||
tools::signal_handler::install(std::bind(&daemonize::t_daemon::stop_p2p, this));
|
||||
|
||||
std::atomic<bool> stop(false);
|
||||
boost::thread([&stop, this] {
|
||||
while (!stop)
|
||||
epee::misc_utils::sleep_no_w(100);
|
||||
this->stop_p2p();
|
||||
}).detach();
|
||||
tools::signal_handler::install([&stop](int){ stop = true; });
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@ -442,7 +442,8 @@ bool t_rpc_command_executor::show_status() {
|
||||
}
|
||||
}
|
||||
|
||||
tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us")
|
||||
std::stringstream str;
|
||||
str << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections")
|
||||
% (unsigned long long)ires.height
|
||||
% (unsigned long long)net_height
|
||||
% get_sync_percentage(ires)
|
||||
@ -455,12 +456,21 @@ bool t_rpc_command_executor::show_status() {
|
||||
% (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked")
|
||||
% (unsigned)ires.outgoing_connections_count
|
||||
% (unsigned)ires.incoming_connections_count
|
||||
% (unsigned int)floor(uptime / 60.0 / 60.0 / 24.0)
|
||||
% (unsigned int)floor(fmod((uptime / 60.0 / 60.0), 24.0))
|
||||
% (unsigned int)floor(fmod((uptime / 60.0), 60.0))
|
||||
% (unsigned int)fmod(uptime, 60.0)
|
||||
;
|
||||
|
||||
// restricted RPC does not disclose start time
|
||||
if (ires.start_time)
|
||||
{
|
||||
str << boost::format(", uptime %ud %uh %um %us")
|
||||
% (unsigned int)floor(uptime / 60.0 / 60.0 / 24.0)
|
||||
% (unsigned int)floor(fmod((uptime / 60.0 / 60.0), 24.0))
|
||||
% (unsigned int)floor(fmod((uptime / 60.0), 60.0))
|
||||
% (unsigned int)fmod(uptime, 60.0)
|
||||
;
|
||||
}
|
||||
|
||||
tools::success_msg_writer() << str.str();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -41,13 +41,26 @@ namespace hw {
|
||||
/* SETUP */
|
||||
/* ======================================================================= */
|
||||
|
||||
static std::unique_ptr<device_registry> registry;
|
||||
static device_registry *get_device_registry(bool clear = false){
|
||||
static device_registry *registry = new device_registry();
|
||||
if (clear)
|
||||
{
|
||||
delete registry;
|
||||
registry = NULL;
|
||||
}
|
||||
return registry;
|
||||
}
|
||||
|
||||
static void clear_device_registry(){
|
||||
get_device_registry(true);
|
||||
}
|
||||
|
||||
device_registry::device_registry(){
|
||||
hw::core::register_all(registry);
|
||||
#ifdef WITH_DEVICE_LEDGER
|
||||
hw::ledger::register_all(registry);
|
||||
#endif
|
||||
atexit(clear_device_registry);
|
||||
}
|
||||
|
||||
bool device_registry::register_device(const std::string & device_name, device * hw_device){
|
||||
@ -80,18 +93,12 @@ namespace hw {
|
||||
}
|
||||
|
||||
device& get_device(const std::string & device_descriptor) {
|
||||
if (!registry){
|
||||
registry.reset(new device_registry());
|
||||
}
|
||||
|
||||
device_registry *registry = get_device_registry();
|
||||
return registry->get_device(device_descriptor);
|
||||
}
|
||||
|
||||
bool register_device(const std::string & device_name, device * hw_device){
|
||||
if (!registry){
|
||||
registry.reset(new device_registry());
|
||||
}
|
||||
|
||||
device_registry *registry = get_device_registry();
|
||||
return registry->register_device(device_name, hw_device);
|
||||
}
|
||||
|
||||
|
||||
@ -86,13 +86,13 @@ namespace hw {
|
||||
public:
|
||||
bool hid_verbose = false;
|
||||
|
||||
const unsigned int OR_SELECT = 1;
|
||||
const unsigned int AND_SELECT = 2;
|
||||
static const unsigned int OR_SELECT = 1;
|
||||
static const unsigned int AND_SELECT = 2;
|
||||
|
||||
const unsigned char DEFAULT_CHANNEL = 0x0001;
|
||||
const unsigned char DEFAULT_TAG = 0x01;
|
||||
const unsigned int DEFAULT_PACKET_SIZE = 64;
|
||||
const unsigned int DEFAULT_TIMEOUT = 120000;
|
||||
static const unsigned short DEFAULT_CHANNEL = 0x0001;
|
||||
static const unsigned char DEFAULT_TAG = 0x01;
|
||||
static const unsigned int DEFAULT_PACKET_SIZE = 64;
|
||||
static const unsigned int DEFAULT_TIMEOUT = 120000;
|
||||
|
||||
device_io_hid(unsigned short channel, unsigned char tag, unsigned int packet_zize, unsigned int timeout);
|
||||
device_io_hid();
|
||||
|
||||
@ -48,21 +48,12 @@ namespace hw {
|
||||
/* ===================================================================== */
|
||||
/* === Debug ==== */
|
||||
/* ===================================================================== */
|
||||
#ifdef WIN32
|
||||
static char *pcsc_stringify_error(LONG rv) {
|
||||
static __thread char out[20];
|
||||
sprintf_s(out, sizeof(out), "0x%08lX", rv);
|
||||
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
||||
void set_apdu_verbose(bool verbose) {
|
||||
apdu_verbose = verbose;
|
||||
}
|
||||
|
||||
#define TRACKD MTRACE("hw")
|
||||
#define ASSERT_RV(rv) CHECK_AND_ASSERT_THROW_MES((rv)==SCARD_S_SUCCESS, "Fail SCard API : (" << (rv) << ") "<< pcsc_stringify_error(rv)<<" Device="<<this->id<<", hCard="<<hCard<<", hContext="<<hContext);
|
||||
#define ASSERT_SW(sw,ok,msk) CHECK_AND_ASSERT_THROW_MES(((sw)&(mask))==(ok), "Wrong Device Status : SW=" << std::hex << (sw) << " (EXPECT=" << std::hex << (ok) << ", MASK=" << std::hex << (mask) << ")") ;
|
||||
#define ASSERT_T0(exp) CHECK_AND_ASSERT_THROW_MES(exp, "Protocol assert failure: "#exp ) ;
|
||||
#define ASSERT_X(exp,msg) CHECK_AND_ASSERT_THROW_MES(exp, msg);
|
||||
@ -185,7 +176,7 @@ namespace hw {
|
||||
#define INS_GET_RESPONSE 0xc0
|
||||
|
||||
|
||||
device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 10000) {
|
||||
device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 120000) {
|
||||
this->id = device_id++;
|
||||
this->reset_buffer();
|
||||
this->mode = NONE;
|
||||
|
||||
@ -104,7 +104,7 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
|
||||
wallets[n]->decrypt_keys(pwd_container->password());
|
||||
if (!tools::wallet2::verify_multisig_info(wallets[n]->get_multisig_info(), sk[n], pk[n]))
|
||||
{
|
||||
tools::fail_msg_writer() << tr("Failed to verify multisig info");
|
||||
tools::fail_msg_writer() << genms::tr("Failed to verify multisig info");
|
||||
return false;
|
||||
}
|
||||
wallets[n]->encrypt_keys(pwd_container->password());
|
||||
@ -130,8 +130,8 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
|
||||
ss << " " << name << std::endl;
|
||||
}
|
||||
|
||||
// finalize step if needed
|
||||
if (!extra_info[0].empty())
|
||||
//exchange keys unless exchange_multisig_keys returns no extra info
|
||||
while (!extra_info[0].empty())
|
||||
{
|
||||
std::unordered_set<crypto::public_key> pkeys;
|
||||
std::vector<crypto::public_key> signers(total);
|
||||
@ -145,11 +145,7 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
|
||||
}
|
||||
for (size_t n = 0; n < total; ++n)
|
||||
{
|
||||
if (!wallets[n]->finalize_multisig(pwd_container->password(), pkeys, signers))
|
||||
{
|
||||
tools::fail_msg_writer() << genms::tr("Error finalizing multisig");
|
||||
return false;
|
||||
}
|
||||
extra_info[n] = wallets[n]->exchange_multisig_keys(pwd_container->password(), pkeys, signers);
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +163,8 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
TRY_ENTRY();
|
||||
|
||||
po::options_description desc_params(wallet_args::tr("Wallet options"));
|
||||
command_line::add_arg(desc_params, arg_filename_base);
|
||||
command_line::add_arg(desc_params, arg_scheme);
|
||||
@ -244,15 +242,10 @@ int main(int argc, char* argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (threshold != total-1 && threshold != total)
|
||||
{
|
||||
tools::fail_msg_writer() << genms::tr("Error: unsupported scheme: only N/N and N-1/N are supported");
|
||||
return 1;
|
||||
}
|
||||
bool create_address_file = command_line::get_arg(*vm, arg_create_address_file);
|
||||
if (!generate_multisig(threshold, total, basename, testnet ? TESTNET : stagenet ? STAGENET : MAINNET, create_address_file))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
//CATCH_ENTRY_L0("main", 1);
|
||||
CATCH_ENTRY_L0("main", 1);
|
||||
}
|
||||
|
||||
@ -84,6 +84,43 @@ namespace cryptonote
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
std::vector<crypto::public_key> generate_multisig_derivations(const account_keys &keys, const std::vector<crypto::public_key> &derivations)
|
||||
{
|
||||
std::vector<crypto::public_key> multisig_keys;
|
||||
crypto::secret_key blinded_skey = get_multisig_blinded_secret_key(keys.m_spend_secret_key);
|
||||
for (const auto &k: derivations)
|
||||
{
|
||||
rct::key d = rct::scalarmultKey(rct::pk2rct(k), rct::sk2rct(blinded_skey));
|
||||
multisig_keys.push_back(rct::rct2pk(d));
|
||||
}
|
||||
|
||||
return multisig_keys;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
crypto::secret_key calculate_multisig_signer_key(const std::vector<crypto::secret_key>& multisig_keys)
|
||||
{
|
||||
rct::key secret_key = rct::zero();
|
||||
for (const auto &k: multisig_keys)
|
||||
{
|
||||
sc_add(secret_key.bytes, secret_key.bytes, (const unsigned char*)k.data);
|
||||
}
|
||||
|
||||
return rct::rct2sk(secret_key);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
std::vector<crypto::secret_key> calculate_multisig_keys(const std::vector<crypto::public_key>& derivations)
|
||||
{
|
||||
std::vector<crypto::secret_key> multisig_keys;
|
||||
multisig_keys.reserve(derivations.size());
|
||||
|
||||
for (const auto &k: derivations)
|
||||
{
|
||||
multisig_keys.emplace_back(get_multisig_blinded_secret_key(rct::rct2sk(rct::pk2rct(k))));
|
||||
}
|
||||
|
||||
return multisig_keys;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys)
|
||||
{
|
||||
rct::key view_skey = rct::sk2rct(get_multisig_blinded_secret_key(skey));
|
||||
@ -92,7 +129,7 @@ namespace cryptonote
|
||||
return rct::rct2sk(view_skey);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
crypto::public_key generate_multisig_N1_N_spend_public_key(const std::vector<crypto::public_key> &pkeys)
|
||||
crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector<crypto::public_key> &pkeys)
|
||||
{
|
||||
rct::key spend_public_key = rct::identity();
|
||||
for (const auto &pk: pkeys)
|
||||
@ -141,4 +178,9 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
uint32_t multisig_rounds_required(uint32_t participants, uint32_t threshold)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(participants >= threshold, "participants must be greater or equal than threshold");
|
||||
return participants - threshold + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,9 +41,31 @@ namespace cryptonote
|
||||
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key);
|
||||
void generate_multisig_N_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
|
||||
void generate_multisig_N1_N(const account_keys &keys, const std::vector<crypto::public_key> &spend_keys, std::vector<crypto::secret_key> &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey);
|
||||
/**
|
||||
* @brief generate_multisig_derivations performs common DH key derivation.
|
||||
* Each middle round in M/N scheme is DH exchange of public multisig keys of other participants multiplied by secret spend key of current participant.
|
||||
* this functions does the following: new multisig key = secret spend * public multisig key
|
||||
* @param keys - current wallet's keys
|
||||
* @param derivations - public multisig keys of other participants
|
||||
* @return new public multisig keys derived from previous round. This data needs to be exchange with other participants
|
||||
*/
|
||||
std::vector<crypto::public_key> generate_multisig_derivations(const account_keys &keys, const std::vector<crypto::public_key> &derivations);
|
||||
crypto::secret_key calculate_multisig_signer_key(const std::vector<crypto::secret_key>& derivations);
|
||||
/**
|
||||
* @brief calculate_multisig_keys. Calculates secret multisig keys from others' participants ones as follows: mi = H(Mi)
|
||||
* @param derivations - others' participants public multisig keys.
|
||||
* @return vector of current wallet's multisig secret keys
|
||||
*/
|
||||
std::vector<crypto::secret_key> calculate_multisig_keys(const std::vector<crypto::public_key>& derivations);
|
||||
crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector<crypto::secret_key> &skeys);
|
||||
crypto::public_key generate_multisig_N1_N_spend_public_key(const std::vector<crypto::public_key> &pkeys);
|
||||
/**
|
||||
* @brief generate_multisig_M_N_spend_public_key calculates multisig wallet's spend public key by summing all of public multisig keys
|
||||
* @param pkeys unique public multisig keys
|
||||
* @return multisig wallet's spend public key
|
||||
*/
|
||||
crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector<crypto::public_key> &pkeys);
|
||||
bool generate_multisig_key_image(const account_keys &keys, size_t multisig_key_index, const crypto::public_key& out_key, crypto::key_image& ki);
|
||||
void generate_multisig_LR(const crypto::public_key pkey, const crypto::secret_key &k, crypto::public_key &L, crypto::public_key &R);
|
||||
bool generate_multisig_composite_key_image(const account_keys &keys, const std::unordered_map<crypto::public_key, cryptonote::subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key &tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, const std::vector<crypto::key_image> &pkis, crypto::key_image &ki);
|
||||
uint32_t multisig_rounds_required(uint32_t participants, uint32_t threshold);
|
||||
}
|
||||
|
||||
@ -937,7 +937,7 @@ namespace nodetool
|
||||
bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()),
|
||||
epee::string_tools::num_to_string_fast(ipv4.port()),
|
||||
m_config.m_net_config.connection_timeout,
|
||||
con, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip);
|
||||
con);
|
||||
|
||||
if(!res)
|
||||
{
|
||||
@ -1002,7 +1002,7 @@ namespace nodetool
|
||||
bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()),
|
||||
epee::string_tools::num_to_string_fast(ipv4.port()),
|
||||
m_config.m_net_config.connection_timeout,
|
||||
con, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip);
|
||||
con);
|
||||
|
||||
if (!res) {
|
||||
bool is_priority = is_priority_node(na);
|
||||
@ -1617,7 +1617,7 @@ namespace nodetool
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip);
|
||||
});
|
||||
if(!r)
|
||||
{
|
||||
LOG_WARNING_CC(context, "Failed to call connect_async, network error.");
|
||||
|
||||
@ -137,47 +137,6 @@ namespace
|
||||
|
||||
const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""};
|
||||
|
||||
#ifdef WIN32
|
||||
// Translate from CP850 to UTF-8;
|
||||
// std::getline for a Windows console returns a string in CP437 or CP850; as simplewallet,
|
||||
// like all of Monero, is assumed to work internally with UTF-8 throughout, even on Windows
|
||||
// (although only implemented partially), a translation to UTF-8 is needed for input.
|
||||
//
|
||||
// Note that if a program is started inside the MSYS2 shell somebody already translates
|
||||
// console input to UTF-8, but it's not clear how one could detect that in order to avoid
|
||||
// double-translation; this code here thus breaks UTF-8 input within a MSYS2 shell,
|
||||
// unfortunately.
|
||||
//
|
||||
// Note also that input for passwords is NOT translated, to remain compatible with any
|
||||
// passwords containing special characters that predate this switch to UTF-8 support.
|
||||
template<typename T>
|
||||
static T cp850_to_utf8(const T &cp850_str)
|
||||
{
|
||||
boost::locale::generator gen;
|
||||
gen.locale_cache_enabled(true);
|
||||
std::locale loc = gen("en_US.CP850");
|
||||
const boost::locale::conv::method_type how = boost::locale::conv::default_method;
|
||||
T result;
|
||||
const char *begin = cp850_str.data();
|
||||
const char *end = begin + cp850_str.size();
|
||||
result.reserve(end-begin);
|
||||
typedef std::back_insert_iterator<T> inserter_type;
|
||||
inserter_type inserter(result);
|
||||
boost::locale::utf::code_point c;
|
||||
while(begin!=end) {
|
||||
c=boost::locale::utf::utf_traits<char>::template decode<char const *>(begin,end);
|
||||
if(c==boost::locale::utf::illegal || c==boost::locale::utf::incomplete) {
|
||||
if(how==boost::locale::conv::stop)
|
||||
throw boost::locale::conv::conversion_error();
|
||||
}
|
||||
else {
|
||||
boost::locale::utf::utf_traits<char>::template encode<inserter_type>(c,inserter);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string input_line(const std::string& prompt)
|
||||
{
|
||||
#ifdef HAVE_READLINE
|
||||
@ -186,9 +145,10 @@ namespace
|
||||
std::cout << prompt;
|
||||
|
||||
std::string buf;
|
||||
#ifdef _WIN32
|
||||
buf = tools::input_line_win();
|
||||
#else
|
||||
std::getline(std::cin, buf);
|
||||
#ifdef WIN32
|
||||
buf = cp850_to_utf8(buf);
|
||||
#endif
|
||||
|
||||
return epee::string_tools::trim(buf);
|
||||
@ -208,10 +168,6 @@ namespace
|
||||
|
||||
epee::wipeable_string buf = pwd_container->password();
|
||||
|
||||
#ifdef WIN32
|
||||
buf = cp850_to_utf8(buf);
|
||||
#endif
|
||||
|
||||
buf.trim();
|
||||
return buf;
|
||||
}
|
||||
@ -224,14 +180,14 @@ namespace
|
||||
auto pwd_container = tools::password_container::prompt(verify, prompt);
|
||||
if (!pwd_container)
|
||||
{
|
||||
tools::fail_msg_writer() << tr("failed to read wallet password");
|
||||
tools::fail_msg_writer() << sw::tr("failed to read wallet password");
|
||||
}
|
||||
return pwd_container;
|
||||
}
|
||||
|
||||
boost::optional<tools::password_container> default_password_prompter(bool verify)
|
||||
{
|
||||
return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify);
|
||||
return password_prompter(verify ? sw::tr("Enter a new password for the wallet") : sw::tr("Wallet password"), verify);
|
||||
}
|
||||
|
||||
inline std::string interpret_rpc_response(bool ok, const std::string& status)
|
||||
@ -309,7 +265,7 @@ namespace
|
||||
}
|
||||
else
|
||||
{
|
||||
fail_msg_writer() << tr("invalid argument: must be either 0/1, true/false, y/n, yes/no");
|
||||
fail_msg_writer() << sw::tr("invalid argument: must be either 0/1, true/false, y/n, yes/no");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -365,18 +321,18 @@ namespace
|
||||
std::string dnssec_str;
|
||||
if (dnssec_valid)
|
||||
{
|
||||
dnssec_str = tr("DNSSEC validation passed");
|
||||
dnssec_str = sw::tr("DNSSEC validation passed");
|
||||
}
|
||||
else
|
||||
{
|
||||
dnssec_str = tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!");
|
||||
dnssec_str = sw::tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!");
|
||||
}
|
||||
std::stringstream prompt;
|
||||
prompt << tr("For URL: ") << url
|
||||
prompt << sw::tr("For URL: ") << url
|
||||
<< ", " << dnssec_str << std::endl
|
||||
<< tr(" Monero Address = ") << addresses[0]
|
||||
<< sw::tr(" Monero Address = ") << addresses[0]
|
||||
<< std::endl
|
||||
<< tr("Is this OK? (Y/n) ")
|
||||
<< sw::tr("Is this OK? (Y/n) ")
|
||||
;
|
||||
// prompt the user for confirmation given the dns query and dnssec status
|
||||
std::string confirm_dns_ok = input_line(prompt.str());
|
||||
@ -386,7 +342,7 @@ namespace
|
||||
}
|
||||
if (!command_line::is_yes(confirm_dns_ok))
|
||||
{
|
||||
std::cout << tr("you have cancelled the transfer request") << std::endl;
|
||||
std::cout << sw::tr("you have cancelled the transfer request") << std::endl;
|
||||
return {};
|
||||
}
|
||||
return addresses[0];
|
||||
@ -407,7 +363,7 @@ namespace
|
||||
uint32_t subaddr_index;
|
||||
if(!epee::string_tools::get_xtype_from_string(subaddr_index, subaddr_index_str))
|
||||
{
|
||||
fail_msg_writer() << tr("failed to parse index: ") << subaddr_index_str;
|
||||
fail_msg_writer() << sw::tr("failed to parse index: ") << subaddr_index_str;
|
||||
subaddr_indices.clear();
|
||||
return false;
|
||||
}
|
||||
@ -420,7 +376,7 @@ namespace
|
||||
{
|
||||
auto r = tools::parse_subaddress_lookahead(str);
|
||||
if (!r)
|
||||
fail_msg_writer() << tr("invalid format for subaddress lookahead; must be <major>:<minor>");
|
||||
fail_msg_writer() << sw::tr("invalid format for subaddress lookahead; must be <major>:<minor>");
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -433,27 +389,27 @@ namespace
|
||||
}
|
||||
catch (const tools::error::daemon_busy&)
|
||||
{
|
||||
fail_msg_writer() << tr("daemon is busy. Please try again later.");
|
||||
fail_msg_writer() << sw::tr("daemon is busy. Please try again later.");
|
||||
}
|
||||
catch (const tools::error::no_connection_to_daemon&)
|
||||
{
|
||||
fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running.");
|
||||
fail_msg_writer() << sw::tr("no connection to daemon. Please make sure daemon is running.");
|
||||
}
|
||||
catch (const tools::error::wallet_rpc_error& e)
|
||||
{
|
||||
LOG_ERROR("RPC error: " << e.to_string());
|
||||
fail_msg_writer() << tr("RPC error: ") << e.what();
|
||||
fail_msg_writer() << sw::tr("RPC error: ") << e.what();
|
||||
}
|
||||
catch (const tools::error::get_outs_error &e)
|
||||
{
|
||||
fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what();
|
||||
fail_msg_writer() << sw::tr("failed to get random outputs to mix: ") << e.what();
|
||||
}
|
||||
catch (const tools::error::not_enough_unlocked_money& e)
|
||||
{
|
||||
LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") %
|
||||
print_money(e.available()) %
|
||||
print_money(e.tx_amount()));
|
||||
fail_msg_writer() << tr("Not enough money in unlocked balance");
|
||||
fail_msg_writer() << sw::tr("Not enough money in unlocked balance");
|
||||
warn_of_possible_attack = false;
|
||||
}
|
||||
catch (const tools::error::not_enough_money& e)
|
||||
@ -461,7 +417,7 @@ namespace
|
||||
LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") %
|
||||
print_money(e.available()) %
|
||||
print_money(e.tx_amount()));
|
||||
fail_msg_writer() << tr("Not enough money in unlocked balance");
|
||||
fail_msg_writer() << sw::tr("Not enough money in unlocked balance");
|
||||
warn_of_possible_attack = false;
|
||||
}
|
||||
catch (const tools::error::tx_not_possible& e)
|
||||
@ -471,30 +427,30 @@ namespace
|
||||
print_money(e.tx_amount() + e.fee()) %
|
||||
print_money(e.tx_amount()) %
|
||||
print_money(e.fee()));
|
||||
fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees");
|
||||
fail_msg_writer() << sw::tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees");
|
||||
warn_of_possible_attack = false;
|
||||
}
|
||||
catch (const tools::error::not_enough_outs_to_mix& e)
|
||||
{
|
||||
auto writer = fail_msg_writer();
|
||||
writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
|
||||
writer << sw::tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
|
||||
for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs())
|
||||
{
|
||||
writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
|
||||
writer << "\n" << sw::tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << sw::tr("found outputs to use") << " = " << outs_for_amount.second;
|
||||
}
|
||||
writer << tr("Please use sweep_unmixable.");
|
||||
writer << sw::tr("Please use sweep_unmixable.");
|
||||
}
|
||||
catch (const tools::error::tx_not_constructed&)
|
||||
{
|
||||
fail_msg_writer() << tr("transaction was not constructed");
|
||||
{
|
||||
fail_msg_writer() << sw::tr("transaction was not constructed");
|
||||
warn_of_possible_attack = false;
|
||||
}
|
||||
catch (const tools::error::tx_rejected& e)
|
||||
{
|
||||
fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
|
||||
fail_msg_writer() << (boost::format(sw::tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
|
||||
std::string reason = e.reason();
|
||||
if (!reason.empty())
|
||||
fail_msg_writer() << tr("Reason: ") << reason;
|
||||
fail_msg_writer() << sw::tr("Reason: ") << reason;
|
||||
}
|
||||
catch (const tools::error::tx_sum_overflow& e)
|
||||
{
|
||||
@ -503,38 +459,38 @@ namespace
|
||||
}
|
||||
catch (const tools::error::zero_destination&)
|
||||
{
|
||||
fail_msg_writer() << tr("one of destinations is zero");
|
||||
fail_msg_writer() << sw::tr("one of destinations is zero");
|
||||
warn_of_possible_attack = false;
|
||||
}
|
||||
catch (const tools::error::tx_too_big& e)
|
||||
{
|
||||
fail_msg_writer() << tr("failed to find a suitable way to split transactions");
|
||||
fail_msg_writer() << sw::tr("failed to find a suitable way to split transactions");
|
||||
warn_of_possible_attack = false;
|
||||
}
|
||||
catch (const tools::error::transfer_error& e)
|
||||
{
|
||||
LOG_ERROR("unknown transfer error: " << e.to_string());
|
||||
fail_msg_writer() << tr("unknown transfer error: ") << e.what();
|
||||
fail_msg_writer() << sw::tr("unknown transfer error: ") << e.what();
|
||||
}
|
||||
catch (const tools::error::multisig_export_needed& e)
|
||||
{
|
||||
LOG_ERROR("Multisig error: " << e.to_string());
|
||||
fail_msg_writer() << tr("Multisig error: ") << e.what();
|
||||
fail_msg_writer() << sw::tr("Multisig error: ") << e.what();
|
||||
warn_of_possible_attack = false;
|
||||
}
|
||||
catch (const tools::error::wallet_internal_error& e)
|
||||
{
|
||||
LOG_ERROR("internal error: " << e.to_string());
|
||||
fail_msg_writer() << tr("internal error: ") << e.what();
|
||||
fail_msg_writer() << sw::tr("internal error: ") << e.what();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR("unexpected error: " << e.what());
|
||||
fail_msg_writer() << tr("unexpected error: ") << e.what();
|
||||
fail_msg_writer() << sw::tr("unexpected error: ") << e.what();
|
||||
}
|
||||
|
||||
if (warn_of_possible_attack)
|
||||
fail_msg_writer() << tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information.");
|
||||
fail_msg_writer() << sw::tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information.");
|
||||
}
|
||||
|
||||
bool check_file_overwrite(const std::string &filename)
|
||||
@ -544,10 +500,10 @@ namespace
|
||||
{
|
||||
if (boost::ends_with(filename, ".keys"))
|
||||
{
|
||||
fail_msg_writer() << boost::format(tr("File %s likely stores wallet private keys! Use a different file name.")) % filename;
|
||||
fail_msg_writer() << boost::format(sw::tr("File %s likely stores wallet private keys! Use a different file name.")) % filename;
|
||||
return false;
|
||||
}
|
||||
return command_line::is_yes(input_line((boost::format(tr("File %s already exists. Are you sure to overwrite it? (Y/Yes/N/No): ")) % filename).str()));
|
||||
return command_line::is_yes(input_line((boost::format(sw::tr("File %s already exists. Are you sure to overwrite it? (Y/Yes/N/No): ")) % filename).str()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -968,7 +924,7 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args)
|
||||
{
|
||||
success_msg_writer() << tr("Another step is needed");
|
||||
success_msg_writer() << multisig_extra_info;
|
||||
success_msg_writer() << tr("Send this multisig info to all other participants, then use finalize_multisig <info1> [<info2>...] with others' multisig info");
|
||||
success_msg_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys <info1> [<info2>...] with others' multisig info");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1042,6 +998,61 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool simple_wallet::exchange_multisig_keys(const std::vector<std::string> &args) {
|
||||
bool ready;
|
||||
if (m_wallet->key_on_device())
|
||||
{
|
||||
fail_msg_writer() << tr("command not supported by HW wallet");
|
||||
return true;
|
||||
}
|
||||
if (!m_wallet->multisig(&ready))
|
||||
{
|
||||
fail_msg_writer() << tr("This wallet is not multisig");
|
||||
return true;
|
||||
}
|
||||
if (ready)
|
||||
{
|
||||
fail_msg_writer() << tr("This wallet is already finalized");
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto orig_pwd_container = get_and_verify_password();
|
||||
if(orig_pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Your original password was incorrect.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.size() < 2)
|
||||
{
|
||||
fail_msg_writer() << tr("usage: exchange_multisig_keys <multisiginfo1> [<multisiginfo2>...]");
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::string multisig_extra_info = m_wallet->exchange_multisig_keys(orig_pwd_container->password(), args);
|
||||
if (!multisig_extra_info.empty())
|
||||
{
|
||||
message_writer() << tr("Another step is needed");
|
||||
message_writer() << multisig_extra_info;
|
||||
message_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys <info1> [<info2>...] with others' multisig info");
|
||||
return true;
|
||||
} else {
|
||||
uint32_t threshold, total;
|
||||
m_wallet->multisig(NULL, &threshold, &total);
|
||||
success_msg_writer() << tr("Multisig wallet has been successfully created. Current wallet type: ") << threshold << "/" << total;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
fail_msg_writer() << tr("Failed to perform multisig keys exchange: ") << e.what();
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool simple_wallet::export_multisig(const std::vector<std::string> &args)
|
||||
{
|
||||
bool ready;
|
||||
@ -2392,7 +2403,7 @@ simple_wallet::simple_wallet()
|
||||
"store-tx-info <1|0>\n "
|
||||
" Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference.\n "
|
||||
"default-ring-size <n>\n "
|
||||
" Set the default ring size (default and minimum is 5).\n "
|
||||
" Set the default ring size (obsolete).\n "
|
||||
"auto-refresh <1|0>\n "
|
||||
" Whether to automatically synchronize new blocks from the daemon.\n "
|
||||
"refresh-type <full|optimize-coinbase|no-coinbase|default>\n "
|
||||
@ -2552,6 +2563,10 @@ simple_wallet::simple_wallet()
|
||||
boost::bind(&simple_wallet::finalize_multisig, this, _1),
|
||||
tr("finalize_multisig <string> [<string>...]"),
|
||||
tr("Turn this wallet into a multisig wallet, extra step for N-1/N wallets"));
|
||||
m_cmd_binder.set_handler("exchange_multisig_keys",
|
||||
boost::bind(&simple_wallet::exchange_multisig_keys, this, _1),
|
||||
tr("exchange_multisig_keys <string> [<string>...]"),
|
||||
tr("Performs extra multisig keys exchange rounds. Needed for arbitrary M/N multisig wallets"));
|
||||
m_cmd_binder.set_handler("export_multisig_info",
|
||||
boost::bind(&simple_wallet::export_multisig, this, _1),
|
||||
tr("export_multisig_info <filename>"),
|
||||
@ -3017,7 +3032,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
||||
return false;
|
||||
}
|
||||
crypto::secret_key viewkey;
|
||||
if (viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
|
||||
if (!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
|
||||
{
|
||||
fail_msg_writer() << tr("failed to parse view key secret key");
|
||||
return false;
|
||||
@ -3275,7 +3290,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_wallet = tools::wallet2::make_from_json(vm, false, m_generate_from_json, password_prompter);
|
||||
auto rc = tools::wallet2::make_from_json(vm, false, m_generate_from_json, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
password = rc.second.password();
|
||||
m_wallet_file = m_wallet->path();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
@ -6619,16 +6636,16 @@ static std::string get_human_readable_timespan(std::chrono::seconds seconds)
|
||||
{
|
||||
uint64_t ts = seconds.count();
|
||||
if (ts < 60)
|
||||
return std::to_string(ts) + tr(" seconds");
|
||||
return std::to_string(ts) + sw::tr(" seconds");
|
||||
if (ts < 3600)
|
||||
return std::to_string((uint64_t)(ts / 60)) + tr(" minutes");
|
||||
return std::to_string((uint64_t)(ts / 60)) + sw::tr(" minutes");
|
||||
if (ts < 3600 * 24)
|
||||
return std::to_string((uint64_t)(ts / 3600)) + tr(" hours");
|
||||
return std::to_string((uint64_t)(ts / 3600)) + sw::tr(" hours");
|
||||
if (ts < 3600 * 24 * 30.5)
|
||||
return std::to_string((uint64_t)(ts / (3600 * 24))) + tr(" days");
|
||||
return std::to_string((uint64_t)(ts / (3600 * 24))) + sw::tr(" days");
|
||||
if (ts < 3600 * 24 * 365.25)
|
||||
return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + tr(" months");
|
||||
return tr("a long time");
|
||||
return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + sw::tr(" months");
|
||||
return sw::tr("a long time");
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
|
||||
@ -8073,6 +8090,8 @@ void simple_wallet::commit_or_save(std::vector<tools::wallet2::pending_tx>& ptx_
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
TRY_ENTRY();
|
||||
|
||||
#ifdef WIN32
|
||||
// Activate UTF-8 support for Boost filesystem classes on Windows
|
||||
std::locale::global(boost::locale::generator().generate(""));
|
||||
@ -8136,7 +8155,7 @@ int main(int argc, char* argv[])
|
||||
if (!command.empty())
|
||||
{
|
||||
if (!w.process_command(command))
|
||||
fail_msg_writer() << tr("Unknown command: ") << command.front();
|
||||
fail_msg_writer() << sw::tr("Unknown command: ") << command.front();
|
||||
w.stop();
|
||||
w.deinit();
|
||||
}
|
||||
@ -8167,5 +8186,5 @@ int main(int argc, char* argv[])
|
||||
w.deinit();
|
||||
}
|
||||
return 0;
|
||||
//CATCH_ENTRY_L0("main", 1);
|
||||
CATCH_ENTRY_L0("main", 1);
|
||||
}
|
||||
|
||||
@ -210,6 +210,7 @@ namespace cryptonote
|
||||
bool prepare_multisig(const std::vector<std::string>& args);
|
||||
bool make_multisig(const std::vector<std::string>& args);
|
||||
bool finalize_multisig(const std::vector<std::string> &args);
|
||||
bool exchange_multisig_keys(const std::vector<std::string> &args);
|
||||
bool export_multisig(const std::vector<std::string>& args);
|
||||
bool import_multisig(const std::vector<std::string>& args);
|
||||
bool accept_loaded_tx(const tools::wallet2::multisig_tx_set &txs);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
|
||||
#define DEF_MONERO_VERSION "0.13.0.1-rc"
|
||||
#define DEF_MONERO_VERSION "0.13.0.3"
|
||||
#define DEF_MONERO_RELEASE_NAME "Beryllium Bullet"
|
||||
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
|
||||
|
||||
|
||||
@ -96,6 +96,9 @@ namespace {
|
||||
throw runtime_error("Multisig wallet is not finalized yet");
|
||||
}
|
||||
}
|
||||
void checkMultisigWalletReady(const std::unique_ptr<tools::wallet2> &wallet) {
|
||||
return checkMultisigWalletReady(wallet.get());
|
||||
}
|
||||
|
||||
void checkMultisigWalletNotReady(const tools::wallet2* wallet) {
|
||||
if (!wallet) {
|
||||
@ -111,6 +114,9 @@ namespace {
|
||||
throw runtime_error("Multisig wallet is already finalized");
|
||||
}
|
||||
}
|
||||
void checkMultisigWalletNotReady(const std::unique_ptr<tools::wallet2> &wallet) {
|
||||
return checkMultisigWalletNotReady(wallet.get());
|
||||
}
|
||||
}
|
||||
|
||||
struct Wallet2CallbackImpl : public tools::i_wallet2_callback
|
||||
@ -376,15 +382,15 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
|
||||
, m_rebuildWalletCache(false)
|
||||
, m_is_connected(false)
|
||||
{
|
||||
m_wallet = std::make_unique<tools::wallet2>(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true);
|
||||
m_history = std::make_unique<TransactionHistoryImpl>(this);
|
||||
m_wallet2Callback = std::make_unique<Wallet2CallbackImpl>(this);
|
||||
m_wallet->callback(m_wallet2Callback);
|
||||
m_wallet.reset(new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true));
|
||||
m_history.reset(new TransactionHistoryImpl(this));
|
||||
m_wallet2Callback.reset(new Wallet2CallbackImpl(this));
|
||||
m_wallet->callback(m_wallet2Callback.get());
|
||||
m_refreshThreadDone = false;
|
||||
m_refreshEnabled = false;
|
||||
m_addressBook = std::make_unique<AddressBookImpl>(this);
|
||||
m_subaddress = std::make_unique<SubaddressImpl>(this);
|
||||
m_subaddressAccount = std::make_unique<SubaddressAccountImpl>(this);
|
||||
m_addressBook.reset(new AddressBookImpl(this));
|
||||
m_subaddress.reset(new SubaddressImpl(this));
|
||||
m_subaddressAccount.reset(new SubaddressAccountImpl(this));
|
||||
|
||||
|
||||
m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS;
|
||||
@ -399,6 +405,7 @@ WalletImpl::~WalletImpl()
|
||||
{
|
||||
|
||||
LOG_PRINT_L1(__FUNCTION__);
|
||||
m_wallet->callback(NULL);
|
||||
// Pause refresh thread - prevents refresh from starting again
|
||||
pauseRefresh();
|
||||
// Close wallet - stores cache and stops ongoing refresh operation
|
||||
@ -603,7 +610,7 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path,
|
||||
LOG_PRINT_L1("Generated new view only wallet from keys");
|
||||
}
|
||||
if(has_spendkey && !has_viewkey) {
|
||||
m_wallet->generate(path, password, spendkey, true, false, false);
|
||||
m_wallet->generate(path, password, spendkey, true, false);
|
||||
setSeedLanguage(language);
|
||||
LOG_PRINT_L1("Generated deterministic wallet from spend key with seed language: " + language);
|
||||
}
|
||||
@ -623,7 +630,7 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p
|
||||
m_recoveringFromDevice = true;
|
||||
try
|
||||
{
|
||||
m_wallet->restore(path, password, device_name, false);
|
||||
m_wallet->restore(path, password, device_name);
|
||||
LOG_PRINT_L1("Generated new wallet from device: " + device_name);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
@ -1181,6 +1188,20 @@ string WalletImpl::makeMultisig(const vector<string>& info, uint32_t threshold)
|
||||
return string();
|
||||
}
|
||||
|
||||
std::string WalletImpl::exchangeMultisigKeys(const std::vector<std::string> &info) {
|
||||
try {
|
||||
clearStatus();
|
||||
checkMultisigWalletNotReady(m_wallet);
|
||||
|
||||
return m_wallet->exchange_multisig_keys(epee::wipeable_string(m_password), info);
|
||||
} catch (const exception& e) {
|
||||
LOG_ERROR("Error on exchanging multisig keys: ") << e.what();
|
||||
setStatusError(string(tr("Failed to make multisig: ")) + e.what());
|
||||
}
|
||||
|
||||
return string();
|
||||
}
|
||||
|
||||
bool WalletImpl::finalizeMultisig(const vector<string>& extraMultisigInfo) {
|
||||
try {
|
||||
clearStatus();
|
||||
@ -2146,6 +2167,28 @@ bool WalletImpl::blackballOutputs(const std::vector<std::string> &outputs, bool
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WalletImpl::blackballOutput(const std::string &amount, const std::string &offset)
|
||||
{
|
||||
uint64_t raw_amount, raw_offset;
|
||||
if (!epee::string_tools::get_xtype_from_string(raw_amount, amount))
|
||||
{
|
||||
setStatusError(tr("Failed to parse output amount"));
|
||||
return false;
|
||||
}
|
||||
if (!epee::string_tools::get_xtype_from_string(raw_offset, offset))
|
||||
{
|
||||
setStatusError(tr("Failed to parse output offset"));
|
||||
return false;
|
||||
}
|
||||
bool ret = m_wallet->blackball_output(std::make_pair(raw_amount, raw_offset));
|
||||
if (!ret)
|
||||
{
|
||||
setStatusError(tr("Failed to blackball output"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WalletImpl::unblackballOutput(const std::string &amount, const std::string &offset)
|
||||
{
|
||||
uint64_t raw_amount, raw_offset;
|
||||
|
||||
@ -137,6 +137,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;
|
||||
bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) override;
|
||||
bool exportMultisigImages(std::string& images) override;
|
||||
size_t importMultisigImages(const std::vector<std::string>& images) override;
|
||||
@ -182,7 +183,8 @@ public:
|
||||
virtual std::string getDefaultDataDir() const override;
|
||||
virtual bool lightWalletLogin(bool &isNewWallet) const override;
|
||||
virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) override;
|
||||
virtual bool blackballOutputs(const std::vector<std::string> &pubkeys, bool add) override;
|
||||
virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) override;
|
||||
virtual bool blackballOutput(const std::string &amount, const std::string &offset) override;
|
||||
virtual bool unblackballOutput(const std::string &amount, const std::string &offset) override;
|
||||
virtual bool getRing(const std::string &key_image, std::vector<uint64_t> &ring) const override;
|
||||
virtual bool getRings(const std::string &txid, std::vector<std::pair<std::string, std::vector<uint64_t>>> &rings) const override;
|
||||
|
||||
@ -706,6 +706,12 @@ struct Wallet
|
||||
* @return in case of N / N wallets returns empty string since no more key exchanges needed. For N - 1 / N wallets returns base58 encoded extra multisig info
|
||||
*/
|
||||
virtual std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) = 0;
|
||||
/**
|
||||
* @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
|
||||
* @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;
|
||||
/**
|
||||
* @brief finalizeMultisig - finalizes N - 1 / N multisig wallets creation
|
||||
* @param extraMultisigInfo - wallet participants' extra multisig info obtained with makeMultisig call
|
||||
@ -881,7 +887,10 @@ struct Wallet
|
||||
virtual bool rescanSpent() = 0;
|
||||
|
||||
//! blackballs a set of outputs
|
||||
virtual bool blackballOutputs(const std::vector<std::string> &pubkeys, bool add) = 0;
|
||||
virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) = 0;
|
||||
|
||||
//! blackballs an output
|
||||
virtual bool blackballOutput(const std::string &amount, const std::string &offset) = 0;
|
||||
|
||||
//! unblackballs an output
|
||||
virtual bool unblackballOutput(const std::string &amount, const std::string &offset) = 0;
|
||||
|
||||
@ -66,6 +66,7 @@ using namespace epee;
|
||||
#include "memwipe.h"
|
||||
#include "common/base58.h"
|
||||
#include "common/dns_utils.h"
|
||||
#include "common/notify.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#include "ringdb.h"
|
||||
|
||||
@ -113,14 +114,15 @@ using namespace cryptonote;
|
||||
|
||||
#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003"
|
||||
|
||||
#define SEGREGATION_FORK_HEIGHT 1546000
|
||||
#define TESTNET_SEGREGATION_FORK_HEIGHT 1000000
|
||||
#define STAGENET_SEGREGATION_FORK_HEIGHT 1000000
|
||||
#define SEGREGATION_FORK_HEIGHT 99999999
|
||||
#define TESTNET_SEGREGATION_FORK_HEIGHT 99999999
|
||||
#define STAGENET_SEGREGATION_FORK_HEIGHT 99999999
|
||||
#define SEGREGATION_FORK_VICINITY 1500 /* blocks */
|
||||
|
||||
#define FIRST_REFRESH_GRANULARITY 1024
|
||||
|
||||
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
|
||||
static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -132,6 +134,42 @@ namespace
|
||||
dir /= ".shared-ringdb";
|
||||
return dir.string();
|
||||
}
|
||||
|
||||
std::string pack_multisignature_keys(const std::string& prefix, const std::vector<crypto::public_key>& keys, const crypto::secret_key& signer_secret_key)
|
||||
{
|
||||
std::string data;
|
||||
crypto::public_key signer;
|
||||
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(signer_secret_key, signer), "Failed to derive public spend key");
|
||||
data += std::string((const char *)&signer, sizeof(crypto::public_key));
|
||||
|
||||
for (const auto &key: keys)
|
||||
{
|
||||
data += std::string((const char *)&key, sizeof(crypto::public_key));
|
||||
}
|
||||
|
||||
data.resize(data.size() + sizeof(crypto::signature));
|
||||
|
||||
crypto::hash hash;
|
||||
crypto::cn_fast_hash(data.data(), data.size() - sizeof(crypto::signature), hash);
|
||||
crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
|
||||
crypto::generate_signature(hash, signer, signer_secret_key, signature);
|
||||
|
||||
return MULTISIG_EXTRA_INFO_MAGIC + tools::base58::encode(data);
|
||||
}
|
||||
|
||||
std::vector<crypto::public_key> secret_keys_to_public_keys(const std::vector<crypto::secret_key>& keys)
|
||||
{
|
||||
std::vector<crypto::public_key> public_keys;
|
||||
public_keys.reserve(keys.size());
|
||||
|
||||
std::transform(keys.begin(), keys.end(), std::back_inserter(public_keys), [] (const crypto::secret_key& k) -> crypto::public_key {
|
||||
crypto::public_key p;
|
||||
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(k, p), "Failed to derive public spend key");
|
||||
return p;
|
||||
});
|
||||
|
||||
return public_keys;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
@ -162,6 +200,7 @@ struct options {
|
||||
};
|
||||
const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
|
||||
const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
|
||||
const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
|
||||
};
|
||||
|
||||
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
|
||||
@ -256,7 +295,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||
trusted_daemon = false;
|
||||
if (tools::is_local_address(daemon_address))
|
||||
{
|
||||
MINFO(tr("Daemon is local, assuming trusted"));
|
||||
MINFO(tools::wallet2::tr("Daemon is local, assuming trusted"));
|
||||
trusted_daemon = true;
|
||||
}
|
||||
}
|
||||
@ -268,6 +307,17 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
||||
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
|
||||
wallet->set_ring_database(ringdb_path.string());
|
||||
wallet->device_name(device_name);
|
||||
|
||||
try
|
||||
{
|
||||
if (!command_line::is_arg_defaulted(vm, opts.tx_notify))
|
||||
wallet->set_tx_notify(std::shared_ptr<tools::Notify>(new tools::Notify(command_line::get_arg(vm, opts.tx_notify).c_str())));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
MERROR("Failed to parse tx notify spec");
|
||||
}
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@ -297,10 +347,10 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(!password_prompter, tools::error::wallet_internal_error, tools::wallet2::tr("no password specified; use --prompt-for-password to prompt for a password"));
|
||||
|
||||
return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify);
|
||||
return password_prompter(verify ? tools::wallet2::tr("Enter a new password for the wallet") : tools::wallet2::tr("Wallet password"), verify);
|
||||
}
|
||||
|
||||
std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const bool testnet = command_line::get_arg(vm, opts.testnet);
|
||||
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
|
||||
@ -310,6 +360,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
|
||||
false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly
|
||||
fails. This large wrapper is for the use of that macro */
|
||||
std::unique_ptr<tools::wallet2> wallet;
|
||||
epee::wipeable_string password;
|
||||
const auto do_generate = [&]() -> bool {
|
||||
std::string buf;
|
||||
if (!epee::file_io_utils::load_file_to_string(json_file, buf)) {
|
||||
@ -447,10 +498,12 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
|
||||
if (!field_seed.empty())
|
||||
{
|
||||
wallet->generate(field_filename, field_password, recovery_key, recover, false, create_address_file);
|
||||
password = field_password;
|
||||
}
|
||||
else if (field_viewkey.empty() && !field_spendkey.empty())
|
||||
{
|
||||
wallet->generate(field_filename, field_password, spendkey, recover, false, create_address_file);
|
||||
password = field_password;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -477,6 +530,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
|
||||
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Address must be specified in order to create watch-only wallet"));
|
||||
}
|
||||
wallet->generate(field_filename, field_password, address, viewkey, create_address_file);
|
||||
password = field_password;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -484,6 +538,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
|
||||
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
|
||||
}
|
||||
wallet->generate(field_filename, field_password, address, spendkey, viewkey, create_address_file);
|
||||
password = field_password;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -496,9 +551,9 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
|
||||
|
||||
if (do_generate())
|
||||
{
|
||||
return wallet;
|
||||
return {std::move(wallet), tools::password_container(password)};
|
||||
}
|
||||
return nullptr;
|
||||
return {nullptr, tools::password_container{}};
|
||||
}
|
||||
|
||||
static void throw_on_rpc_response_error(const boost::optional<std::string> &status, const char *method)
|
||||
@ -758,6 +813,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
|
||||
m_callback(0),
|
||||
m_trusted_daemon(false),
|
||||
m_nettype(nettype),
|
||||
m_multisig_rounds_passed(0),
|
||||
m_always_confirm_transfers(true),
|
||||
m_print_ring_members(false),
|
||||
m_store_tx_info(true),
|
||||
@ -838,9 +894,10 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
|
||||
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
|
||||
command_line::add_arg(desc_params, opts.kdf_rounds);
|
||||
command_line::add_arg(desc_params, opts.hw_device);
|
||||
command_line::add_arg(desc_params, opts.tx_notify);
|
||||
}
|
||||
|
||||
std::unique_ptr<wallet2> 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)
|
||||
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)
|
||||
{
|
||||
const options opts{};
|
||||
return generate_from_json(json_file, vm, unattended, opts, password_prompter);
|
||||
@ -1325,9 +1382,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
// (that is, the prunable stuff may or may not be included)
|
||||
if (!miner_tx && !pool)
|
||||
process_unconfirmed(txid, tx, height);
|
||||
std::vector<size_t> outs;
|
||||
std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; // per receiving subaddress index
|
||||
crypto::public_key tx_pub_key = null_pkey;
|
||||
bool notify = false;
|
||||
|
||||
std::vector<tx_extra_field> local_tx_extra_fields;
|
||||
if (tx_cache_data.tx_extra_fields.empty())
|
||||
@ -1347,6 +1404,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
uint64_t total_received_1 = 0;
|
||||
while (!tx.vout.empty())
|
||||
{
|
||||
std::vector<size_t> outs;
|
||||
// if tx.vout is not empty, we loop through all tx pubkeys
|
||||
|
||||
tx_extra_pub_key pub_key_field;
|
||||
@ -1567,6 +1625,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
|
||||
}
|
||||
total_received_1 += amount;
|
||||
notify = true;
|
||||
}
|
||||
else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx_scan_info[o].amount)
|
||||
{
|
||||
@ -1634,6 +1693,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
|
||||
}
|
||||
total_received_1 += extra_amount;
|
||||
notify = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1707,10 +1767,14 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
}
|
||||
|
||||
// remove change sent to the spending subaddress account from the list of received funds
|
||||
uint64_t sub_change = 0;
|
||||
for (auto i = tx_money_got_in_outs.begin(); i != tx_money_got_in_outs.end();)
|
||||
{
|
||||
if (subaddr_account && i->first.major == *subaddr_account)
|
||||
{
|
||||
sub_change += i->second;
|
||||
i = tx_money_got_in_outs.erase(i);
|
||||
}
|
||||
else
|
||||
++i;
|
||||
}
|
||||
@ -1755,7 +1819,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t total_received_2 = 0;
|
||||
uint64_t total_received_2 = sub_change;
|
||||
for (const auto& i : tx_money_got_in_outs)
|
||||
total_received_2 += i.second;
|
||||
if (total_received_1 != total_received_2)
|
||||
@ -1790,6 +1854,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
LOG_PRINT_L2("Payment found in " << (pool ? "pool" : "block") << ": " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount);
|
||||
}
|
||||
}
|
||||
|
||||
if (notify)
|
||||
{
|
||||
std::shared_ptr<tools::Notify> tx_notify = m_tx_notify;
|
||||
if (tx_notify)
|
||||
tx_notify->notify(epee::string_tools::pod_to_hex(txid).c_str());
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height)
|
||||
@ -2885,6 +2956,7 @@ bool wallet2::clear()
|
||||
m_address_book.clear();
|
||||
m_subaddresses.clear();
|
||||
m_subaddress_labels.clear();
|
||||
m_multisig_rounds_passed = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2899,6 +2971,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
||||
{
|
||||
std::string account_data;
|
||||
std::string multisig_signers;
|
||||
std::string multisig_derivations;
|
||||
cryptonote::account_base account = m_account;
|
||||
|
||||
crypto::chacha_key key;
|
||||
@ -2951,6 +3024,14 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig signers");
|
||||
value.SetString(multisig_signers.c_str(), multisig_signers.length());
|
||||
json.AddMember("multisig_signers", value, json.GetAllocator());
|
||||
|
||||
r = ::serialization::dump_binary(m_multisig_derivations, multisig_derivations);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig derivations");
|
||||
value.SetString(multisig_derivations.c_str(), multisig_derivations.length());
|
||||
json.AddMember("multisig_derivations", value, json.GetAllocator());
|
||||
|
||||
value2.SetUint(m_multisig_rounds_passed);
|
||||
json.AddMember("multisig_rounds_passed", value2, json.GetAllocator());
|
||||
}
|
||||
|
||||
value2.SetInt(m_always_confirm_transfers ? 1 :0);
|
||||
@ -3123,6 +3204,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||
m_multisig = false;
|
||||
m_multisig_threshold = 0;
|
||||
m_multisig_signers.clear();
|
||||
m_multisig_rounds_passed = 0;
|
||||
m_multisig_derivations.clear();
|
||||
m_always_confirm_transfers = false;
|
||||
m_print_ring_members = false;
|
||||
m_default_mixin = 0;
|
||||
@ -3181,6 +3264,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||
m_multisig = field_multisig;
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_threshold, unsigned int, Uint, m_multisig, 0);
|
||||
m_multisig_threshold = field_multisig_threshold;
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_rounds_passed, unsigned int, Uint, false, 0);
|
||||
m_multisig_rounds_passed = field_multisig_rounds_passed;
|
||||
if (m_multisig)
|
||||
{
|
||||
if (!json.HasMember("multisig_signers"))
|
||||
@ -3201,6 +3286,24 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||
LOG_ERROR("Field multisig_signers found in JSON, but failed to parse");
|
||||
return false;
|
||||
}
|
||||
|
||||
//previous version of multisig does not have this field
|
||||
if (json.HasMember("multisig_derivations"))
|
||||
{
|
||||
if (!json["multisig_derivations"].IsString())
|
||||
{
|
||||
LOG_ERROR("Field multisig_derivations found in JSON, but not String");
|
||||
return false;
|
||||
}
|
||||
const char *field_multisig_derivations = json["multisig_derivations"].GetString();
|
||||
std::string multisig_derivations = std::string(field_multisig_derivations, field_multisig_derivations + json["multisig_derivations"].GetStringLength());
|
||||
r = ::serialization::parse_binary(multisig_derivations, m_multisig_derivations);
|
||||
if (!r)
|
||||
{
|
||||
LOG_ERROR("Field multisig_derivations found in JSON, but failed to parse");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false, true);
|
||||
m_always_confirm_transfers = field_always_confirm_transfers;
|
||||
@ -3282,9 +3385,16 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
||||
encrypted_secret_keys = field_encrypted_secret_keys;
|
||||
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string());
|
||||
if (m_device_name.empty() && field_device_name_found)
|
||||
if (m_device_name.empty())
|
||||
{
|
||||
m_device_name = field_device_name;
|
||||
if (field_device_name_found)
|
||||
{
|
||||
m_device_name = field_device_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -3501,6 +3611,7 @@ bool wallet2::query_device(hw::device::device_type& device_type, const std::stri
|
||||
if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
|
||||
crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
|
||||
|
||||
device_type = hw::device::device_type::SOFTWARE;
|
||||
// The contents should be JSON if the wallet follows the new format.
|
||||
if (json.Parse(account_data.c_str()).HasParseError())
|
||||
{
|
||||
@ -3829,12 +3940,12 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
||||
CHECK_AND_ASSERT_THROW_MES(!view_keys.empty(), "empty view keys");
|
||||
CHECK_AND_ASSERT_THROW_MES(view_keys.size() == spend_keys.size(), "Mismatched view/spend key sizes");
|
||||
CHECK_AND_ASSERT_THROW_MES(threshold > 1 && threshold <= spend_keys.size() + 1, "Invalid threshold");
|
||||
CHECK_AND_ASSERT_THROW_MES(threshold == spend_keys.size() || threshold == spend_keys.size() + 1, "Unsupported threshold case");
|
||||
|
||||
std::string extra_multisig_info;
|
||||
crypto::hash hash;
|
||||
|
||||
clear();
|
||||
std::vector<crypto::secret_key> multisig_keys;
|
||||
rct::key spend_pkey = rct::identity();
|
||||
rct::key spend_skey;
|
||||
std::vector<crypto::public_key> multisig_signers;
|
||||
|
||||
// decrypt keys
|
||||
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
|
||||
@ -3847,43 +3958,78 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
||||
keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
|
||||
}
|
||||
|
||||
MINFO("Creating spend key...");
|
||||
std::vector<crypto::secret_key> multisig_keys;
|
||||
rct::key spend_pkey, spend_skey;
|
||||
// In common multisig scheme there are 4 types of key exchange rounds:
|
||||
// 1. First round is exchange of view secret keys and public spend keys.
|
||||
// 2. Middle round is exchange of derivations: Ki = b * Mj, where b - spend secret key,
|
||||
// M - public multisig key (in first round it equals to public spend key), K - new public multisig key.
|
||||
// 3. Secret spend establishment round sets your secret multisig keys as follows: kl = H(Ml), where M - is *your* public multisig key,
|
||||
// k - secret multisig key used to sign transactions. k and M are sets of keys, of course.
|
||||
// And secret spend key as the sum of all participant's secret multisig keys
|
||||
// 4. Last round establishes multisig wallet's public spend key. Participants exchange their public multisig keys
|
||||
// and calculate common spend public key as sum of all unique participants' public multisig keys.
|
||||
// Note that N/N scheme has only first round. N-1/N has 2 rounds: first and last. Common M/N has all 4 rounds.
|
||||
|
||||
// IMPORTANT: wallet's public spend key is not equal to secret_spend_key * G!
|
||||
// Wallet's public spend key is the sum of unique public multisig keys of all participants.
|
||||
// secret_spend_key * G = public signer key
|
||||
|
||||
if (threshold == spend_keys.size() + 1)
|
||||
{
|
||||
// In N / N case we only need to do one round and calculate secret multisig keys and new secret spend key
|
||||
MINFO("Creating spend key...");
|
||||
|
||||
// Calculates all multisig keys and spend key
|
||||
cryptonote::generate_multisig_N_N(get_account().get_keys(), spend_keys, multisig_keys, spend_skey, spend_pkey);
|
||||
}
|
||||
else if (threshold == spend_keys.size())
|
||||
{
|
||||
cryptonote::generate_multisig_N1_N(get_account().get_keys(), spend_keys, multisig_keys, spend_skey, spend_pkey);
|
||||
|
||||
// We need an extra step, so we package all the composite public keys
|
||||
// we know about, and make a signed string out of them
|
||||
std::string data;
|
||||
crypto::public_key signer;
|
||||
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(rct::rct2sk(spend_skey), signer), "Failed to derive public spend key");
|
||||
data += std::string((const char *)&signer, sizeof(crypto::public_key));
|
||||
|
||||
for (const auto &msk: multisig_keys)
|
||||
{
|
||||
rct::key pmsk = rct::scalarmultBase(rct::sk2rct(msk));
|
||||
data += std::string((const char *)&pmsk, sizeof(crypto::public_key));
|
||||
}
|
||||
|
||||
data.resize(data.size() + sizeof(crypto::signature));
|
||||
crypto::cn_fast_hash(data.data(), data.size() - sizeof(signature), hash);
|
||||
crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)];
|
||||
crypto::generate_signature(hash, signer, rct::rct2sk(spend_skey), signature);
|
||||
|
||||
extra_multisig_info = std::string("MultisigxV1") + tools::base58::encode(data);
|
||||
// Our signer key is b * G, where b is secret spend key.
|
||||
multisig_signers = spend_keys;
|
||||
multisig_signers.push_back(get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(false, "Unsupported threshold case");
|
||||
// We just got public spend keys of all participants and deriving multisig keys (set of Mi = b * Bi).
|
||||
// note that derivations are public keys as DH exchange suppose it to be
|
||||
auto derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), spend_keys);
|
||||
|
||||
spend_pkey = rct::identity();
|
||||
multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1, crypto::null_pkey);
|
||||
|
||||
if (threshold == spend_keys.size())
|
||||
{
|
||||
// N - 1 / N case
|
||||
|
||||
// We need an extra step, so we package all the composite public keys
|
||||
// we know about, and make a signed string out of them
|
||||
MINFO("Creating spend key...");
|
||||
|
||||
// Calculating set of our secret multisig keys as follows: mi = H(Mi),
|
||||
// where mi - secret multisig key, Mi - others' participants public multisig key
|
||||
multisig_keys = cryptonote::calculate_multisig_keys(derivations);
|
||||
|
||||
// calculating current participant's spend secret key as sum of all secret multisig keys for current participant.
|
||||
// IMPORTANT: participant's secret spend key is not an entire wallet's secret spend!
|
||||
// Entire wallet's secret spend is sum of all unique secret multisig keys
|
||||
// among all of participants and is not held by anyone!
|
||||
spend_skey = rct::sk2rct(cryptonote::calculate_multisig_signer_key(multisig_keys));
|
||||
|
||||
// Preparing data for the last round to calculate common public spend key. The data contains public multisig keys.
|
||||
extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), rct::rct2sk(spend_skey));
|
||||
}
|
||||
else
|
||||
{
|
||||
// M / N case
|
||||
MINFO("Preparing keys for next exchange round...");
|
||||
|
||||
// Preparing data for middle round - packing new public multisig keys to exchage with others.
|
||||
extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, derivations, m_account.get_keys().m_spend_secret_key);
|
||||
spend_skey = rct::sk2rct(m_account.get_keys().m_spend_secret_key);
|
||||
|
||||
// Need to store middle keys to be able to proceed in case of wallet shutdown.
|
||||
m_multisig_derivations = derivations;
|
||||
}
|
||||
}
|
||||
|
||||
// the multisig view key is shared by all, make one all can derive
|
||||
clear();
|
||||
MINFO("Creating view key...");
|
||||
crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(get_account().get_keys().m_view_secret_key, view_keys);
|
||||
|
||||
@ -3895,18 +4041,10 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
||||
m_account_public_address = m_account.get_keys().m_account_address;
|
||||
m_watch_only = false;
|
||||
m_multisig = true;
|
||||
m_multisig_threshold = threshold;
|
||||
m_key_device_type = hw::device::device_type::SOFTWARE;
|
||||
|
||||
if (threshold == spend_keys.size() + 1)
|
||||
{
|
||||
m_multisig_signers = spend_keys;
|
||||
m_multisig_signers.push_back(get_multisig_signer_public_key());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1, crypto::null_pkey);
|
||||
}
|
||||
m_multisig_threshold = threshold;
|
||||
m_multisig_signers = multisig_signers;
|
||||
++m_multisig_rounds_passed;
|
||||
|
||||
// re-encrypt keys
|
||||
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
|
||||
@ -3921,13 +4059,147 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
||||
return extra_multisig_info;
|
||||
}
|
||||
|
||||
std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
||||
const std::vector<std::string> &info,
|
||||
uint32_t threshold)
|
||||
std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password,
|
||||
const std::vector<std::string> &info)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(info.empty(),
|
||||
error::wallet_internal_error, "Empty multisig info");
|
||||
|
||||
if (info[0].substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(false,
|
||||
error::wallet_internal_error, "Unsupported info string");
|
||||
}
|
||||
|
||||
std::vector<crypto::public_key> signers;
|
||||
std::unordered_set<crypto::public_key> pkeys;
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(!unpack_extra_multisig_info(info, signers, pkeys),
|
||||
error::wallet_internal_error, "Bad extra multisig info");
|
||||
|
||||
return exchange_multisig_keys(password, pkeys, signers);
|
||||
}
|
||||
|
||||
std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password,
|
||||
std::unordered_set<crypto::public_key> derivations,
|
||||
std::vector<crypto::public_key> signers)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(!derivations.empty(), "empty pkeys");
|
||||
CHECK_AND_ASSERT_THROW_MES(!signers.empty(), "empty signers");
|
||||
|
||||
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");
|
||||
|
||||
// keys are decrypted
|
||||
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
|
||||
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
|
||||
{
|
||||
crypto::chacha_key chacha_key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
|
||||
m_account.encrypt_viewkey(chacha_key);
|
||||
m_account.decrypt_keys(chacha_key);
|
||||
keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
|
||||
}
|
||||
|
||||
if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 1)
|
||||
{
|
||||
// the last round is passed and we have to calculate spend public key
|
||||
// add ours if not included
|
||||
crypto::public_key local_signer = get_multisig_signer_public_key();
|
||||
|
||||
if (std::find(signers.begin(), signers.end(), local_signer) == signers.end())
|
||||
{
|
||||
signers.push_back(local_signer);
|
||||
for (const auto &msk: get_account().get_multisig_keys())
|
||||
{
|
||||
derivations.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(msk))));
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(signers.size() == m_multisig_signers.size(), "Bad signers size");
|
||||
|
||||
// Summing all of unique public multisig keys to calculate common public spend key
|
||||
crypto::public_key spend_public_key = cryptonote::generate_multisig_M_N_spend_public_key(std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
|
||||
m_account_public_address.m_spend_public_key = spend_public_key;
|
||||
m_account.finalize_multisig(spend_public_key);
|
||||
|
||||
m_multisig_signers = signers;
|
||||
std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); });
|
||||
|
||||
++m_multisig_rounds_passed;
|
||||
m_multisig_derivations.clear();
|
||||
|
||||
// keys are encrypted again
|
||||
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
|
||||
|
||||
if (!m_wallet_file.empty())
|
||||
{
|
||||
bool r = store_keys(m_keys_file, password, false);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
|
||||
|
||||
if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
|
||||
{
|
||||
r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype));
|
||||
if(!r) MERROR("String with address text not saved");
|
||||
}
|
||||
}
|
||||
|
||||
m_subaddresses.clear();
|
||||
m_subaddress_labels.clear();
|
||||
add_subaddress_account(tr("Primary account"));
|
||||
|
||||
if (!m_wallet_file.empty())
|
||||
store();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// Below are either middle or secret spend key establishment rounds
|
||||
|
||||
for (const auto& key: m_multisig_derivations)
|
||||
derivations.erase(key);
|
||||
|
||||
// Deriving multisig keys (set of Mi = b * Bi) according to DH from other participants' multisig keys.
|
||||
auto new_derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), std::vector<crypto::public_key>(derivations.begin(), derivations.end()));
|
||||
|
||||
std::string extra_multisig_info;
|
||||
if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 2) // next round is last
|
||||
{
|
||||
// Next round is last therefore we are performing secret spend establishment round as described above.
|
||||
MINFO("Creating spend key...");
|
||||
|
||||
// Calculating our secret multisig keys by hashing our public multisig keys.
|
||||
auto multisig_keys = cryptonote::calculate_multisig_keys(std::vector<crypto::public_key>(new_derivations.begin(), new_derivations.end()));
|
||||
// And summing it to get personal secret spend key
|
||||
crypto::secret_key spend_skey = cryptonote::calculate_multisig_signer_key(multisig_keys);
|
||||
|
||||
m_account.make_multisig(m_account.get_keys().m_view_secret_key, spend_skey, rct::rct2pk(rct::identity()), multisig_keys);
|
||||
|
||||
// Packing public multisig keys to exchange with others and calculate common public spend key in the last round
|
||||
extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), spend_skey);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is just middle round
|
||||
MINFO("Preparing keys for next exchange round...");
|
||||
extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, new_derivations, m_account.get_keys().m_spend_secret_key);
|
||||
m_multisig_derivations = new_derivations;
|
||||
}
|
||||
|
||||
++m_multisig_rounds_passed;
|
||||
|
||||
create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
|
||||
return extra_multisig_info;
|
||||
}
|
||||
|
||||
void wallet2::unpack_multisig_info(const std::vector<std::string>& info,
|
||||
std::vector<crypto::public_key> &public_keys,
|
||||
std::vector<crypto::secret_key> &secret_keys) const
|
||||
{
|
||||
// parse all multisig info
|
||||
std::vector<crypto::secret_key> secret_keys(info.size());
|
||||
std::vector<crypto::public_key> public_keys(info.size());
|
||||
public_keys.resize(info.size());
|
||||
secret_keys.resize(info.size());
|
||||
for (size_t i = 0; i < info.size(); ++i)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(!verify_multisig_info(info[i], secret_keys[i], public_keys[i]),
|
||||
@ -3971,75 +4243,51 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
||||
"Found local spend public key, but not local view secret key - something very weird");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
||||
const std::vector<std::string> &info,
|
||||
uint32_t threshold)
|
||||
{
|
||||
std::vector<crypto::secret_key> secret_keys(info.size());
|
||||
std::vector<crypto::public_key> public_keys(info.size());
|
||||
unpack_multisig_info(info, public_keys, secret_keys);
|
||||
return make_multisig(password, secret_keys, public_keys, threshold);
|
||||
}
|
||||
|
||||
bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unordered_set<crypto::public_key> pkeys, std::vector<crypto::public_key> signers)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(!pkeys.empty(), "empty pkeys");
|
||||
exchange_multisig_keys(password, pkeys, signers);
|
||||
return true;
|
||||
}
|
||||
|
||||
// keys are decrypted
|
||||
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
|
||||
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
|
||||
bool wallet2::unpack_extra_multisig_info(const std::vector<std::string>& info,
|
||||
std::vector<crypto::public_key> &signers,
|
||||
std::unordered_set<crypto::public_key> &pkeys) const
|
||||
{
|
||||
// parse all multisig info
|
||||
signers.resize(info.size(), crypto::null_pkey);
|
||||
for (size_t i = 0; i < info.size(); ++i)
|
||||
{
|
||||
crypto::chacha_key chacha_key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
|
||||
m_account.encrypt_viewkey(chacha_key);
|
||||
m_account.decrypt_keys(chacha_key);
|
||||
keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
|
||||
if (!verify_extra_multisig_info(info[i], pkeys, signers[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// add ours if not included
|
||||
crypto::public_key local_signer;
|
||||
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(get_account().get_keys().m_spend_secret_key, local_signer),
|
||||
"Failed to derive public spend key");
|
||||
if (std::find(signers.begin(), signers.end(), local_signer) == signers.end())
|
||||
{
|
||||
signers.push_back(local_signer);
|
||||
for (const auto &msk: get_account().get_multisig_keys())
|
||||
{
|
||||
pkeys.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(msk))));
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_THROW_MES(signers.size() == m_multisig_signers.size(), "Bad signers size");
|
||||
|
||||
crypto::public_key spend_public_key = cryptonote::generate_multisig_N1_N_spend_public_key(std::vector<crypto::public_key>(pkeys.begin(), pkeys.end()));
|
||||
m_account_public_address.m_spend_public_key = spend_public_key;
|
||||
m_account.finalize_multisig(spend_public_key);
|
||||
|
||||
m_multisig_signers = signers;
|
||||
std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); });
|
||||
|
||||
// keys are encrypted again
|
||||
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
|
||||
|
||||
create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
|
||||
|
||||
m_subaddresses.clear();
|
||||
m_subaddress_labels.clear();
|
||||
add_subaddress_account(tr("Primary account"));
|
||||
|
||||
if (!m_wallet_file.empty())
|
||||
store();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::vector<std::string> &info)
|
||||
{
|
||||
// parse all multisig info
|
||||
std::unordered_set<crypto::public_key> public_keys;
|
||||
std::vector<crypto::public_key> signers(info.size(), crypto::null_pkey);
|
||||
for (size_t i = 0; i < info.size(); ++i)
|
||||
std::vector<crypto::public_key> signers;
|
||||
if (!unpack_extra_multisig_info(info, signers, public_keys))
|
||||
{
|
||||
if (!verify_extra_multisig_info(info[i], public_keys, signers[i]))
|
||||
{
|
||||
MERROR("Bad multisig info");
|
||||
return false;
|
||||
}
|
||||
MERROR("Bad multisig info");
|
||||
return false;
|
||||
}
|
||||
|
||||
return finalize_multisig(password, public_keys, signers);
|
||||
}
|
||||
|
||||
@ -4102,14 +4350,13 @@ bool wallet2::verify_multisig_info(const std::string &data, crypto::secret_key &
|
||||
|
||||
bool wallet2::verify_extra_multisig_info(const std::string &data, std::unordered_set<crypto::public_key> &pkeys, crypto::public_key &signer)
|
||||
{
|
||||
const size_t header_len = strlen("MultisigxV1");
|
||||
if (data.size() < header_len || data.substr(0, header_len) != "MultisigxV1")
|
||||
if (data.size() < MULTISIG_EXTRA_INFO_MAGIC.size() || data.substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC)
|
||||
{
|
||||
MERROR("Multisig info header check error");
|
||||
return false;
|
||||
}
|
||||
std::string decoded;
|
||||
if (!tools::base58::decode(data.substr(header_len), decoded))
|
||||
if (!tools::base58::decode(data.substr(MULTISIG_EXTRA_INFO_MAGIC.size()), decoded))
|
||||
{
|
||||
MERROR("Multisig info decoding error");
|
||||
return false;
|
||||
@ -5317,10 +5564,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
|
||||
rct::RangeProofType range_proof_type = rct::RangeProofBorromean;
|
||||
if (sd.use_bulletproofs)
|
||||
{
|
||||
range_proof_type = rct::RangeProofBulletproof;
|
||||
for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
|
||||
if (proof.V.size() > 1)
|
||||
range_proof_type = rct::RangeProofPaddedBulletproof;
|
||||
range_proof_type = rct::RangeProofPaddedBulletproof;
|
||||
}
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
|
||||
@ -68,6 +68,7 @@ namespace tools
|
||||
{
|
||||
class ringdb;
|
||||
class wallet2;
|
||||
class Notify;
|
||||
|
||||
class wallet_keys_unlocker
|
||||
{
|
||||
@ -176,7 +177,7 @@ namespace tools
|
||||
static void init_options(boost::program_options::options_description& desc_params);
|
||||
|
||||
//! Uses stdin and stdout. Returns a wallet2 if no errors.
|
||||
static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
static std::pair<std::unique_ptr<wallet2>, password_container> make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
|
||||
//! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors.
|
||||
static std::pair<std::unique_ptr<wallet2>, password_container>
|
||||
@ -554,7 +555,7 @@ namespace tools
|
||||
* \param device_name name of HW to use
|
||||
* \param create_address_file Whether to create an address file
|
||||
*/
|
||||
void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file);
|
||||
void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file = false);
|
||||
|
||||
/*!
|
||||
* \brief Creates a multisig wallet
|
||||
@ -573,6 +574,14 @@ namespace tools
|
||||
const std::vector<crypto::secret_key> &view_keys,
|
||||
const std::vector<crypto::public_key> &spend_keys,
|
||||
uint32_t threshold);
|
||||
std::string exchange_multisig_keys(const epee::wipeable_string &password,
|
||||
const std::vector<std::string> &info);
|
||||
/*!
|
||||
* \brief Any but first round of keys exchange
|
||||
*/
|
||||
std::string exchange_multisig_keys(const epee::wipeable_string &password,
|
||||
std::unordered_set<crypto::public_key> pkeys,
|
||||
std::vector<crypto::public_key> signers);
|
||||
/*!
|
||||
* \brief Finalizes creation of a multisig wallet
|
||||
*/
|
||||
@ -1176,6 +1185,8 @@ namespace tools
|
||||
|
||||
void change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password);
|
||||
|
||||
void set_tx_notify(const std::shared_ptr<tools::Notify> ¬ify) { m_tx_notify = notify; }
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief Stores wallet information to wallet file.
|
||||
@ -1245,6 +1256,12 @@ namespace tools
|
||||
bool get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
|
||||
|
||||
uint64_t get_segregation_fork_height() const;
|
||||
void unpack_multisig_info(const std::vector<std::string>& info,
|
||||
std::vector<crypto::public_key> &public_keys,
|
||||
std::vector<crypto::secret_key> &secret_keys) const;
|
||||
bool unpack_extra_multisig_info(const std::vector<std::string>& info,
|
||||
std::vector<crypto::public_key> &signers,
|
||||
std::unordered_set<crypto::public_key> &pkeys) const;
|
||||
|
||||
void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const;
|
||||
|
||||
@ -1295,6 +1312,9 @@ namespace tools
|
||||
bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */
|
||||
uint32_t m_multisig_threshold;
|
||||
std::vector<crypto::public_key> m_multisig_signers;
|
||||
//in case of general M/N multisig wallet we should perform N - M + 1 key exchange rounds and remember how many rounds are passed.
|
||||
uint32_t m_multisig_rounds_passed;
|
||||
std::vector<crypto::public_key> m_multisig_derivations;
|
||||
bool m_always_confirm_transfers;
|
||||
bool m_print_ring_members;
|
||||
bool m_store_tx_info; /*!< request txkey to be returned in RPC, and store in the wallet cache file */
|
||||
@ -1353,6 +1373,8 @@ namespace tools
|
||||
boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh;
|
||||
|
||||
bool m_unattended;
|
||||
|
||||
std::shared_ptr<tools::Notify> m_tx_notify;
|
||||
};
|
||||
}
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 25)
|
||||
|
||||
@ -2173,8 +2173,8 @@ namespace tools
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
|
||||
if (i->second.m_tx_hash == txid)
|
||||
{
|
||||
fill_transfer_entry(res.transfer, i->second.m_tx_hash, i->first, i->second);
|
||||
return true;
|
||||
res.transfers.resize(res.transfers.size() + 1);
|
||||
fill_transfer_entry(res.transfers.back(), i->second.m_tx_hash, i->first, i->second);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2183,8 +2183,8 @@ namespace tools
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
|
||||
if (i->first == txid)
|
||||
{
|
||||
fill_transfer_entry(res.transfer, i->first, i->second);
|
||||
return true;
|
||||
res.transfers.resize(res.transfers.size() + 1);
|
||||
fill_transfer_entry(res.transfers.back(), i->first, i->second);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2193,8 +2193,8 @@ namespace tools
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
|
||||
if (i->first == txid)
|
||||
{
|
||||
fill_transfer_entry(res.transfer, i->first, i->second);
|
||||
return true;
|
||||
res.transfers.resize(res.transfers.size() + 1);
|
||||
fill_transfer_entry(res.transfers.back(), i->first, i->second);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2205,11 +2205,17 @@ namespace tools
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
|
||||
if (i->second.m_pd.m_tx_hash == txid)
|
||||
{
|
||||
fill_transfer_entry(res.transfer, i->first, i->second);
|
||||
return true;
|
||||
res.transfers.resize(res.transfers.size() + 1);
|
||||
fill_transfer_entry(res.transfers.back(), i->first, i->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (!res.transfers.empty())
|
||||
{
|
||||
res.transfer = res.transfers.front(); // backward compat
|
||||
return true;
|
||||
}
|
||||
|
||||
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
|
||||
er.message = "Transaction not found.";
|
||||
return false;
|
||||
@ -3119,7 +3125,7 @@ namespace tools
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req.multisig_info.size() < threshold - 1)
|
||||
if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED;
|
||||
er.message = "Needs multisig info from more participants";
|
||||
@ -3146,6 +3152,55 @@ namespace tools
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
if (m_restricted)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||
er.message = "Command unavailable in restricted mode.";
|
||||
return false;
|
||||
}
|
||||
bool ready;
|
||||
uint32_t threshold, total;
|
||||
if (!m_wallet->multisig(&ready, &threshold, &total))
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG;
|
||||
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;
|
||||
}
|
||||
|
||||
if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED;
|
||||
er.message = "Needs multisig info from more participants";
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
res.multisig_info = m_wallet->exchange_multisig_keys(req.password, req.multisig_info);
|
||||
if (res.multisig_info.empty())
|
||||
{
|
||||
res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
er.message = std::string("Error calling exchange_multisig_info: ") + e.what();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool wallet_rpc_server::on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er)
|
||||
{
|
||||
if (!m_wallet) return not_open(er);
|
||||
@ -3352,7 +3407,8 @@ public:
|
||||
{
|
||||
try
|
||||
{
|
||||
wal = tools::wallet2::make_from_json(vm, true, from_json, password_prompt);
|
||||
auto rc = tools::wallet2::make_from_json(vm, true, from_json, password_prompt);
|
||||
wal = std::move(rc.first);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
@ -3457,6 +3513,8 @@ public:
|
||||
std::string const t_executor::NAME = "Wallet RPC Daemon";
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
TRY_ENTRY();
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
const auto arg_wallet_file = wallet_args::arg_wallet_file();
|
||||
@ -3500,4 +3558,5 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
return daemonizer::daemonize(argc, const_cast<const char**>(argv), t_executor{}, *vm) ? 0 : 1;
|
||||
CATCH_ENTRY_L0("main", 1);
|
||||
}
|
||||
|
||||
@ -141,6 +141,7 @@ namespace tools
|
||||
MAP_JON_RPC_WE("export_multisig_info", on_export_multisig, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG)
|
||||
MAP_JON_RPC_WE("import_multisig_info", on_import_multisig, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG)
|
||||
MAP_JON_RPC_WE("finalize_multisig", on_finalize_multisig, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG)
|
||||
MAP_JON_RPC_WE("exchange_multisig_keys", on_exchange_multisig_keys, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS)
|
||||
MAP_JON_RPC_WE("sign_multisig", on_sign_multisig, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG)
|
||||
MAP_JON_RPC_WE("submit_multisig", on_submit_multisig, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG)
|
||||
MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION)
|
||||
@ -218,6 +219,7 @@ namespace tools
|
||||
bool on_export_multisig(const wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
bool on_import_multisig(const wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
bool on_finalize_multisig(const wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
bool on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er);
|
||||
bool on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
bool on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er);
|
||||
bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er);
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
// advance which version they will stop working with
|
||||
// Don't go over 32767 for any of these
|
||||
#define WALLET_RPC_VERSION_MAJOR 1
|
||||
#define WALLET_RPC_VERSION_MINOR 3
|
||||
#define WALLET_RPC_VERSION_MINOR 4
|
||||
#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
|
||||
@ -1399,9 +1399,11 @@ namespace wallet_rpc
|
||||
struct response
|
||||
{
|
||||
transfer_entry transfer;
|
||||
std::list<transfer_entry> transfers;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(transfer);
|
||||
KV_SERIALIZE(transfers);
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
@ -1988,6 +1990,31 @@ namespace wallet_rpc
|
||||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_EXCHANGE_MULTISIG_KEYS
|
||||
{
|
||||
struct request
|
||||
{
|
||||
std::string password;
|
||||
std::vector<std::string> multisig_info;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(password)
|
||||
KV_SERIALIZE(multisig_info)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
struct response
|
||||
{
|
||||
std::string address;
|
||||
std::string multisig_info;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(address)
|
||||
KV_SERIALIZE(multisig_info)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_SIGN_MULTISIG
|
||||
{
|
||||
struct request
|
||||
|
||||
@ -544,6 +544,7 @@ inline bool do_replay_file(const std::string& filename)
|
||||
}
|
||||
return do_replay_events<t_test_class>(events);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#define GENERATE_ACCOUNT(account) \
|
||||
cryptonote::account_base account; \
|
||||
@ -556,47 +557,7 @@ inline bool do_replay_file(const std::string& filename)
|
||||
{ \
|
||||
for (size_t msidx = 0; msidx < total; ++msidx) \
|
||||
account[msidx].generate(); \
|
||||
std::unordered_set<crypto::public_key> all_multisig_keys; \
|
||||
std::vector<std::vector<crypto::secret_key>> view_keys(total); \
|
||||
std::vector<std::vector<crypto::public_key>> spend_keys(total); \
|
||||
for (size_t msidx = 0; msidx < total; ++msidx) \
|
||||
{ \
|
||||
for (size_t msidx_inner = 0; msidx_inner < total; ++msidx_inner) \
|
||||
{ \
|
||||
if (msidx_inner != msidx) \
|
||||
{ \
|
||||
crypto::secret_key vkh = cryptonote::get_multisig_blinded_secret_key(account[msidx_inner].get_keys().m_view_secret_key); \
|
||||
view_keys[msidx].push_back(vkh); \
|
||||
crypto::secret_key skh = cryptonote::get_multisig_blinded_secret_key(account[msidx_inner].get_keys().m_spend_secret_key); \
|
||||
crypto::public_key pskh; \
|
||||
crypto::secret_key_to_public_key(skh, pskh); \
|
||||
spend_keys[msidx].push_back(pskh); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
for (size_t msidx = 0; msidx < total; ++msidx) \
|
||||
{ \
|
||||
std::vector<crypto::secret_key> multisig_keys; \
|
||||
crypto::secret_key spend_skey; \
|
||||
crypto::public_key spend_pkey; \
|
||||
if (threshold == total) \
|
||||
cryptonote::generate_multisig_N_N(account[msidx].get_keys(), spend_keys[msidx], multisig_keys, (rct::key&)spend_skey, (rct::key&)spend_pkey); \
|
||||
else \
|
||||
cryptonote::generate_multisig_N1_N(account[msidx].get_keys(), spend_keys[msidx], multisig_keys, (rct::key&)spend_skey, (rct::key&)spend_pkey); \
|
||||
crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(account[msidx].get_keys().m_view_secret_key, view_keys[msidx]); \
|
||||
account[msidx].make_multisig(view_skey, spend_skey, spend_pkey, multisig_keys); \
|
||||
for (const auto &k: multisig_keys) \
|
||||
all_multisig_keys.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(k)))); \
|
||||
} \
|
||||
if (threshold < total) \
|
||||
{ \
|
||||
std::vector<crypto::public_key> spend_public_keys; \
|
||||
for (const auto &k: all_multisig_keys) \
|
||||
spend_public_keys.push_back(k); \
|
||||
crypto::public_key spend_pkey = cryptonote::generate_multisig_N1_N_spend_public_key(spend_public_keys); \
|
||||
for (size_t msidx = 0; msidx < total; ++msidx) \
|
||||
account[msidx].finalize_multisig(spend_pkey); \
|
||||
} \
|
||||
make_multisig_accounts(account, threshold); \
|
||||
} while(0)
|
||||
|
||||
#define MAKE_ACCOUNT(VEC_EVENTS, account) \
|
||||
|
||||
@ -223,6 +223,16 @@ int main(int argc, char* argv[])
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1__no_threshold);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1_2_no_threshold);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1_3_no_threshold);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_24_1_2);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_24_1_2_many_inputs);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_25_1_2);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_25_1_2_many_inputs);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_234);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_234_many_inputs);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_24_1_no_signers);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_25_1_no_signers);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_no_signers);
|
||||
GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_23_no_threshold);
|
||||
|
||||
GENERATE_AND_PLAY(gen_bp_tx_valid_1);
|
||||
GENERATE_AND_PLAY(gen_bp_tx_invalid_1_1);
|
||||
|
||||
@ -41,6 +41,87 @@ using namespace cryptonote;
|
||||
|
||||
//#define NO_MULTISIG
|
||||
|
||||
void make_multisig_accounts(std::vector<cryptonote::account_base>& account, uint32_t threshold)
|
||||
{
|
||||
std::vector<crypto::secret_key> all_view_keys;
|
||||
std::vector<std::vector<crypto::public_key>> derivations(account.size());
|
||||
//storage for all set of multisig derivations and spend public key (in first round)
|
||||
std::unordered_set<crypto::public_key> exchanging_keys;
|
||||
|
||||
for (size_t msidx = 0; msidx < account.size(); ++msidx)
|
||||
{
|
||||
crypto::secret_key vkh = cryptonote::get_multisig_blinded_secret_key(account[msidx].get_keys().m_view_secret_key);
|
||||
all_view_keys.push_back(vkh);
|
||||
|
||||
crypto::secret_key skh = cryptonote::get_multisig_blinded_secret_key(account[msidx].get_keys().m_spend_secret_key);
|
||||
crypto::public_key pskh;
|
||||
crypto::secret_key_to_public_key(skh, pskh);
|
||||
|
||||
derivations[msidx].push_back(pskh);
|
||||
exchanging_keys.insert(pskh);
|
||||
}
|
||||
|
||||
uint32_t roundsTotal = 1;
|
||||
if (threshold < account.size())
|
||||
roundsTotal = account.size() - threshold;
|
||||
|
||||
//secret multisig keys of every account
|
||||
std::vector<std::vector<crypto::secret_key>> multisig_keys(account.size());
|
||||
std::vector<crypto::secret_key> spend_skey(account.size());
|
||||
std::vector<crypto::public_key> spend_pkey(account.size());
|
||||
for (uint32_t round = 0; round < roundsTotal; ++round)
|
||||
{
|
||||
std::unordered_set<crypto::public_key> roundKeys;
|
||||
for (size_t msidx = 0; msidx < account.size(); ++msidx)
|
||||
{
|
||||
// subtracting one's keys from set of all unique keys is the same as key exchange
|
||||
auto myKeys = exchanging_keys;
|
||||
for (const auto& d: derivations[msidx])
|
||||
myKeys.erase(d);
|
||||
|
||||
if (threshold == account.size())
|
||||
{
|
||||
cryptonote::generate_multisig_N_N(account[msidx].get_keys(), std::vector<crypto::public_key>(myKeys.begin(), myKeys.end()), multisig_keys[msidx], (rct::key&)spend_skey[msidx], (rct::key&)spend_pkey[msidx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
derivations[msidx] = cryptonote::generate_multisig_derivations(account[msidx].get_keys(), std::vector<crypto::public_key>(myKeys.begin(), myKeys.end()));
|
||||
roundKeys.insert(derivations[msidx].begin(), derivations[msidx].end());
|
||||
}
|
||||
}
|
||||
|
||||
exchanging_keys = roundKeys;
|
||||
roundKeys.clear();
|
||||
}
|
||||
|
||||
std::unordered_set<crypto::public_key> all_multisig_keys;
|
||||
for (size_t msidx = 0; msidx < account.size(); ++msidx)
|
||||
{
|
||||
std::unordered_set<crypto::secret_key> view_keys(all_view_keys.begin(), all_view_keys.end());
|
||||
view_keys.erase(all_view_keys[msidx]);
|
||||
|
||||
crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(account[msidx].get_keys().m_view_secret_key, std::vector<secret_key>(view_keys.begin(), view_keys.end()));
|
||||
if (threshold < account.size())
|
||||
{
|
||||
multisig_keys[msidx] = cryptonote::calculate_multisig_keys(derivations[msidx]);
|
||||
spend_skey[msidx] = cryptonote::calculate_multisig_signer_key(multisig_keys[msidx]);
|
||||
}
|
||||
account[msidx].make_multisig(view_skey, spend_skey[msidx], spend_pkey[msidx], multisig_keys[msidx]);
|
||||
for (const auto &k: multisig_keys[msidx]) {
|
||||
all_multisig_keys.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(k))));
|
||||
}
|
||||
}
|
||||
|
||||
if (threshold < account.size())
|
||||
{
|
||||
std::vector<crypto::public_key> public_keys(std::vector<crypto::public_key>(all_multisig_keys.begin(), all_multisig_keys.end()));
|
||||
crypto::public_key spend_pkey = cryptonote::generate_multisig_M_N_spend_public_key(public_keys);
|
||||
|
||||
for (size_t msidx = 0; msidx < account.size(); ++msidx)
|
||||
account[msidx].finalize_multisig(spend_pkey);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// Tests
|
||||
|
||||
@ -55,7 +136,6 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
|
||||
|
||||
CHECK_AND_ASSERT_MES(total >= 2, false, "Bad scheme");
|
||||
CHECK_AND_ASSERT_MES(threshold <= total, false, "Bad scheme");
|
||||
CHECK_AND_ASSERT_MES(threshold >= total - 1, false, "Unsupported scheme");
|
||||
#ifdef NO_MULTISIG
|
||||
CHECK_AND_ASSERT_MES(total <= 5, false, "Unsupported scheme");
|
||||
#endif
|
||||
@ -480,6 +560,48 @@ bool gen_multisig_tx_valid_89_3_1245789::generate(std::vector<test_event_entry>&
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 8, 9, 3, {1, 2, 4, 5, 7, 8, 9}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_24_1_2::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 2, 4, 1, {2}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_24_1_2_many_inputs::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 4, mixin, amount_paid, true, 2, 4, 1, {2}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_25_1_2::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 2, 5, 1, {2}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_25_1_2_many_inputs::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 4, mixin, amount_paid, true, 2, 5, 1, {2}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_48_1_234::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, true, 4, 8, 1, {2, 3, 4}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_48_1_234_many_inputs::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 4, mixin, amount_paid, true, 4, 8, 1, {2, 3, 4}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_invalid_22_1__no_threshold::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
@ -521,3 +643,31 @@ bool gen_multisig_tx_invalid_45_5_23_no_threshold::generate(std::vector<test_eve
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, false, 4, 5, 5, {2, 3}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_24_1_no_signers::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, false, 2, 4, 1, {}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_25_1_no_signers::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, false, 2, 5, 1, {}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_48_1_no_signers::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, false, 4, 8, 1, {}, NULL, NULL);
|
||||
}
|
||||
|
||||
bool gen_multisig_tx_valid_48_1_23_no_threshold::generate(std::vector<test_event_entry>& events) const
|
||||
{
|
||||
const size_t mixin = 4;
|
||||
const uint64_t amount_paid = 10000;
|
||||
return generate_with(events, 2, mixin, amount_paid, false, 4, 8, 1, {2, 3}, NULL, NULL);
|
||||
}
|
||||
|
||||
@ -161,6 +161,42 @@ struct gen_multisig_tx_valid_89_3_1245789: public gen_multisig_tx_validation_bas
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_89_3_1245789>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_24_1_2: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_24_1_2>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_24_1_2_many_inputs: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_24_1_2_many_inputs>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_25_1_2: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_25_1_2>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_25_1_2_many_inputs: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_25_1_2_many_inputs>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_48_1_234: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_48_1_234>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_48_1_234_many_inputs: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_48_1_234_many_inputs>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
// invalid
|
||||
struct gen_multisig_tx_invalid_22_1__no_threshold: public gen_multisig_tx_validation_base
|
||||
{
|
||||
@ -197,3 +233,27 @@ struct gen_multisig_tx_invalid_45_5_23_no_threshold: public gen_multisig_tx_vali
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_invalid_45_5_23_no_threshold>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_24_1_no_signers: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_24_1_no_signers>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_25_1_no_signers: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_25_1_no_signers>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_48_1_no_signers: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_48_1_no_signers>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
struct gen_multisig_tx_valid_48_1_23_no_threshold: public gen_multisig_tx_validation_base
|
||||
{
|
||||
bool generate(std::vector<test_event_entry>& events) const;
|
||||
};
|
||||
template<> struct get_test_options<gen_multisig_tx_valid_48_1_23_no_threshold>: public get_test_options<gen_multisig_tx_validation_base> {};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -26,6 +26,10 @@
|
||||
# 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.
|
||||
|
||||
if(WIN32)
|
||||
set(EXTRA_LIBRARIES "${EXTRA_LIBRARIES};bcrypt")
|
||||
endif()
|
||||
|
||||
set(functional_tests_sources
|
||||
main.cpp
|
||||
transactions_flow_test.cpp
|
||||
|
||||
@ -46,6 +46,8 @@ static int __AFL_LOOP(int)
|
||||
|
||||
int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer)
|
||||
{
|
||||
TRY_ENTRY();
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
std::cout << "usage: " << argv[0] << " " << "<filename>" << std::endl;
|
||||
@ -69,4 +71,6 @@ int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
CATCH_ENTRY_L0("run_fuzzer", 1);
|
||||
}
|
||||
|
||||
@ -75,10 +75,10 @@ int main(int argc, char** argv)
|
||||
const command_line::arg_descriptor<bool> arg_verbose = { "verbose", "Verbose output", false };
|
||||
const command_line::arg_descriptor<bool> arg_stats = { "stats", "Including statistics (min/median)", false };
|
||||
const command_line::arg_descriptor<unsigned> arg_loop_multiplier = { "loop-multiplier", "Run for that many times more loops", 1 };
|
||||
command_line::add_arg(desc_options, arg_filter, "");
|
||||
command_line::add_arg(desc_options, arg_verbose, "");
|
||||
command_line::add_arg(desc_options, arg_stats, "");
|
||||
command_line::add_arg(desc_options, arg_loop_multiplier, "");
|
||||
command_line::add_arg(desc_options, arg_filter);
|
||||
command_line::add_arg(desc_options, arg_verbose);
|
||||
command_line::add_arg(desc_options, arg_stats);
|
||||
command_line::add_arg(desc_options, arg_loop_multiplier);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
|
||||
@ -109,7 +109,7 @@ TEST(AddressFromURL, Failure)
|
||||
{
|
||||
bool dnssec_result = false;
|
||||
|
||||
std::vector<std::string> addresses = tools::dns_utils::addresses_from_url("example.invalid", dnssec_result);
|
||||
std::vector<std::string> addresses = tools::dns_utils::addresses_from_url("example.veryinvalid", dnssec_result);
|
||||
|
||||
// for a non-existing domain such as "example.invalid", the non-existence is proved with NSEC records
|
||||
ASSERT_TRUE(dnssec_result);
|
||||
|
||||
@ -53,6 +53,8 @@ namespace cryptonote { template class t_cryptonote_protocol_handler<cryptonote::
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
TRY_ENTRY();
|
||||
|
||||
tools::on_startup();
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
mlog_configure(mlog_get_default_log_path("unit_tests.log"), true);
|
||||
@ -76,5 +78,7 @@ int main(int argc, char** argv)
|
||||
|
||||
unit_test::data_dir = command_line::get_arg(vm, arg_data_dir);
|
||||
|
||||
CATCH_ENTRY_L0("main", 1);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@ -46,14 +46,14 @@ TEST(mlocker, distinct_1)
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[8 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data), 1);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, 1);
|
||||
epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, 1);
|
||||
std::shared_ptr<epee::mlocker> m0{new epee::mlocker(BASE(data), 1)};
|
||||
std::shared_ptr<epee::mlocker> m1{new epee::mlocker(BASE(data) + 2 * page_size, 1)};
|
||||
std::shared_ptr<epee::mlocker> m2{new epee::mlocker(BASE(data) + 3 * page_size, 1)};
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
|
||||
delete m0;
|
||||
delete m1;
|
||||
delete m2;
|
||||
m0 = NULL;
|
||||
m1 = NULL;
|
||||
m2 = NULL;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
@ -65,14 +65,14 @@ TEST(mlocker, distinct_full_page)
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[8 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data), page_size);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, page_size);
|
||||
epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, page_size);
|
||||
std::shared_ptr<epee::mlocker> m0{new epee::mlocker(BASE(data), page_size)};
|
||||
std::shared_ptr<epee::mlocker> m1{new epee::mlocker(BASE(data) + 2 * page_size, page_size)};
|
||||
std::shared_ptr<epee::mlocker> m2{new epee::mlocker(BASE(data) + 3 * page_size, page_size)};
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
|
||||
delete m0;
|
||||
delete m1;
|
||||
delete m2;
|
||||
m0 = NULL;
|
||||
m1 = NULL;
|
||||
m2 = NULL;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
@ -84,16 +84,16 @@ TEST(mlocker, identical)
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[8 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, 32);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size, 32);
|
||||
epee::mlocker *m2 = new epee::mlocker(BASE(data) + page_size, 32);
|
||||
std::shared_ptr<epee::mlocker> m0{new epee::mlocker(BASE(data) + page_size, 32)};
|
||||
std::shared_ptr<epee::mlocker> m1{new epee::mlocker(BASE(data) + page_size, 32)};
|
||||
std::shared_ptr<epee::mlocker> m2{new epee::mlocker(BASE(data) + page_size, 32)};
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
|
||||
delete m1;
|
||||
m1 = NULL;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
|
||||
delete m0;
|
||||
delete m2;
|
||||
m0 = NULL;
|
||||
m2 = NULL;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
@ -105,16 +105,16 @@ TEST(mlocker, overlapping_small)
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[8 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data), 32);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data) + 16, 32);
|
||||
epee::mlocker *m2 = new epee::mlocker(BASE(data) + 8, 32);
|
||||
std::shared_ptr<epee::mlocker> m0{new epee::mlocker(BASE(data), 32)};
|
||||
std::shared_ptr<epee::mlocker> m1{new epee::mlocker(BASE(data) + 16, 32)};
|
||||
std::shared_ptr<epee::mlocker> m2{new epee::mlocker(BASE(data) + 8, 32)};
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3);
|
||||
delete m1;
|
||||
m1 = NULL;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
|
||||
delete m2;
|
||||
delete m0;
|
||||
m2 = NULL;
|
||||
m0 = NULL;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
@ -126,16 +126,16 @@ TEST(mlocker, multi_page)
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[8 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, page_size * 3);
|
||||
std::shared_ptr<epee::mlocker> m0{new epee::mlocker(BASE(data) + page_size, page_size * 3)};
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size * 7, page_size);
|
||||
std::shared_ptr<epee::mlocker> m1{new epee::mlocker(BASE(data) + page_size * 7, page_size)};
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 4);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
|
||||
delete m0;
|
||||
m0 = NULL;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
|
||||
delete m1;
|
||||
m1 = NULL;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
@ -147,10 +147,10 @@ TEST(mlocker, cross_page)
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[2 * page_size]};
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size - 1, 2);
|
||||
std::shared_ptr<epee::mlocker> m0{new epee::mlocker(BASE(data) + page_size - 1, 2)};
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 2);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
|
||||
delete m0;
|
||||
m0 = NULL;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
@ -158,21 +158,22 @@ TEST(mlocker, cross_page)
|
||||
TEST(mlocker, redundant)
|
||||
{
|
||||
const size_t page_size = epee::mlocker::get_page_size();
|
||||
ASSERT_TRUE(page_size > 0);
|
||||
const size_t base_pages = epee::mlocker::get_num_locked_pages();
|
||||
const size_t base_objects = epee::mlocker::get_num_locked_objects();
|
||||
std::unique_ptr<char[]> data{new char[2 * page_size]};
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
epee::mlocker *m0 = new epee::mlocker(BASE(data), 32);
|
||||
std::shared_ptr<epee::mlocker> m0{new epee::mlocker(BASE(data), 32)};
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
|
||||
epee::mlocker *m1 = new epee::mlocker(BASE(data), 32);
|
||||
std::shared_ptr<epee::mlocker> m1{new epee::mlocker(BASE(data), 32)};
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2);
|
||||
delete m1;
|
||||
m1 = NULL;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1);
|
||||
delete m0;
|
||||
m0 = NULL;
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0);
|
||||
ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0);
|
||||
}
|
||||
|
||||
@ -49,9 +49,19 @@ static const struct
|
||||
{
|
||||
"9t6Hn946u3eah5cuncH1hB5hGzsTUoevtf4SY7MHN5NgJZh2SFWsyVt3vUhuHyRKyrCQvr71Lfc1AevG3BXE11PQFoXDtD8",
|
||||
"bbd3175ef9fd9f5eefdc43035f882f74ad14c4cf1799d8b6f9001bc197175d02"
|
||||
},
|
||||
{
|
||||
"9zmAWoNyNPbgnYSm3nJNpAKHm6fCcs3MR94gBWxp9MCDUiMUhyYFfyQETUDLPF7DP6ZsmNo6LRxwPP9VmhHNxKrER9oGigT",
|
||||
"f2efae45bef1917a7430cda8fcffc4ee010e3178761aa41d4628e23b1fe2d501"
|
||||
},
|
||||
{
|
||||
"9ue8NJMg3WzKxTtmjeXzWYF5KmU6dC7LHEt9wvYdPn2qMmoFUa8hJJHhSHvJ46UEwpDyy5jSboNMRaDBKwU54NT42YcNUp5",
|
||||
"a4cef54ed3fd61cd78a2ceb82ecf85a903ad2db9a86fb77ff56c35c56016280a"
|
||||
}
|
||||
};
|
||||
|
||||
static const size_t KEYS_COUNT = 5;
|
||||
|
||||
static void make_wallet(unsigned int idx, tools::wallet2 &wallet)
|
||||
{
|
||||
ASSERT_TRUE(idx < sizeof(test_addresses) / sizeof(test_addresses[0]));
|
||||
@ -76,126 +86,87 @@ static void make_wallet(unsigned int idx, tools::wallet2 &wallet)
|
||||
}
|
||||
}
|
||||
|
||||
static void make_M_2_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, unsigned int M)
|
||||
static std::vector<std::string> exchange_round(std::vector<tools::wallet2>& wallets, const std::vector<std::string>& mis)
|
||||
{
|
||||
ASSERT_TRUE(M <= 2);
|
||||
|
||||
make_wallet(0, wallet0);
|
||||
make_wallet(1, wallet1);
|
||||
|
||||
std::vector<crypto::secret_key> sk0(1), sk1(1);
|
||||
std::vector<crypto::public_key> pk0(1), pk1(1);
|
||||
|
||||
wallet0.decrypt_keys("");
|
||||
std::string mi0 = wallet0.get_multisig_info();
|
||||
wallet0.encrypt_keys("");
|
||||
wallet1.decrypt_keys("");
|
||||
std::string mi1 = wallet1.get_multisig_info();
|
||||
wallet1.encrypt_keys("");
|
||||
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0]));
|
||||
|
||||
ASSERT_FALSE(wallet0.multisig() || wallet1.multisig());
|
||||
wallet0.make_multisig("", sk0, pk0, M);
|
||||
wallet1.make_multisig("", sk1, pk1, M);
|
||||
|
||||
ASSERT_TRUE(wallet0.get_account().get_public_address_str(cryptonote::TESTNET) == wallet1.get_account().get_public_address_str(cryptonote::TESTNET));
|
||||
|
||||
bool ready;
|
||||
uint32_t threshold, total;
|
||||
ASSERT_TRUE(wallet0.multisig(&ready, &threshold, &total));
|
||||
ASSERT_TRUE(ready);
|
||||
ASSERT_TRUE(threshold == M);
|
||||
ASSERT_TRUE(total == 2);
|
||||
ASSERT_TRUE(wallet1.multisig(&ready, &threshold, &total));
|
||||
ASSERT_TRUE(ready);
|
||||
ASSERT_TRUE(threshold == M);
|
||||
ASSERT_TRUE(total == 2);
|
||||
}
|
||||
|
||||
static void make_M_3_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, tools::wallet2 &wallet2, unsigned int M)
|
||||
{
|
||||
ASSERT_TRUE(M <= 3);
|
||||
|
||||
make_wallet(0, wallet0);
|
||||
make_wallet(1, wallet1);
|
||||
make_wallet(2, wallet2);
|
||||
|
||||
std::vector<crypto::secret_key> sk0(2), sk1(2), sk2(2);
|
||||
std::vector<crypto::public_key> pk0(2), pk1(2), pk2(2);
|
||||
|
||||
wallet0.decrypt_keys("");
|
||||
std::string mi0 = wallet0.get_multisig_info();
|
||||
wallet0.encrypt_keys("");
|
||||
wallet1.decrypt_keys("");
|
||||
std::string mi1 = wallet1.get_multisig_info();
|
||||
wallet1.encrypt_keys("");
|
||||
wallet2.decrypt_keys("");
|
||||
std::string mi2 = wallet2.get_multisig_info();
|
||||
wallet2.encrypt_keys("");
|
||||
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk0[1], pk0[1]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk1[1], pk1[1]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk2[0], pk2[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk2[1], pk2[1]));
|
||||
|
||||
ASSERT_FALSE(wallet0.multisig() || wallet1.multisig() || wallet2.multisig());
|
||||
std::string mxi0 = wallet0.make_multisig("", sk0, pk0, M);
|
||||
std::string mxi1 = wallet1.make_multisig("", sk1, pk1, M);
|
||||
std::string mxi2 = wallet2.make_multisig("", sk2, pk2, M);
|
||||
|
||||
const size_t nset = !mxi0.empty() + !mxi1.empty() + !mxi2.empty();
|
||||
ASSERT_TRUE((M < 3 && nset == 3) || (M == 3 && nset == 0));
|
||||
|
||||
if (nset > 0)
|
||||
{
|
||||
std::unordered_set<crypto::public_key> pkeys;
|
||||
std::vector<crypto::public_key> signers(3, crypto::null_pkey);
|
||||
ASSERT_TRUE(tools::wallet2::verify_extra_multisig_info(mxi0, pkeys, signers[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_extra_multisig_info(mxi1, pkeys, signers[1]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_extra_multisig_info(mxi2, pkeys, signers[2]));
|
||||
ASSERT_TRUE(pkeys.size() == 3);
|
||||
ASSERT_TRUE(wallet0.finalize_multisig("", pkeys, signers));
|
||||
ASSERT_TRUE(wallet1.finalize_multisig("", pkeys, signers));
|
||||
ASSERT_TRUE(wallet2.finalize_multisig("", pkeys, signers));
|
||||
std::vector<std::string> new_infos;
|
||||
for (size_t i = 0; i < wallets.size(); ++i) {
|
||||
new_infos.push_back(wallets[i].exchange_multisig_keys("", mis));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(wallet0.get_account().get_public_address_str(cryptonote::TESTNET) == wallet1.get_account().get_public_address_str(cryptonote::TESTNET));
|
||||
ASSERT_TRUE(wallet0.get_account().get_public_address_str(cryptonote::TESTNET) == wallet2.get_account().get_public_address_str(cryptonote::TESTNET));
|
||||
return new_infos;
|
||||
}
|
||||
|
||||
bool ready;
|
||||
uint32_t threshold, total;
|
||||
ASSERT_TRUE(wallet0.multisig(&ready, &threshold, &total));
|
||||
ASSERT_TRUE(ready);
|
||||
ASSERT_TRUE(threshold == M);
|
||||
ASSERT_TRUE(total == 3);
|
||||
ASSERT_TRUE(wallet1.multisig(&ready, &threshold, &total));
|
||||
ASSERT_TRUE(ready);
|
||||
ASSERT_TRUE(threshold == M);
|
||||
ASSERT_TRUE(total == 3);
|
||||
ASSERT_TRUE(wallet2.multisig(&ready, &threshold, &total));
|
||||
ASSERT_TRUE(ready);
|
||||
ASSERT_TRUE(threshold == M);
|
||||
ASSERT_TRUE(total == 3);
|
||||
static void make_wallets(std::vector<tools::wallet2>& wallets, unsigned int M)
|
||||
{
|
||||
ASSERT_TRUE(wallets.size() > 1 && wallets.size() <= KEYS_COUNT);
|
||||
ASSERT_TRUE(M <= wallets.size());
|
||||
|
||||
std::vector<std::string> mis(wallets.size());
|
||||
|
||||
for (size_t i = 0; i < wallets.size(); ++i) {
|
||||
make_wallet(i, wallets[i]);
|
||||
|
||||
wallets[i].decrypt_keys("");
|
||||
mis[i] = wallets[i].get_multisig_info();
|
||||
wallets[i].encrypt_keys("");
|
||||
}
|
||||
|
||||
for (auto& wallet: wallets) {
|
||||
ASSERT_FALSE(wallet.multisig() || wallet.multisig() || wallet.multisig());
|
||||
}
|
||||
|
||||
std::vector<std::string> mxis;
|
||||
for (size_t i = 0; i < wallets.size(); ++i) {
|
||||
// it's ok to put all of multisig keys in this function. it throws in case of error
|
||||
mxis.push_back(wallets[i].make_multisig("", mis, M));
|
||||
}
|
||||
|
||||
while (!mxis[0].empty()) {
|
||||
mxis = exchange_round(wallets, mxis);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < wallets.size(); ++i) {
|
||||
ASSERT_TRUE(mxis[i].empty());
|
||||
bool ready;
|
||||
uint32_t threshold, total;
|
||||
ASSERT_TRUE(wallets[i].multisig(&ready, &threshold, &total));
|
||||
ASSERT_TRUE(ready);
|
||||
ASSERT_TRUE(threshold == M);
|
||||
ASSERT_TRUE(total == wallets.size());
|
||||
|
||||
if (i != 0) {
|
||||
// "equals" is transitive relation so we need only to compare first wallet's address to each others' addresses. no need to compare 0's address with itself.
|
||||
ASSERT_TRUE(wallets[0].get_account().get_public_address_str(cryptonote::TESTNET) == wallets[i].get_account().get_public_address_str(cryptonote::TESTNET));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(multisig, make_2_2)
|
||||
{
|
||||
tools::wallet2 wallet0, wallet1;
|
||||
make_M_2_wallet(wallet0, wallet1, 2);
|
||||
std::vector<tools::wallet2> wallets(2);
|
||||
make_wallets(wallets, 2);
|
||||
}
|
||||
|
||||
TEST(multisig, make_3_3)
|
||||
{
|
||||
tools::wallet2 wallet0, wallet1, wallet2;
|
||||
make_M_3_wallet(wallet0, wallet1, wallet2, 3);
|
||||
std::vector<tools::wallet2> wallets(3);
|
||||
make_wallets(wallets, 3);
|
||||
}
|
||||
|
||||
TEST(multisig, make_2_3)
|
||||
{
|
||||
tools::wallet2 wallet0, wallet1, wallet2;
|
||||
make_M_3_wallet(wallet0, wallet1, wallet2, 2);
|
||||
std::vector<tools::wallet2> wallets(3);
|
||||
make_wallets(wallets, 2);
|
||||
}
|
||||
|
||||
TEST(multisig, make_2_4)
|
||||
{
|
||||
std::vector<tools::wallet2> wallets(4);
|
||||
make_wallets(wallets, 2);
|
||||
}
|
||||
|
||||
TEST(multisig, make_2_5)
|
||||
{
|
||||
std::vector<tools::wallet2> wallets(5);
|
||||
make_wallets(wallets, 2);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -139,4 +139,4 @@ RUN cd /src \
|
||||
CMAKE_LIBRARY_PATH="${PREFIX}/lib" \
|
||||
ANDROID_STANDALONE_TOOLCHAIN_PATH=${TOOLCHAIN_DIR} \
|
||||
USE_SINGLE_BUILDDIR=1 \
|
||||
PATH=${HOST_PATH} make release-static-android -j${NPROC}
|
||||
PATH=${HOST_PATH} make release-static-android-armv7 -j${NPROC}
|
||||
|
||||
142
utils/build_scripts/android64.Dockerfile
Normal file
142
utils/build_scripts/android64.Dockerfile
Normal file
@ -0,0 +1,142 @@
|
||||
FROM debian:stable
|
||||
|
||||
RUN apt-get update && apt-get install -y unzip automake build-essential curl file pkg-config git python libtool
|
||||
|
||||
WORKDIR /opt/android
|
||||
## INSTALL ANDROID SDK
|
||||
ENV ANDROID_SDK_REVISION 4333796
|
||||
ENV ANDROID_SDK_HASH 92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9
|
||||
RUN curl -s -O https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_REVISION}.zip \
|
||||
&& echo "${ANDROID_SDK_HASH} sdk-tools-linux-${ANDROID_SDK_REVISION}.zip" | sha256sum -c \
|
||||
&& unzip sdk-tools-linux-${ANDROID_SDK_REVISION}.zip \
|
||||
&& rm -f sdk-tools-linux-${ANDROID_SDK_REVISION}.zip
|
||||
|
||||
## INSTALL ANDROID NDK
|
||||
ENV ANDROID_NDK_REVISION 17b
|
||||
ENV ANDROID_NDK_HASH 5dfbbdc2d3ba859fed90d0e978af87c71a91a5be1f6e1c40ba697503d48ccecd
|
||||
RUN curl -s -O https://dl.google.com/android/repository/android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip \
|
||||
&& echo "${ANDROID_NDK_HASH} android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip" | sha256sum -c \
|
||||
&& unzip android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip \
|
||||
&& rm -f android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip
|
||||
|
||||
ENV WORKDIR /opt/android
|
||||
ENV ANDROID_SDK_ROOT ${WORKDIR}/tools
|
||||
ENV ANDROID_NDK_ROOT ${WORKDIR}/android-ndk-r${ANDROID_NDK_REVISION}
|
||||
ENV PREFIX /opt/android/prefix
|
||||
|
||||
ENV TOOLCHAIN_DIR ${WORKDIR}/toolchain-arm
|
||||
RUN ${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py \
|
||||
--arch arm64 \
|
||||
--api 21 \
|
||||
--install-dir ${TOOLCHAIN_DIR} \
|
||||
--stl=libc++
|
||||
|
||||
#INSTALL cmake
|
||||
ENV CMAKE_VERSION 3.12.1
|
||||
RUN cd /usr \
|
||||
&& curl -s -O https://cmake.org/files/v3.12/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \
|
||||
&& tar -xzf /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \
|
||||
&& rm -f /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz
|
||||
ENV PATH /usr/cmake-${CMAKE_VERSION}-Linux-x86_64/bin:$PATH
|
||||
|
||||
## Boost
|
||||
ARG BOOST_VERSION=1_68_0
|
||||
ARG BOOST_VERSION_DOT=1.68.0
|
||||
ARG BOOST_HASH=7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7
|
||||
RUN set -ex \
|
||||
&& curl -s -L -o boost_${BOOST_VERSION}.tar.bz2 https://dl.bintray.com/boostorg/release/${BOOST_VERSION_DOT}/source/boost_${BOOST_VERSION}.tar.bz2 \
|
||||
&& echo "${BOOST_HASH} boost_${BOOST_VERSION}.tar.bz2" | sha256sum -c \
|
||||
&& tar -xvf boost_${BOOST_VERSION}.tar.bz2 \
|
||||
&& rm -f boost_${BOOST_VERSION}.tar.bz2 \
|
||||
&& cd boost_${BOOST_VERSION} \
|
||||
&& ./bootstrap.sh --prefix=${PREFIX}
|
||||
|
||||
ENV HOST_PATH $PATH
|
||||
ENV PATH $TOOLCHAIN_DIR/aarch64-linux-android/bin:$TOOLCHAIN_DIR/bin:$PATH
|
||||
|
||||
ARG NPROC=1
|
||||
|
||||
# Build iconv for lib boost locale
|
||||
ENV ICONV_VERSION 1.15
|
||||
ENV ICONV_HASH ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
|
||||
RUN curl -s -O http://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar.gz \
|
||||
&& echo "${ICONV_HASH} libiconv-${ICONV_VERSION}.tar.gz" | sha256sum -c \
|
||||
&& tar -xzf libiconv-${ICONV_VERSION}.tar.gz \
|
||||
&& rm -f libiconv-${ICONV_VERSION}.tar.gz \
|
||||
&& cd libiconv-${ICONV_VERSION} \
|
||||
&& CC=aarch64-linux-android-clang CXX=aarch64-linux-android-clang++ ./configure --build=x86_64-linux-gnu --host=arm-eabi --prefix=${PREFIX} --disable-rpath \
|
||||
&& make -j${NPROC} && make install
|
||||
|
||||
## Build BOOST
|
||||
RUN cd boost_${BOOST_VERSION} \
|
||||
&& ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --with-locale --build-dir=android --stagedir=android toolset=clang threading=multi threadapi=pthread target-os=android -sICONV_PATH=${PREFIX} install -j${NPROC}
|
||||
|
||||
#Note : we build openssl because the default lacks DSA1
|
||||
|
||||
# download, configure and make Zlib
|
||||
ENV ZLIB_VERSION 1.2.11
|
||||
ENV ZLIB_HASH c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
RUN curl -s -O https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \
|
||||
&& echo "${ZLIB_HASH} zlib-${ZLIB_VERSION}.tar.gz" | sha256sum -c \
|
||||
&& tar -xzf zlib-${ZLIB_VERSION}.tar.gz \
|
||||
&& rm zlib-${ZLIB_VERSION}.tar.gz \
|
||||
&& mv zlib-${ZLIB_VERSION} zlib \
|
||||
&& cd zlib && CC=clang CXX=clang++ ./configure --static \
|
||||
&& make -j${NPROC}
|
||||
|
||||
# open ssl
|
||||
ARG OPENSSL_VERSION=1.0.2p
|
||||
ARG OPENSSL_HASH=50a98e07b1a89eb8f6a99477f262df71c6fa7bef77df4dc83025a2845c827d00
|
||||
RUN curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \
|
||||
&& echo "${OPENSSL_HASH} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c \
|
||||
&& tar -xzf openssl-${OPENSSL_VERSION}.tar.gz \
|
||||
&& rm openssl-${OPENSSL_VERSION}.tar.gz \
|
||||
&& cd openssl-${OPENSSL_VERSION} \
|
||||
&& sed -i -e "s/mandroid/target\ aarch64\-linux\-android/" Configure \
|
||||
&& CC=clang CXX=clang++ \
|
||||
./Configure android \
|
||||
no-asm \
|
||||
no-shared --static \
|
||||
--with-zlib-include=${WORKDIR}/zlib/include --with-zlib-lib=${WORKDIR}/zlib/lib \
|
||||
--prefix=${PREFIX} --openssldir=${PREFIX} \
|
||||
&& make -j${NPROC} \
|
||||
&& make install
|
||||
|
||||
# ZMQ
|
||||
ARG ZMQ_VERSION=master
|
||||
ARG ZMQ_HASH=501d0815bf2b0abb93be8214fc66519918ef6c40
|
||||
RUN git clone https://github.com/zeromq/libzmq.git -b ${ZMQ_VERSION} \
|
||||
&& cd libzmq \
|
||||
&& git checkout ${ZMQ_HASH} \
|
||||
&& ./autogen.sh \
|
||||
&& CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared \
|
||||
&& make -j${NPROC} \
|
||||
&& make install
|
||||
|
||||
# zmq.hpp
|
||||
ARG CPPZMQ_VERSION=v4.2.3
|
||||
ARG CPPZMQ_HASH=6aa3ab686e916cb0e62df7fa7d12e0b13ae9fae6
|
||||
RUN git clone https://github.com/zeromq/cppzmq.git -b ${CPPZMQ_VERSION} \
|
||||
&& cd cppzmq \
|
||||
&& test `git rev-parse HEAD` = ${CPPZMQ_HASH} || exit 1 \
|
||||
&& cp *.hpp ${PREFIX}/include
|
||||
|
||||
# Sodium
|
||||
ARG SODIUM_VERSION=1.0.16
|
||||
ARG SODIUM_HASH=675149b9b8b66ff44152553fb3ebf9858128363d
|
||||
RUN set -ex \
|
||||
&& git clone https://github.com/jedisct1/libsodium.git -b ${SODIUM_VERSION} \
|
||||
&& cd libsodium \
|
||||
&& test `git rev-parse HEAD` = ${SODIUM_HASH} || exit 1 \
|
||||
&& ./autogen.sh \
|
||||
&& CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared \
|
||||
&& make -j${NPROC} \
|
||||
&& make install
|
||||
|
||||
ADD . /src
|
||||
RUN cd /src \
|
||||
&& CMAKE_INCLUDE_PATH="${PREFIX}/include" \
|
||||
CMAKE_LIBRARY_PATH="${PREFIX}/lib" \
|
||||
ANDROID_STANDALONE_TOOLCHAIN_PATH=${TOOLCHAIN_DIR} \
|
||||
USE_SINGLE_BUILDDIR=1 \
|
||||
PATH=${HOST_PATH} make release-static-android-armv8 -j${NPROC}
|
||||
Loading…
x
Reference in New Issue
Block a user