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"]
path = external/trezor-common
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"]
path = external/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/randomwow)
check_submodule(external/supercop)
check_submodule(external/polyseed)
check_submodule(external/utf8proc)
endif()
endif()
@@ -459,7 +461,7 @@ endif()
# elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*")
# 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)
cmake_policy(SET CMP0042 NEW)

View File

@@ -104,7 +104,7 @@ release-all:
release-static:
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:
@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 && 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:
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)

View File

@@ -1,44 +1,36 @@
package=boost
$(package)_version=1.90.0
package=boost
$(package)_version=1.69.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)_file_name=$(package)_$(subst .,_,$($(package)_version)).tar.gz
$(package)_sha256_hash=9a2c2819310839ea373f42d69e733c339b4e9a19deab6bfec448281554aa4dbb
$(package)_dependencies=libiconv
$(package)_patches=fix_aroptions.patch fix_arm_arch.patch
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
# Darwin (macOS) Defaults
$(package)_config_opts_darwin=target-os=darwin runtime-link=shared
# Windows Defaults
$(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static
# Architecture Specifics
$(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
# 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
$(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=shared
$(package)_config_opts_android=threadapi=pthread runtime-link=static target-os=android
$(package)_config_opts_darwin=--toolset=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)_toolset_$(host_os)=gcc
$(package)_archiver_$(host_os)=$($(package)_ar)
$(package)_toolset_darwin=darwin
$(package)_archiver_darwin=$($(package)_libtool)
$(package)_config_libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale
$(package)_cxxflags=-std=c++11
$(package)_cxxflags_linux=-fPIC
$(package)_cxxflags_freebsd=-fPIC
endef
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
endef
@@ -52,4 +44,4 @@ endef
define $(package)_stage_cmds
./b2 -d0 -j4 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) install
endef
endef

View File

@@ -1,9 +1,10 @@
package=hidapi
$(package)_version=0.15.0
$(package)_version=0.13.1
$(package)_download_path=https://github.com/libusb/hidapi/archive/refs/tags
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=5d84dec684c27b97b921d2f3b73218cb773cf4ea915caee317ac8fc73cef8136
$(package)_sha256_hash=476a2c9a4dc7d1fc97dd223b84338dbea3809a84caea2dcd887d9778725490e3
$(package)_linux_dependencies=libusb eudev
$(package)_patches=missing_win_include.patch
define $(package)_set_vars
$(package)_config_opts=--enable-static --disable-shared
@@ -16,7 +17,7 @@ $(package)_config_opts_linux+=--with-pic
endef
define $(package)_preprocess_cmds
./bootstrap
patch -p1 < $($(package)_patch_dir)/missing_win_include.patch && ./bootstrap
endef
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
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
#include <sstream>
#include <string>
#include "easylogging++.h"
@@ -41,15 +40,9 @@
#define MAX_LOG_FILE_SIZE 104850000 // 100 MB - 7600 bytes
#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 { \
if (el::Loggers::allowed(level, cat)) { \
LOG_TO_STRING(x); \
el::base::Writer(level, color, __FILE__, __LINE__, ELPP_FUNC, type).construct(cat) << str; \
el::base::Writer(level, color, __FILE__, __LINE__, ELPP_FUNC, type).construct(cat) << x; \
} \
} while (0)
@@ -98,8 +91,7 @@
do { \
if (el::Loggers::allowed(level, cat)) { \
init; \
LOG_TO_STRING(x); \
el::base::Writer(level, color, __FILE__, __LINE__, ELPP_FUNC, type).construct(cat) << str; \
el::base::Writer(level, color, __FILE__, __LINE__, ELPP_FUNC, type).construct(cat) << x; \
} \
} while(0)
#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 <utility>
#include <list>
#include <cstdint>
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.http"

View File

