Compare commits

..

68 Commits

Author SHA1 Message Date
acx
48845d06f7 feat: add method for fetching both clockhain and target height 2025-12-11 01:50:33 +00:00
anoncontributorxmr
27baf4e4ba update polyseed 2025-12-11 01:50:27 +00:00
-
2c48a261ba add bool last param to newBlock callback 2025-12-11 01:50:20 +00:00
-
a33a0c328f enotes changes 2025-12-11 01:50:09 +00:00
anoncontributorxmr
42a4f0975d fix typo 2025-12-11 01:49:32 +00:00
-
bb58b931d3 chore: remove MONERUJO_HIDAPI 2025-12-11 01:49:11 +00:00
ANONERO
a29b709408 Android Makefile 2025-12-11 01:48:47 +00:00
Czarek Nakamoto
685c11b019 PATCH: coin control 2025-12-11 01:48:16 +00:00
Czarek Nakamoto
56dc72f9e1 PATCH: polyseed 2025-12-11 01:47:41 +00:00
j-berman
cfd2a69624 wallet2: warn instead of throw when RingDB doesn't include spend
A reorg can end up causing an output's position in the chain to
move. Since the wallet doesn't update the RingDB on reorg, it
may refer to the output's stale position in the chain.

This seems a reasonable solution rather than introducing complex
logic to update the stale ring member's value on rerog, since
RingDB can be deprecated with FCMP++.
2025-10-10 08:49:03 +03:00
jeffro256
2e1fdd9f48 cryptonote_basic: remove redundant call to get_transaction_hash() in overload
Issue noticed by DataHoarder.
2025-10-10 08:48:06 +03:00
j-berman
48e34c2336 Cleaner validation (faster and saner) 2025-10-10 08:47:44 +03:00
j-berman
4bfb5acd8f Daemon RPC: fix on_getblockhash error return on too high height 2025-10-10 08:47:20 +03:00
j-berman
28e8844d3d Daemon RPC: add max_block_count field to /getblocks.bin 2025-09-29 06:25:45 +03:00
nahuhh
4f10510f68 simplewallet: batch address creation limit to match rpc 2025-09-29 06:25:14 +03:00
WeebDataHoarder
7a5cc4afb9 Send ZMQ miner notifications after txpool additions
Bug was introduced in c069c04ede338929c50297558fee15192aa0f67c, before this txpool additions were not notified on block addition

When receiving blocks with previously unknown conditions, miner data was sent first, but txpool add events for already-added transactions in previous block were sent afterward. Miners would then include already-mined transactions in their new templates due to receiving the mistimed txpool add event.

The fix is to send miner notifications AFTER txpool events are sent, and before normal block notifications are sent (for mining switch speed purposes)

Fixes c069c04ede338929c50297558fee15192aa0f67c / #9135
Fixes dfee15eee1 / #7891
2025-09-29 06:24:51 +03:00
rbrunner7
95c1fd7679 p2p: Improved peer selection with /24 subnet deduplication to disadvantage 'spy nodes' [v0.18] 2025-09-29 06:23:55 +03:00
Lee *!* Clagett
b9fdf9c288 Fix logging lock, future optimizations may needed 2025-09-25 11:24:48 +03:00
nahuhh
5b0bed2950 cryptonote_core: --dns-versions-check is deprecated 2025-09-25 11:24:14 +03:00
moneromooo-monero
77ba1f2c6a wallet_rpc_server: allow creating more than 64 addresses at once
it's too low a limit (at least one person mentioned having to
call create_address in a loop due to it)
2025-09-25 11:23:37 +03:00
wowario
c98cfd8f2f remove github actions 2025-09-20 07:55:00 +03:00
wowario
1a94126ae4 update checkpoints 2025-09-19 21:01:46 +03:00
wowario
5049ad3d6f v0.11 ASCII art 2025-09-19 21:01:45 +03:00
wowario
9f429cfc89 wownero skin pack 2025-09-19 21:01:45 +03:00
wowario
4a80a4a693 add checkpoints and fork heights 2025-09-19 21:01:45 +03:00
wowario
a65fd1b3e1 change to debug level 2025-09-19 21:01:44 +03:00
wowario
e0f486f7b2 add clear screen command 2025-09-19 21:01:44 +03:00
wowario
34bc7d2b63 show full version 2025-09-19 21:01:44 +03:00
wowario
de22caa968 cleanup old bp 2025-09-19 21:01:43 +03:00
wowario
f7299beb66 vote by block 2025-09-19 21:01:43 +03:00
wowario
88e126db9c miner block header signing 2025-09-19 21:01:43 +03:00
wowario
2c544dee74 add nettype to diff algo 2025-09-19 21:01:43 +03:00
wowario
8077b9473a difficulty is fun 2025-09-19 21:01:42 +03:00
wowario
9c11ef2292 only allocate slow hash before RX 2025-09-19 21:01:42 +03:00
wowario
ed340c208e mod variant4_random_math 2025-09-19 21:01:42 +03:00
wowario
b7fb278873 set pow variants 2025-09-19 21:01:41 +03:00
wowario
945e4c9644 bump RX block version 2025-09-19 21:01:41 +03:00
wowario
e60f0dc3da add RandomWOW 2025-09-19 21:01:41 +03:00
wowario
b7fb563f83 shorten timestamp check window 2025-09-19 21:01:41 +03:00
wowario
bf12e2745d limit future blk time to 10 min 2025-09-19 21:01:40 +03:00
wowario
30dba37f1f bump unlock time to 288 blks 2025-09-19 21:01:40 +03:00
wowario
076254eb38 config wallet2 settings 2025-09-19 21:01:40 +03:00
wowario
8a44f2f375 add wowario gpg key 2025-09-19 21:01:39 +03:00
wowario
17cdf02cae update gitian 2025-09-19 21:01:39 +03:00
wowario
cf58f70251 fix unit tests 2025-09-19 21:01:39 +03:00
wowario
1f110b4dc0 automatic submodule update 2025-09-19 21:01:39 +03:00
wowario
220c474ac1 fix mismatched daemon check bug 2025-09-19 21:01:38 +03:00
wowario
52da6f97f5 update average block sizes table 2025-09-19 21:01:38 +03:00
wowario
77e80a6416 set decimal point 2025-09-19 21:01:38 +03:00
wowario
4f2e8f603f add seeds 2025-09-19 21:01:37 +03:00
wowario
e38bf823fd adjust approx_blockchain_height 2025-09-19 21:01:37 +03:00
wowario
72cf07bba0 set last v1 block 2025-09-19 21:01:37 +03:00
wowario
e8e42507ad set quick height for syncing 2025-09-19 21:01:36 +03:00
wowario
8851bf7d14 set genesis block timestamp 2025-09-19 21:01:36 +03:00
wowario
09b891cad4 correct length of addresses 2025-09-19 21:01:36 +03:00
wowario
f3631823a8 bump ring size to 22 2025-09-19 21:01:36 +03:00
wowario
8765ee2d4a add release-minimal to Makefile 2025-09-19 21:01:35 +03:00
wowario
5e73899cb8 add wow readme 2025-09-19 21:01:28 +03:00
wowario
6cffebac15 initialize genesis block 2025-09-19 07:06:29 +03:00
wowario
6e0b31d344 config cryptonote 2025-09-19 07:06:28 +03:00
wowario
46507bf22e del seeds 2025-09-19 07:06:28 +03:00
wowario
b2dc3f3e33 del testnet/stagenet blocks 2025-09-19 07:06:20 +03:00
wowario
866dbaaeca del checkpoints and fork heights 2025-09-19 07:06:20 +03:00
wowario
5a7a993f99 del README.md 2025-09-19 07:06:19 +03:00
wowario
af0a0978a1 del seed ips 2025-09-19 07:06:12 +03:00
wowario
fe5ed3fc69 del dns ips 2025-09-19 07:06:03 +03:00
wowario
f666f1074a del moneropulse urls 2025-09-19 07:05:54 +03:00
wowario
189b0d17fc del monero tx bug fixes 2025-09-19 07:05:44 +03:00
55 changed files with 1235 additions and 796 deletions

View File

@@ -1,327 +0,0 @@
name: Build Wownero Core (Libevent FailSafe)
on:
push:
branches: [ master, main ]
tags: [ 'v*' ]
workflow_dispatch:
env:
NODE_TLS_REJECT_UNAUTHORIZED: '0'
jobs:
build-all:
runs-on: ubuntu-latest
container:
image: wownero-builder-base:latest
strategy:
fail-fast: false
matrix:
target:
- x86_64-linux-gnu # Linux x64
- x86_64-w64-mingw32 # Windows x64
- aarch64-linux-gnu # Linux ARM64
- riscv64-linux-gnu # Linux RISC-V
- x86_64-apple-darwin11 # macOS Intel
- aarch64-apple-darwin11 # macOS ARM
steps:
- name: Fix DNS
run: echo "192.168.88.230 git.such.software" >> /etc/hosts
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
token: ${{ secrets.GITHUB_TOKEN }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Configure Git URLs
run: |
git config --global --add safe.directory '*'
git config --global url."http://oauth2:${GITHUB_TOKEN}@git.such.software:3000/Builds/RandomWOW.git".insteadOf "https://codeberg.org/wownero/RandomWOW"
git config --global url."http://oauth2:${GITHUB_TOKEN}@git.such.software:3000/Builds/wownero.git".insteadOf "https://codeberg.org/wownero/wownero.git"
- name: Download macOS SDK
if: contains(matrix.target, 'apple')
run: |
mkdir -p contrib/depends/SDKs
curl -L -k -o contrib/depends/SDKs/MacOSX10.15.sdk.tar.gz \
"https://github.com/phracker/MacOSX-SDKs/releases/download/10.15/MacOSX10.15.sdk.tar.gz"
- name: Install Modern CMake
run: |
curl -L -o cmake.tar.gz https://github.com/Kitware/CMake/releases/download/v3.28.1/cmake-3.28.1-linux-x86_64.tar.gz
tar -xf cmake.tar.gz
echo "$(pwd)/cmake-3.28.1-linux-x86_64/bin" >> $GITHUB_PATH
# -----------------------------------------------------------------------
# 1. BUILD CONFIGURATION
# -----------------------------------------------------------------------
- name: Sanitize Makefiles (Stable + Mac Compat)
run: |
curl -L -o contrib/depends/funcs.mk https://codeberg.org/wownero/wownero/raw/branch/master/contrib/depends/funcs.mk
curl -L -o contrib/depends/packages/packages.mk https://codeberg.org/wownero/wownero/raw/branch/master/contrib/depends/packages/packages.mk
# Inject packages: libevent added before unbound
sed -i 's/^packages :=.*/packages := boost openssl expat libusb hidapi protobuf libiconv sodium zeromq libevent unbound zlib zstd/' contrib/depends/packages/packages.mk
# GENERATE LIBEVENT.MK (Attempt 1: Makefiles)
cat <<'EOF' > contrib/depends/packages/libevent.mk
package=libevent
$(package)_version=2.1.12-stable
$(package)_download_path=https://github.com/libevent/libevent/releases/download/release-$($(package)_version)/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=92e6de1be9ec176428fd2367677e61ecedc2fc1cb11bc61069461ea910c1d4d7
define $(package)_set_vars
$(package)_config_opts=--disable-shared --disable-openssl --disable-libenc --disable-samples --disable-debug-mode --prefix=$($(package)_staging_prefix_dir)
endef
define $(package)_config_cmds
./configure $($(package)_config_opts) --host=${host}
endef
define $(package)_build_cmds
$(MAKE)
endef
define $(package)_stage_cmds
$(MAKE) install
endef
EOF
sed -i 's/^ /\t/' contrib/depends/packages/libevent.mk
# GENERATE ZLIB.MK
cat <<'EOF' > contrib/depends/packages/zlib.mk
package=zlib
$(package)_version=1.3.1
$(package)_download_path=https://zlib.net/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23
define $(package)_set_vars
$(package)_config_opts=--static
endef
define $(package)_config_cmds
CHOST=${host} ./configure $($(package)_config_opts) --prefix=$($(package)_staging_prefix_dir)
endef
define $(package)_build_cmds
$(MAKE) libz.a
endef
define $(package)_stage_cmds
$(MAKE) install
endef
EOF
sed -i 's/^ /\t/' contrib/depends/packages/zlib.mk
# GENERATE ZSTD.MK
cat <<'EOF' > contrib/depends/packages/zstd.mk
package=zstd
$(package)_version=1.5.5
$(package)_download_path=https://github.com/facebook/zstd/releases/download/v$($(package)_version)/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=9c4396cc829cfae319a6e2615202e82aad41372073482fce286fac78646d3ee4
define $(package)_build_cmds
$(MAKE) -C lib libzstd.a
endef
define $(package)_stage_cmds
cp lib/libzstd.a $($(package)_staging_prefix_dir)/lib/
cp lib/zstd.h $($(package)_staging_prefix_dir)/include/
endef
EOF
sed -i 's/^ /\t/' contrib/depends/packages/zstd.mk
# GENERATE BOOST.MK
cat <<'EOF' > contrib/depends/packages/boost.mk
package=boost
$(package)_version=1.90.0
$(package)_download_path=https://archives.boost.io/release/$($(package)_version)/source/
$(package)_file_name=$(package)_$(subst .,_,$($(package)_version)).tar.bz2
$(package)_sha256_hash=49551aff3b22cbc5c5a9ed3dbc92f0e23ea50a0f7325b0d198b705e8ee3fc305
$(package)_dependencies=libiconv
define $(package)_set_vars
$(package)_config_opts_release=variant=release
$(package)_config_opts_debug=variant=debug
$(package)_config_opts+=--layout=system --user-config=user-config.jam
$(package)_config_opts+=threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1
$(package)_config_opts_linux=threadapi=pthread runtime-link=static
$(package)_config_opts_darwin=target-os=darwin runtime-link=shared
$(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static
$(package)_config_opts_x86_64_mingw32=address-model=64
$(package)_config_opts_i686_mingw32=address-model=32
$(package)_config_opts_i686_linux=address-model=32 architecture=x86
$(package)_config_opts_x86_64_darwin=address-model=64
$(package)_config_opts_aarch64_darwin=address-model=64 architecture=arm binary-format=mach-o abi=aapcs
$(package)_toolset_$(host_os)=gcc
$(package)_archiver_$(host_os)=$($(package)_ar)
$(package)_toolset_darwin=darwin
$(package)_archiver_darwin=$($(package)_libtool)
$(package)_config_libraries=atomic,chrono,date_time,filesystem,program_options,regex,serialization,system,thread,locale,context,coroutine
$(package)_cxxflags=-std=c++17 -fPIC
endef
define $(package)_preprocess_cmds
echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : <cxxflags>\"$($(package)_cxxflags) $($(package)_cppflags)\" <linkflags>\"$($(package)_ldflags)\" <archiver>\"$(boost_archiver_$(host_os))\" <arflags>\"$($(package)_arflags)\" <striper>\"$(host_STRIP)\" <ranlib>\"$(host_RANLIB)\" <rc>\"$(host_WINDRES)\" : ;" > user-config.jam
endef
define $(package)_config_cmds
./bootstrap.sh --without-icu --with-libraries=$(boost_config_libraries)
endef
define $(package)_build_cmds
./b2 -d2 -j2 -d1 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) stage
endef
define $(package)_stage_cmds
./b2 -d0 -j4 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) install
endef
EOF
sed -i 's/^ /\t/' contrib/depends/packages/boost.mk
- name: Build Dependencies
run: |
cd contrib/depends
make HOST=${{ matrix.target }} -j$(nproc)
- name: Build Wownero Core
run: |
PREFIX=$(pwd)/contrib/depends/${{ matrix.target }}
mkdir build && cd build
export CXXFLAGS="-D_GNU_SOURCE"
export CFLAGS="-D_GNU_SOURCE"
STATIC_RUNTIME="ON"
if [[ "${{ matrix.target }}" == *"apple"* ]]; then
STATIC_RUNTIME="OFF"
perl -pi -e 's/locale//g' ../CMakeLists.txt
perl -pi -e 's/locale//g' ../external/monero/CMakeLists.txt
fi
cmake .. \
-DCMAKE_TOOLCHAIN_FILE=../contrib/depends/${{ matrix.target }}/share/toolchain.cmake \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_STATIC=ON \
-DBoost_USE_STATIC_RUNTIME=$STATIC_RUNTIME
make -j$(nproc)
# -----------------------------------------------------------------------
# 2. FAIL-SAFE (Now includes Libevent)
# -----------------------------------------------------------------------
- name: Ensure Static Libs Exist
run: |
LIB_DIR="$(pwd)/contrib/depends/${{ matrix.target }}/lib"
INC_DIR="$(pwd)/contrib/depends/${{ matrix.target }}/include"
TOOLCHAIN="$(pwd)/contrib/depends/${{ matrix.target }}/share/toolchain.cmake"
mkdir -p "$LIB_DIR"
mkdir -p "$INC_DIR"
# --- FAIL-SAFE 1: ZSTD ---
if [ ! -f "$LIB_DIR/libzstd.a" ]; then
echo "Zstd missing! Activating Fail-Safe Build..."
mkdir -p temp_zstd && cd temp_zstd
curl -L -o zstd.tar.gz https://github.com/facebook/zstd/releases/download/v1.5.5/zstd-1.5.5.tar.gz
tar -xf zstd.tar.gz && cd zstd-1.5.5/build/cmake
cmake . -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN" \
-DCMAKE_INSTALL_PREFIX="$(pwd)/../../inst" \
-DCMAKE_BUILD_TYPE=Release \
-DZSTD_BUILD_STATIC=ON -DZSTD_BUILD_SHARED=OFF \
-DZSTD_BUILD_PROGRAMS=OFF -DZSTD_BUILD_TESTS=OFF
make -j$(nproc)
if [ -f lib/libzstd.a ]; then cp lib/libzstd.a "$LIB_DIR/"; fi
if [ -f lib/libzstdstatic.a ]; then cp lib/libzstdstatic.a "$LIB_DIR/libzstd.a"; fi
cp ../../lib/zstd.h "$INC_DIR/"
cd ../../..
fi
# --- FAIL-SAFE 2: ZLIB ---
if [ ! -f "$LIB_DIR/libz.a" ]; then
echo "Zlib missing! Activating Fail-Safe Build..."
mkdir -p temp_zlib && cd temp_zlib
curl -L -o zlib.tar.gz https://zlib.net/zlib-1.3.1.tar.gz
tar -xf zlib.tar.gz && cd zlib-1.3.1
cmake . -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN" \
-DCMAKE_INSTALL_PREFIX="$(pwd)/inst" \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF
make -j$(nproc)
if [ -f libz.a ]; then cp libz.a "$LIB_DIR/"; fi
if [ -f libzlibstatic.a ]; then cp libzlibstatic.a "$LIB_DIR/libz.a"; fi
cp zlib.h "$INC_DIR/"
cp zconf.h "$INC_DIR/"
cd ../..
fi
# --- FAIL-SAFE 3: LIBEVENT (The Fix) ---
if [ ! -f "$LIB_DIR/libevent.a" ]; then
echo "Libevent missing! Activating Fail-Safe Build..."
mkdir -p temp_event && cd temp_event
curl -L -o libevent.tar.gz https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz
tar -xf libevent.tar.gz && cd libevent-2.1.12-stable
cmake . -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN" \
-DCMAKE_INSTALL_PREFIX="$(pwd)/inst" \
-DCMAKE_BUILD_TYPE=Release \
-DEVENT__DISABLE_OPENSSL=ON \
-DEVENT__DISABLE_MBEDTLS=ON \
-DEVENT__DISABLE_TESTS=ON \
-DEVENT__DISABLE_SAMPLES=ON \
-DEVENT__LIBRARY_TYPE=STATIC
make -j$(nproc)
# Locate and copy the static lib (name might vary, grab any .a)
find . -name "libevent.a" -exec cp {} "$LIB_DIR/" \;
find . -name "libevent_core.a" -exec cp {} "$LIB_DIR/" \;
find . -name "libevent_extra.a" -exec cp {} "$LIB_DIR/" \;
# Also copy headers
mkdir -p "$INC_DIR/event2"
cp -r include/event2/* "$INC_DIR/event2/"
cd ../..
fi
- name: Package Artifacts
run: |
LIB_DIR="contrib/depends/${{ matrix.target }}/lib"
INC_DIR="contrib/depends/${{ matrix.target }}/include"
mkdir -p output/lib output/include output/bin
find build/bin -type f -exec cp -v {} output/bin/ \;
cp -v $LIB_DIR/*.a output/lib/
# Final Verification (The "Stop if Fucked" check)
ls -l output/lib/libz*.a
if [ ! -f output/lib/libzstd.a ]; then echo "FATAL: libzstd.a missing"; exit 1; fi
if [ ! -f output/lib/libz.a ]; then echo "FATAL: libz.a missing"; exit 1; fi
# Check for ANY libevent static lib (it might be libevent_core.a etc)
if [ -z "$(find output/lib -name 'libevent*.a')" ]; then
echo "FATAL: libevent.a missing (checked libevent*.a)"
exit 1
fi
cp -r src output/include/wownero-src
cp -r $INC_DIR/* output/include/
tar -czf wownero-core-${{ matrix.target }}.tar.gz -C output .
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: wownero-core-${{ matrix.target }}
path: wownero-core-${{ matrix.target }}.tar.gz
release:
needs: build-all
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Download Artifacts
uses: actions/download-artifact@v3
- name: Publish Release
uses: softprops/action-gh-release@v1
with:
files: |
wownero-core-*/wownero-core-*.tar.gz

6
.gitmodules vendored
View File

@@ -7,6 +7,12 @@
[submodule "external/trezor-common"] [submodule "external/trezor-common"]
path = external/trezor-common path = external/trezor-common
url = https://github.com/trezor/trezor-common.git url = https://github.com/trezor/trezor-common.git
[submodule "external/utf8proc"]
path = external/utf8proc
url = https://github.com/JuliaStrings/utf8proc.git
[submodule "external/polyseed"]
path = external/polyseed
url = https://github.com/tevador/polyseed.git
[submodule "external/supercop"] [submodule "external/supercop"]
path = external/supercop path = external/supercop
url = https://github.com/monero-project/supercop url = https://github.com/monero-project/supercop

View File

@@ -370,6 +370,8 @@ if(NOT MANUAL_SUBMODULES)
check_submodule(external/trezor-common) check_submodule(external/trezor-common)
check_submodule(external/randomwow) check_submodule(external/randomwow)
check_submodule(external/supercop) check_submodule(external/supercop)
check_submodule(external/polyseed)
check_submodule(external/utf8proc)
endif() endif()
endif() endif()
@@ -459,7 +461,7 @@ endif()
# elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") # elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*")
# set(BSDI TRUE) # set(BSDI TRUE)
include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include) include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include external/polyseed/include external/utf8proc)
if(APPLE) if(APPLE)
cmake_policy(SET CMP0042 NEW) cmake_policy(SET CMP0042 NEW)

View File

@@ -104,7 +104,7 @@ release-all:
release-static: release-static:
mkdir -p $(builddir)/release mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE) cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
release-minimal: release-minimal:
@echo "Starting minimal Wownero build... for full build, run: make release-all" @echo "Starting minimal Wownero build... for full build, run: make release-all"
@@ -137,6 +137,23 @@ release-static-android-armv8:
cd $(builddir)/release/translations && cmake ../../../translations && $(MAKE) 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 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) 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 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-android-armv7-wallet_api:
mkdir -p $(builddir)/release
cd $(builddir)/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -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" -D NO_AES=true ../.. && $(MAKE) wallet_api
release-static-android-armv8-wallet_api:
mkdir -p $(builddir)/release
cd $(builddir)/release && CC=aarch64-linux-android-clang CXX=aarch64-linux-android-clang++ cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -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) wallet_api
release-static-android-x86_64-wallet_api:
mkdir -p $(builddir)/release
cd $(builddir)/release && CC=x86_64-linux-android-clang CXX=x86_64-linux-android-clang++ cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="x86-64" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android-x86_64" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI="x86_64" ../.. && $(MAKE) wallet_api
release-static-android-x86-wallet_api:
mkdir -p $(builddir)/release
cd $(builddir)/release && CC=i686-linux-android-clang CXX=i686-linux-android-clang++ cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="i686" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android-x86" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI="x86" ../.. && $(MAKE) wallet_api
release-static-linux-armv8: release-static-linux-armv8:
mkdir -p $(builddir)/release mkdir -p $(builddir)/release
cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="linux-armv8" $(topdir) && $(MAKE) cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="linux-armv8" $(topdir) && $(MAKE)

View File

@@ -1,44 +1,36 @@
package=boost package=boost
$(package)_version=1.90.0 $(package)_version=1.69.0
$(package)_download_path=https://archives.boost.io/release/$($(package)_version)/source/ $(package)_download_path=https://archives.boost.io/release/$($(package)_version)/source/
$(package)_file_name=$(package)_$(subst .,_,$($(package)_version)).tar.bz2 $(package)_file_name=$(package)_$(subst .,_,$($(package)_version)).tar.gz
$(package)_sha256_hash=49551aff3b22cbc5c5a9ed3dbc92f0e23ea50a0f7325b0d198b705e8ee3fc305 $(package)_sha256_hash=9a2c2819310839ea373f42d69e733c339b4e9a19deab6bfec448281554aa4dbb
$(package)_dependencies=libiconv $(package)_dependencies=libiconv
$(package)_patches=fix_aroptions.patch fix_arm_arch.patch
define $(package)_set_vars define $(package)_set_vars
$(package)_config_opts_release=variant=release $(package)_config_opts_release=variant=release
$(package)_config_opts_debug=variant=debug $(package)_config_opts_debug=variant=debug
$(package)_config_opts+=--layout=system --user-config=user-config.jam
$(package)_config_opts+=--layout=system --user-config=user-config.jam $(package)_config_opts+=threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1
$(package)_config_opts+=threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1 $(package)_config_opts_linux=threadapi=pthread runtime-link=shared
$(package)_config_opts_android=threadapi=pthread runtime-link=static target-os=android
$(package)_config_opts_linux=threadapi=pthread runtime-link=static $(package)_config_opts_darwin=--toolset=darwin runtime-link=shared
$(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static
# Darwin (macOS) Defaults $(package)_config_opts_x86_64_mingw32=address-model=64
$(package)_config_opts_darwin=target-os=darwin runtime-link=shared $(package)_config_opts_i686_mingw32=address-model=32
$(package)_config_opts_i686_linux=address-model=32 architecture=x86
# Windows Defaults $(package)_toolset_$(host_os)=gcc
$(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static $(package)_archiver_$(host_os)=$($(package)_ar)
$(package)_toolset_darwin=darwin
# Architecture Specifics $(package)_archiver_darwin=$($(package)_libtool)
$(package)_config_opts_x86_64_mingw32=address-model=64 $(package)_config_libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale
$(package)_config_opts_i686_mingw32=address-model=32 $(package)_cxxflags=-std=c++11
$(package)_config_opts_i686_linux=address-model=32 architecture=x86 $(package)_cxxflags_linux=-fPIC
$(package)_cxxflags_freebsd=-fPIC
# MACOS ARM64 FIX: Explicitly tell B2 this is ARM + Mach-O
$(package)_config_opts_x86_64_darwin=address-model=64
$(package)_config_opts_aarch64_darwin=address-model=64 architecture=arm binary-format=mach-o abi=aapcs
$(package)_toolset_$(host_os)=gcc
$(package)_archiver_$(host_os)=$($(package)_ar)
$(package)_toolset_darwin=darwin
$(package)_archiver_darwin=$($(package)_libtool)
$(package)_config_libraries=atomic,chrono,date_time,filesystem,program_options,regex,serialization,system,thread,locale,context,coroutine
$(package)_cxxflags=-std=c++17 -fPIC
endef endef
define $(package)_preprocess_cmds define $(package)_preprocess_cmds
patch -p1 < $($(package)_patch_dir)/fix_aroptions.patch &&\
patch -p1 < $($(package)_patch_dir)/fix_arm_arch.patch &&\
echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : <cxxflags>\"$($(package)_cxxflags) $($(package)_cppflags)\" <linkflags>\"$($(package)_ldflags)\" <archiver>\"$(boost_archiver_$(host_os))\" <arflags>\"$($(package)_arflags)\" <striper>\"$(host_STRIP)\" <ranlib>\"$(host_RANLIB)\" <rc>\"$(host_WINDRES)\" : ;" > user-config.jam echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : <cxxflags>\"$($(package)_cxxflags) $($(package)_cppflags)\" <linkflags>\"$($(package)_ldflags)\" <archiver>\"$(boost_archiver_$(host_os))\" <arflags>\"$($(package)_arflags)\" <striper>\"$(host_STRIP)\" <ranlib>\"$(host_RANLIB)\" <rc>\"$(host_WINDRES)\" : ;" > user-config.jam
endef endef
@@ -52,4 +44,4 @@ endef
define $(package)_stage_cmds define $(package)_stage_cmds
./b2 -d0 -j4 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) install ./b2 -d0 -j4 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) install
endef endef

View File

@@ -1,9 +1,10 @@
package=hidapi package=hidapi
$(package)_version=0.15.0 $(package)_version=0.13.1
$(package)_download_path=https://github.com/libusb/hidapi/archive/refs/tags $(package)_download_path=https://github.com/libusb/hidapi/archive/refs/tags
$(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=5d84dec684c27b97b921d2f3b73218cb773cf4ea915caee317ac8fc73cef8136 $(package)_sha256_hash=476a2c9a4dc7d1fc97dd223b84338dbea3809a84caea2dcd887d9778725490e3
$(package)_linux_dependencies=libusb eudev $(package)_linux_dependencies=libusb eudev
$(package)_patches=missing_win_include.patch
define $(package)_set_vars define $(package)_set_vars
$(package)_config_opts=--enable-static --disable-shared $(package)_config_opts=--enable-static --disable-shared
@@ -16,7 +17,7 @@ $(package)_config_opts_linux+=--with-pic
endef endef
define $(package)_preprocess_cmds define $(package)_preprocess_cmds
./bootstrap patch -p1 < $($(package)_patch_dir)/missing_win_include.patch && ./bootstrap
endef endef
define $(package)_config_cmds define $(package)_config_cmds

View File

@@ -1,4 +1,4 @@
packages:=boost openssl expat libusb hidapi protobuf libiconv sodium zeromq unbound zlib zstd packages:=boost openssl zeromq libiconv expat unbound
# ccache is useless in gitian builds # ccache is useless in gitian builds
ifneq ($(GITIAN),1) ifneq ($(GITIAN),1)

View File

@@ -1,21 +0,0 @@
package=zlib
$(package)_version=1.3.1
$(package)_download_path=https://zlib.net/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23
define $(package)_set_vars
$(package)_config_opts=--static
endef
define $(package)_config_cmds
CHOST=${host} ./configure $($(package)_config_opts) --prefix=$($(package)_staging_prefix_dir)
endef
define $(package)_build_cmds
$(MAKE) libz.a
endef
define $(package)_stage_cmds
$(MAKE) install
endef

View File

@@ -1,14 +0,0 @@
package=zstd
$(package)_version=1.5.5
$(package)_download_path=https://github.com/facebook/zstd/releases/download/v$($(package)_version)/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=9c4396cc829cfae319a6e2615202e82aad41372073482fce286fac78646d3ee4
define $(package)_build_cmds
$(MAKE) -C lib libzstd.a
endef
define $(package)_stage_cmds
cp lib/libzstd.a $($(package)_staging_prefix_dir)/lib/
cp lib/zstd.h $($(package)_staging_prefix_dir)/include/
endef

View File

@@ -0,0 +1,21 @@
From a77b066311da42ed7654e39c0356a3b951b2e296 Mon Sep 17 00:00:00 2001
From: selsta <selsta@sent.at>
Date: Wed, 10 Nov 2021 02:28:54 +0100
Subject: [PATCH] windows: add missing include for mingw32
---
windows/hid.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/windows/hid.c b/windows/hid.c
index 24756a4..6d8394c 100644
--- a/windows/hid.c
+++ b/windows/hid.c
@@ -33,6 +33,7 @@ typedef LONG NTSTATUS;
#endif
#ifdef __MINGW32__
+#include <devpropdef.h>
#include <ntdef.h>
#include <winbase.h>
#endif

View File

@@ -30,7 +30,6 @@
#ifdef __cplusplus #ifdef __cplusplus
#include <sstream>
#include <string> #include <string>
#include "easylogging++.h" #include "easylogging++.h"
@@ -41,15 +40,9 @@
#define MAX_LOG_FILE_SIZE 104850000 // 100 MB - 7600 bytes #define MAX_LOG_FILE_SIZE 104850000 // 100 MB - 7600 bytes
#define MAX_LOG_FILES 50 #define MAX_LOG_FILES 50
#define LOG_TO_STRING(x) \
std::stringstream ss; \
ss << x; \
const std::string str = ss.str();
#define MCLOG_TYPE(level, cat, color, type, x) do { \ #define MCLOG_TYPE(level, cat, color, type, x) do { \
if (el::Loggers::allowed(level, cat)) { \ if (el::Loggers::allowed(level, cat)) { \
LOG_TO_STRING(x); \ el::base::Writer(level, color, __FILE__, __LINE__, ELPP_FUNC, type).construct(cat) << x; \
el::base::Writer(level, color, __FILE__, __LINE__, ELPP_FUNC, type).construct(cat) << str; \
} \ } \
} while (0) } while (0)
@@ -98,8 +91,7 @@
do { \ do { \
if (el::Loggers::allowed(level, cat)) { \ if (el::Loggers::allowed(level, cat)) { \
init; \ init; \
LOG_TO_STRING(x); \ el::base::Writer(level, color, __FILE__, __LINE__, ELPP_FUNC, type).construct(cat) << x; \
el::base::Writer(level, color, __FILE__, __LINE__, ELPP_FUNC, type).construct(cat) << str; \
} \ } \
} while(0) } while(0)
#define MIDEBUG(init, x) IFLOG(el::Level::Debug, MONERO_DEFAULT_LOG_CATEGORY, el::Color::Default, el::base::DispatchAction::NormalLog, init, x) #define MIDEBUG(init, x) IFLOG(el::Level::Debug, MONERO_DEFAULT_LOG_CATEGORY, el::Color::Default, el::base::DispatchAction::NormalLog, init, x)

View File

@@ -34,7 +34,6 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include <list> #include <list>
#include <cstdint>
#undef MONERO_DEFAULT_LOG_CATEGORY #undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.http" #define MONERO_DEFAULT_LOG_CATEGORY "net.http"

View File

@@ -34,6 +34,7 @@
#include <string> #include <string>
#include "memwipe.h" #include "memwipe.h"
#include "fnv1.h" #include "fnv1.h"
#include "serialization/keyvalue_serialization.h"
namespace epee namespace epee
{ {
@@ -75,6 +76,12 @@ namespace epee
bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; } bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; }
wipeable_string &operator=(wipeable_string &&other); wipeable_string &operator=(wipeable_string &&other);
wipeable_string &operator=(const wipeable_string &other); wipeable_string &operator=(const wipeable_string &other);
char& operator[](size_t idx);
const char& operator[](size_t idx) const;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(buffer)
END_KV_SERIALIZE_MAP()
private: private:
void grow(size_t sz, size_t reserved = 0); void grow(size_t sz, size_t reserved = 0);

View File

@@ -261,4 +261,14 @@ wipeable_string &wipeable_string::operator=(const wipeable_string &other)
return *this; return *this;
} }
char& wipeable_string::operator[](size_t idx) {
CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds");
return buffer[idx];
}
const char& wipeable_string::operator[](size_t idx) const {
CHECK_AND_ASSERT_THROW_MES(idx < buffer.size(), "Index out of bounds");
return buffer[idx];
}
} }

View File

@@ -57,7 +57,7 @@ The dockrun.sh script will do everything to build the binaries. Just specify the
version to build as its only argument, e.g. version to build as its only argument, e.g.
```bash ```bash
VERSION=v0.18.4.5 VERSION=v0.18.4.2
./dockrun.sh $VERSION ./dockrun.sh $VERSION
``` ```

View File

@@ -133,7 +133,7 @@ Common setup part:
su - gitianuser su - gitianuser
GH_USER=YOUR_GITHUB_USER_NAME GH_USER=YOUR_GITHUB_USER_NAME
VERSION=v0.18.4.5 VERSION=v0.18.4.2
``` ```
Where `GH_USER` is your GitHub user name and `VERSION` is the version tag you want to build. Where `GH_USER` is your GitHub user name and `VERSION` is the version tag you want to build.

View File