@@ -34,6 +34,7 @@
#include <string>
#include "memwipe.h"
#include "fnv1.h"
#include "serialization/keyvalue_serialization.h"
namespace epee
{
@@ -75,6 +76,12 @@ namespace epee
bool operator!=(const wipeable_string &other) const noexcept { return buffer != other.buffer; }
wipeable_string &operator=(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:
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;
}
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.
```bash
VERSION=v0.18.4.5
VERSION=v0.18.4.2
./dockrun.sh $VERSION
```

View File

@@ -133,7 +133,7 @@ Common setup part:
su - gitianuser
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.

View File

@@ -71,3 +71,5 @@ add_subdirectory(db_drivers)
add_subdirectory(easylogging++)
add_subdirectory(qrcodegen)
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
}
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) {
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
@@ -3065,9 +3057,8 @@ void Writer::triggerDispatch(void) {
}
if (m_proceed && m_level == Level::Fatal
&& !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)
<< str;
<< "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]";
std::stringstream reasonStream;
reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]"
<< " 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
static std::string currentHost(void);
/// @brief Whether or not the named terminal supports colors
static bool termSupportsColor(std::string& term);
/// @brief Whether or not the process's current terminal supports colors
/// @brief Whether or not terminal supports colors
static bool termSupportsColor(void);
};
/// @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();
}
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 (m_proceed) {
m_messageBuilder << log;
@@ -3283,6 +3283,26 @@ class Writer : base::NoCopy {
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() {
return true;
}
@@ -3599,9 +3619,8 @@ class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback {
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,
m_data->loggerId().c_str()) << str;
m_data->loggerId().c_str()) << ss.str();
}
private:
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(blockchain_db)
add_subdirectory(mnemonics)
add_subdirectory(polyseed)
add_subdirectory(rpc)
if(NOT IOS)
add_subdirectory(serialization)

View File

@@ -71,6 +71,7 @@ target_link_libraries(cryptonote_basic
checkpoints
cryptonote_format_utils_basic
device
polyseed_wrapper
${Boost_DATE_TIME_LIBRARY}
${Boost_PROGRAM_OPTIONS_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)
{
// 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();
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_spend_secret_key.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
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 (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_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)
@@ -255,6 +261,21 @@ DISABLE_VS_WARNINGS(4244 4345)
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)
{
m_keys.m_account_address.m_spend_public_key = spend_public_key;

View File

@@ -33,6 +33,7 @@
#include "cryptonote_basic.h"
#include "crypto/crypto.h"
#include "serialization/keyvalue_serialization.h"
#include "polyseed/polyseed.hpp"
namespace cryptonote
{
@@ -45,6 +46,8 @@ namespace cryptonote
std::vector<crypto::secret_key> m_multisig_keys;
hw::device *m_device = &hw::get_device("default");
crypto::chacha_iv m_encryption_iv;
crypto::secret_key m_polyseed;
epee::wipeable_string m_passphrase; // Only used with polyseed
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_account_address)
@@ -53,6 +56,8 @@ namespace cryptonote
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
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_FORCE(m_polyseed)
KV_SERIALIZE(m_passphrase)
END_KV_SERIALIZE_MAP()
void encrypt(const crypto::chacha_key &key);
@@ -79,6 +84,7 @@ namespace cryptonote
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_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);
const account_keys& get_keys() const;
std::string get_public_address_str(network_type nettype) const;

View File

@@ -223,6 +223,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),
//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

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...
*
*/
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__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
uint64_t i = 0;
uint64_t current_multiplier = 1;
uint64_t sz = current_height = m_db->height();
uint64_t sz = m_db->height();
if(!sz)
return true;

View File

@@ -445,11 +445,10 @@ namespace cryptonote
* 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 current_height the current blockchain height, return-by-reference
*
* @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

View File

@@ -1302,23 +1302,20 @@ namespace cryptonote
NOTIFY_NEW_FLUFFY_BLOCK::request arg{};
arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height();
std::vector<crypto::hash> missed_txs;
for (const auto &tx_hash : b.tx_hashes)
{
if (m_blockchain_storage.have_tx(tx_hash))
continue;
missed_txs.push_back(tx_hash);
}
std::vector<cryptonote::blobdata> txs;
m_blockchain_storage.get_transactions_blobs(b.tx_hashes, txs, missed_txs);
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");
return true;
}
CHECK_AND_ASSERT_MES(!missed_txs.size(), false, "can't find some transactions in found block:" << get_block_hash(b)
<< " b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size());
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());
block_to_blob(b, arg.b.block);
// Relay an empty fluffy block
arg.b.txs.clear();
//pack transactions
for(auto& tx: txs)
arg.b.txs.push_back({tx, crypto::null_hash});
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);
}
//-----------------------------------------------------------------------------------------------
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)

View File

@@ -580,7 +580,7 @@ namespace cryptonote
*
* @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

View File