@@ -71,3 +71,5 @@ add_subdirectory(db_drivers)
add_subdirectory(easylogging++) add_subdirectory(easylogging++)
add_subdirectory(qrcodegen) add_subdirectory(qrcodegen)
add_subdirectory(randomwow EXCLUDE_FROM_ALL) add_subdirectory(randomwow EXCLUDE_FROM_ALL)
add_subdirectory(polyseed EXCLUDE_FROM_ALL)
add_subdirectory(utf8proc EXCLUDE_FROM_ALL)

View File

@@ -1244,19 +1244,11 @@ std::string OS::currentHost(void) {
#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID #endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID
} }
static bool endswith(const std::string &s, const std::string &ending)
{
return s.size() >= ending.size() && s.substr(s.size() - ending.size()) == ending;
}
bool OS::termSupportsColor(std::string& term) {
return term == "xterm" || term == "screen" || term == "linux" || term == "cygwin"
|| endswith(term, "-color") || endswith(term, "-256color");
}
bool OS::termSupportsColor(void) { bool OS::termSupportsColor(void) {
std::string term = getEnvironmentVariable("TERM", ""); std::string term = getEnvironmentVariable("TERM", "");
return termSupportsColor(term); return term == "xterm" || term == "xterm-color" || term == "xterm-256color"
|| term == "screen" || term == "linux" || term == "cygwin"
|| term == "screen-256color" || term == "screen.xterm-256color";
} }
// DateTime // DateTime
@@ -3065,9 +3057,8 @@ void Writer::triggerDispatch(void) {
} }
if (m_proceed && m_level == Level::Fatal if (m_proceed && m_level == Level::Fatal
&& !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) {
const std::string str = "Aborting application. Reason: Fatal log at [" + std::string(m_file) + ":" + std::to_string(m_line) + "]";
base::Writer(Level::Warning, Color::Default, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) base::Writer(Level::Warning, Color::Default, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
<< str; << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]";
std::stringstream reasonStream; std::stringstream reasonStream;
reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]"
<< " If you wish to disable 'abort on fatal log' please use " << " If you wish to disable 'abort on fatal log' please use "

View File

@@ -1210,9 +1210,7 @@ class OS : base::StaticClass {
/// ///
/// @detail For android systems this is device name with its manufacturer and model seperated by hyphen /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen
static std::string currentHost(void); static std::string currentHost(void);
/// @brief Whether or not the named terminal supports colors /// @brief Whether or not terminal supports colors
static bool termSupportsColor(std::string& term);
/// @brief Whether or not the process's current terminal supports colors
static bool termSupportsColor(void); static bool termSupportsColor(void);
}; };
/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str /// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str
@@ -3274,7 +3272,9 @@ class Writer : base::NoCopy {
processDispatch(); processDispatch();
} }
Writer& operator<<(const std::string &log) { template <typename T>
inline typename std::enable_if<std::is_integral<T>::value, Writer&>::type
operator<<(T log) {
#if ELPP_LOGGING_ENABLED #if ELPP_LOGGING_ENABLED
if (m_proceed) { if (m_proceed) {
m_messageBuilder << log; m_messageBuilder << log;
@@ -3283,6 +3283,26 @@ class Writer : base::NoCopy {
return *this; return *this;
} }
template <typename T>
inline typename std::enable_if<!std::is_integral<T>::value, Writer&>::type
operator<<(const T& log) {
#if ELPP_LOGGING_ENABLED
if (m_proceed) {
m_messageBuilder << log;
}
#endif // ELPP_LOGGING_ENABLED
return *this;
}
inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) {
#if ELPP_LOGGING_ENABLED
if (m_proceed) {
m_messageBuilder << log;
}
#endif // ELPP_LOGGING_ENABLED
return *this;
}
inline operator bool() { inline operator bool() {
return true; return true;
} }
@@ -3599,9 +3619,8 @@ class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback {
ss << ELPP_LITERAL("]"); ss << ELPP_LITERAL("]");
} }
} }
const std::string str = ss.str();
el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1,
m_data->loggerId().c_str()) << str; m_data->loggerId().c_str()) << ss.str();
} }
private: private:
const PerformanceTrackingData* m_data; const PerformanceTrackingData* m_data;

1
external/polyseed vendored Submodule

Submodule external/polyseed added at dfb05d8edb

1
external/utf8proc vendored Submodule

Submodule external/utf8proc added at 1cb28a66ca

View File

@@ -95,6 +95,7 @@ add_subdirectory(net)
add_subdirectory(hardforks) add_subdirectory(hardforks)
add_subdirectory(blockchain_db) add_subdirectory(blockchain_db)
add_subdirectory(mnemonics) add_subdirectory(mnemonics)
add_subdirectory(polyseed)
add_subdirectory(rpc) add_subdirectory(rpc)
if(NOT IOS) if(NOT IOS)
add_subdirectory(serialization) add_subdirectory(serialization)

View File

@@ -71,6 +71,7 @@ target_link_libraries(cryptonote_basic
checkpoints checkpoints
cryptonote_format_utils_basic cryptonote_format_utils_basic
device device
polyseed_wrapper
${Boost_DATE_TIME_LIBRARY} ${Boost_DATE_TIME_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_SERIALIZATION_LIBRARY} ${Boost_SERIALIZATION_LIBRARY}

View File

@@ -87,12 +87,16 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_keys::xor_with_key_stream(const crypto::chacha_key &key) void account_keys::xor_with_key_stream(const crypto::chacha_key &key)
{ {
// encrypt a large enough byte stream with chacha20 // encrypt a large enough byte stream with chacha20
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size())); epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (3 + m_multisig_keys.size()) + m_passphrase.size());
const char *ptr = key_stream.data(); const char *ptr = key_stream.data();
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_spend_secret_key.data[i] ^= *ptr++; m_spend_secret_key.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_view_secret_key.data[i] ^= *ptr++; m_view_secret_key.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_polyseed.data[i] ^= *ptr++;
for (size_t i = 0; i < m_passphrase.size(); ++i)
m_passphrase.data()[i] ^= *ptr++;
for (crypto::secret_key &k: m_multisig_keys) for (crypto::secret_key &k: m_multisig_keys)
{ {
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
@@ -150,6 +154,8 @@ DISABLE_VS_WARNINGS(4244 4345)
{ {
m_keys.m_spend_secret_key = crypto::secret_key(); m_keys.m_spend_secret_key = crypto::secret_key();
m_keys.m_multisig_keys.clear(); m_keys.m_multisig_keys.clear();
m_keys.m_polyseed = crypto::secret_key();
m_keys.m_passphrase.wipe();
} }
//----------------------------------------------------------------- //-----------------------------------------------------------------
void account_base::set_spend_key(const crypto::secret_key& spend_secret_key) void account_base::set_spend_key(const crypto::secret_key& spend_secret_key)
@@ -255,6 +261,21 @@ DISABLE_VS_WARNINGS(4244 4345)
create_from_keys(address, fake, viewkey); create_from_keys(address, fake, viewkey);
} }
//----------------------------------------------------------------- //-----------------------------------------------------------------
void account_base::create_from_polyseed(const polyseed::data& seed, const epee::wipeable_string &passphrase)
{
crypto::secret_key secret_key;
seed.keygen(&secret_key, sizeof(secret_key));
if (!passphrase.empty()) {
secret_key = cryptonote::decrypt_key(secret_key, passphrase);
}
generate(secret_key, true, false);
seed.save(m_keys.m_polyseed.data);
m_keys.m_passphrase = passphrase;
}
//-----------------------------------------------------------------
bool account_base::make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys) bool account_base::make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys)
{ {
m_keys.m_account_address.m_spend_public_key = spend_public_key; m_keys.m_account_address.m_spend_public_key = spend_public_key;

View File

@@ -33,6 +33,7 @@
#include "cryptonote_basic.h" #include "cryptonote_basic.h"
#include "crypto/crypto.h" #include "crypto/crypto.h"
#include "serialization/keyvalue_serialization.h" #include "serialization/keyvalue_serialization.h"
#include "polyseed/polyseed.hpp"
namespace cryptonote namespace cryptonote
{ {
@@ -45,6 +46,8 @@ namespace cryptonote
std::vector<crypto::secret_key> m_multisig_keys; std::vector<crypto::secret_key> m_multisig_keys;
hw::device *m_device = &hw::get_device("default"); hw::device *m_device = &hw::get_device("default");
crypto::chacha_iv m_encryption_iv; crypto::chacha_iv m_encryption_iv;
crypto::secret_key m_polyseed;
epee::wipeable_string m_passphrase; // Only used with polyseed
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_account_address) KV_SERIALIZE(m_account_address)
@@ -53,6 +56,8 @@ namespace cryptonote
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}}; const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}};
KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv) KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_polyseed)
KV_SERIALIZE(m_passphrase)
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
void encrypt(const crypto::chacha_key &key); void encrypt(const crypto::chacha_key &key);
@@ -79,6 +84,7 @@ namespace cryptonote
void create_from_device(hw::device &hwdev); void create_from_device(hw::device &hwdev);
void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey); void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey);
void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey); void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey);
void create_from_polyseed(const polyseed::data &polyseed, const epee::wipeable_string &passphrase);
bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys); bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys);
const account_keys& get_keys() const; const account_keys& get_keys() const;
std::string get_public_address_str(network_type nettype) const; std::string get_public_address_str(network_type nettype) const;

View File

@@ -223,6 +223,8 @@
#define DNS_BLOCKLIST_LIFETIME (86400 * 8) #define DNS_BLOCKLIST_LIFETIME (86400 * 8)
#define POLYSEED_COIN POLYSEED_MONERO
//The limit is enough for the mandatory transaction content with 16 outputs (547 bytes), //The limit is enough for the mandatory transaction content with 16 outputs (547 bytes),
//a custom tag (1 byte) and up to 32 bytes of custom data for each recipient. //a custom tag (1 byte) and up to 32 bytes of custom data for each recipient.
// (1+32) + (1+1+16*32) + (1+16*32) = 1060 // (1+32) + (1+1+16*32) + (1+16*32) = 1060

View File

@@ -728,13 +728,13 @@ crypto::hash Blockchain::get_tail_id() const
* powers of 2 less recent from there, so 13, 17, 25, etc... * powers of 2 less recent from there, so 13, 17, 25, etc...
* *
*/ */
bool Blockchain::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t& current_height) const bool Blockchain::get_short_chain_history(std::list<crypto::hash>& ids) const
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock); CRITICAL_REGION_LOCAL(m_blockchain_lock);
uint64_t i = 0; uint64_t i = 0;
uint64_t current_multiplier = 1; uint64_t current_multiplier = 1;
uint64_t sz = current_height = m_db->height(); uint64_t sz = m_db->height();
if(!sz) if(!sz)
return true; return true;

View File

@@ -445,11 +445,10 @@ namespace cryptonote
* powers of 2 less recent from there, so 13, 17, 25, etc... * powers of 2 less recent from there, so 13, 17, 25, etc...
* *
* @param ids return-by-reference list to put the resulting hashes in * @param ids return-by-reference list to put the resulting hashes in
* @param current_height the current blockchain height, return-by-reference
* *
* @return true * @return true
*/ */
bool get_short_chain_history(std::list<crypto::hash>& ids, uint64_t& current_height) const; bool get_short_chain_history(std::list<crypto::hash>& ids) const;
/** /**
* @brief get recent block hashes for a foreign chain * @brief get recent block hashes for a foreign chain

View File

@@ -1302,23 +1302,20 @@ namespace cryptonote
NOTIFY_NEW_FLUFFY_BLOCK::request arg{}; NOTIFY_NEW_FLUFFY_BLOCK::request arg{};
arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height();
std::vector<crypto::hash> missed_txs; std::vector<crypto::hash> missed_txs;
for (const auto &tx_hash : b.tx_hashes) std::vector<cryptonote::blobdata> txs;
{ m_blockchain_storage.get_transactions_blobs(b.tx_hashes, txs, missed_txs);
if (m_blockchain_storage.have_tx(tx_hash))
continue;
missed_txs.push_back(tx_hash);
}
if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b))
{ {
LOG_PRINT_L1("Block found but, seems that reorganize just happened after that, do not relay this block"); LOG_PRINT_L1("Block found but, seems that reorganize just happened after that, do not relay this block");
return true; return true;
} }
CHECK_AND_ASSERT_MES(!missed_txs.size(), false, "can't find some transactions in found block:" << get_block_hash(b) CHECK_AND_ASSERT_MES(txs.size() == b.tx_hashes.size() && !missed_txs.size(), false, "can't find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size()
<< " b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size()); << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size());
block_to_blob(b, arg.b.block); block_to_blob(b, arg.b.block);
// Relay an empty fluffy block //pack transactions
arg.b.txs.clear(); for(auto& tx: txs)
arg.b.txs.push_back({tx, crypto::null_hash});
m_pprotocol->relay_block(arg, exclude_context); m_pprotocol->relay_block(arg, exclude_context);
} }
@@ -1536,9 +1533,9 @@ namespace cryptonote
return m_mempool.get_pool_for_rpc(tx_infos, key_image_infos); return m_mempool.get_pool_for_rpc(tx_infos, key_image_infos);
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool core::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t& current_height) const bool core::get_short_chain_history(std::list<crypto::hash>& ids) const
{ {
return m_blockchain_storage.get_short_chain_history(ids, current_height); return m_blockchain_storage.get_short_chain_history(ids);
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote_connection_context& context) bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote_connection_context& context)

View File

@@ -580,7 +580,7 @@ namespace cryptonote
* *
* @note see Blockchain::get_short_chain_history * @note see Blockchain::get_short_chain_history
*/ */
bool get_short_chain_history(std::list<crypto::hash>& ids, uint64_t& current_height) const; bool get_short_chain_history(std::list<crypto::hash>& ids) const;
/** /**
* @copydoc Blockchain::find_blockchain_supplement(const std::list<crypto::hash>&, NOTIFY_RESPONSE_CHAIN_ENTRY::request&) const * @copydoc Blockchain::find_blockchain_supplement(const std::list<crypto::hash>&, NOTIFY_RESPONSE_CHAIN_ENTRY::request&) const

View File

@@ -344,8 +344,7 @@ namespace cryptonote
} }
tvc.m_verifivation_failed = false; tvc.m_verifivation_failed = false;
if (tvc.m_added_to_pool) m_txpool_weight += tx_weight;
m_txpool_weight += tx_weight;
++m_cookie; ++m_cookie;

View File

@@ -153,26 +153,18 @@ uint64_t block_queue::get_next_needed_height(uint64_t blockchain_height) const
boost::unique_lock<boost::recursive_mutex> lock(mutex); boost::unique_lock<boost::recursive_mutex> lock(mutex);
if (blocks.empty()) if (blocks.empty())
return blockchain_height; return blockchain_height;
uint64_t last_needed_height = blockchain_height;
uint64_t covered_until = blockchain_height; bool first = true;
for (const auto &span: blocks) for (const auto &span: blocks)
{ {
// Ignore spans entirely below current chain height if (span.start_block_height + span.nblocks - 1 < blockchain_height)
const uint64_t span_end = span.start_block_height + span.nblocks - 1;
if (span_end < blockchain_height)
continue; continue;
if (span.start_block_height != last_needed_height || (first && span.blocks.empty()))
// If this span starts after what we already have/scheduled, we found the first gap return last_needed_height;
if (span.start_block_height > covered_until) last_needed_height = span.start_block_height + span.nblocks;
return covered_until; first = false;
// This span overlaps or is adjacent; extend coverage regardless of filled/scheduled
if (span.start_block_height <= covered_until)
covered_until = std::max(covered_until, span.start_block_height + span.nblocks);
} }
return last_needed_height;
return covered_until;
} }
void block_queue::print() const void block_queue::print() const

View File

@@ -44,7 +44,6 @@
#include "net/network_throttle-detail.hpp" #include "net/network_throttle-detail.hpp"
#include "common/pruning.h" #include "common/pruning.h"
#include "common/util.h" #include "common/util.h"
#include "misc_log_ex.h"
#undef MONERO_DEFAULT_LOG_CATEGORY #undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.cn" #define MONERO_DEFAULT_LOG_CATEGORY "net.cn"
@@ -56,10 +55,8 @@
const char *cat = "net.p2p.msg"; \ const char *cat = "net.p2p.msg"; \
if (ELPP->vRegistry()->allowed(level, cat)) { \ if (ELPP->vRegistry()->allowed(level, cat)) { \
init; \ init; \
if (test) { \ if (test) \
LOG_TO_STRING(x); \ el::base::Writer(level, el::Color::Default, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(cat) << x; \
el::base::Writer(level, el::Color::Default, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(cat) << str; \
} \
} \ } \
} while(0) } while(0)
@@ -274,7 +271,8 @@ namespace cryptonote
{ {
NOTIFY_REQUEST_CHAIN::request r = {}; NOTIFY_REQUEST_CHAIN::request r = {};
context.m_needed_objects.clear(); context.m_needed_objects.clear();
m_core.get_short_chain_history(r.block_ids, context.m_expect_height); context.m_expect_height = m_core.get_current_blockchain_height();
m_core.get_short_chain_history(r.block_ids);
handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?) handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?)
r.prune = m_sync_pruned_blocks; r.prune = m_sync_pruned_blocks;
context.m_last_request_time = boost::posix_time::microsec_clock::universal_time(); context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
@@ -729,7 +727,8 @@ namespace cryptonote
context.m_needed_objects.clear(); context.m_needed_objects.clear();
context.m_state = cryptonote_connection_context::state_synchronizing; context.m_state = cryptonote_connection_context::state_synchronizing;
NOTIFY_REQUEST_CHAIN::request r = {}; NOTIFY_REQUEST_CHAIN::request r = {};
m_core.get_short_chain_history(r.block_ids, context.m_expect_height); context.m_expect_height = m_core.get_current_blockchain_height();
m_core.get_short_chain_history(r.block_ids);
handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?) handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?)
r.prune = m_sync_pruned_blocks; r.prune = m_sync_pruned_blocks;
context.m_last_request_time = boost::posix_time::microsec_clock::universal_time(); context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
@@ -2352,7 +2351,8 @@ skip:
{//we have to fetch more objects ids, request blockchain entry {//we have to fetch more objects ids, request blockchain entry
NOTIFY_REQUEST_CHAIN::request r = {}; NOTIFY_REQUEST_CHAIN::request r = {};
m_core.get_short_chain_history(r.block_ids, context.m_expect_height); context.m_expect_height = m_core.get_current_blockchain_height();
m_core.get_short_chain_history(r.block_ids);
CHECK_AND_ASSERT_MES(!r.block_ids.empty(), false, "Short chain history is empty"); CHECK_AND_ASSERT_MES(!r.block_ids.empty(), false, "Short chain history is empty");
// we'll want to start off from where we are on that peer, which may not be added yet // we'll want to start off from where we are on that peer, which may not be added yet
@@ -2488,7 +2488,7 @@ skip:
int t_cryptonote_protocol_handler<t_core>::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context) int t_cryptonote_protocol_handler<t_core>::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context)
{ {
MLOG_P2P_MESSAGE("Received NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size() MLOG_P2P_MESSAGE("Received NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size()
<< ", m_start_height=" << arg.start_height << ", m_total_height=" << arg.total_height << ", expect height=" << context.m_expect_height); << ", m_start_height=" << arg.start_height << ", m_total_height=" << arg.total_height);
MLOG_PEER_STATE("received chain"); MLOG_PEER_STATE("received chain");
if (context.m_expect_response != NOTIFY_RESPONSE_CHAIN_ENTRY::ID) if (context.m_expect_response != NOTIFY_RESPONSE_CHAIN_ENTRY::ID)

View File

@@ -409,7 +409,7 @@ namespace hw {
this->length_send = set_command_header_noopt(ins, p1); this->length_send = set_command_header_noopt(ins, p1);
if (ins == INS_GET_KEY && p1 == IO_SECRET_KEY) { if (ins == INS_GET_KEY && p1 == IO_SECRET_KEY) {
// export view key user input // export view key user input
CHECK_AND_ASSERT_THROW_MES(this->exchange_wait_on_input() == 0, "Key export rejected on device."); this->exchange_wait_on_input();
} else { } else {
this->exchange(); this->exchange();
} }
@@ -528,7 +528,6 @@ namespace hw {
{0x2c97, 0x0005, 0, 0xffa0}, {0x2c97, 0x0005, 0, 0xffa0},
{0x2c97, 0x0006, 0, 0xffa0}, {0x2c97, 0x0006, 0, 0xffa0},
{0x2c97, 0x0007, 0, 0xffa0}, {0x2c97, 0x0007, 0, 0xffa0},
{0x2c97, 0x0008, 0, 0xffa0},
}; };
bool device_ledger::connect(void) { bool device_ledger::connect(void) {
@@ -619,14 +618,15 @@ namespace hw {
send_simple(INS_GET_KEY, 0x02); send_simple(INS_GET_KEY, 0x02);
//View key is retrievied, if allowed, to speed up blockchain parsing //View key is retrievied, if allowed, to speed up blockchain parsing
crypto::secret_key view_secret_key; memmove(this->viewkey.data, this->buffer_recv+0, 32);
memmove(view_secret_key.data, this->buffer_recv+0, 32); if (is_fake_view_key(this->viewkey)) {
MDEBUG("Have Not view key");
CHECK_AND_ASSERT_THROW_MES(!is_fake_view_key(view_secret_key), "Key export rejected on device."); this->has_view_key = false;
} else {
this->viewkey = view_secret_key; MDEBUG("Have view key");
this->has_view_key = true; this->has_view_key = true;
}
#ifdef DEBUG_HWDEVICE #ifdef DEBUG_HWDEVICE
send_simple(INS_GET_KEY, 0x04); send_simple(INS_GET_KEY, 0x04);
memmove(dbg_viewkey.data, this->buffer_recv+0, 32); memmove(dbg_viewkey.data, this->buffer_recv+0, 32);

View File

@@ -177,8 +177,8 @@ namespace hw {
HMACmap hmac_map; HMACmap hmac_map;
// To speed up blockchain parsing the view key maybe handle here. // To speed up blockchain parsing the view key maybe handle here.
crypto::secret_key viewkey = crypto::null_skey; crypto::secret_key viewkey;
bool has_view_key = false; bool has_view_key;
device *controle_device; device *controle_device;

View File

@@ -0,0 +1,25 @@
set(polyseed_sources
pbkdf2.c
polyseed.cpp
)
monero_find_all_headers(polyseed_private_headers "${CMAKE_CURRENT_SOURCE_DIR}")
monero_private_headers(polyseed_wrapper
${polyseed_private_headers}
)
monero_add_library(polyseed_wrapper
${polyseed_sources}
${polyseed_headers}
${polyseed_private_headers}
)
target_link_libraries(polyseed_wrapper
PUBLIC
polyseed
utf8proc
${SODIUM_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES}
)

85
src/polyseed/pbkdf2.c Normal file
View File

@@ -0,0 +1,85 @@
// Copyright (c) 2023, The Monero Project
// Copyright (c) 2021, tevador <tevador@gmail.com>
// Copyright (c) 2005,2007,2009 Colin Percival
//
// 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.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <string.h>
#include <sodium/crypto_auth_hmacsha256.h>
#include <sodium/utils.h>
static inline void
store32_be(uint8_t dst[4], uint32_t w)
{
dst[3] = (uint8_t) w; w >>= 8;
dst[2] = (uint8_t) w; w >>= 8;
dst[1] = (uint8_t) w; w >>= 8;
dst[0] = (uint8_t) w;
}
void
crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen,
const uint8_t* salt, size_t saltlen, uint64_t c,
uint8_t* buf, size_t dkLen)
{
crypto_auth_hmacsha256_state Phctx, PShctx, hctx;
size_t i;
uint8_t ivec[4];
uint8_t U[32];
uint8_t T[32];
uint64_t j;
int k;
size_t clen;
crypto_auth_hmacsha256_init(&Phctx, passwd, passwdlen);
PShctx = Phctx;
crypto_auth_hmacsha256_update(&PShctx, salt, saltlen);
for (i = 0; i * 32 < dkLen; i++) {
store32_be(ivec, (uint32_t)(i + 1));
hctx = PShctx;
crypto_auth_hmacsha256_update(&hctx, ivec, 4);
crypto_auth_hmacsha256_final(&hctx, U);
memcpy(T, U, 32);
for (j = 2; j <= c; j++) {
hctx = Phctx;
crypto_auth_hmacsha256_update(&hctx, U, 32);
crypto_auth_hmacsha256_final(&hctx, U);
for (k = 0; k < 32; k++) {
T[k] ^= U[k];
}
}
clen = dkLen - i * 32;
if (clen > 32) {
clen = 32;
}
memcpy(&buf[i * 32], T, clen);
}
sodium_memzero((void*)&Phctx, sizeof Phctx);
sodium_memzero((void*)&PShctx, sizeof PShctx);
}

46
src/polyseed/pbkdf2.h Normal file
View File

@@ -0,0 +1,46 @@
// Copyright (c) 2023, The Monero Project
// Copyright (c) 2021, tevador <tevador@gmail.com>
//
// 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.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
#ifndef PBKDF2_H
#define PBKDF2_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void
crypto_pbkdf2_sha256(const uint8_t* passwd, size_t passwdlen,
const uint8_t* salt, size_t saltlen, uint64_t c,
uint8_t* buf, size_t dkLen);
#ifdef __cplusplus
}
#endif
#endif

182
src/polyseed/polyseed.cpp Normal file
View File

@@ -0,0 +1,182 @@
// Copyright (c) 2023, The Monero Project
// Copyright (c) 2021, tevador <tevador@gmail.com>
//
// 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.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 "polyseed.hpp"
#include "pbkdf2.h"
#include <sodium/core.h>
#include <sodium/utils.h>
#include <sodium/randombytes.h>
#include <utf8proc.h>
#include <cstring>
#include <algorithm>
#include <array>
namespace polyseed {
inline size_t utf8_norm(const char* str, polyseed_str norm, utf8proc_option_t options) {
utf8proc_int32_t buffer[POLYSEED_STR_SIZE];
utf8proc_ssize_t result;
result = utf8proc_decompose(reinterpret_cast<const uint8_t*>(str), 0, buffer, POLYSEED_STR_SIZE, options);
if (result < 0) {
return POLYSEED_STR_SIZE;
}
if (result > POLYSEED_STR_SIZE - 1) {
return result;
}
result = utf8proc_reencode(buffer, result, options);
strcpy(norm, reinterpret_cast<const char*>(buffer));
sodium_memzero(buffer, POLYSEED_STR_SIZE);
return result;
}
static size_t utf8_nfc(const char* str, polyseed_str norm) {
// Note: UTF8PROC_LUMP is used here to replace the ideographic space with a regular space for Japanese phrases
// to allow wallets to split on ' '.
return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_COMPOSE | UTF8PROC_STRIPNA | UTF8PROC_LUMP));
}
static size_t utf8_nfkd(const char* str, polyseed_str norm) {
return utf8_norm(str, norm, (utf8proc_option_t)(UTF8PROC_NULLTERM | UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT | UTF8PROC_STRIPNA));
}
struct dependency {
dependency();
std::vector<language> languages;
};
static dependency deps;
dependency::dependency() {
if (sodium_init() == -1) {
throw std::runtime_error("sodium_init failed");
}
polyseed_dependency pd;
pd.randbytes = &randombytes_buf;
pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256;
pd.memzero = &sodium_memzero;
pd.u8_nfc = &utf8_nfc;
pd.u8_nfkd = &utf8_nfkd;
pd.time = nullptr;
pd.alloc = nullptr;
pd.free = nullptr;
polyseed_inject(&pd);
for (int i = 0; i < polyseed_get_num_langs(); ++i) {
languages.push_back(language(polyseed_get_lang(i)));
}
}
static language invalid_lang;
const std::vector<language>& get_langs() {
return deps.languages;
}
const language& get_lang_by_name(const std::string& name) {
for (auto& lang : deps.languages) {
if (name == lang.name_en()) {
return lang;
}
if (name == lang.name()) {
return lang;
}
}
return invalid_lang;
}
inline void data::check_init() const {
if (valid()) {
throw std::runtime_error("already initialized");
}
}
static std::array<const char*, 8> error_desc = {
"Success",
"Wrong number of words in the phrase",
"Unknown language or unsupported words",
"Checksum mismatch",
"Unsupported seed features",
"Invalid seed format",
"Memory allocation failure",
"Unicode normalization failed"
};
static error get_error(polyseed_status status) {
if (status > 0 && status < sizeof(error_desc) / sizeof(const char*)) {
return error(error_desc[(int)status], status);
}
return error("Unknown error", status);
}
void data::create(feature_type features) {
check_init();
auto status = polyseed_create(features, &m_data);
if (status != POLYSEED_OK) {
throw get_error(status);
}
}
void data::split(const language& lang, polyseed_phrase& words) {
check_init();
if (!lang.valid()) {
throw std::runtime_error("invalid language");
}
}
void data::load(polyseed_storage storage) {
check_init();
auto status = polyseed_load(storage, &m_data);
if (status != POLYSEED_OK) {
throw get_error(status);
}
}
void data::load(const crypto::secret_key &key) {
polyseed_storage d;
memcpy(&d, &key.data, 32);
auto status = polyseed_load(d, &m_data);
if (status != POLYSEED_OK) {
throw get_error(status);
}
}
language data::decode(const char* phrase) {
check_init();
const polyseed_lang* lang;
auto status = polyseed_decode(phrase, m_coin, &lang, &m_data);
if (status != POLYSEED_OK) {
throw get_error(status);
}
return language(lang);
}
}

167
src/polyseed/polyseed.hpp Normal file
View File

@@ -0,0 +1,167 @@
// Copyright (c) 2023, The Monero Project
// Copyright (c) 2021, tevador <tevador@gmail.com>
//
// 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.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
#ifndef POLYSEED_HPP
#define POLYSEED_HPP
#include <polyseed/include/polyseed.h>
#include <polyseed/src/lang.h>
#include <vector>
#include <stdexcept>
#include <string>
#include "crypto/crypto.h"
namespace polyseed {
class data;
class language {
public:
language() : m_lang(nullptr) {}
language(const language&) = default;
language(const polyseed_lang* lang) : m_lang(lang) {}
const char* name() const {
return polyseed_get_lang_name(m_lang);
}
const char* name_en() const {
return polyseed_get_lang_name_en(m_lang);
}
const char* separator() const {
return m_lang->separator;
}
bool valid() const {
return m_lang != nullptr;
}
const polyseed_lang* m_lang;
private:
friend class data;
};
const std::vector<language>& get_langs();
const language& get_lang_by_name(const std::string& name);
class error : public std::runtime_error {
public:
error(const char* msg, polyseed_status status)
: std::runtime_error(msg), m_status(status)
{
}
polyseed_status status() const {
return m_status;
}
private:
polyseed_status m_status;
};
using feature_type = unsigned int;
inline int enable_features(feature_type features) {
return polyseed_enable_features(features);
}
class data {
public:
data(const data&) = delete;
data(polyseed_coin coin) : m_data(nullptr), m_coin(coin) {}
~data() {
polyseed_free(m_data);
}
void create(feature_type features);
void load(polyseed_storage storage);
void load(const crypto::secret_key &key);
language decode(const char* phrase);
template<class str_type>
void encode(const language& lang, str_type& str) const {
check_valid();
if (!lang.valid()) {
throw std::runtime_error("invalid language");
}
str.resize(POLYSEED_STR_SIZE);
auto size = polyseed_encode(m_data, lang.m_lang, m_coin, &str[0]);
str.resize(size);
}
void split(const language& lang, polyseed_phrase& words);
void save(polyseed_storage storage) const {
check_valid();
polyseed_store(m_data, storage);
}
void save(void *storage) const {
check_valid();
polyseed_store(m_data, (uint8_t*)storage);
}
void crypt(const char* password) {
check_valid();
polyseed_crypt(m_data, password);
}
void keygen(void* ptr, size_t key_size) const {
check_valid();
polyseed_keygen(m_data, m_coin, key_size, (uint8_t*)ptr);
}
bool valid() const {
return m_data != nullptr;
}
bool encrypted() const {
check_valid();
return polyseed_is_encrypted(m_data);
}
uint64_t birthday() const {
check_valid();
return polyseed_get_birthday(m_data);
}
bool has_feature(feature_type feature) const {
check_valid();
return polyseed_get_feature(m_data, feature) != 0;
}
private:
void check_valid() const {
if (m_data == nullptr) {
throw std::runtime_error("invalid object");
}
}
void check_init() const;
polyseed_data* m_data;
polyseed_coin m_coin;
};
}
#endif //POLYSEED_HPP

View File

@@ -3428,7 +3428,7 @@ simple_wallet::simple_wallet()
tr("Show the blockchain height.")); tr("Show the blockchain height."));
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::on_command, this, &simple_wallet::transfer, _1), m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::on_command, this, &simple_wallet::transfer, _1),
tr(USAGE_TRANSFER), tr(USAGE_TRANSFER),
tr("Transfer <address> <amount>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included). The \"subtractfeefrom=\" list allows you to choose which destinations to fund the tx fee from instead of the change output. The fee will be split across the chosen destinations proportionally equally. For example, to make 3 transfers where the fee is taken from the first and third destinations, one could do: \"transfer <addr1> 3 <addr2> 0.5 <addr3> 1 subtractfeefrom=0,2\". Let's say the tx fee is 0.1. The balance would drop by exactly 4.5 XMR including fees, and addr1 & addr3 would receive 2.925 & 0.975 XMR, respectively. Use \"subtractfeefrom=all\" to spread the fee across all destinations.")); tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included). The \"subtractfeefrom=\" list allows you to choose which destinations to fund the tx fee from instead of the change output. The fee will be split across the chosen destinations proportionally equally. For example, to make 3 transfers where the fee is taken from the first and third destinations, one could do: \"transfer <addr1> 3 <addr2> 0.5 <addr3> 1 subtractfeefrom=0,2\". Let's say the tx fee is 0.1. The balance would drop by exactly 4.5 XMR including fees, and addr1 & addr3 would receive 2.925 & 0.975 XMR, respectively. Use \"subtractfeefrom=all\" to spread the fee across all destinations."));
m_cmd_binder.set_handler("sweep_unmixable", m_cmd_binder.set_handler("sweep_unmixable",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _1),
tr("Send all unmixable outputs to yourself with ring_size 1")); tr("Send all unmixable outputs to yourself with ring_size 1"));
@@ -5833,7 +5833,7 @@ bool simple_wallet::save_bc(const std::vector<std::string>& args)
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block) void simple_wallet::on_new_block(uint64_t height, bool last, const cryptonote::block& block)
{ {
if (m_locked) if (m_locked)
return; return;
@@ -6971,7 +6971,7 @@ bool simple_wallet::transfer_main(const std::vector<std::string> &args_, bool ca
{ {
// figure out what tx will be necessary // figure out what tx will be necessary
auto ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, priority, extra, auto ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, priority, extra,
m_current_subaddress_account, subaddr_indices, subtract_fee_from_outputs); m_current_subaddress_account, subaddr_indices, {}, subtract_fee_from_outputs);
if (ptx_vector.empty()) if (ptx_vector.empty())
{ {

View File

@@ -345,7 +345,7 @@ namespace cryptonote
bool check_daemon_rpc_prices(const std::string &daemon_url, uint32_t &actual_cph, uint32_t &claimed_cph); bool check_daemon_rpc_prices(const std::string &daemon_url, uint32_t &actual_cph, uint32_t &claimed_cph);
//----------------- i_wallet2_callback --------------------- //----------------- i_wallet2_callback ---------------------
virtual void on_new_block(uint64_t height, const cryptonote::block& block); virtual void on_new_block(uint64_t height, bool last, const cryptonote::block& block);
virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, uint64_t burnt, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time); virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, uint64_t burnt, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time);
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index); virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index);
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index); virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index);

View File

@@ -47,6 +47,7 @@
#include <boost/locale.hpp> #include <boost/locale.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <vector>
using namespace std; using namespace std;
using namespace cryptonote; using namespace cryptonote;
@@ -175,7 +176,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
return m_listener; return m_listener;
} }
virtual void on_new_block(uint64_t height, const cryptonote::block& block) virtual void on_new_block(uint64_t height, bool last, const cryptonote::block& block)
{ {
// Don't flood the GUI with signals. On fast refresh - send signal every 1000th block // Don't flood the GUI with signals. On fast refresh - send signal every 1000th block
// get_refresh_from_block_height() returns the blockheight from when the wallet was // get_refresh_from_block_height() returns the blockheight from when the wallet was
@@ -183,7 +184,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
if(height >= m_wallet->m_wallet->get_refresh_from_block_height() || height % 1000 == 0) { if(height >= m_wallet->m_wallet->get_refresh_from_block_height() || height % 1000 == 0) {
// LOG_PRINT_L3(__FUNCTION__ << ": new block. height: " << height); // LOG_PRINT_L3(__FUNCTION__ << ": new block. height: " << height);
if (m_listener) { if (m_listener) {
m_listener->newBlock(height); m_listener->newBlock(height, last);
} }
} }
} }
@@ -244,10 +245,10 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
} }
// Light wallet callbacks // Light wallet callbacks
virtual void on_lw_new_block(uint64_t height) virtual void on_lw_new_block(uint64_t height, bool last)
{ {
if (m_listener) { if (m_listener) {
m_listener->newBlock(height); m_listener->newBlock(height, last);
} }
} }
@@ -724,6 +725,28 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p
return true; return true;
} }
bool WalletImpl::createFromPolyseed(const std::string &path, const std::string &password, const std::string &seed,
const std::string &passphrase, bool newWallet, uint64_t restoreHeight)
{
clearStatus();
m_recoveringFromSeed = !newWallet;
m_recoveringFromDevice = false;
polyseed::data polyseed(POLYSEED_COIN);
try {
auto lang = polyseed.decode(seed.data());
m_wallet->set_seed_language(lang.name());
m_wallet->generate(path, password, polyseed, passphrase, !newWallet);
}
catch (const std::exception &e) {
setStatusError(e.what());
return false;
}
return true;
}
Wallet::Device WalletImpl::getDeviceType() const Wallet::Device WalletImpl::getDeviceType() const
{ {
return static_cast<Wallet::Device>(m_wallet->get_device_type()); return static_cast<Wallet::Device>(m_wallet->get_device_type());
@@ -834,6 +857,55 @@ std::string WalletImpl::seed(const std::string& seed_offset) const
return std::string(seed.data(), seed.size()); // TODO return std::string(seed.data(), seed.size()); // TODO
} }
bool WalletImpl::getPolyseed(std::string &seed_words, std::string &passphrase) const
{
epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size());
epee::wipeable_string passphrase_epee(passphrase.c_str(), passphrase.size());
clearStatus();
if (!m_wallet) {
return false;
}
bool result = m_wallet->get_polyseed(seed_words_epee, passphrase_epee);
seed_words.assign(seed_words_epee.data(), seed_words_epee.size());
passphrase.assign(passphrase_epee.data(), passphrase_epee.size());
return result;
}
std::vector<std::pair<std::string, std::string>> Wallet::getPolyseedLanguages()
{
std::vector<std::pair<std::string, std::string>> languages;
auto langs = polyseed::get_langs();
for (const auto &lang : langs) {
languages.emplace_back(std::pair<std::string, std::string>(lang.name_en(), lang.name()));
}
return languages;
}
bool Wallet::createPolyseed(std::string &seed_words, std::string &err, const std::string &language)
{
epee::wipeable_string seed_words_epee(seed_words.c_str(), seed_words.size());
try {
polyseed::data polyseed(POLYSEED_COIN);
polyseed.create(0);
polyseed.encode(polyseed::get_lang_by_name(language), seed_words_epee);
seed_words.assign(seed_words_epee.data(), seed_words_epee.size());
}
catch (const std::exception &e) {
err = e.what();
return false;
}
return true;
}
std::string WalletImpl::getSeedLanguage() const std::string WalletImpl::getSeedLanguage() const
{ {
return m_wallet->get_seed_language(); return m_wallet->get_seed_language();
@@ -1658,7 +1730,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat
// - unconfirmed_transfer_details; // - unconfirmed_transfer_details;
// - confirmed_transfer_details) // - confirmed_transfer_details)
PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<string> &dst_addr, const string &payment_id, optional<std::vector<uint64_t>> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<string> &dst_addr, const string &payment_id, optional<std::vector<uint64_t>> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::set<std::string> &preferred_inputs)
{ {
clearStatus(); clearStatus();
@@ -1727,6 +1799,19 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
} }
} }
} }
std::vector<crypto::key_image> preferred_input_list;
if (!preferred_inputs.empty()) {
for (const auto &public_key : preferred_inputs) {
crypto::key_image keyImage;
bool r = epee::string_tools::hex_to_pod(public_key, keyImage);
if (!r) {
error = true;
setStatusError(tr("failed to parse key image"));
break;
}
preferred_input_list.push_back(keyImage);
}
}
if (error) { if (error) {
break; break;
} }
@@ -1741,11 +1826,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
if (amount) { if (amount) {
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count,
adjusted_priority, adjusted_priority,
extra, subaddr_account, subaddr_indices); extra, subaddr_account, subaddr_indices, preferred_input_list);
} else { } else {
transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count, transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count,
adjusted_priority, adjusted_priority,
extra, subaddr_account, subaddr_indices); extra, subaddr_account, subaddr_indices, preferred_input_list);
} }
pendingTxPostProcess(transaction); pendingTxPostProcess(transaction);
@@ -1826,10 +1911,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
} }
PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count, PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::set<std::string> &preferred_inputs)
{ {
return createTransactionMultDest(std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {*amount}) : (optional<std::vector<uint64_t>>()), mixin_count, priority, subaddr_account, subaddr_indices); return createTransactionMultDest(std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {*amount}) : (optional<std::vector<uint64_t>>()), mixin_count, priority, subaddr_account, subaddr_indices, preferred_inputs);
} }
PendingTransaction *WalletImpl::createSweepUnmixableTransaction() PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
@@ -1955,6 +2040,56 @@ AddressBook *WalletImpl::addressBook()
return m_addressBook.get(); return m_addressBook.get();
} }
std::vector<Enote> WalletImpl::enotes()
{
LOG_PRINT_L2("Refreshing coins");
boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet->m_transfers_mutex);
std::vector<Enote> enotes;
for (size_t i = 0; i < m_wallet->get_num_transfer_details(); ++i)
{
const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i);
Enote enote;
enote.idx = i;
enote.blockHeight = td.m_block_height;
enote.hash = epee::string_tools::pod_to_hex(td.m_txid);
enote.internalOutputIndex = td.m_internal_output_index;
enote.globalOutputIndex = td.m_global_output_index;
enote.spent = td.m_spent;
enote.frozen = td.m_frozen;
enote.spentHeight = td.m_spent_height;
enote.amount = td.m_amount;
enote.rct = td.m_rct;
enote.keyImageKnown = td.m_key_image_known;
enote.pkIndex = td.m_pk_index;
enote.subaddrIndex = td.m_subaddr_index.minor;
enote.subaddrAccount = td.m_subaddr_index.major;
enote.address = m_wallet->get_subaddress_as_str(td.m_subaddr_index); // todo: this is expensive, cache maybe?
enote.addressLabel = m_wallet->get_subaddress_label(td.m_subaddr_index);
enote.keyImage = epee::string_tools::pod_to_hex(td.m_key_image);
enote.unlockTime = td.m_tx.unlock_time;
enote.unlocked = m_wallet->is_transfer_unlocked(td);
enote.pubKey = epee::string_tools::pod_to_hex(td.get_public_key());
enote.coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen);
enote.description = m_wallet->get_tx_note(td.m_txid);
enotes.push_back(enote);
}
return enotes;
}
void WalletImpl::freeze(size_t idx) {
m_wallet->freeze(idx);
}
void WalletImpl::thaw(size_t idx) {
m_wallet->thaw(idx);
}
Subaddress *WalletImpl::subaddress() Subaddress *WalletImpl::subaddress()
{ {
return m_subaddress.get(); return m_subaddress.get();

View File

@@ -79,9 +79,19 @@ public:
bool recoverFromDevice(const std::string &path, bool recoverFromDevice(const std::string &path,
const std::string &password, const std::string &password,
const std::string &device_name); const std::string &device_name);
bool createFromPolyseed(const std::string &path,
const std::string &password,
const std::string &seed,
const std::string &passphrase = "",
bool newWallet = true,
uint64_t restoreHeight = 0);
Device getDeviceType() const override; Device getDeviceType() const override;
bool close(bool store = true); bool close(bool store = true);
std::string seed(const std::string& seed_offset = "") const override; std::string seed(const std::string& seed_offset = "") const override;
bool getPolyseed(std::string &seed_words, std::string &passphrase) const override;
std::string getSeedLanguage() const override; std::string getSeedLanguage() const override;
void setSeedLanguage(const std::string &arg) override; void setSeedLanguage(const std::string &arg) override;
// void setListener(Listener *) {} // void setListener(Listener *) {}
@@ -156,12 +166,14 @@ public:
optional<std::vector<uint64_t>> amount, uint32_t mixin_count, optional<std::vector<uint64_t>> amount, uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low, PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0, uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) override; std::set<uint32_t> subaddr_indices = {},
const std::set<std::string> &preferred_inputs = {}) override;
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
optional<uint64_t> amount, uint32_t mixin_count, optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low, PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0, uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) override; std::set<uint32_t> subaddr_indices = {},
const std::set<std::string> &preferred_inputs = {}) override;
virtual PendingTransaction * createSweepUnmixableTransaction() override; virtual PendingTransaction * createSweepUnmixableTransaction() override;
bool submitTransaction(const std::string &fileName) override; bool submitTransaction(const std::string &fileName) override;
virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override;
@@ -183,6 +195,9 @@ public:
PendingTransaction::Priority priority) const override; PendingTransaction::Priority priority) const override;
virtual TransactionHistory * history() override; virtual TransactionHistory * history() override;
virtual AddressBook * addressBook() override; virtual AddressBook * addressBook() override;
virtual std::vector<Enote> enotes() override;
virtual void freeze(size_t idx) override;
virtual void thaw(size_t idx) override;
virtual Subaddress * subaddress() override; virtual Subaddress * subaddress() override;
virtual SubaddressAccount * subaddressAccount() override; virtual SubaddressAccount * subaddressAccount() override;
virtual void setListener(WalletListener * l) override; virtual void setListener(WalletListener * l) override;

View File

@@ -32,6 +32,7 @@
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include <list> #include <list>
#include <set> #include <set>
@@ -261,6 +262,35 @@ struct AddressBook
virtual int lookupPaymentID(const std::string &payment_id) const = 0; virtual int lookupPaymentID(const std::string &payment_id) const = 0;
}; };
/**
* @brief Enote - enote (utxo) information
*/
struct Enote
{
size_t idx;
uint64_t blockHeight;
std::string hash;
size_t internalOutputIndex;
uint64_t globalOutputIndex;
bool spent;
bool frozen;
uint64_t spentHeight;
uint64_t amount;
bool rct;
bool keyImageKnown;
size_t pkIndex;
uint32_t subaddrIndex;
uint32_t subaddrAccount;
std::string address;
std::string addressLabel;
std::string keyImage;
uint64_t unlockTime;
bool unlocked;
std::string pubKey;
bool coinbase;
std::string description;
};
struct SubaddressRow { struct SubaddressRow {
public: public:
SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label): SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label):
@@ -371,8 +401,9 @@ struct WalletListener
/** /**
* @brief newBlock - called when new block received * @brief newBlock - called when new block received
* @param height - block height * @param height - block height
* @param last - true if the block is the last block in the batch
*/ */
virtual void newBlock(uint64_t height) = 0; virtual void newBlock(uint64_t height, bool last) = 0;
/** /**
* @brief updated - generic callback, called when any event (sent/received/block reveived/etc) happened with the wallet; * @brief updated - generic callback, called when any event (sent/received/block reveived/etc) happened with the wallet;
@@ -706,6 +737,10 @@ struct Wallet
static void warning(const std::string &category, const std::string &str); static void warning(const std::string &category, const std::string &str);
static void error(const std::string &category, const std::string &str); static void error(const std::string &category, const std::string &str);
virtual bool getPolyseed(std::string &seed, std::string &passphrase) const = 0;
static bool createPolyseed(std::string &seed_words, std::string &err, const std::string &language = "English");
static std::vector<std::pair<std::string, std::string>> getPolyseedLanguages();
/** /**
* @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds)
*/ */
@@ -849,7 +884,8 @@ struct Wallet
optional<std::vector<uint64_t>> amount, uint32_t mixin_count, optional<std::vector<uint64_t>> amount, uint32_t mixin_count,
PendingTransaction::Priority = PendingTransaction::Priority_Low, PendingTransaction::Priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0, uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) = 0; std::set<uint32_t> subaddr_indices = {},
const std::set<std::string> &preferred_inputs = {}) = 0;
/*! /*!
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
@@ -868,7 +904,8 @@ struct Wallet
optional<uint64_t> amount, uint32_t mixin_count, optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority = PendingTransaction::Priority_Low, PendingTransaction::Priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0, uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) = 0; std::set<uint32_t> subaddr_indices = {},
const std::set<std::string> &preferred_inputs = {}) = 0;
/*! /*!
* \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs.
@@ -980,6 +1017,9 @@ struct Wallet
virtual TransactionHistory * history() = 0; virtual TransactionHistory * history() = 0;
virtual AddressBook * addressBook() = 0; virtual AddressBook * addressBook() = 0;
virtual std::vector<Enote> enotes() = 0;
virtual void freeze(size_t idx) = 0;
virtual void thaw(size_t idx) = 0;
virtual Subaddress * subaddress() = 0; virtual Subaddress * subaddress() = 0;
virtual SubaddressAccount * subaddressAccount() = 0; virtual SubaddressAccount * subaddressAccount() = 0;
virtual void setListener(WalletListener *) = 0; virtual void setListener(WalletListener *) = 0;
@@ -1298,6 +1338,27 @@ struct WalletManager
uint64_t kdf_rounds = 1, uint64_t kdf_rounds = 1,
WalletListener * listener = nullptr) = 0; WalletListener * listener = nullptr) = 0;
/*!
* \brief creates a wallet from a polyseed mnemonic phrase
* \param path Name of the wallet file to be created
* \param password Password of wallet file
* \param nettype Network type
* \param mnemonic Polyseed mnemonic
* \param passphrase Optional seed offset passphrase
* \param newWallet Whether it is a new wallet
* \param restoreHeight Override the embedded restore height
* \param kdf_rounds Number of rounds for key derivation function
* @return
*/
virtual Wallet * createWalletFromPolyseed(const std::string &path,
const std::string &password,
NetworkType nettype,
const std::string &mnemonic,
const std::string &passphrase = "",
bool newWallet = true,
uint64_t restore_height = 0,
uint64_t kdf_rounds = 1) = 0;
/*! /*!
* \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted * \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted
* \param wallet previously opened / created wallet instance * \param wallet previously opened / created wallet instance
@@ -1364,6 +1425,9 @@ struct WalletManager
//! returns current blockchain target height //! returns current blockchain target height
virtual uint64_t blockchainTargetHeight() = 0; virtual uint64_t blockchainTargetHeight() = 0;
//! returns current blockchain and target height
virtual std::pair<uint64_t, uint64_t> blockchainAndTargetHeight() = 0;
//! returns current network difficulty //! returns current network difficulty
virtual uint64_t networkDifficulty() = 0; virtual uint64_t networkDifficulty() = 0;

View File

@@ -156,6 +156,15 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path,
return wallet; return wallet;
} }
Wallet *WalletManagerImpl::createWalletFromPolyseed(const std::string &path, const std::string &password, NetworkType nettype,
const std::string &mnemonic, const std::string &passphrase,
bool newWallet, uint64_t restoreHeight, uint64_t kdf_rounds)
{
WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds);
wallet->createFromPolyseed(path, password, mnemonic, passphrase, newWallet, restoreHeight);
return wallet;
}
bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store) bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store)
{ {
WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet); WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet);
@@ -271,6 +280,18 @@ uint64_t WalletManagerImpl::blockchainTargetHeight()
return ires.target_height >= ires.height ? ires.target_height : ires.height; return ires.target_height >= ires.height ? ires.target_height : ires.height;
} }
std::pair<uint64_t, uint64_t> WalletManagerImpl::blockchainAndTargetHeight()
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
if (!epee::net_utils::invoke_http_json("/getinfo", ireq, ires, m_http_client))
return std::make_pair(0, 0);
uint64_t height = ires.height;
uint64_t target_height = ires.target_height >= ires.height ? ires.target_height : ires.height;
return std::make_pair(height, target_height);
}
uint64_t WalletManagerImpl::networkDifficulty() uint64_t WalletManagerImpl::networkDifficulty()
{ {
cryptonote::COMMAND_RPC_GET_INFO::request ireq; cryptonote::COMMAND_RPC_GET_INFO::request ireq;

View File

@@ -75,6 +75,16 @@ public:
const std::string &subaddressLookahead = "", const std::string &subaddressLookahead = "",
uint64_t kdf_rounds = 1, uint64_t kdf_rounds = 1,
WalletListener * listener = nullptr) override; WalletListener * listener = nullptr) override;
virtual Wallet * createWalletFromPolyseed(const std::string &path,
const std::string &password,
NetworkType nettype,
const std::string &mnemonic,
const std::string &passphrase,
bool newWallet = true,
uint64_t restore_height = 0,
uint64_t kdf_rounds = 1) override;
virtual bool closeWallet(Wallet *wallet, bool store = true) override; virtual bool closeWallet(Wallet *wallet, bool store = true) override;
bool walletExists(const std::string &path) override; bool walletExists(const std::string &path) override;
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override; bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override;
@@ -85,6 +95,7 @@ public:
bool connected(uint32_t *version = NULL) override; bool connected(uint32_t *version = NULL) override;
uint64_t blockchainHeight() override; uint64_t blockchainHeight() override;
uint64_t blockchainTargetHeight() override; uint64_t blockchainTargetHeight() override;
std::pair<uint64_t, uint64_t> blockchainAndTargetHeight() override;
uint64_t networkDifficulty() override; uint64_t networkDifficulty() override;
double miningHashRate() override; double miningHashRate() override;
uint64_t blockTarget() override; uint64_t blockTarget() override;

View File

@@ -92,6 +92,7 @@ using namespace epee;
#include "device/device_cold.hpp" #include "device/device_cold.hpp"
#include "device_trezor/device_trezor.hpp" #include "device_trezor/device_trezor.hpp"
#include "net/socks_connect.h" #include "net/socks_connect.h"
#include "polyseed/include/polyseed.h"
extern "C" extern "C"
{ {
@@ -927,11 +928,6 @@ bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pend
return false; return false;
} }
uint64_t get_outgoing_amount(const cryptonote::transaction &tx, const uint64_t amount_spent)
{
return tx.version == 1 ? get_outs_money_amount(tx) : (amount_spent - tx.rct_signatures.txnFee);
}
tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
{ {
tools::wallet2::tx_construction_data construction_data = ptx.construction_data; tools::wallet2::tx_construction_data construction_data = ptx.construction_data;
@@ -958,6 +954,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra)
return idx + extra; return idx + extra;
} }
bool is_preferred_input(const std::vector<crypto::key_image>& preferred_input_list, const crypto::key_image& input) {
if (!preferred_input_list.empty()) {
auto it = std::find(preferred_input_list.begin(), preferred_input_list.end(), input);
if (it == preferred_input_list.end()) {
return false;
}
}
return true;
}
static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet) static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet)
{ {
shim->get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, std::placeholders::_1); shim->get_tx_pub_key_from_received_outs = std::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, std::placeholders::_1);
@@ -1275,7 +1281,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
m_enable_multisig(false), m_enable_multisig(false),
m_pool_info_query_time(0), m_pool_info_query_time(0),
m_has_ever_refreshed_from_node(false), m_has_ever_refreshed_from_node(false),
m_allow_mismatched_daemon_version(false) m_allow_mismatched_daemon_version(false),
m_polyseed(false)
{ {
set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); set_rpc_client_secret_key(rct::rct2sk(rct::skGen()));
} }
@@ -1462,10 +1469,25 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab
key = cryptonote::encrypt_key(key, passphrase); key = cryptonote::encrypt_key(key, passphrase);
if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language)) if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language))
{ {
std::cout << "Failed to create seed from key for language: " << seed_language << std::endl; std::cout << "Failed to create seed from key for language: " << seed_language << ", falling back to English." << std::endl;
crypto::ElectrumWords::bytes_to_words(key, electrum_words, "English");
}
return true;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::get_polyseed(epee::wipeable_string& polyseed, epee::wipeable_string& passphrase) const
{
if (!m_polyseed) {
return false; return false;
} }
polyseed::data data(POLYSEED_COIN);
data.load(get_account().get_keys().m_polyseed);
data.encode(polyseed::get_lang_by_name(seed_language), polyseed);
passphrase = get_account().get_keys().m_passphrase;
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@@ -2103,12 +2125,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const
return false; return false;
} }
void wallet2::freeze(const crypto::public_key &pk)
{
freeze(get_transfer_details(pk));
}
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::freeze(const crypto::key_image &ki) void wallet2::freeze(const crypto::key_image &ki)
{ {
freeze(get_transfer_details(ki)); freeze(get_transfer_details(ki));
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::thaw(const crypto::public_key &pk)
{
thaw(get_transfer_details(pk));
}
//----------------------------------------------------------------------------------------------------
void wallet2::thaw(const crypto::key_image &ki) void wallet2::thaw(const crypto::key_image &ki)
{ {
thaw(get_transfer_details(ki)); thaw(get_transfer_details(ki));
@@ -2119,6 +2150,18 @@ bool wallet2::frozen(const crypto::key_image &ki) const
return frozen(get_transfer_details(ki)); return frozen(get_transfer_details(ki));
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
size_t wallet2::get_transfer_details(const crypto::public_key &pk) const
{
for (size_t idx = 0; idx < m_transfers.size(); ++idx)
{
const transfer_details &td = m_transfers[idx];
if (td.get_public_key() == pk) {
return idx;
}
}
CHECK_AND_ASSERT_THROW_MES(false, "Public key not found");
}
//----------------------------------------------------------------------------------------------------
size_t wallet2::get_transfer_details(const crypto::key_image &ki) const size_t wallet2::get_transfer_details(const crypto::key_image &ki) const
{ {
for (size_t idx = 0; idx < m_transfers.size(); ++idx) for (size_t idx = 0; idx < m_transfers.size(); ++idx)
@@ -2533,6 +2576,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount; uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
if (!pool) if (!pool)
{ {
boost::unique_lock<boost::shared_mutex> lock(m_transfers_mutex);
m_transfers.push_back(transfer_details{}); m_transfers.push_back(transfer_details{});
transfer_details& td = m_transfers.back(); transfer_details& td = m_transfers.back();
td.m_block_height = height; td.m_block_height = height;
@@ -2636,6 +2680,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t extra_amount = amount - burnt; uint64_t extra_amount = amount - burnt;
if (!pool) if (!pool)
{ {
boost::unique_lock<boost::shared_mutex> lock(m_transfers_mutex);
transfer_details &td = m_transfers[kit->second]; transfer_details &td = m_transfers[kit->second];
td.m_block_height = height; td.m_block_height = height;
td.m_internal_output_index = o; td.m_internal_output_index = o;
@@ -2725,10 +2770,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
LOG_ERROR("spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect"); LOG_ERROR("spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect");
subaddr_account = td.m_subaddr_index.major; subaddr_account = td.m_subaddr_index.major;
subaddr_indices.insert(td.m_subaddr_index.minor); subaddr_indices.insert(td.m_subaddr_index.minor);
LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid);
set_spent(it->second, height);
if (!pool) if (!pool)
{ {
LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid);
set_spent(it->second, height);
if (!ignore_callbacks && 0 != m_callback) if (!ignore_callbacks && 0 != m_callback)
m_callback->on_money_spent(height, txid, tx, amount, tx, td.m_subaddr_index); m_callback->on_money_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
@@ -2817,33 +2862,21 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t fee = miner_tx ? 0 : tx.version == 1 ? tx_money_spent_in_ins - get_outs_money_amount(tx) : tx.rct_signatures.txnFee; uint64_t fee = miner_tx ? 0 : tx.version == 1 ? tx_money_spent_in_ins - get_outs_money_amount(tx) : tx.rct_signatures.txnFee;
if (tx_money_spent_in_ins > 0) if (tx_money_spent_in_ins > 0 && !pool)
{ {
uint64_t self_received = std::accumulate<decltype(tx_money_got_in_outs.begin()), uint64_t>(tx_money_got_in_outs.begin(), tx_money_got_in_outs.end(), 0, uint64_t self_received = std::accumulate<decltype(tx_money_got_in_outs.begin()), uint64_t>(tx_money_got_in_outs.begin(), tx_money_got_in_outs.end(), 0,
[&subaddr_account] (uint64_t acc, const std::pair<cryptonote::subaddress_index, uint64_t>& p) [&subaddr_account] (uint64_t acc, const std::pair<cryptonote::subaddress_index, uint64_t>& p)
{ {
return acc + (p.first.major == *subaddr_account ? p.second : 0); return acc + (p.first.major == *subaddr_account ? p.second : 0);
}); });
if (!pool) process_outgoing(txid, tx, height, ts, tx_money_spent_in_ins, self_received, *subaddr_account, subaddr_indices);
// if sending to yourself at the same subaddress account, set the outgoing payment amount to 0 so that it's less confusing
if (tx_money_spent_in_ins == self_received + fee)
{ {
process_outgoing(txid, tx, height, ts, tx_money_spent_in_ins, self_received, *subaddr_account, subaddr_indices); auto i = m_confirmed_txs.find(txid);
// if sending to yourself at the same subaddress account, set the outgoing payment amount to 0 so that it's less confusing THROW_WALLET_EXCEPTION_IF(i == m_confirmed_txs.end(), error::wallet_internal_error,
if (tx_money_spent_in_ins == self_received + fee) "confirmed tx wasn't found: " + string_tools::pod_to_hex(txid));
{ i->second.m_change = self_received;
auto i = m_confirmed_txs.find(txid);
THROW_WALLET_EXCEPTION_IF(i == m_confirmed_txs.end(), error::wallet_internal_error,
"confirmed tx wasn't found: " + string_tools::pod_to_hex(txid));
i->second.m_change = self_received;
}
}
else if (!m_unconfirmed_txs.count(txid))
{
// Add to unconfirmed txs if not already there (e.g. restoring wallet, or running the wallet in parallel to the sending wallet w/same seed)
add_unconfirmed_tx(txid, tx, tx_money_spent_in_ins, {}/*don't know dests*/, crypto::null_hash/*don't know payment_id*/, self_received, *subaddr_account, subaddr_indices);
auto i = m_unconfirmed_txs.find(txid);
THROW_WALLET_EXCEPTION_IF(i == m_unconfirmed_txs.end(), error::wallet_internal_error,
"unconfirmed tx wasn't found: " + string_tools::pod_to_hex(txid));
i->second.m_amount_out = get_outgoing_amount(tx, tx_money_spent_in_ins);
} }
} }
@@ -2992,7 +3025,10 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
// wallet (eg, we're a cold wallet and the hot wallet sent it). For RCT transactions, // wallet (eg, we're a cold wallet and the hot wallet sent it). For RCT transactions,
// we only see 0 input amounts, so have to deduce amount out from other parameters. // we only see 0 input amounts, so have to deduce amount out from other parameters.
entry.first->second.m_amount_in = spent; entry.first->second.m_amount_in = spent;
entry.first->second.m_amount_out = get_outgoing_amount(tx, spent); if (tx.version == 1)
entry.first->second.m_amount_out = get_outs_money_amount(tx);
else
entry.first->second.m_amount_out = spent - tx.rct_signatures.txnFee;
entry.first->second.m_change = received; entry.first->second.m_change = received;
std::vector<tx_extra_field> tx_extra_fields; std::vector<tx_extra_field> tx_extra_fields;
@@ -3028,7 +3064,7 @@ bool wallet2::should_skip_block(const cryptonote::block &b, uint64_t height) con
return !(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height && height >= m_skip_to_height); return !(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height && height >= m_skip_to_height);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, bool last, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
{ {
THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error,
"block transactions=" + std::to_string(bche.txs.size()) + "block transactions=" + std::to_string(bche.txs.size()) +
@@ -3063,7 +3099,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
m_blockchain.push_back(bl_id); m_blockchain.push_back(bl_id);
if (0 != m_callback) if (0 != m_callback)
m_callback->on_new_block(height, b); m_callback->on_new_block(height, last, b);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity) const void wallet2::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity) const
@@ -3424,9 +3460,11 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
const crypto::hash &bl_id = parsed_blocks[i].hash; const crypto::hash &bl_id = parsed_blocks[i].hash;
const cryptonote::block &bl = parsed_blocks[i].block; const cryptonote::block &bl = parsed_blocks[i].block;
bool last = i == blocks.size() - 1;
if(current_index >= m_blockchain.size()) if(current_index >= m_blockchain.size())
{ {
process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache); process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, last, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
++blocks_added; ++blocks_added;
} }
else if(bl_id != m_blockchain[current_index]) else if(bl_id != m_blockchain[current_index])
@@ -3443,7 +3481,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
std::to_string(reorg_depth)); std::to_string(reorg_depth));
handle_reorg(current_index, output_tracker_cache); handle_reorg(current_index, output_tracker_cache);
process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache); process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, last, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
} }
else else
{ {
@@ -3676,35 +3714,6 @@ bool wallet2::accept_pool_tx_for_processing(const crypto::hash &txid)
// Process an unconfirmed transfer after we know whether it's in the pool or not // Process an unconfirmed transfer after we know whether it's in the pool or not
void wallet2::process_unconfirmed_transfer(bool incremental, const crypto::hash &txid, wallet2::unconfirmed_transfer_details &tx_details, bool seen_in_pool, std::chrono::system_clock::time_point now, bool refreshed) void wallet2::process_unconfirmed_transfer(bool incremental, const crypto::hash &txid, wallet2::unconfirmed_transfer_details &tx_details, bool seen_in_pool, std::chrono::system_clock::time_point now, bool refreshed)
{ {
const auto set_tx_key_images_spent = [&](const bool spent)
{
for (size_t vini = 0; vini < tx_details.m_tx.vin.size(); ++vini)
{
if (tx_details.m_tx.vin[vini].type() != typeid(txin_to_key))
continue;
const crypto::key_image &key_image = boost::get<txin_to_key>(tx_details.m_tx.vin[vini]).k_image;
const auto it_ki = m_key_images.find(key_image);
if (it_ki == m_key_images.end())
continue;
const std::size_t i = it_ki->second;
if (i >= m_transfers.size())
continue;
const transfer_details &td = m_transfers.at(i);
if (td.m_key_image != key_image)
continue;
if (td.m_spent == spent)
continue;
LOG_PRINT_L1("Resetting spent status for output " << vini << ": " << key_image << " (spent=" << spent << ")");
if (spent)
set_spent(i, 0);
else
set_unspent(i);
}
};
// TODO: set tx_propagation_timeout to CRYPTONOTE_DANDELIONPP_EMBARGO_AVERAGE * 3 / 2 after v15 hardfork // TODO: set tx_propagation_timeout to CRYPTONOTE_DANDELIONPP_EMBARGO_AVERAGE * 3 / 2 after v15 hardfork
constexpr const std::chrono::seconds tx_propagation_timeout{500}; constexpr const std::chrono::seconds tx_propagation_timeout{500};
if (seen_in_pool) if (seen_in_pool)
@@ -3714,10 +3723,6 @@ void wallet2::process_unconfirmed_transfer(bool incremental, const crypto::hash
tx_details.m_state = wallet2::unconfirmed_transfer_details::pending_in_pool; tx_details.m_state = wallet2::unconfirmed_transfer_details::pending_in_pool;
MINFO("Pending txid " << txid << " seen in pool, marking as pending in pool"); MINFO("Pending txid " << txid << " seen in pool, marking as pending in pool");
} }
// The inputs are spent, they're in the pool! It's possible the tx was previously marked as failed, so we
// make sure to re-mark the outputs as spent.
set_tx_key_images_spent(true/*spent*/);
} }
else else
{ {
@@ -3743,7 +3748,23 @@ void wallet2::process_unconfirmed_transfer(bool incremental, const crypto::hash
tx_details.m_state = wallet2::unconfirmed_transfer_details::failed; tx_details.m_state = wallet2::unconfirmed_transfer_details::failed;
// the inputs aren't spent anymore, since the tx failed // the inputs aren't spent anymore, since the tx failed
set_tx_key_images_spent(false/*spent*/); for (size_t vini = 0; vini < tx_details.m_tx.vin.size(); ++vini)
{
if (tx_details.m_tx.vin[vini].type() == typeid(txin_to_key))
{
txin_to_key &tx_in_to_key = boost::get<txin_to_key>(tx_details.m_tx.vin[vini]);
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details &td = m_transfers[i];
if (td.m_key_image == tx_in_to_key.k_image)
{
LOG_PRINT_L1("Resetting spent status for output " << vini << ": " << td.m_key_image);
set_unspent(i);
break;
}
}
}
}
} }
} }
} }
@@ -4018,7 +4039,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
if (0 != m_callback) if (0 != m_callback)
{ // FIXME: this isn't right, but simplewallet just logs that we got a block. { // FIXME: this isn't right, but simplewallet just logs that we got a block.
cryptonote::block dummy; cryptonote::block dummy;
m_callback->on_new_block(current_index, dummy); m_callback->on_new_block(current_index, false, dummy);
} }
} }
else if(bl_id != m_blockchain[current_index]) else if(bl_id != m_blockchain[current_index])
@@ -4113,7 +4134,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
if(m_light_wallet_blockchain_height != prev_height) if(m_light_wallet_blockchain_height != prev_height)
{ {
MDEBUG("new block since last time!"); MDEBUG("new block since last time!");
m_callback->on_lw_new_block(m_light_wallet_blockchain_height - 1); // this fork does not support light wallets, so `last` param is just set to false
m_callback->on_lw_new_block(m_light_wallet_blockchain_height - 1, false);
} }
m_light_wallet_connected = true; m_light_wallet_connected = true;
MDEBUG("lw scanned block height: " << m_light_wallet_scanned_block_height); MDEBUG("lw scanned block height: " << m_light_wallet_scanned_block_height);
@@ -4128,18 +4150,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// Lighwallet refresh done // Lighwallet refresh done
return; return;
} }
if (!m_first_refresh_done)
{
// We want to process the whole pool again, in case we identify received outputs in the chain we might have spent in the pool
m_pool_info_query_time = 0;
m_scanned_pool_txs[0].clear();
m_scanned_pool_txs[1].clear();
// Clear unconfirmed (received) payments because the data is 100% recovered when scanning
m_unconfirmed_payments.clear();
// Don't clear unconfirmed (sent) txs because some data is not recover-able when scanning (dests)
}
received_money = false; received_money = false;
blocks_fetched = 0; blocks_fetched = 0;
uint64_t added_blocks = 0; uint64_t added_blocks = 0;
@@ -4871,6 +4881,9 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const crypt
value2.SetInt(m_enable_multisig ? 1 : 0); value2.SetInt(m_enable_multisig ? 1 : 0);
json.AddMember("enable_multisig", value2, json.GetAllocator()); json.AddMember("enable_multisig", value2, json.GetAllocator());
value2.SetInt(m_polyseed ? 1 : 0);
json.AddMember("polyseed", value2, json.GetAllocator());
if (m_background_sync_type == BackgroundSyncCustomPassword && !background_keys_file && m_custom_background_key) if (m_background_sync_type == BackgroundSyncCustomPassword && !background_keys_file && m_custom_background_key)
{ {
value.SetString(reinterpret_cast<const char*>(m_custom_background_key.get().data()), m_custom_background_key.get().size()); value.SetString(reinterpret_cast<const char*>(m_custom_background_key.get().data()), m_custom_background_key.get().size());
@@ -5109,6 +5122,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
m_credits_target = 0; m_credits_target = 0;
m_enable_multisig = false; m_enable_multisig = false;
m_allow_mismatched_daemon_version = false; m_allow_mismatched_daemon_version = false;
m_polyseed = false;
m_custom_background_key = boost::none; m_custom_background_key = boost::none;
} }
else if(json.IsObject()) else if(json.IsObject())
@@ -5350,6 +5364,9 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, background_sync_type, BackgroundSyncType, Int, false, BackgroundSyncOff); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, background_sync_type, BackgroundSyncType, Int, false, BackgroundSyncOff);
m_background_sync_type = field_background_sync_type; m_background_sync_type = field_background_sync_type;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, polyseed, int, Int, false, false);
m_polyseed = field_polyseed;
// Load encryption key used to encrypt background cache // Load encryption key used to encrypt background cache
crypto::chacha_key custom_background_key; crypto::chacha_key custom_background_key;
m_custom_background_key = boost::none; m_custom_background_key = boost::none;
@@ -5669,6 +5686,48 @@ void wallet2::init_type(hw::device::device_type device_type)
m_key_device_type = device_type; m_key_device_type = device_type;
} }
/*!
* \brief Generates a polyseed wallet or restores one.
* \param wallet_ Name of wallet file
* \param password Password of wallet file
* \param passphrase Seed offset passphrase
* \param recover Whether it is a restore
* \param seed_words If it is a restore, the polyseed
* \param create_address_file Whether to create an address file
* \return The secret key of the generated wallet
*/
void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
const polyseed::data &seed, const epee::wipeable_string& passphrase, bool recover, uint64_t restoreHeight, bool create_address_file)
{
clear();
prepare_file_names(wallet_);
if (!wallet_.empty()) {
boost::system::error_code ignored_ec;
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
}
m_account.create_from_polyseed(seed, passphrase);
init_type(hw::device::device_type::SOFTWARE);
m_polyseed = true;
setup_keys(password);
if (recover) {
m_refresh_from_block_height = estimate_blockchain_height(restoreHeight > 0 ? restoreHeight : seed.birthday());
} else {
m_refresh_from_block_height = estimate_blockchain_height();
}
create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
setup_new_blockchain();
if (!wallet_.empty())
store();
}
/*! /*!
* \brief Generates a wallet or restores one. Assumes the multisig setup * \brief Generates a wallet or restores one. Assumes the multisig setup
* has already completed for the provided multisig info. * has already completed for the provided multisig info.
@@ -5796,7 +5855,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
return retval; return retval;
} }
uint64_t wallet2::estimate_blockchain_height() uint64_t wallet2::estimate_blockchain_height(uint64_t time)
{ {
// -1 month for fluctuations in block time and machine date/time setup. // -1 month for fluctuations in block time and machine date/time setup.
// avg seconds per block // avg seconds per block
@@ -5820,7 +5879,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
// the daemon is currently syncing. // the daemon is currently syncing.
// If we use the approximate height we subtract one month as // If we use the approximate height we subtract one month as
// a safety margin. // a safety margin.
height = get_approximate_blockchain_height(); height = get_approximate_blockchain_height(time);
uint64_t target_height = get_daemon_blockchain_target_height(err); uint64_t target_height = get_daemon_blockchain_target_height(err);
if (err.empty()) { if (err.empty()) {
if (target_height < height) if (target_height < height)
@@ -7534,9 +7593,9 @@ uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> un
return found_money; return found_money;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::add_unconfirmed_tx(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices) void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices)
{ {
unconfirmed_transfer_details& utd = m_unconfirmed_txs[txid]; unconfirmed_transfer_details& utd = m_unconfirmed_txs[cryptonote::get_transaction_hash(tx)];
utd.m_amount_in = amount_in; utd.m_amount_in = amount_in;
utd.m_amount_out = 0; utd.m_amount_out = 0;
for (const auto &d: dests) for (const auto &d: dests)
@@ -7649,7 +7708,7 @@ void wallet2::commit_tx(pending_tx& ptx)
for(size_t idx: ptx.selected_transfers) for(size_t idx: ptx.selected_transfers)
amount_in += m_transfers[idx].amount(); amount_in += m_transfers[idx].amount();
} }
add_unconfirmed_tx(txid, ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount, ptx.construction_data.subaddr_account, ptx.construction_data.subaddr_indices); add_unconfirmed_tx(ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount, ptx.construction_data.subaddr_account, ptx.construction_data.subaddr_indices);
if (store_tx_info() && ptx.tx_key != crypto::null_skey) if (store_tx_info() && ptx.tx_key != crypto::null_skey)
{ {
m_tx_keys[txid] = ptx.tx_key; m_tx_keys[txid] = ptx.tx_key;
@@ -10432,7 +10491,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
LOG_PRINT_L2("transfer_selected_rct done"); LOG_PRINT_L2("transfer_selected_rct done");
} }
std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list)
{ {
std::vector<size_t> picks; std::vector<size_t> picks;
float current_output_relatdness = 1.0f; float current_output_relatdness = 1.0f;
@@ -10443,6 +10502,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for (size_t i = 0; i < m_transfers.size(); ++i) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[i]; const transfer_details& td = m_transfers[i];
if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
continue;
}
if (!is_spent(td, false) && !td.m_frozen && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) if (!is_spent(td, false) && !td.m_frozen && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{ {
if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
@@ -10463,6 +10525,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for (size_t i = 0; i < m_transfers.size(); ++i) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[i]; const transfer_details& td = m_transfers[i];
if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
continue;
}
if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{ {
if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
@@ -10474,6 +10539,9 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for (size_t j = i + 1; j < m_transfers.size(); ++j) for (size_t j = i + 1; j < m_transfers.size(); ++j)
{ {
const transfer_details& td2 = m_transfers[j]; const transfer_details& td2 = m_transfers[j];
if (!is_preferred_input(preferred_input_list, td2.m_key_image)) {
continue;
}
if (td2.amount() > m_ignore_outputs_above || td2.amount() < m_ignore_outputs_below) if (td2.amount() > m_ignore_outputs_above || td2.amount() < m_ignore_outputs_below)
{ {
MDEBUG("Ignoring output " << j << " of amount " << print_money(td2.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]"); MDEBUG("Ignoring output " << j << " of amount " << print_money(td2.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]");
@@ -11046,7 +11114,7 @@ bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image,
// This system allows for sending (almost) the entire balance, since it does // This system allows for sending (almost) the entire balance, since it does
// not generate spurious change in all txes, thus decreasing the instantaneous // not generate spurious change in all txes, thus decreasing the instantaneous
// usable balance. // usable balance.
std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs) std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list, const unique_index_container& subtract_fee_from_outputs)
{ {
//ensure device is let in NONE mode in any case //ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device(); hw::device &hwdev = m_account.get_device();
@@ -11255,6 +11323,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
for (size_t i = 0; i < m_transfers.size(); ++i) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[i]; const transfer_details& td = m_transfers[i];
if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
continue;
}
if (m_ignore_fractional_outputs && td.amount() < fractional_threshold) if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
{ {
MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below fractional threshold " << print_money(fractional_threshold)); MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below fractional threshold " << print_money(fractional_threshold));
@@ -11340,7 +11411,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// will get us a known fee. // will get us a known fee.
uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, bulletproof_plus_full_commit, use_view_tags, base_fee, fee_quantization_mask); uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, bulletproof_plus_full_commit, use_view_tags, base_fee, fee_quantization_mask);
total_needed_money = needed_money + (subtract_fee_from_outputs.size() ? 0 : estimated_fee); total_needed_money = needed_money + (subtract_fee_from_outputs.size() ? 0 : estimated_fee);
preferred_inputs = pick_preferred_rct_inputs(total_needed_money, subaddr_account, subaddr_indices); preferred_inputs = pick_preferred_rct_inputs(total_needed_money, subaddr_account, subaddr_indices, preferred_input_list);
if (!preferred_inputs.empty()) if (!preferred_inputs.empty())
{ {
string s; string s;
@@ -11819,7 +11890,7 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, c
return true; return true;
} }
std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list)
{ {
std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices; std::vector<size_t> unused_dust_indices;
@@ -11849,6 +11920,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
for (size_t i = 0; i < m_transfers.size(); ++i) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[i]; const transfer_details& td = m_transfers[i];
if (!is_preferred_input(preferred_input_list, td.m_key_image)) {
continue;
}
if (m_ignore_fractional_outputs && td.amount() < fractional_threshold) if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
{ {
MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold)); MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold));
@@ -13631,7 +13705,7 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err)
return target_height; return target_height;
} }
uint64_t wallet2::get_approximate_blockchain_height() const uint64_t wallet2::get_approximate_blockchain_height(uint64_t t) const
{ {
uint64_t approx_blockchain_height = m_nettype == TESTNET ? 0 : (time(NULL) - 1522624244)/307; uint64_t approx_blockchain_height = m_nettype == TESTNET ? 0 : (time(NULL) - 1522624244)/307;
LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height); LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
@@ -15741,6 +15815,21 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day) uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day)
{ {
std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 };
date.tm_year = year - 1900;
date.tm_mon = month - 1;
date.tm_mday = day;
if (date.tm_mon < 0 || 11 < date.tm_mon || date.tm_mday < 1 || 31 < date.tm_mday)
{
throw std::runtime_error("month or day out of range");
}
uint64_t timestamp_target = std::mktime(&date);
return get_blockchain_height_by_timestamp(timestamp_target);
}
uint64_t wallet2::get_blockchain_height_by_timestamp(uint64_t timestamp_target) {
uint32_t version; uint32_t version;
if (!check_connection(&version)) if (!check_connection(&version))
{ {
@@ -15750,15 +15839,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
{ {
throw std::runtime_error("this function requires RPC version 1.6 or higher"); throw std::runtime_error("this function requires RPC version 1.6 or higher");
} }
std::tm date = { 0, 0, 0, 0, 0, 0, 0, 0 };
date.tm_year = year - 1900;
date.tm_mon = month - 1;
date.tm_mday = day;
if (date.tm_mon < 0 || 11 < date.tm_mon || date.tm_mday < 1 || 31 < date.tm_mday)
{
throw std::runtime_error("month or day out of range");
}
uint64_t timestamp_target = std::mktime(&date);
std::string err; std::string err;
uint64_t height_min = 0; uint64_t height_min = 0;
uint64_t height_max = get_daemon_blockchain_height(err) - 1; uint64_t height_max = get_daemon_blockchain_height(err) - 1;

View File

@@ -72,6 +72,7 @@
#include "message_store.h" #include "message_store.h"
#include "wallet_light_rpc.h" #include "wallet_light_rpc.h"
#include "wallet_rpc_helpers.h" #include "wallet_rpc_helpers.h"
#include "polyseed/polyseed.hpp"
#undef MONERO_DEFAULT_LOG_CATEGORY #undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
@@ -138,7 +139,7 @@ private:
{ {
public: public:
// Full wallet callbacks // Full wallet callbacks
virtual void on_new_block(uint64_t height, const cryptonote::block& block) {} virtual void on_new_block(uint64_t height, bool last, const cryptonote::block& block) {}
virtual void on_reorg(uint64_t height, uint64_t blocks_detached, size_t transfers_detached) {} virtual void on_reorg(uint64_t height, uint64_t blocks_detached, size_t transfers_detached) {}
virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, uint64_t burnt, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) {} virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, uint64_t burnt, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) {}
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {} virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {}
@@ -146,7 +147,7 @@ private:
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {} virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {}
virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason) { return boost::none; } virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason) { return boost::none; }
// Light wallet callbacks // Light wallet callbacks
virtual void on_lw_new_block(uint64_t height) {} virtual void on_lw_new_block(uint64_t height, bool last) {}
virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {} virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {} virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {} virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
@@ -921,6 +922,20 @@ private:
void generate(const std::string& wallet_, const epee::wipeable_string& password, void generate(const std::string& wallet_, const epee::wipeable_string& password,
const epee::wipeable_string& multisig_data, bool create_address_file = false); const epee::wipeable_string& multisig_data, bool create_address_file = false);
/*!
* \brief Generates a wallet from a polyseed.
* @param wallet_ Name of wallet file
* @param password Password of wallet file
* @param seed Polyseed data
* @param passphrase Optional seed offset passphrase
* @param recover Whether it is a restore
* @param restoreHeight Override the embedded restore height
* @param create_address_file Whether to create an address file
*/
void generate(const std::string& wallet_, const epee::wipeable_string& password,
const polyseed::data &seed, const epee::wipeable_string& passphrase = "",
bool recover = false, uint64_t restoreHeight = 0, bool create_address_file = false);
/*! /*!
* \brief Generates a wallet or restores one. * \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file * \param wallet_ Name of wallet file
@@ -1093,6 +1108,15 @@ private:
bool is_deterministic() const; bool is_deterministic() const;
bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const;
/*!
* \brief get_polyseed Gets the polyseed (if available) and passphrase (if set) needed to recover the wallet.
* @param seed Polyseed mnemonic phrase
* @param passphrase Seed offset passphrase that was used to restore the wallet
* @return Returns true if the wallet has a polyseed.
* Note: both the mnemonic phrase and the passphrase are needed to recover the wallet
*/
bool get_polyseed(epee::wipeable_string& seed, epee::wipeable_string &passphrase) const;
/*! /*!
* \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned. * \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned.
*/ */
@@ -1196,8 +1220,8 @@ private:
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const; bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL); bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func); bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list = {}, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list = {});
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra); std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra); std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra);
bool sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, const std::vector<cryptonote::tx_destination_entry>& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const; bool sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, const std::vector<cryptonote::tx_destination_entry>& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const;
@@ -1549,6 +1573,7 @@ private:
uint64_t get_num_rct_outputs(); uint64_t get_num_rct_outputs();
size_t get_num_transfer_details() const { return m_transfers.size(); } size_t get_num_transfer_details() const { return m_transfers.size(); }
const transfer_details &get_transfer_details(size_t idx) const; const transfer_details &get_transfer_details(size_t idx) const;
size_t get_transfer_details(const crypto::public_key &pk) const;
uint8_t get_current_hard_fork(); uint8_t get_current_hard_fork();
void get_hard_fork_info(uint8_t version, uint64_t &earliest_height); void get_hard_fork_info(uint8_t version, uint64_t &earliest_height);
@@ -1566,8 +1591,8 @@ private:
/*! /*!
* \brief Calculates the approximate blockchain height from current date/time. * \brief Calculates the approximate blockchain height from current date/time.
*/ */
uint64_t get_approximate_blockchain_height() const; uint64_t get_approximate_blockchain_height(uint64_t time = 0) const;
uint64_t estimate_blockchain_height(); uint64_t estimate_blockchain_height(uint64_t time = 0);
std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct); std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct);
std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f); std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f);
std::vector<size_t> select_available_unmixable_outputs(); std::vector<size_t> select_available_unmixable_outputs();
@@ -1659,6 +1684,7 @@ private:
bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error); bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error);
uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31 uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31
uint64_t get_blockchain_height_by_timestamp(uint64_t timestamp);
bool is_synced(); bool is_synced();
@@ -1777,7 +1803,9 @@ private:
void freeze(size_t idx); void freeze(size_t idx);
void thaw(size_t idx); void thaw(size_t idx);
bool frozen(size_t idx) const; bool frozen(size_t idx) const;
void freeze(const crypto::public_key &pk);
void freeze(const crypto::key_image &ki); void freeze(const crypto::key_image &ki);
void thaw(const crypto::public_key &pk);
void thaw(const crypto::key_image &ki); void thaw(const crypto::key_image &ki);
bool frozen(const crypto::key_image &ki) const; bool frozen(const crypto::key_image &ki) const;
bool frozen(const transfer_details &td) const; bool frozen(const transfer_details &td) const;
@@ -1818,6 +1846,8 @@ private:
static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; }
boost::shared_mutex m_transfers_mutex;
private: private:
/*! /*!
* \brief Stores wallet information to wallet file. * \brief Stores wallet information to wallet file.
@@ -1846,7 +1876,7 @@ private:
void load_wallet_cache(const bool use_fs, const std::string& cache_buf = ""); void load_wallet_cache(const bool use_fs, const std::string& cache_buf = "");
void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL, bool ignore_callbacks = false); void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL, bool ignore_callbacks = false);
bool should_skip_block(const cryptonote::block &b, uint64_t height) const; bool should_skip_block(const cryptonote::block &b, uint64_t height) const;
void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, bool last, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
detached_blockchain_data detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); detached_blockchain_data detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
void handle_reorg(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void handle_reorg(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const; void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const;
@@ -1875,7 +1905,7 @@ private:
bool prepare_file_names(const std::string& file_path); bool prepare_file_names(const std::string& file_path);
void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height); void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height);
void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices); void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices);
void add_unconfirmed_tx(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices); void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices);
void generate_genesis(cryptonote::block& b) const; void generate_genesis(cryptonote::block& b) const;
void check_genesis(const crypto::hash& genesis_hash) const; //throws void check_genesis(const crypto::hash& genesis_hash) const; //throws
bool generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const; bool generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const;
@@ -1889,7 +1919,7 @@ private:
std::vector<uint64_t> get_unspent_amounts_vector(bool strict); std::vector<uint64_t> get_unspent_amounts_vector(bool strict);
uint64_t get_dynamic_base_fee_estimate(); uint64_t get_dynamic_base_fee_estimate();
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const; float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices); std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices, const std::vector<crypto::key_image>& preferred_input_list);
void set_spent(size_t idx, uint64_t height); void set_spent(size_t idx, uint64_t height);
void set_unspent(size_t idx); void set_unspent(size_t idx);
bool is_spent(const transfer_details &td, bool strict = true) const; bool is_spent(const transfer_details &td, bool strict = true) const;
@@ -2004,6 +2034,7 @@ private:
std::string seed_language; /*!< Language of the mnemonics (seed). */ std::string seed_language; /*!< Language of the mnemonics (seed). */
bool is_old_file_format; /*!< Whether the wallet file is of an old file format */ bool is_old_file_format; /*!< Whether the wallet file is of an old file format */
bool m_watch_only; /*!< no spend key */ bool m_watch_only; /*!< no spend key */
bool m_polyseed;
bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */ bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */
uint32_t m_multisig_threshold; uint32_t m_multisig_threshold;
std::vector<crypto::public_key> m_multisig_signers; std::vector<crypto::public_key> m_multisig_signers;