@@ -344,8 +344,7 @@ namespace cryptonote
}
tvc.m_verifivation_failed = false;
if (tvc.m_added_to_pool)
m_txpool_weight += tx_weight;
m_txpool_weight += tx_weight;
++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);
if (blocks.empty())
return blockchain_height;
uint64_t covered_until = blockchain_height;
uint64_t last_needed_height = blockchain_height;
bool first = true;
for (const auto &span: blocks)
{
// Ignore spans entirely below current chain height
const uint64_t span_end = span.start_block_height + span.nblocks - 1;
if (span_end < blockchain_height)
if (span.start_block_height + span.nblocks - 1 < blockchain_height)
continue;
// If this span starts after what we already have/scheduled, we found the first gap
if (span.start_block_height > covered_until)
return covered_until;
// 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);
if (span.start_block_height != last_needed_height || (first && span.blocks.empty()))
return last_needed_height;
last_needed_height = span.start_block_height + span.nblocks;
first = false;
}
return covered_until;
return last_needed_height;
}
void block_queue::print() const

View File

@@ -44,7 +44,6 @@
#include "net/network_throttle-detail.hpp"
#include "common/pruning.h"
#include "common/util.h"
#include "misc_log_ex.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.cn"
@@ -56,10 +55,8 @@
const char *cat = "net.p2p.msg"; \
if (ELPP->vRegistry()->allowed(level, cat)) { \
init; \
if (test) { \
LOG_TO_STRING(x); \
el::base::Writer(level, el::Color::Default, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(cat) << str; \
} \
if (test) \
el::base::Writer(level, el::Color::Default, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(cat) << x; \
} \
} while(0)
@@ -274,7 +271,8 @@ namespace cryptonote
{
NOTIFY_REQUEST_CHAIN::request r = {};
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(?)
r.prune = m_sync_pruned_blocks;
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_state = cryptonote_connection_context::state_synchronizing;
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(?)
r.prune = m_sync_pruned_blocks;
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
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");
// 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)
{
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");
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);
if (ins == INS_GET_KEY && p1 == IO_SECRET_KEY) {
// 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 {
this->exchange();
}
@@ -528,7 +528,6 @@ namespace hw {
{0x2c97, 0x0005, 0, 0xffa0},
{0x2c97, 0x0006, 0, 0xffa0},
{0x2c97, 0x0007, 0, 0xffa0},
{0x2c97, 0x0008, 0, 0xffa0},
};
bool device_ledger::connect(void) {
@@ -619,14 +618,15 @@ namespace hw {
send_simple(INS_GET_KEY, 0x02);
//View key is retrievied, if allowed, to speed up blockchain parsing
crypto::secret_key view_secret_key;
memmove(view_secret_key.data, this->buffer_recv+0, 32);
CHECK_AND_ASSERT_THROW_MES(!is_fake_view_key(view_secret_key), "Key export rejected on device.");
this->viewkey = view_secret_key;
this->has_view_key = true;
memmove(this->viewkey.data, this->buffer_recv+0, 32);
if (is_fake_view_key(this->viewkey)) {
MDEBUG("Have Not view key");
this->has_view_key = false;
} else {
MDEBUG("Have view key");
this->has_view_key = true;
}
#ifdef DEBUG_HWDEVICE
send_simple(INS_GET_KEY, 0x04);
memmove(dbg_viewkey.data, this->buffer_recv+0, 32);

View File

@@ -177,8 +177,8 @@ namespace hw {
HMACmap hmac_map;
// To speed up blockchain parsing the view key maybe handle here.
crypto::secret_key viewkey = crypto::null_skey;
bool has_view_key = false;
crypto::secret_key viewkey;
bool has_view_key;
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."));
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::on_command, this, &simple_wallet::transfer, _1),
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",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _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;
}
//----------------------------------------------------------------------------------------------------
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)
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
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())
{

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);
//----------------- 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_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);

View File

@@ -47,6 +47,7 @@
#include <boost/locale.hpp>
#include <boost/filesystem.hpp>
#include <vector>
using namespace std;
using namespace cryptonote;
@@ -175,7 +176,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
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
// 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) {
// LOG_PRINT_L3(__FUNCTION__ << ": new block. height: " << height);
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
virtual void on_lw_new_block(uint64_t height)
virtual void on_lw_new_block(uint64_t height, bool last)
{
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;
}
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
{
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
}
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
{
return m_wallet->get_seed_language();
@@ -1658,7 +1730,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat
// - unconfirmed_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();
@@ -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) {
break;
}
@@ -1741,11 +1826,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
if (amount) {
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count,
adjusted_priority,
extra, subaddr_account, subaddr_indices);
extra, subaddr_account, subaddr_indices, preferred_input_list);
} else {
transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count,
adjusted_priority,
extra, subaddr_account, subaddr_indices);
extra, subaddr_account, subaddr_indices, preferred_input_list);
}
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::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()
@@ -1955,6 +2040,56 @@ AddressBook *WalletImpl::addressBook()
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()
{
return m_subaddress.get();

View File

@@ -79,9 +79,19 @@ public:
bool recoverFromDevice(const std::string &path,
const std::string &password,
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;
bool close(bool store = true);
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;
void setSeedLanguage(const std::string &arg) override;
// void setListener(Listener *) {}
@@ -156,12 +166,14 @@ public:
optional<std::vector<uint64_t>> amount, uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
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,
optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
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;
bool submitTransaction(const std::string &fileName) override;
virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override;
@@ -183,6 +195,9 @@ public:
PendingTransaction::Priority priority) const override;
virtual TransactionHistory * history() 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 SubaddressAccount * subaddressAccount() override;
virtual void setListener(WalletListener * l) override;

View File

@@ -32,6 +32,7 @@
#include <string>
#include <utility>
#include <vector>
#include <list>
#include <set>
@@ -261,6 +262,35 @@ struct AddressBook
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 {
public:
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
* @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;
@@ -706,6 +737,10 @@ struct Wallet
static void warning(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)
*/
@@ -849,7 +884,8 @@ struct Wallet
optional<std::vector<uint64_t>> amount, uint32_t mixin_count,
PendingTransaction::Priority = PendingTransaction::Priority_Low,
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
@@ -868,7 +904,8 @@ struct Wallet
optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority = PendingTransaction::Priority_Low,
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.
@@ -980,6 +1017,9 @@ struct Wallet
virtual TransactionHistory * history() = 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 SubaddressAccount * subaddressAccount() = 0;
virtual void setListener(WalletListener *) = 0;
@@ -1298,6 +1338,27 @@ struct WalletManager
uint64_t kdf_rounds = 1,
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
* \param wallet previously opened / created wallet instance
@@ -1364,6 +1425,9 @@ struct WalletManager
//! returns current blockchain target height
virtual uint64_t blockchainTargetHeight() = 0;
//! returns current blockchain and target height
virtual std::pair<uint64_t, uint64_t> blockchainAndTargetHeight() = 0;
//! returns current network difficulty
virtual uint64_t networkDifficulty() = 0;

View File

@@ -156,6 +156,15 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path,
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)
{
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;
}
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()
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;

View File

@@ -75,6 +75,16 @@ public:
const std::string &subaddressLookahead = "",
uint64_t kdf_rounds = 1,
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;
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;
@@ -85,6 +95,7 @@ public:
bool connected(uint32_t *version = NULL) override;
uint64_t blockchainHeight() override;
uint64_t blockchainTargetHeight() override;
std::pair<uint64_t, uint64_t> blockchainAndTargetHeight() override;
uint64_t networkDifficulty() override;
double miningHashRate() override;
uint64_t blockTarget() override;

View File

@@ -92,6 +92,7 @@ using namespace epee;
#include "device/device_cold.hpp"
#include "device_trezor/device_trezor.hpp"
#include "net/socks_connect.h"
#include "polyseed/include/polyseed.h"
extern "C"
{
@@ -927,11 +928,6 @@ bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pend
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 construction_data = ptx.construction_data;
@@ -958,6 +954,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t 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)
{
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_pool_info_query_time(0),
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()));
}
@@ -1462,10 +1469,25 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab
key = cryptonote::encrypt_key(key, passphrase);
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;
}
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;
}
//----------------------------------------------------------------------------------------------------
@@ -2103,12 +2125,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const
return false;
}
void wallet2::freeze(const crypto::public_key &pk)
{
freeze(get_transfer_details(pk));
}
//----------------------------------------------------------------------------------------------------
void wallet2::freeze(const crypto::key_image &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)
{
thaw(get_transfer_details(ki));
@@ -2119,6 +2150,18 @@ bool wallet2::frozen(const crypto::key_image &ki) const
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
{
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;
if (!pool)
{
boost::unique_lock<boost::shared_mutex> lock(m_transfers_mutex);
m_transfers.push_back(transfer_details{});
transfer_details& td = m_transfers.back();
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;
if (!pool)
{
boost::unique_lock<boost::shared_mutex> lock(m_transfers_mutex);
transfer_details &td = m_transfers[kit->second];
td.m_block_height = height;
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");
subaddr_account = td.m_subaddr_index.major;
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)
{
LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid);
set_spent(it->second, height);
if (!ignore_callbacks && 0 != m_callback)
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;
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,
[&subaddr_account] (uint64_t acc, const std::pair<cryptonote::subaddress_index, uint64_t>& p)
{
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);
// 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)
{
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);
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;
}
}
@@ -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,
// 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_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;
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);
}
//----------------------------------------------------------------------------------------------------
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,
"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);
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
@@ -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 cryptonote::block &bl = parsed_blocks[i].block;
bool last = i == blocks.size() - 1;
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;
}
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));
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
{
@@ -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
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
constexpr const std::chrono::seconds tx_propagation_timeout{500};
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;
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
{
@@ -3743,7 +3748,23 @@ void wallet2::process_unconfirmed_transfer(bool incremental, const crypto::hash
tx_details.m_state = wallet2::unconfirmed_transfer_details::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)
{ // FIXME: this isn't right, but simplewallet just logs that we got a block.
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])
@@ -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)
{
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;
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
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;
blocks_fetched = 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);
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)
{
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_enable_multisig = false;
m_allow_mismatched_daemon_version = false;
m_polyseed = false;
m_custom_background_key = boost::none;
}
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);
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
crypto::chacha_key custom_background_key;
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;
}
/*!
* \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
* 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;
}
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.
// 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.
// If we use the approximate height we subtract one month as
// a safety margin.
height = get_approximate_blockchain_height();
height = get_approximate_blockchain_height(time);
uint64_t target_height = get_daemon_blockchain_target_height(err);
if (err.empty()) {
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;
}
//----------------------------------------------------------------------------------------------------
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_out = 0;
for (const auto &d: dests)
@@ -7649,7 +7708,7 @@ void wallet2::commit_tx(pending_tx& ptx)
for(size_t idx: ptx.selected_transfers)
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)
{
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");
}
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;
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)
{
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 (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)
{
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 (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)
{
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)
{
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
// not generate spurious change in all txes, thus decreasing the instantaneous
// 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
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)
{
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)
{
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.
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);
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())
{
string s;
@@ -11819,7 +11890,7 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, c
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_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)
{
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)
{
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;
}
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;
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)
{
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;
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");
}
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;
uint64_t height_min = 0;
uint64_t height_max = get_daemon_blockchain_height(err) - 1;

View File

@@ -72,6 +72,7 @@
#include "message_store.h"
#include "wallet_light_rpc.h"
#include "wallet_rpc_helpers.h"
#include "polyseed/polyseed.hpp"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
@@ -138,7 +139,7 @@ private:
{
public:
// 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_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) {}
@@ -146,7 +147,7 @@ private:
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; }
// 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_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) {}
@@ -921,6 +922,20 @@ private:
void generate(const std::string& wallet_, const epee::wipeable_string& password,
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.
* \param wallet_ Name of wallet file
@@ -1093,6 +1108,15 @@ private:
bool is_deterministic() 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.
*/
@@ -1196,8 +1220,8 @@ private:
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
std::vector<wallet2::pending_tx> create_transactions_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, 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_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;
@@ -1549,6 +1573,7 @@ private:
uint64_t get_num_rct_outputs();
size_t get_num_transfer_details() const { return m_transfers.size(); }
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();
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.
*/
uint64_t get_approximate_blockchain_height() const;
uint64_t estimate_blockchain_height();
uint64_t get_approximate_blockchain_height(uint64_t time = 0) const;
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(const std::function<bool(const transfer_details &td)> &f);
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);
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();
@@ -1777,7 +1803,9 @@ private:
void freeze(size_t idx);
void thaw(size_t idx);
bool frozen(size_t idx) const;
void freeze(const crypto::public_key &pk);
void freeze(const crypto::key_image &ki);
void thaw(const crypto::public_key &pk);
void thaw(const crypto::key_image &ki);
bool frozen(const crypto::key_image &ki) 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; }
boost::shared_mutex m_transfers_mutex;
private:
/*!
* \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 process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL, bool ignore_callbacks = false);
bool should_skip_block(const cryptonote::block &b, uint64_t height) const;
void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
void 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);
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;
@@ -1875,7 +1905,7 @@ private:
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_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 check_genesis(const crypto::hash& genesis_hash) const; //throws
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);
uint64_t get_dynamic_base_fee_estimate();
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_unspent(size_t idx);
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). */
bool is_old_file_format; /*!< Whether the wallet file is of an old file format */
bool m_watch_only; /*!< no spend key */
bool m_polyseed;
bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */
uint32_t m_multisig_threshold;
std::vector<crypto::public_key> m_multisig_signers;