View File

@@ -1261,7 +1261,7 @@ namespace tools
{ {
uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
uint32_t priority = m_wallet->adjust_priority(req.priority); uint32_t priority = m_wallet->adjust_priority(req.priority);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices, req.subtract_fee_from_outputs); std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices, {}, req.subtract_fee_from_outputs);
if (ptx_vector.empty()) if (ptx_vector.empty())
{ {

View File

@@ -29,7 +29,6 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import print_function from __future__ import print_function
import json
import random import random
"""Test multisig transfers """Test multisig transfers
@@ -409,37 +408,10 @@ class MultisigTest():
assert len(res.tx_hash_list) == 1 assert len(res.tx_hash_list) == 1
txid = res.tx_hash_list[0] txid = res.tx_hash_list[0]
# Retrieve spent key images from daemon
res = daemon.get_transactions([txid], decode_as_json = True)
assert len(res.txs) == 1
tx = res.txs[0]
assert tx.tx_hash == txid
assert len(tx.as_json) > 0
try:
j = json.loads(tx.as_json)
except:
j = None
assert j
assert len(j['vin']) >= 1
spent_key_images = [vin['key']['k_image'] for vin in j['vin']]
assert len(spent_key_images) == len(j['vin'])
for i in range(len(self.wallet)): for i in range(len(self.wallet)):
# Check if the wallet knows about any spent key images (all signers *should*, non-signers *might*)
is_a_signer = len([x for x in signers if x == i]) > 0
knows_key_image = False
for ki in spent_key_images:
try:
res = self.wallet[i].frozen(ki)
knows_key_image = True
except AssertionError:
if is_a_signer:
raise ValueError('Signer should know about spent key image')
pass
self.wallet[i].refresh() self.wallet[i].refresh()
res = self.wallet[i].get_transfers() res = self.wallet[i].get_transfers()
# Any wallet that knows about any spent key images should be able to detect the spend in the pool assert len([x for x in (res['pending'] if 'pending' in res else []) if x.txid == txid]) == (1 if i == signers[-1] else 0)
assert len([x for x in (res['pending'] if 'pending' in res else []) if x.txid == txid]) == (1 if knows_key_image else 0)
assert len([x for x in (res['out'] if 'out' in res else []) if x.txid == txid]) == 0 assert len([x for x in (res['out'] if 'out' in res else []) if x.txid == txid]) == 0
daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1) daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1)
@@ -535,13 +507,9 @@ class MultisigTest():
txid = res.tx_hash_list[0] txid = res.tx_hash_list[0]
for i in range(len(self.wallet)): for i in range(len(self.wallet)):
# Make sure wallet knows about the key image
frozen = self.wallet[i].frozen(ki).frozen
assert not frozen
self.wallet[i].refresh() self.wallet[i].refresh()
res = self.wallet[i].get_transfers() res = self.wallet[i].get_transfers()
# Since all wallets should have key image, all wallets should be able to detect the spend in the pool assert len([x for x in (res['pending'] if 'pending' in res else []) if x.txid == txid]) == (1 if i == signers[-1] else 0)
assert len([x for x in (res['pending'] if 'pending' in res else []) if x.txid == txid]) == 1
assert len([x for x in (res['out'] if 'out' in res else []) if x.txid == txid]) == 0 assert len([x for x in (res['out'] if 'out' in res else []) if x.txid == txid]) == 0
daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1) daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1)