View File

@@ -1261,7 +1261,7 @@ namespace tools
{
uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
uint32_t priority = m_wallet->adjust_priority(req.priority);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, 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())
{

View File

@@ -29,7 +29,6 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import print_function
import json
import random
"""Test multisig transfers
@@ -409,37 +408,10 @@ class MultisigTest():
assert len(res.tx_hash_list) == 1
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)):
# 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()
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 knows_key_image else 0)
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['out'] if 'out' in res else []) if x.txid == txid]) == 0
daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1)
@@ -535,13 +507,9 @@ class MultisigTest():
txid = res.tx_hash_list[0]
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()
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
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['out'] if 'out' in res else []) if x.txid == txid]) == 0
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
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():
def run_test(self):
self.reset()
@@ -89,7 +78,6 @@ class TransferTest():
self.check_background_sync()
self.check_background_sync_reorg_recovery()
self.check_subaddress_lookahead()
self.check_pool_scanner()
def reset(self):
print('Resetting blockchain')
@@ -277,7 +265,6 @@ class TransferTest():
assert len(res.multisig_txset) == 0
assert len(res.unsigned_txset) == 0
tx_blob = res.tx_blob
running_balances[0] -= 1000000000000 + fee
res = daemon.send_raw_transaction(tx_blob)
assert res.not_relayed == False
@@ -319,6 +306,7 @@ class TransferTest():
daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1)
res = daemon.getlastblockheader()
running_balances[0] -= 1000000000000 + fee
running_balances[0] += res.block_header.reward
self.wallet[1].refresh()
running_balances[1] += 1000000000000
@@ -1166,6 +1154,14 @@ class TransferTest():
except: invalid_password = True
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):
diff_transfers(wallet.get_transfers(), expected_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
# properly pick up txs which do not have a change output back to sender.
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()
res = sender_wallet.incoming_transfers(transfer_type = 'available')
unlocked = [x for x in res.transfers if x.unlocked and x.amount > 0]
@@ -1194,7 +1193,10 @@ class TransferTest():
# set up receiver_wallet
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()
res = receiver_wallet.get_transfers()
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
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'
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()
@@ -1277,7 +1279,7 @@ class TransferTest():
# Close wallet while background syncing, then reopen
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'
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()
@@ -1291,7 +1293,7 @@ class TransferTest():
# Close wallet while syncing normally, then reopen
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'
sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
sender_wallet.refresh()
@@ -1303,7 +1305,7 @@ class TransferTest():
# Create background cache using custom password, then use it to sync, then reopen main wallet
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.keys')
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)
# 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.refresh()
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)
# 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.keys')
same_password = False
@@ -1340,7 +1342,7 @@ class TransferTest():
# Turn off background sync
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'
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:
@@ -1365,7 +1367,8 @@ class TransferTest():
sender_wallet.open_wallet('test1', password = 'test_password')
# 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()
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
# 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.start_background_sync()
receiver_wallet.refresh()
@@ -1424,7 +1427,7 @@ class TransferTest():
assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance)
# 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.start_background_sync()
receiver_wallet.refresh()
@@ -1437,7 +1440,8 @@ class TransferTest():
assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance)
# 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()
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['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__':
TransferTest().run_test()

View File

@@ -847,7 +847,7 @@ struct MyWalletListener : public Monero::WalletListener
// cv_receive.notify_one();
}
virtual void newBlock(uint64_t height)
virtual void newBlock(uint64_t height, bool last)
{
// std::cout << "wallet: " << wallet->mainAddress()
// <<", 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
#include <condition_variable>
#include <mutex>
#include <thread>
#include <boost/filesystem.hpp>
#include "gtest/gtest.h"
#include "file_io_utils.h"
@@ -219,55 +215,3 @@ TEST(logging, empty_configurations_throws)
const el::Configurations 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) {}
bool init(const boost::program_options::variables_map& vm) {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_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;}