View File

@@ -58,17 +58,6 @@ def diff_incoming_transfers(actual_transfers, expected_transfers):
# wallet2 m_transfers container is ordered and order should be the same across rescans # wallet2 m_transfers container is ordered and order should be the same across rescans
diff_transfers(actual_transfers, expected_transfers, ignore_order = False) diff_transfers(actual_transfers, expected_transfers, ignore_order = False)
def restore_wallet(wallet, seed, restore_height = 0, filename = '', password = ''):
try: wallet.close_wallet()
except: pass
if filename != '':
util_resources.remove_wallet_files(filename)
wallet.auto_refresh(enable = False)
wallet.restore_deterministic_wallet(seed = seed, restore_height = restore_height, filename = filename, password = password)
res = wallet.get_transfers()
assert not 'in' in res or len(res['in']) == 0
assert not 'out' in res or len(res.out) == 0
class TransferTest(): class TransferTest():
def run_test(self): def run_test(self):
self.reset() self.reset()
@@ -89,7 +78,6 @@ class TransferTest():
self.check_background_sync() self.check_background_sync()
self.check_background_sync_reorg_recovery() self.check_background_sync_reorg_recovery()
self.check_subaddress_lookahead() self.check_subaddress_lookahead()
self.check_pool_scanner()
def reset(self): def reset(self):
print('Resetting blockchain') print('Resetting blockchain')
@@ -277,7 +265,6 @@ class TransferTest():
assert len(res.multisig_txset) == 0 assert len(res.multisig_txset) == 0
assert len(res.unsigned_txset) == 0 assert len(res.unsigned_txset) == 0
tx_blob = res.tx_blob tx_blob = res.tx_blob
running_balances[0] -= 1000000000000 + fee
res = daemon.send_raw_transaction(tx_blob) res = daemon.send_raw_transaction(tx_blob)
assert res.not_relayed == False assert res.not_relayed == False
@@ -319,6 +306,7 @@ class TransferTest():
daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1) daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1)
res = daemon.getlastblockheader() res = daemon.getlastblockheader()
running_balances[0] -= 1000000000000 + fee
running_balances[0] += res.block_header.reward running_balances[0] += res.block_header.reward
self.wallet[1].refresh() self.wallet[1].refresh()
running_balances[1] += 1000000000000 running_balances[1] += 1000000000000
@@ -1166,6 +1154,14 @@ class TransferTest():
except: invalid_password = True except: invalid_password = True
assert invalid_password assert invalid_password
def restore_wallet(wallet, seed, filename = '', password = ''):
wallet.close_wallet()
if filename != '':
util_resources.remove_wallet_files(filename)
wallet.restore_deterministic_wallet(seed = seed, filename = filename, password = password)
wallet.auto_refresh(enable = False)
assert wallet.get_transfers() == {}
def assert_correct_transfers(wallet, expected_transfers, expected_inc_transfers, expected_balance): def assert_correct_transfers(wallet, expected_transfers, expected_inc_transfers, expected_balance):
diff_transfers(wallet.get_transfers(), expected_transfers) diff_transfers(wallet.get_transfers(), expected_transfers)
diff_incoming_transfers(wallet.incoming_transfers(transfer_type = 'all'), expected_inc_transfers) diff_incoming_transfers(wallet.incoming_transfers(transfer_type = 'all'), expected_inc_transfers)
@@ -1175,7 +1171,10 @@ class TransferTest():
# We're testing a sweep because it makes sure background sync can # We're testing a sweep because it makes sure background sync can
# properly pick up txs which do not have a change output back to sender. # properly pick up txs which do not have a change output back to sender.
sender_wallet = self.wallet[0] sender_wallet = self.wallet[0]
restore_wallet(sender_wallet, seeds[0]) try: sender_wallet.close_wallet()
except: pass
sender_wallet.restore_deterministic_wallet(seed = seeds[0])
sender_wallet.auto_refresh(enable = False)
sender_wallet.refresh() sender_wallet.refresh()
res = sender_wallet.incoming_transfers(transfer_type = 'available') res = sender_wallet.incoming_transfers(transfer_type = 'available')
unlocked = [x for x in res.transfers if x.unlocked and x.amount > 0] unlocked = [x for x in res.transfers if x.unlocked and x.amount > 0]
@@ -1194,7 +1193,10 @@ class TransferTest():
# set up receiver_wallet # set up receiver_wallet
receiver_wallet = self.wallet[1] receiver_wallet = self.wallet[1]
restore_wallet(receiver_wallet, seeds[1]) try: receiver_wallet.close_wallet()
except: pass
receiver_wallet.restore_deterministic_wallet(seed = seeds[1])
receiver_wallet.auto_refresh(enable = False)
receiver_wallet.refresh() receiver_wallet.refresh()
res = receiver_wallet.get_transfers() res = receiver_wallet.get_transfers()
in_len = 0 if 'in' not in res else len(res['in']) in_len = 0 if 'in' not in res else len(res['in'])
@@ -1265,7 +1267,7 @@ class TransferTest():
# Check stopping a wallet with wallet files saved to disk # Check stopping a wallet with wallet files saved to disk
for background_sync_type in [reuse_password, custom_password]: for background_sync_type in [reuse_password, custom_password]:
restore_wallet(sender_wallet, seeds[0], filename = 'test1', password = 'test_password') restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
background_cache_password = None if background_sync_type == reuse_password else 'background_password' background_cache_password = None if background_sync_type == reuse_password else 'background_password'
sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password) sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
sender_wallet.start_background_sync() sender_wallet.start_background_sync()
@@ -1277,7 +1279,7 @@ class TransferTest():
# Close wallet while background syncing, then reopen # Close wallet while background syncing, then reopen
for background_sync_type in [reuse_password, custom_password]: for background_sync_type in [reuse_password, custom_password]:
restore_wallet(sender_wallet, seeds[0], filename = 'test1', password = 'test_password') restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
background_cache_password = None if background_sync_type == reuse_password else 'background_password' background_cache_password = None if background_sync_type == reuse_password else 'background_password'
sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password) sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
sender_wallet.start_background_sync() sender_wallet.start_background_sync()
@@ -1291,7 +1293,7 @@ class TransferTest():
# Close wallet while syncing normally, then reopen # Close wallet while syncing normally, then reopen
for background_sync_type in [reuse_password, custom_password]: for background_sync_type in [reuse_password, custom_password]:
restore_wallet(sender_wallet, seeds[0], filename = 'test1', password = 'test_password') restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
background_cache_password = None if background_sync_type == reuse_password else 'background_password' background_cache_password = None if background_sync_type == reuse_password else 'background_password'
sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password) sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
sender_wallet.refresh() sender_wallet.refresh()
@@ -1303,7 +1305,7 @@ class TransferTest():
# Create background cache using custom password, then use it to sync, then reopen main wallet # Create background cache using custom password, then use it to sync, then reopen main wallet
for background_cache_password in ['background_password', '']: for background_cache_password in ['background_password', '']:
restore_wallet(sender_wallet, seeds[0], filename = 'test1', password = 'test_password') restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
assert not util_resources.file_exists('test1.background') assert not util_resources.file_exists('test1.background')
assert not util_resources.file_exists('test1.background.keys') assert not util_resources.file_exists('test1.background.keys')
sender_wallet.setup_background_sync(background_sync_type = custom_password, wallet_password = 'test_password', background_cache_password = background_cache_password) sender_wallet.setup_background_sync(background_sync_type = custom_password, wallet_password = 'test_password', background_cache_password = background_cache_password)
@@ -1319,7 +1321,7 @@ class TransferTest():
assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
# Check that main wallet keeps background cache encrypted with custom password in sync # Check that main wallet keeps background cache encrypted with custom password in sync
restore_wallet(sender_wallet, seeds[0], filename = 'test1', password = 'test_password') restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = 'background_password') sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = 'background_password')
sender_wallet.refresh() sender_wallet.refresh()
assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
@@ -1328,7 +1330,7 @@ class TransferTest():
assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
# Try using wallet password as custom background password # Try using wallet password as custom background password
restore_wallet(sender_wallet, seeds[0], filename = 'test1', password = 'test_password') restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
assert not util_resources.file_exists('test1.background') assert not util_resources.file_exists('test1.background')
assert not util_resources.file_exists('test1.background.keys') assert not util_resources.file_exists('test1.background.keys')
same_password = False same_password = False
@@ -1340,7 +1342,7 @@ class TransferTest():
# Turn off background sync # Turn off background sync
for background_sync_type in [reuse_password, custom_password]: for background_sync_type in [reuse_password, custom_password]:
restore_wallet(sender_wallet, seeds[0], filename = 'test1', password = 'test_password') restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
background_cache_password = None if background_sync_type == reuse_password else 'background_password' background_cache_password = None if background_sync_type == reuse_password else 'background_password'
sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password) sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
if background_sync_type == custom_password: if background_sync_type == custom_password:
@@ -1365,7 +1367,8 @@ class TransferTest():
sender_wallet.open_wallet('test1', password = 'test_password') sender_wallet.open_wallet('test1', password = 'test_password')
# Sanity check against outgoing wallet restored at height 0 # Sanity check against outgoing wallet restored at height 0
restore_wallet(sender_wallet, seeds[0], restore_height = 0) sender_wallet.close_wallet()
sender_wallet.restore_deterministic_wallet(seed = seeds[0], restore_height = 0)
sender_wallet.refresh() sender_wallet.refresh()
assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
@@ -1414,7 +1417,7 @@ class TransferTest():
assert receiver_wallet.get_balance().balance == expected_receiver_balance assert receiver_wallet.get_balance().balance == expected_receiver_balance
# Check a fresh incoming wallet with wallet files saved to disk and encrypted with password # Check a fresh incoming wallet with wallet files saved to disk and encrypted with password
restore_wallet(receiver_wallet, seeds[1], filename = 'test2', password = 'test_password') restore_wallet(receiver_wallet, seeds[1], 'test2', 'test_password')
receiver_wallet.setup_background_sync(background_sync_type = reuse_password, wallet_password = 'test_password') receiver_wallet.setup_background_sync(background_sync_type = reuse_password, wallet_password = 'test_password')
receiver_wallet.start_background_sync() receiver_wallet.start_background_sync()
receiver_wallet.refresh() receiver_wallet.refresh()
@@ -1424,7 +1427,7 @@ class TransferTest():
assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance) assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance)
# Close receiver's wallet while background sync is enabled then reopen # Close receiver's wallet while background sync is enabled then reopen
restore_wallet(receiver_wallet, seeds[1], filename = 'test2', password = 'test_password') restore_wallet(receiver_wallet, seeds[1], 'test2', 'test_password')
receiver_wallet.setup_background_sync(background_sync_type = reuse_password, wallet_password = 'test_password') receiver_wallet.setup_background_sync(background_sync_type = reuse_password, wallet_password = 'test_password')
receiver_wallet.start_background_sync() receiver_wallet.start_background_sync()
receiver_wallet.refresh() receiver_wallet.refresh()
@@ -1437,7 +1440,8 @@ class TransferTest():
assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance) assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance)
# Sanity check against incoming wallet restored at height 0 # Sanity check against incoming wallet restored at height 0
restore_wallet(receiver_wallet, seeds[1], restore_height = 0) receiver_wallet.close_wallet()
receiver_wallet.restore_deterministic_wallet(seed = seeds[1], restore_height = 0)
receiver_wallet.refresh() receiver_wallet.refresh()
assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance) assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance)
@@ -1554,62 +1558,5 @@ class TransferTest():
assert balance_info_0_999['blocks_to_unlock'] == 9 assert balance_info_0_999['blocks_to_unlock'] == 9
assert balance_info_0_999['time_to_unlock'] == 0 assert balance_info_0_999['time_to_unlock'] == 0
def check_pool_scanner(self):
daemon = Daemon()
print('Checking pool scanner')
# Sync first wallet
daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1)
self.wallet[0].refresh()
# Open second wallet with same seed as first
restore_wallet(self.wallet[1], seeds[0])
assert self.wallet[0].get_address().address == self.wallet[1].get_address().address
# Send to another wallet, spending from first wallet
dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': 1000000000000}
res = self.wallet[0].transfer([dst])
assert len(res.tx_hash) == 32*2
txid = res.tx_hash
assert res.fee > 0
fee = res.fee
# Sync both wallets
self.wallet[0].refresh()
self.wallet[1].refresh()
# Both wallets should be able to detect the spend tx in the pool
res_wallet0 = self.wallet[0].get_transfers()
res_wallet1 = self.wallet[1].get_transfers()
# After restoring, should still be able to detect the spend in the pool
restore_wallet(self.wallet[1], seed = seeds[0])
self.wallet[1].refresh()
res_wallet1_after_restore = self.wallet[1].get_transfers()
for res in [res_wallet0, res_wallet1, res_wallet1_after_restore]:
assert len(res.pending) == 1
assert not 'pool' in res or len(res.pool) == 0
assert not 'failed' in res or len(res.failed) == 0
e = res.pending[0]
assert e.txid == txid
assert e.payment_id in ['', '0000000000000000']
assert e.type == 'pending'
assert e.unlock_time == 0
assert e.subaddr_index.major == 0
assert e.subaddr_indices == [{'major': 0, 'minor': 0}]
assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert e.double_spend_seen == False
assert not 'confirmations' in e or e.confirmations == 0
assert e.amount == dst['amount']
assert e.fee == fee
# Mine a block to mine the tx and reset 2nd wallet
daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1)
restore_wallet(self.wallet[1], seeds[1])
self.wallet[1].refresh()
self.wallet[0].refresh()
if __name__ == '__main__': if __name__ == '__main__':
TransferTest().run_test() TransferTest().run_test()

View File

@@ -847,7 +847,7 @@ struct MyWalletListener : public Monero::WalletListener
// cv_receive.notify_one(); // cv_receive.notify_one();
} }
virtual void newBlock(uint64_t height) virtual void newBlock(uint64_t height, bool last)
{ {
// std::cout << "wallet: " << wallet->mainAddress() // std::cout << "wallet: " << wallet->mainAddress()
// <<", new block received, blockHeight: " << height << std::endl; // <<", new block received, blockHeight: " << height << std::endl;

View File

@@ -28,10 +28,6 @@
// //
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <condition_variable>
#include <mutex>
#include <thread>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "file_io_utils.h" #include "file_io_utils.h"
@@ -219,55 +215,3 @@ TEST(logging, empty_configurations_throws)
const el::Configurations cfg; const el::Configurations cfg;
EXPECT_ANY_THROW(log1.configure(cfg)); EXPECT_ANY_THROW(log1.configure(cfg));
} }
TEST(logging, deadlock)
{
std::mutex inner_mutex;
// 1. Thread 1 starts logger
// 2. Thread 2 grabs inner mutex shared across threads
// 3. Thread 2 logs
// 4. Thread 1 grabs inner mutex shared across threads
// 5. Thread 1 finishes logging
std::condition_variable cv1, cv2;
std::mutex mutex1, mutex2;
std::unique_lock<std::mutex> lock_until_t1_starts_logger(mutex1);
std::unique_lock<std::mutex> lock_until_t2_finishes_logging(mutex2);
bool t1_started_logger = false;
bool t2_finished_logging = false;
const auto thread1_func = [&]
{
const auto thread1_inner_func = [&]() -> std::string
{
t1_started_logger = true;
lock_until_t1_starts_logger.unlock();
cv1.notify_one();
cv2.wait(lock_until_t2_finishes_logging, [&]{return t2_finished_logging;});
std::lock_guard<std::mutex> guard(inner_mutex);
return "world!";
};
MGINFO("Hello, " << thread1_inner_func() << " - Sincerely, thread 1");
};
const auto thread2_func = [&]
{
cv1.wait(lock_until_t1_starts_logger, [&]{return t1_started_logger;});
{
std::lock_guard<std::mutex> guard(inner_mutex);
MGINFO("Hello, world! - Sincerely, thread 2");
}
t2_finished_logging = true;
lock_until_t2_finishes_logging.unlock();
cv2.notify_one();
};
std::thread t1(thread1_func);
std::thread t2(thread2_func);
t1.join();
t2.join();
}

View File

@@ -56,7 +56,7 @@ public:
void set_target_blockchain_height(uint64_t) {} void set_target_blockchain_height(uint64_t) {}
bool init(const boost::program_options::variables_map& vm) {return true ;} bool init(const boost::program_options::variables_map& vm) {return true ;}
bool deinit(){return true;} bool deinit(){return true;}
bool get_short_chain_history(std::list<crypto::hash>& ids, uint64_t& current_height) const { return true; } bool get_short_chain_history(std::list<crypto::hash>& ids) const { return true; }
bool have_block(const crypto::hash& id, int *where = NULL) const {return false;} bool have_block(const crypto::hash& id, int *where = NULL) const {return false;}
bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const {return false;} bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const {return false;}
void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;} void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;}